diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 9e55fd4b5a84..fee681921bb3 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -13,6 +13,7 @@ import { logger, nodeStackLineParser, stackParserFromStackParserOptions, + tracingContextFromHeaders, } from '@sentry/utils'; import { setNodeAsyncContextStrategy } from './async'; @@ -129,8 +130,9 @@ export function init(options: NodeOptions = {}): void { options.dsn = process.env.SENTRY_DSN; } - if (options.tracesSampleRate === undefined && process.env.SENTRY_TRACES_SAMPLE_RATE) { - const tracesSampleRate = parseFloat(process.env.SENTRY_TRACES_SAMPLE_RATE); + const sentryTracesSampleRate = process.env.SENTRY_TRACES_SAMPLE_RATE; + if (options.tracesSampleRate === undefined && sentryTracesSampleRate) { + const tracesSampleRate = parseFloat(sentryTracesSampleRate); if (isFinite(tracesSampleRate)) { options.tracesSampleRate = tracesSampleRate; } @@ -171,6 +173,8 @@ export function init(options: NodeOptions = {}): void { if (options.autoSessionTracking) { startSessionTracking(); } + + updateScopeFromEnvVariables(); } /** @@ -285,3 +289,19 @@ function startSessionTracking(): void { if (session && !terminalStates.includes(session.status)) hub.endSession(); }); } + +/** + * Update scope and propagation context based on environmental variables. + * + * See https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md + * for more details. + */ +function updateScopeFromEnvVariables(): void { + const sentryUseEnvironment = (process.env.SENTRY_USE_ENVIRONMENT || '').toLowerCase(); + if (!['false', 'n', 'no', 'off', '0'].includes(sentryUseEnvironment)) { + const sentryTraceEnv = process.env.SENTRY_TRACE; + const baggageEnv = process.env.SENTRY_BAGGAGE; + const { propagationContext } = tracingContextFromHeaders(sentryTraceEnv, baggageEnv); + getCurrentHub().getScope().setPropagationContext(propagationContext); + } +} diff --git a/packages/node/test/index.test.ts b/packages/node/test/index.test.ts index ab9bd41adaf9..60b37b44acb1 100644 --- a/packages/node/test/index.test.ts +++ b/packages/node/test/index.test.ts @@ -488,4 +488,47 @@ describe('SentryNode initialization', () => { expect(instrumenter).toEqual('otel'); }); }); + + describe('propagation context', () => { + beforeEach(() => { + process.env.SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-0'; + process.env.SENTRY_BAGGAGE = 'sentry-release=1.0.0,sentry-environment=production'; + + getCurrentHub().getScope().clear(); + }); + + afterEach(() => { + delete process.env.SENTRY_TRACE; + delete process.env.SENTRY_BAGGAGE; + }); + + it('reads from environmental variables', () => { + init({ dsn }); + + // @ts-expect-error accessing private method for test + expect(getCurrentHub().getScope()._propagationContext).toEqual({ + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + spanId: expect.any(String), + sampled: false, + dsc: { + release: '1.0.0', + environment: 'production', + }, + }); + }); + + it.each(['false', 'False', 'FALSE', 'n', 'no', 'No', 'NO', 'off', 'Off', 'OFF', '0'])( + 'does not read from environmental variable if SENTRY_USE_ENVIRONMENT is set to %s', + useEnvValue => { + process.env.SENTRY_USE_ENVIRONMENT = useEnvValue; + init({ dsn }); + + // @ts-expect-error accessing private method for test + expect(getCurrentHub().getScope()._propagationContext.traceId).not.toEqual('12312012123120121231201212312012'); + + delete process.env.SENTRY_USE_ENVIRONMENT; + }, + ); + }); }); diff --git a/packages/utils/src/tracing.ts b/packages/utils/src/tracing.ts index 7eee5312ce6d..445c271a0ee5 100644 --- a/packages/utils/src/tracing.ts +++ b/packages/utils/src/tracing.ts @@ -1,4 +1,7 @@ -import type { TraceparentData } from '@sentry/types'; +import type { DynamicSamplingContext, PropagationContext, TraceparentData } from '@sentry/types'; + +import { baggageHeaderToDynamicSamplingContext } from './baggage'; +import { uuid4 } from './misc'; export const TRACEPARENT_REGEXP = new RegExp( '^[ \\t]*' + // whitespace @@ -36,3 +39,40 @@ export function extractTraceparentData(traceparent: string): TraceparentData | u parentSpanId: matches[2], }; } + +/** + * Create tracing context from incoming headers. + */ +export function tracingContextFromHeaders( + sentryTrace: Parameters[0] = '', + baggage: Parameters[0] = '', +): { + traceparentData: ReturnType; + dynamicSamplingContext: ReturnType; + propagationContext: PropagationContext; +} { + const traceparentData = extractTraceparentData(sentryTrace); + const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage); + + const { traceId, parentSpanId, parentSampled } = traceparentData || {}; + + const propagationContext: PropagationContext = { + traceId: traceId || uuid4(), + spanId: uuid4().substring(16), + sampled: parentSampled === undefined ? false : parentSampled, + }; + + if (parentSpanId) { + propagationContext.parentSpanId = parentSpanId; + } + + if (dynamicSamplingContext) { + propagationContext.dsc = dynamicSamplingContext as DynamicSamplingContext; + } + + return { + traceparentData, + dynamicSamplingContext, + propagationContext, + }; +}