diff --git a/packages/core/src/tracing/trace.ts b/packages/core/src/tracing/trace.ts index bdeba55ed9e4..f09ca1e10fa4 100644 --- a/packages/core/src/tracing/trace.ts +++ b/packages/core/src/tracing/trace.ts @@ -1,5 +1,6 @@ import type { Span, SpanTimeInput, StartSpanOptions, TransactionContext } from '@sentry/types'; +import type { propagationContextFromHeaders } from '@sentry/utils'; import { dropUndefinedKeys, logger, tracingContextFromHeaders } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; @@ -213,16 +214,16 @@ export function continueTrace({ sentryTrace, baggage, }: { - sentryTrace: Parameters[0]; - baggage: Parameters[1]; + sentryTrace: Parameters[0]; + baggage: Parameters[1]; }): Partial; export function continueTrace( { sentryTrace, baggage, }: { - sentryTrace: Parameters[0]; - baggage: Parameters[1]; + sentryTrace: Parameters[0]; + baggage: Parameters[1]; }, callback: (transactionContext: Partial) => V, ): V; @@ -238,13 +239,23 @@ export function continueTrace( sentryTrace, baggage, }: { + // eslint-disable-next-line deprecation/deprecation sentryTrace: Parameters[0]; + // eslint-disable-next-line deprecation/deprecation baggage: Parameters[1]; }, callback?: (transactionContext: Partial) => V, ): V | Partial { + // TODO(v8): Change this function so it doesn't do anything besides setting the propagation context on the current scope: + /* + const propagationContext = propagationContextFromHeaders(sentryTrace, baggage); + getCurrentScope().setPropagationContext(propagationContext); + return; + */ + const currentScope = getCurrentScope(); + // eslint-disable-next-line deprecation/deprecation const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTrace, baggage, diff --git a/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts b/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts index 5f2064c690e4..e360e51df56b 100644 --- a/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts +++ b/packages/nextjs/src/client/routing/pagesRouterRoutingInstrumentation.ts @@ -119,6 +119,7 @@ export function pagesRouterInstrumentation( startTransactionOnLocationChange: boolean = true, ): void { const { route, params, sentryTrace, baggage } = extractNextDataTagInformation(); + // eslint-disable-next-line deprecation/deprecation const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTrace, baggage, diff --git a/packages/nextjs/src/common/utils/wrapperUtils.ts b/packages/nextjs/src/common/utils/wrapperUtils.ts index f7e0917f2c39..2b9d58d41616 100644 --- a/packages/nextjs/src/common/utils/wrapperUtils.ts +++ b/packages/nextjs/src/common/utils/wrapperUtils.ts @@ -93,6 +93,7 @@ export function withTracedServerSideDataFetcher Pr const sentryTrace = req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined; const baggage = req.headers?.baggage; + // eslint-disable-next-line deprecation/deprecation const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTrace, baggage, diff --git a/packages/nextjs/src/common/withServerActionInstrumentation.ts b/packages/nextjs/src/common/withServerActionInstrumentation.ts index 01e1c75d6f3f..0d0e6968a3b1 100644 --- a/packages/nextjs/src/common/withServerActionInstrumentation.ts +++ b/packages/nextjs/src/common/withServerActionInstrumentation.ts @@ -76,6 +76,7 @@ async function withServerActionInstrumentationImplementation any>( return new Proxy(routeHandler, { apply: (originalFunction, thisArg, args) => { return runWithAsyncContext(async () => { + // eslint-disable-next-line deprecation/deprecation const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( sentryTraceHeader ?? headers?.get('sentry-trace') ?? undefined, baggageHeader ?? headers?.get('baggage'), diff --git a/packages/node-experimental/src/sdk/init.ts b/packages/node-experimental/src/sdk/init.ts index 353d38be90f2..c0ce8056e336 100644 --- a/packages/node-experimental/src/sdk/init.ts +++ b/packages/node-experimental/src/sdk/init.ts @@ -12,8 +12,8 @@ import { consoleSandbox, dropUndefinedKeys, logger, + propagationContextFromHeaders, stackParserFromStackParserOptions, - tracingContextFromHeaders, } from '@sentry/utils'; import { DEBUG_BUILD } from '../debug-build'; @@ -190,7 +190,7 @@ function updateScopeFromEnvVariables(): void { 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); + const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv); getCurrentScope().setPropagationContext(propagationContext); } } diff --git a/packages/node/src/sdk.ts b/packages/node/src/sdk.ts index 9ef08c88aeb9..8584f66dc083 100644 --- a/packages/node/src/sdk.ts +++ b/packages/node/src/sdk.ts @@ -17,8 +17,8 @@ import { GLOBAL_OBJ, createStackParser, nodeStackLineParser, + propagationContextFromHeaders, stackParserFromStackParserOptions, - tracingContextFromHeaders, } from '@sentry/utils'; import { setNodeAsyncContextStrategy } from './async'; @@ -291,7 +291,7 @@ function updateScopeFromEnvVariables(): void { 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); + const propagationContext = propagationContextFromHeaders(sentryTraceEnv, baggageEnv); getCurrentScope().setPropagationContext(propagationContext); } } diff --git a/packages/opentelemetry/src/propagator.ts b/packages/opentelemetry/src/propagator.ts index bbeb2744e501..0f53876f7240 100644 --- a/packages/opentelemetry/src/propagator.ts +++ b/packages/opentelemetry/src/propagator.ts @@ -3,7 +3,7 @@ import { TraceFlags, propagation, trace } from '@opentelemetry/api'; import { W3CBaggagePropagator, isTracingSuppressed } from '@opentelemetry/core'; import { getDynamicSamplingContextFromClient } from '@sentry/core'; import type { DynamicSamplingContext, PropagationContext } from '@sentry/types'; -import { SENTRY_BAGGAGE_KEY_PREFIX, generateSentryTraceHeader, tracingContextFromHeaders } from '@sentry/utils'; +import { SENTRY_BAGGAGE_KEY_PREFIX, generateSentryTraceHeader, propagationContextFromHeaders } from '@sentry/utils'; import { SENTRY_BAGGAGE_HEADER, SENTRY_TRACE_HEADER } from './constants'; import { getClient } from './custom/hub'; @@ -55,7 +55,7 @@ export class SentryPropagator extends W3CBaggagePropagator { : maybeSentryTraceHeader : undefined; - const { propagationContext } = tracingContextFromHeaders(sentryTraceHeader, maybeBaggageHeader); + const propagationContext = propagationContextFromHeaders(sentryTraceHeader, maybeBaggageHeader); // Add propagation context to context const contextWithPropagationContext = setPropagationContextOnContext(context, propagationContext); diff --git a/packages/remix/src/utils/instrumentServer.ts b/packages/remix/src/utils/instrumentServer.ts index 1ed13e9f28ec..94e8090ac433 100644 --- a/packages/remix/src/utils/instrumentServer.ts +++ b/packages/remix/src/utils/instrumentServer.ts @@ -401,6 +401,7 @@ export function startRequestHandlerTransaction( method: string; }, ): Transaction { + // eslint-disable-next-line deprecation/deprecation const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders( request.headers['sentry-trace'], request.headers.baggage, diff --git a/packages/sveltekit/src/server/utils.ts b/packages/sveltekit/src/server/utils.ts index 4106f7f4a09c..1f3719745bca 100644 --- a/packages/sveltekit/src/server/utils.ts +++ b/packages/sveltekit/src/server/utils.ts @@ -10,9 +10,11 @@ import { DEBUG_BUILD } from '../common/debug-build'; * * Sets propagation context as a side effect. */ +// eslint-disable-next-line deprecation/deprecation export function getTracePropagationData(event: RequestEvent): ReturnType { const sentryTraceHeader = event.request.headers.get('sentry-trace') || ''; const baggageHeader = event.request.headers.get('baggage'); + // eslint-disable-next-line deprecation/deprecation return tracingContextFromHeaders(sentryTraceHeader, baggageHeader); } diff --git a/packages/tracing-internal/src/browser/browserTracingIntegration.ts b/packages/tracing-internal/src/browser/browserTracingIntegration.ts index 87169c55c8b5..f8570b08b3a0 100644 --- a/packages/tracing-internal/src/browser/browserTracingIntegration.ts +++ b/packages/tracing-internal/src/browser/browserTracingIntegration.ts @@ -23,7 +23,7 @@ import { browserPerformanceTimeOrigin, getDomElement, logger, - tracingContextFromHeaders, + propagationContextFromHeaders, } from '@sentry/utils'; import { DEBUG_BUILD } from '../common/debug-build'; @@ -201,21 +201,23 @@ export const _browserTracingIntegration = ((_options: Partial[0], baggage: Parameters[0], @@ -83,6 +86,34 @@ export function tracingContextFromHeaders( } } +/** + * Create a propagation context from incoming headers. + */ +export function propagationContextFromHeaders( + sentryTrace: string | undefined, + baggage: string | number | boolean | string[] | null | undefined, +): PropagationContext { + const traceparentData = extractTraceparentData(sentryTrace); + const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage); + + const { traceId, parentSpanId, parentSampled } = traceparentData || {}; + + if (!traceparentData) { + return { + traceId: traceId || uuid4(), + spanId: uuid4().substring(16), + }; + } else { + return { + traceId: traceId || uuid4(), + parentSpanId: parentSpanId || uuid4().substring(16), + spanId: uuid4().substring(16), + 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 + }; + } +} + /** * Create sentry-trace header from span context values. */ diff --git a/packages/utils/test/tracing.test.ts b/packages/utils/test/tracing.test.ts index 2e7cc4d3d5a5..ee3790322460 100644 --- a/packages/utils/test/tracing.test.ts +++ b/packages/utils/test/tracing.test.ts @@ -1,9 +1,66 @@ -import { tracingContextFromHeaders } from '../src/tracing'; +import { propagationContextFromHeaders, tracingContextFromHeaders } from '../src/tracing'; + +const EXAMPLE_SENTRY_TRACE = '12312012123120121231201212312012-1121201211212012-1'; +const EXAMPLE_BAGGAGE = 'sentry-release=1.2.3,sentry-foo=bar,other=baz'; describe('tracingContextFromHeaders()', () => { it('should produce a frozen baggage (empty object) when there is an incoming trace but no baggage header', () => { + // eslint-disable-next-line deprecation/deprecation const tracingContext = tracingContextFromHeaders('12312012123120121231201212312012-1121201211212012-1', undefined); expect(tracingContext.dynamicSamplingContext).toEqual({}); expect(tracingContext.propagationContext.dsc).toEqual({}); }); }); + +describe('propagationContextFromHeaders()', () => { + it('returns a completely new propagation context when no sentry-trace data is given but baggage data is given', () => { + const result = propagationContextFromHeaders(undefined, undefined); + expect(result).toEqual({ + traceId: expect.any(String), + spanId: expect.any(String), + }); + }); + + it('returns a completely new propagation context when no sentry-trace data is given', () => { + const result = propagationContextFromHeaders(undefined, EXAMPLE_BAGGAGE); + expect(result).toEqual({ + traceId: expect.any(String), + spanId: expect.any(String), + }); + }); + + it('returns the correct traceparent data within the propagation context when sentry trace data is given', () => { + const result = propagationContextFromHeaders(EXAMPLE_SENTRY_TRACE, undefined); + expect(result).toEqual( + expect.objectContaining({ + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + spanId: expect.any(String), + sampled: true, + }), + ); + }); + + it('returns a frozen dynamic sampling context (empty object) when there is an incoming trace but no baggage header', () => { + const result = propagationContextFromHeaders(EXAMPLE_SENTRY_TRACE, undefined); + expect(result).toEqual( + expect.objectContaining({ + dsc: {}, + }), + ); + }); + + it('returns the correct trace parent data when both sentry-trace and baggage are given', () => { + const result = propagationContextFromHeaders(EXAMPLE_SENTRY_TRACE, EXAMPLE_BAGGAGE); + expect(result).toEqual({ + traceId: '12312012123120121231201212312012', + parentSpanId: '1121201211212012', + spanId: expect.any(String), + sampled: true, + dsc: { + release: '1.2.3', + foo: 'bar', + }, + }); + }); +});