UNPKG

@dash0/sdk-web

Version:

Dash0's Web SDK to collect telemetry from end-users' web browsers

131 lines (130 loc) 5.17 kB
import { vars } from "../vars"; import { DEPLOYMENT_ENVIRONMENT_NAME, DEPLOYMENT_ID, DEPLOYMENT_NAME, PAGE_LOAD_ID, SERVICE_NAME, SERVICE_VERSION, USER_AGENT, } from "../semantic-conventions"; import { fetch, generateUniqueId, PAGE_LOAD_ID_BYTES, warn, debug, perf, nav, win, NO_VALUE_FALLBACK, pick, } from "../utils"; import { trackSessions } from "./session"; import { startWebVitalsInstrumentation } from "../instrumentations/web-vitals"; import { startErrorInstrumentation } from "../instrumentations/errors"; import { addAttribute } from "../utils/otel"; import { instrumentFetch } from "../instrumentations/http/fetch"; import { startNavigationInstrumentation } from "../instrumentations/navigation"; import { merge } from "ts-deepmerge"; import { initializeTabId } from "../utils/tab-id"; let hasBeenInitialised = false; export function init(opts) { if (hasBeenInitialised) { debug("Dash0 SDK is being reinitialized, skipping ..."); return; } if (!isClient()) { debug("Looks like we are not running in a browser context. Stopping Dash0 Web SDK initialization."); return; } if (!isSupported()) { debug("Stopping Dash0 Web SDK initialization. This browser does not support the necessary APIs"); return; } vars.endpoints = opts.endpoint instanceof Array ? opts.endpoint : [opts.endpoint]; if (vars.endpoints.length === 0) { warn("No telemetry endpoint configured. Aborting Dash0 Web SDK initialization process."); return; } Object.assign(vars, merge(vars, pick(opts, [ "ignoreUrls", "ignoreErrorMessages", "wrapEventHandlers", "wrapTimers", "propagateTraceHeadersCorsURLs", "maxWaitForResourceTimingsMillis", "maxToleranceForResourceTimingsMillis", "headersToCapture", "pageViewInstrumentation", ]))); initializeResourceAttributes(opts); initializeSignalAttributes(opts); initializeTabId(); trackSessions(opts.sessionInactivityTimeoutMillis, opts.sessionTerminationTimeoutMillis); startNavigationInstrumentation(); startWebVitalsInstrumentation(); startErrorInstrumentation(); instrumentFetch(); hasBeenInitialised = true; } function initializeResourceAttributes(opts) { addAttribute(vars.resource.attributes, SERVICE_NAME, opts["serviceName"]); if (opts.serviceVersion) { addAttribute(vars.resource.attributes, SERVICE_VERSION, opts["serviceVersion"]); } const env = detectEnvironment(opts); if (env) { addAttribute(vars.resource.attributes, DEPLOYMENT_ENVIRONMENT_NAME, env); } const deploymentName = detectDeploymentName(opts); if (deploymentName) { addAttribute(vars.resource.attributes, DEPLOYMENT_NAME, deploymentName); } const deploymentId = detectDeploymentId(opts); if (deploymentId) { addAttribute(vars.resource.attributes, DEPLOYMENT_ID, deploymentId); } } function initializeSignalAttributes(opts) { addAttribute(vars.signalAttributes, PAGE_LOAD_ID, generateUniqueId(PAGE_LOAD_ID_BYTES)); addAttribute(vars.signalAttributes, USER_AGENT, nav?.userAgent ?? NO_VALUE_FALLBACK); if (opts.additionalSignalAttributes) { Object.entries(opts.additionalSignalAttributes).forEach(([key, value]) => { addAttribute(vars.signalAttributes, key, value); }); } } function isSupported() { return typeof fetch === "function" && perf && perf.getEntriesByType; } function isClient() { return win != null; } function detectEnvironment(opts) { // if there is a manually specified value we use that if (opts.environment) { return opts.environment; } // if process isn't defined access to it causes an exception, but we can't check for its present due to how // plugins like webpack define work. try { // vercel // @ts-expect-error -- we need to access like this to allow webpack in the nextjs build to replace this return process?.env?.NEXT_PUBLIC_VERCEL_ENV; } catch (_ignored) { return undefined; } } function detectDeploymentName(opts) { if (opts.deploymentName) { return opts.deploymentName; } // if process isn't defined access to it causes an exception, but we can't check for its present due to how // plugins like webpack define work. try { // vercel // @ts-expect-error -- we need to access like this to allow webpack in the nextjs build to replace this return process?.env?.NEXT_PUBLIC_VERCEL_TARGET_ENV; } catch (_ignored) { return undefined; } } function detectDeploymentId(opts) { if (opts.deploymentId) { return opts.deploymentId; } // if process isn't defined access to it causes an exception, but we can't check for its present due to how // plugins like webpack define work. try { // vercel // @ts-expect-error -- we need to access like this to allow webpack in the nextjs build to replace this return process?.env?.NEXT_PUBLIC_VERCEL_BRANCH_URL; } catch (_ignored) { return undefined; } }