UNPKG

@segment/analytics-next

Version:

Analytics Next (aka Analytics 2.0) is the latest version of Segment’s JavaScript SDK - enabling you to send your data to any tool without having to learn, test, or use a new API every time.

145 lines (119 loc) 3.09 kB
import { fetch } from '../../lib/fetch' import { version } from '../../generated/version' import { getVersionType } from '../../lib/version-type' import { SEGMENT_API_HOST } from '../constants' export interface MetricsOptions { host?: string sampleRate?: number flushTimer?: number maxQueueSize?: number protocol?: 'http' | 'https' } /** * Type expected by the segment metrics API endpoint */ type RemoteMetric = { type: 'Counter' metric: string value: 1 tags: { library: string library_version: string [key: string]: string } } const createRemoteMetric = ( metric: string, tags: string[], versionType: 'web' | 'npm' ): RemoteMetric => { const formattedTags = tags.reduce((acc, t) => { const [k, v] = t.split(':') acc[k] = v return acc }, {} as Record<string, string>) return { type: 'Counter', metric, value: 1, tags: { ...formattedTags, library: 'analytics.js', library_version: versionType === 'web' ? `next-${version}` : `npm:next-${version}`, }, } } function logError(err: unknown): void { console.error('Error sending segment performance metrics', err) } export class RemoteMetrics { private host: string private flushTimer: number private maxQueueSize: number private protocol: string sampleRate: number queue: RemoteMetric[] constructor(options?: MetricsOptions) { this.host = options?.host ?? SEGMENT_API_HOST this.sampleRate = options?.sampleRate ?? 1 this.flushTimer = options?.flushTimer ?? 30 * 1000 /* 30s */ this.maxQueueSize = options?.maxQueueSize ?? 20 this.protocol = options?.protocol ?? 'https' this.queue = [] if (this.sampleRate > 0) { let flushing = false const run = (): void => { if (flushing) { return } flushing = true this.flush().catch(logError) flushing = false setTimeout(run, this.flushTimer) } run() } } increment(metric: string, tags: string[]): void { // All metrics are part of an allow list in Tracking API if (!metric.includes('analytics_js.')) { return } // /m doesn't like empty tags if (tags.length === 0) { return } if (Math.random() > this.sampleRate) { return } if (this.queue.length >= this.maxQueueSize) { return } const remoteMetric = createRemoteMetric(metric, tags, getVersionType()) this.queue.push(remoteMetric) if (metric.includes('error')) { this.flush().catch(logError) } } async flush(): Promise<void> { if (this.queue.length <= 0) { return } await this.send().catch((error) => { logError(error) this.sampleRate = 0 }) } private async send(): Promise<Response> { const payload = { series: this.queue } this.queue = [] const headers = { 'Content-Type': 'text/plain' } const url = `${this.protocol}://${this.host}/m` return fetch(url, { headers, body: JSON.stringify(payload), method: 'POST', }) } }