{"version":3,"file":"telemetry.mjs","names":[],"sources":["../../../src/domain/telemetry/telemetry.ts"],"sourcesContent":["import { clocksNow } from '@openobserve/js-core/time'\nimport { getDebugMode, combine } from '@openobserve/js-core/util'\nimport type { Hook } from '@openobserve/js-core/assembly'\nimport type { RecursivePartial } from '@openobserve/js-core/util'\nimport { DISCARDED } from '@openobserve/js-core/assembly'\nimport type { Context } from '../../tools/serialisation/context'\nimport { NO_ERROR_STACK_PRESENT_MESSAGE, isError } from '../error/error'\nimport { toStackTraceString } from '../../tools/stackTrace/handlingStack'\nimport { getExperimentalFeatures } from '../../tools/experimentalFeatures'\nimport { createEndpointBuilder, createReplicaEndpointBuilder } from '../configuration'\nimport type { Configuration } from '../configuration'\nimport { buildTags } from '../tags'\nimport { INTAKE_SITE_STAGING, INTAKE_SITE_US1_FED, INTAKE_SITE_US2_FED } from '../intakeSites'\nimport { BufferedObservable, Observable } from '../../tools/observable'\nimport { startMonitorErrorCollection } from '../../tools/monitor'\nimport { display } from '../../tools/display'\nimport { sendToExtension } from '../../tools/sendToExtension'\nimport { performDraw } from '../../tools/utils/numberUtils'\nimport { jsonStringify } from '../../tools/serialisation/jsonStringify'\nimport { NonErrorPrefix } from '../error/error.types'\nimport type { StackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'\nimport { getConnectivity } from '../connectivity'\nimport {\n  canUseEventBridge,\n  createFlushController,\n  createHttpRequest,\n  getEventBridge,\n  createBatch,\n} from '../../transport'\nimport { createIdentityEncoder } from '../../tools/encoder'\nimport { createPageMayExitObservable } from '../../browser/pageMayExitObservable'\nimport { globalObject, isWorkerEnvironment } from '../../tools/globalObject'\nimport { noop } from '../../tools/utils/functionUtils'\nimport type { TelemetryEvent } from './telemetryEvent.types'\nimport type {\n  RawTelemetryConfiguration,\n  RawTelemetryEvent,\n  RuntimeEnvInfo,\n  RawTelemetryUsage,\n} from './rawTelemetryEvent.types'\nimport { StatusType, TelemetryType } from './rawTelemetryEvent.types'\n\n// replaced at build time\ndeclare const __BUILD_ENV__SDK_VERSION__: string\ndeclare const __BUILD_ENV__SDK_SETUP__: string\n\nconst ALLOWED_FRAME_URLS = [\n  'https://www.datadoghq-browser-agent.com',\n  'https://www.datad0g-browser-agent.com',\n  'https://d3uc069fcn7uxw.cloudfront.net',\n  'https://d20xtzwzcl0ceb.cloudfront.net',\n  'http://localhost',\n  '<anonymous>',\n]\n\nexport const enum TelemetryService {\n  LOGS = 'browser-logs-sdk',\n  RUM = 'browser-rum-sdk',\n}\n\nexport interface Telemetry {\n  stop: () => void\n  enabled: boolean\n  metricsEnabled: boolean\n}\n\nexport const enum TelemetryMetrics {\n  CUSTOMER_DATA_METRIC_NAME = 'Customer data measures',\n  REMOTE_CONFIGURATION_METRIC_NAME = 'remote configuration metrics',\n  RECORDER_INIT_METRICS_TELEMETRY_NAME = 'Recorder init metrics',\n  SEGMENT_METRICS_TELEMETRY_NAME = 'Segment network request metrics',\n  INITIAL_VIEW_METRICS_TELEMETRY_NAME = 'Initial view metrics',\n  SESSION_MANAGER_INIT_METRICS_TELEMETRY_NAME = 'Session manager init metrics',\n}\n\nconst METRIC_SAMPLE_RATE = 1\n\nconst TELEMETRY_EXCLUDED_SITES: string[] = [INTAKE_SITE_US1_FED, INTAKE_SITE_US2_FED]\nconst MAX_TELEMETRY_EVENTS_PER_PAGE = 15\n\nlet telemetryObservable: BufferedObservable<{ rawEvent: RawTelemetryEvent; metricName?: string }> | undefined\n\nexport function getTelemetryObservable() {\n  if (!telemetryObservable) {\n    telemetryObservable = new BufferedObservable(100)\n  }\n  return telemetryObservable\n}\n\nexport function startTelemetry(\n  telemetryService: TelemetryService,\n  configuration: Configuration,\n  hook: Hook<any, any>\n): Telemetry {\n  const observable = new Observable<TelemetryEvent & Context>()\n  const { enabled, metricsEnabled } = startTelemetryCollection(telemetryService, configuration, hook, observable)\n  const { stop } = startTelemetryTransport(configuration, observable)\n  return {\n    stop,\n    enabled,\n    metricsEnabled,\n  }\n}\n\nexport function startTelemetryCollection(\n  telemetryService: TelemetryService,\n  configuration: Configuration,\n  hook: Hook<any, any>,\n  observable: Observable<TelemetryEvent & Context>,\n  metricSampleRate = METRIC_SAMPLE_RATE,\n  maxTelemetryEventsPerPage = MAX_TELEMETRY_EVENTS_PER_PAGE\n) {\n  const alreadySentEventsByKind: Record<string, Set<string>> = {}\n\n  const telemetryEnabled =\n    !TELEMETRY_EXCLUDED_SITES.includes(configuration.site) && performDraw(configuration.telemetrySampleRate)\n\n  const telemetryEnabledPerType = {\n    [TelemetryType.LOG]: telemetryEnabled,\n    [TelemetryType.CONFIGURATION]: telemetryEnabled && performDraw(configuration.telemetryConfigurationSampleRate),\n    [TelemetryType.USAGE]: telemetryEnabled && performDraw(configuration.telemetryUsageSampleRate),\n    // not an actual \"type\" but using a single draw for all metrics\n    metric: telemetryEnabled && performDraw(metricSampleRate),\n  }\n\n  const runtimeEnvInfo = getRuntimeEnvInfo()\n  const telemetryObservable = getTelemetryObservable()\n  telemetryObservable.subscribe(({ rawEvent, metricName }) => {\n    if ((metricName && !telemetryEnabledPerType['metric']) || !telemetryEnabledPerType[rawEvent.type!]) {\n      return\n    }\n\n    const kind = metricName || (rawEvent.status as string | undefined) || rawEvent.type!\n    let alreadySentEvents = alreadySentEventsByKind[kind]\n    if (!alreadySentEvents) {\n      alreadySentEvents = alreadySentEventsByKind[kind] = new Set()\n    }\n\n    if (alreadySentEvents.size >= maxTelemetryEventsPerPage) {\n      return\n    }\n\n    const stringifiedEvent = jsonStringify(rawEvent)!\n    if (alreadySentEvents.has(stringifiedEvent)) {\n      return\n    }\n\n    const defaultTelemetryEventAttributes = hook.trigger({\n      startTime: clocksNow().relative,\n    })\n\n    if (defaultTelemetryEventAttributes === DISCARDED) {\n      return\n    }\n    const event = toTelemetryEvent(\n      defaultTelemetryEventAttributes as RecursivePartial<TelemetryEvent>,\n      telemetryService,\n      rawEvent,\n      runtimeEnvInfo\n    )\n    observable.notify(event)\n    sendToExtension('telemetry', event)\n    alreadySentEvents.add(stringifiedEvent)\n  })\n  telemetryObservable.unbuffer()\n\n  startMonitorErrorCollection(addTelemetryError)\n\n  return {\n    enabled: telemetryEnabled,\n    metricsEnabled: telemetryEnabledPerType['metric'],\n  }\n\n  function toTelemetryEvent(\n    defaultTelemetryEventAttributes: RecursivePartial<TelemetryEvent>,\n    telemetryService: TelemetryService,\n    rawEvent: RawTelemetryEvent,\n    runtimeEnvInfo: RuntimeEnvInfo\n  ): TelemetryEvent & Context {\n    const clockNow = clocksNow()\n\n    const event = {\n      type: 'telemetry' as const,\n      date: clockNow.timeStamp,\n      service: telemetryService,\n      version: __BUILD_ENV__SDK_VERSION__,\n      source: 'browser' as const,\n      _o2: {\n        format_version: 2 as const,\n      },\n      telemetry: combine(rawEvent, {\n        runtime_env: runtimeEnvInfo,\n        connectivity: getConnectivity(),\n        sdk_setup: __BUILD_ENV__SDK_SETUP__,\n      }) as TelemetryEvent['telemetry'],\n      o2tags: buildTags(configuration).join(','),\n      experimental_features: Array.from(getExperimentalFeatures()),\n    }\n\n    return combine(event, defaultTelemetryEventAttributes) as TelemetryEvent & Context\n  }\n}\n\nexport function startTelemetryTransport(\n  configuration: Configuration,\n  telemetryObservable: Observable<TelemetryEvent & Context>\n) {\n  const cleanupTasks: Array<() => void> = []\n  if (canUseEventBridge()) {\n    const bridge = getEventBridge<'internal_telemetry', TelemetryEvent>()!\n    const telemetrySubscription = telemetryObservable.subscribe((event) => bridge.send('internal_telemetry', event))\n    cleanupTasks.push(telemetrySubscription.unsubscribe)\n  } else {\n    const endpoints = [createEndpointBuilder(configuration, 'rum')]\n    const replicaEndpoint = createReplicaEndpointBuilder(configuration, 'rum')\n    if (replicaEndpoint && isTelemetryReplicationAllowed(configuration)) {\n      endpoints.push(replicaEndpoint)\n    }\n    const telemetryBatch = createBatch({\n      encoder: createIdentityEncoder(),\n      request: createHttpRequest(\n        endpoints,\n        // Ignore transport errors for telemetry\n        noop\n      ),\n      flushController: createFlushController({\n        pageMayExitObservable: createPageMayExitObservable(),\n\n        // We don't use an actual session expire observable here, to make telemetry collection\n        // independent of the session. This allows to start and send telemetry events earlier.\n        sessionExpireObservable: new Observable(),\n      }),\n    })\n    cleanupTasks.push(telemetryBatch.stop)\n    const telemetrySubscription = telemetryObservable.subscribe(telemetryBatch.add)\n    cleanupTasks.push(telemetrySubscription.unsubscribe)\n  }\n\n  return {\n    stop: () => cleanupTasks.forEach((task) => task()),\n  }\n}\n\nfunction getRuntimeEnvInfo(): RuntimeEnvInfo {\n  return {\n    is_local_file: globalObject.location?.protocol === 'file:',\n    is_worker: isWorkerEnvironment,\n  }\n}\n\nexport function resetTelemetry() {\n  telemetryObservable = undefined\n}\n\n/**\n * Avoid mixing telemetry events from different data centers\n * but keep replicating staging events for reliability\n */\nfunction isTelemetryReplicationAllowed(configuration: Configuration) {\n  return configuration.site === INTAKE_SITE_STAGING\n}\n\nexport function addTelemetryDebug(message: string, context?: Context) {\n  if (getDebugMode()) {\n    display.debug('[Telemetry]', message, context)\n  }\n  getTelemetryObservable().notify({\n    rawEvent: {\n      type: TelemetryType.LOG,\n      message,\n      status: StatusType.debug,\n      ...context,\n    },\n  })\n}\n\nexport function addTelemetryError(e: unknown, context?: Context) {\n  getTelemetryObservable().notify({\n    rawEvent: {\n      type: TelemetryType.LOG,\n      status: StatusType.error,\n      ...formatError(e),\n      ...context,\n    },\n  })\n}\n\nexport function addTelemetryConfiguration(configuration: RawTelemetryConfiguration) {\n  getTelemetryObservable().notify({\n    rawEvent: {\n      type: TelemetryType.CONFIGURATION,\n      configuration,\n    },\n  })\n}\n\nexport function addTelemetryMetrics(metricName: TelemetryMetrics, context?: Context) {\n  getTelemetryObservable().notify({\n    rawEvent: {\n      type: TelemetryType.LOG,\n      message: metricName,\n      status: StatusType.debug,\n      ...context,\n    },\n    metricName,\n  })\n}\n\nexport function addTelemetryUsage(usage: RawTelemetryUsage) {\n  getTelemetryObservable().notify({\n    rawEvent: {\n      type: TelemetryType.USAGE,\n      usage,\n    },\n  })\n}\n\nexport function formatError(e: unknown) {\n  if (isError(e)) {\n    const stackTrace = computeStackTrace(e)\n    return {\n      error: {\n        kind: stackTrace.name,\n        stack: toStackTraceString(scrubCustomerFrames(stackTrace)),\n      },\n      message: stackTrace.message!,\n    }\n  }\n  return {\n    error: {\n      stack: NO_ERROR_STACK_PRESENT_MESSAGE,\n    },\n    message: `${NonErrorPrefix.UNCAUGHT} ${jsonStringify(e)!}`,\n  }\n}\n\nexport function scrubCustomerFrames(stackTrace: StackTrace) {\n  stackTrace.stack = stackTrace.stack.filter(\n    (frame) => !frame.url || ALLOWED_FRAME_URLS.some((allowedFrameUrl) => frame.url!.startsWith(allowedFrameUrl))\n  )\n  return stackTrace\n}\n"],"mappings":"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"}