Skip to content

feat(utils): Add propagationContextFromHeaders #10313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions packages/core/src/tracing/trace.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -213,16 +214,16 @@ export function continueTrace({
sentryTrace,
baggage,
}: {
sentryTrace: Parameters<typeof tracingContextFromHeaders>[0];
baggage: Parameters<typeof tracingContextFromHeaders>[1];
sentryTrace: Parameters<typeof propagationContextFromHeaders>[0];
baggage: Parameters<typeof propagationContextFromHeaders>[1];
}): Partial<TransactionContext>;
export function continueTrace<V>(
{
sentryTrace,
baggage,
}: {
sentryTrace: Parameters<typeof tracingContextFromHeaders>[0];
baggage: Parameters<typeof tracingContextFromHeaders>[1];
sentryTrace: Parameters<typeof propagationContextFromHeaders>[0];
baggage: Parameters<typeof propagationContextFromHeaders>[1];
},
callback: (transactionContext: Partial<TransactionContext>) => V,
): V;
Expand All @@ -238,13 +239,23 @@ export function continueTrace<V>(
sentryTrace,
baggage,
}: {
// eslint-disable-next-line deprecation/deprecation
sentryTrace: Parameters<typeof tracingContextFromHeaders>[0];
// eslint-disable-next-line deprecation/deprecation
baggage: Parameters<typeof tracingContextFromHeaders>[1];
},
callback?: (transactionContext: Partial<TransactionContext>) => V,
): V | Partial<TransactionContext> {
// 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/src/common/utils/wrapperUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
}

const currentScope = getCurrentScope();
// eslint-disable-next-line deprecation/deprecation
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
sentryTraceHeader,
baggageHeader,
Expand Down
1 change: 1 addition & 0 deletions packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => 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'),
Expand Down
4 changes: 2 additions & 2 deletions packages/node-experimental/src/sdk/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import {
consoleSandbox,
dropUndefinedKeys,
logger,
propagationContextFromHeaders,
stackParserFromStackParserOptions,
tracingContextFromHeaders,
} from '@sentry/utils';
import { DEBUG_BUILD } from '../debug-build';

Expand Down Expand Up @@ -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);
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/node/src/sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import {
GLOBAL_OBJ,
createStackParser,
nodeStackLineParser,
propagationContextFromHeaders,
stackParserFromStackParserOptions,
tracingContextFromHeaders,
} from '@sentry/utils';

import { setNodeAsyncContextStrategy } from './async';
Expand Down Expand Up @@ -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);
}
}
4 changes: 2 additions & 2 deletions packages/opentelemetry/src/propagator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions packages/remix/src/utils/instrumentServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 2 additions & 0 deletions packages/sveltekit/src/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof tracingContextFromHeaders> {
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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
browserPerformanceTimeOrigin,
getDomElement,
logger,
tracingContextFromHeaders,
propagationContextFromHeaders,
} from '@sentry/utils';

import { DEBUG_BUILD } from '../common/debug-build';
Expand Down Expand Up @@ -201,21 +201,23 @@ export const _browserTracingIntegration = ((_options: Partial<BrowserTracingOpti
if (isPageloadTransaction) {
const sentryTrace = isPageloadTransaction ? getMetaContent('sentry-trace') : '';
const baggage = isPageloadTransaction ? getMetaContent('baggage') : undefined;
const { traceparentData, dynamicSamplingContext } = tracingContextFromHeaders(sentryTrace, baggage);
const { traceId, dsc, parentSpanId, sampled } = propagationContextFromHeaders(sentryTrace, baggage);
expandedContext = {
traceId,
parentSpanId,
parentSampled: sampled,
...context,
...traceparentData,
metadata: {
// eslint-disable-next-line deprecation/deprecation
...context.metadata,
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
dynamicSamplingContext: dsc,
},
trimEnd: true,
};
} else {
expandedContext = {
...context,
trimEnd: true,
...context,
};
}

Expand Down
12 changes: 7 additions & 5 deletions packages/tracing-internal/src/browser/browsertracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
startIdleTransaction,
} from '@sentry/core';
import type { EventProcessor, Integration, Transaction, TransactionContext, TransactionSource } from '@sentry/types';
import { getDomElement, logger, tracingContextFromHeaders } from '@sentry/utils';
import { getDomElement, logger, propagationContextFromHeaders } from '@sentry/utils';

import { DEBUG_BUILD } from '../common/debug-build';
import { registerBackgroundTabDetection } from './backgroundtab';
Expand Down Expand Up @@ -312,21 +312,23 @@ export class BrowserTracing implements Integration {
if (isPageloadTransaction) {
const sentryTrace = isPageloadTransaction ? getMetaContent('sentry-trace') : '';
const baggage = isPageloadTransaction ? getMetaContent('baggage') : undefined;
const { traceparentData, dynamicSamplingContext } = tracingContextFromHeaders(sentryTrace, baggage);
const { traceId, dsc, parentSpanId, sampled } = propagationContextFromHeaders(sentryTrace, baggage);
expandedContext = {
traceId,
parentSpanId,
parentSampled: sampled,
...context,
...traceparentData,
metadata: {
// eslint-disable-next-line deprecation/deprecation
...context.metadata,
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
dynamicSamplingContext: dsc,
},
trimEnd: true,
};
} else {
expandedContext = {
...context,
trimEnd: true,
...context,
};
}

Expand Down
31 changes: 31 additions & 0 deletions packages/utils/src/tracing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ export function extractTraceparentData(traceparent?: string): TraceparentData |

/**
* Create tracing context from incoming headers.
*
* @deprecated Use `propagationContextFromHeaders` instead.
*/
// TODO(v8): Remove this function
export function tracingContextFromHeaders(
sentryTrace: Parameters<typeof extractTraceparentData>[0],
baggage: Parameters<typeof baggageHeaderToDynamicSamplingContext>[0],
Expand Down Expand Up @@ -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.
*/
Expand Down
59 changes: 58 additions & 1 deletion packages/utils/test/tracing.test.ts
Original file line number Diff line number Diff line change
@@ -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',
},
});
});
});