UNPKG

@sentry/core

Version:
164 lines (143 loc) 5.45 kB
import { debug } from './debug-logger.js'; import { baggageHeaderToDynamicSamplingContext } from './baggage.js'; import { extractOrgIdFromClient } from './dsn.js'; import { parseSampleRate } from './parseSampleRate.js'; import { generateTraceId, generateSpanId } from './propagationContext.js'; // eslint-disable-next-line @sentry-internal/sdk/no-regexp-constructor -- RegExp is used for readability here const TRACEPARENT_REGEXP = new RegExp( '^[ \\t]*' + // whitespace '([0-9a-f]{32})?' + // trace_id '-?([0-9a-f]{16})?' + // span_id '-?([01])?' + // sampled '[ \\t]*$', // whitespace ); /** * Extract transaction context data from a `sentry-trace` header. * * @param traceparent Traceparent string * * @returns Object containing data from the header, or undefined if traceparent string is malformed */ function extractTraceparentData(traceparent) { if (!traceparent) { return undefined; } const matches = traceparent.match(TRACEPARENT_REGEXP); if (!matches) { return undefined; } let parentSampled; if (matches[3] === '1') { parentSampled = true; } else if (matches[3] === '0') { parentSampled = false; } return { traceId: matches[1], parentSampled, parentSpanId: matches[2], }; } /** * Create a propagation context from incoming headers or * creates a minimal new one if the headers are undefined. */ function propagationContextFromHeaders( sentryTrace, baggage, ) { const traceparentData = extractTraceparentData(sentryTrace); const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage); if (!traceparentData?.traceId) { return { traceId: generateTraceId(), sampleRand: Math.random(), }; } const sampleRand = getSampleRandFromTraceparentAndDsc(traceparentData, dynamicSamplingContext); // The sample_rand on the DSC needs to be generated based on traceparent + baggage. if (dynamicSamplingContext) { dynamicSamplingContext.sample_rand = sampleRand.toString(); } const { traceId, parentSpanId, parentSampled } = traceparentData; return { traceId, parentSpanId, sampled: parentSampled, dsc: dynamicSamplingContext || {}, // If we have traceparent data but no DSC it means we are not head of trace and we must freeze it sampleRand, }; } /** * Create sentry-trace header from span context values. */ function generateSentryTraceHeader( traceId = generateTraceId(), spanId = generateSpanId(), sampled, ) { let sampledString = ''; if (sampled !== undefined) { sampledString = sampled ? '-1' : '-0'; } return `${traceId}-${spanId}${sampledString}`; } /** * Given any combination of an incoming trace, generate a sample rand based on its defined semantics. * * Read more: https://develop.sentry.dev/sdk/telemetry/traces/#propagated-random-value */ function getSampleRandFromTraceparentAndDsc( traceparentData, dsc, ) { // When there is an incoming sample rand use it. const parsedSampleRand = parseSampleRate(dsc?.sample_rand); if (parsedSampleRand !== undefined) { return parsedSampleRand; } // Otherwise, if there is an incoming sampling decision + sample rate, generate a sample rand that would lead to the same sampling decision. const parsedSampleRate = parseSampleRate(dsc?.sample_rate); if (parsedSampleRate && traceparentData?.parentSampled !== undefined) { return traceparentData.parentSampled ? // Returns a sample rand with positive sampling decision [0, sampleRate) Math.random() * parsedSampleRate : // Returns a sample rand with negative sampling decision [sampleRate, 1) parsedSampleRate + Math.random() * (1 - parsedSampleRate); } else { // If nothing applies, return a random sample rand. return Math.random(); } } /** * Determines whether a new trace should be continued based on the provided baggage org ID and the client's `strictTraceContinuation` option. * If the trace should not be continued, a new trace will be started. * * The result is dependent on the `strictTraceContinuation` option in the client. * See https://develop.sentry.dev/sdk/telemetry/traces/#stricttracecontinuation */ function shouldContinueTrace(client, baggageOrgId) { const clientOrgId = extractOrgIdFromClient(client); // Case: baggage orgID and Client orgID don't match - always start new trace if (baggageOrgId && clientOrgId && baggageOrgId !== clientOrgId) { debug.log( `Won't continue trace because org IDs don't match (incoming baggage: ${baggageOrgId}, SDK options: ${clientOrgId})`, ); return false; } const strictTraceContinuation = client.getOptions().strictTraceContinuation || false; // default for `strictTraceContinuation` is `false` if (strictTraceContinuation) { // With strict continuation enabled, don't continue trace if: // - Baggage has orgID, but Client doesn't have one // - Client has orgID, but baggage doesn't have one if ((baggageOrgId && !clientOrgId) || (!baggageOrgId && clientOrgId)) { debug.log( `Starting a new trace because strict trace continuation is enabled but one org ID is missing (incoming baggage: ${baggageOrgId}, Sentry client: ${clientOrgId})`, ); return false; } } return true; } export { TRACEPARENT_REGEXP, extractTraceparentData, generateSentryTraceHeader, propagationContextFromHeaders, shouldContinueTrace }; //# sourceMappingURL=tracing.js.map