UNPKG

@dash0/sdk-web

Version:

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

84 lines (83 loc) 3.32 kB
import { addEventListener, debug, doc, win, roundToTwoDecimals, error, toNanosTs, getTimeOrigin } from "../../utils"; import { EVENT_NAME, EVENT_NAMES, LOG_SEVERITIES } from "../../semantic-conventions"; import { sendLog } from "../../transport"; import { getTraceContextForPageLoad, addAttribute } from "../../utils/otel"; import { addCommonAttributes } from "../../attributes"; import { transmitPageViewEvent } from "./event"; /** * Tracks page loads as per this OTel spec: * https://github.com/open-telemetry/semantic-conventions/pull/1910/files * * Notable difference: The full URL is transmitted as a signal attribute. */ export function startPageLoadInstrumentation() { try { transmitPageViewEvent(getStartTimeUnixNanos(), win?.location.href ? new URL(win?.location.href) : undefined); } catch (e) { error("Failed to transmit initial page view event", e); } if (doc?.readyState === "complete") { return onLoaded(); } if (win) { addEventListener(win, "load", function () { // we want to get timing data for loadEventEnd, // so asynchronously process this setTimeout(onLoaded, 0); }); } } /** * See https://github.com/open-telemetry/semantic-conventions/pull/1919 */ function onLoaded() { const nt = win?.performance.getEntriesByType("navigation")[0]; if (!nt) { debug("Navigation timings not available. Cannot emit navigation timing log"); return; } const attributes = []; addAttribute(attributes, EVENT_NAME, EVENT_NAMES.NAVIGATION_TIMING); const bodyAttributes = []; addAttribute(bodyAttributes, "name", nt.name); addNavigationTiming(bodyAttributes, nt, "responseStatus"); addNavigationTiming(bodyAttributes, nt, "fetchStart"); addNavigationTiming(bodyAttributes, nt, "requestStart"); addNavigationTiming(bodyAttributes, nt, "responseStart"); addNavigationTiming(bodyAttributes, nt, "domInteractive"); addNavigationTiming(bodyAttributes, nt, "domContentLoadedEventEnd"); addNavigationTiming(bodyAttributes, nt, "domComplete"); addNavigationTiming(bodyAttributes, nt, "loadEventEnd"); addNavigationTiming(bodyAttributes, nt, "transferSize"); addNavigationTiming(bodyAttributes, nt, "encodedBodySize"); addNavigationTiming(bodyAttributes, nt, "decodedBodySize"); const log = { timeUnixNano: getStartTimeUnixNanos(), attributes: attributes, severityNumber: LOG_SEVERITIES.INFO, severityText: "INFO", body: { kvlistValue: { values: bodyAttributes, }, }, }; addCommonAttributes(log.attributes); const traceContext = getTraceContextForPageLoad(); if (traceContext) { log.traceId = traceContext.traceId; log.spanId = traceContext.spanId; } sendLog(log); } function addNavigationTiming(attributes, nt, field) { // @ts-expect-error index access not recognized by TS, but this makes the code more reusable const value = nt[field]; if (typeof value === "number" && !isNaN(value)) { addAttribute(attributes, field, Number.isInteger(value) ? value : roundToTwoDecimals(value)); } } function getStartTimeUnixNanos() { return toNanosTs(Math.round(getTimeOrigin())); }