Skip to content

Commit 6bc7527

Browse files
committed
ref(nextjs): Set propagation context for tracing
1 parent 6b4b044 commit 6bc7527

File tree

5 files changed

+66
-55
lines changed

5 files changed

+66
-55
lines changed

packages/nextjs/src/client/performance.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { getCurrentHub } from '@sentry/core';
22
import { WINDOW } from '@sentry/react';
3-
import type { Primitive, TraceparentData, Transaction, TransactionContext, TransactionSource } from '@sentry/types';
4-
import {
5-
baggageHeaderToDynamicSamplingContext,
6-
extractTraceparentData,
7-
logger,
8-
stripUrlQueryAndFragment,
9-
} from '@sentry/utils';
3+
import type {
4+
DynamicSamplingContext,
5+
Primitive,
6+
TraceparentData,
7+
Transaction,
8+
TransactionContext,
9+
TransactionSource,
10+
} from '@sentry/types';
11+
import { logger, stripUrlQueryAndFragment, tracingContextFromHeaders } from '@sentry/utils';
1012
import type { NEXT_DATA as NextData } from 'next/dist/next-server/lib/utils';
1113
import { default as Router } from 'next/router';
1214
import type { ParsedUrlQuery } from 'querystring';
@@ -38,7 +40,7 @@ interface SentryEnhancedNextData extends NextData {
3840
interface NextDataTagInfo {
3941
route?: string;
4042
traceParentData?: TraceparentData;
41-
baggage?: string;
43+
dynamicSamplingContext?: Partial<DynamicSamplingContext>;
4244
params?: ParsedUrlQuery;
4345
}
4446

@@ -83,13 +85,23 @@ function extractNextDataTagInformation(): NextDataTagInfo {
8385
nextDataTagInfo.params = query;
8486

8587
if (props && props.pageProps) {
86-
if (props.pageProps._sentryBaggage) {
87-
nextDataTagInfo.baggage = props.pageProps._sentryBaggage;
88+
const sentryTrace = props.pageProps._sentryTraceData;
89+
const baggage = props.pageProps._sentryBaggage;
90+
91+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
92+
sentryTrace,
93+
baggage,
94+
);
95+
96+
if (dynamicSamplingContext) {
97+
nextDataTagInfo.dynamicSamplingContext = dynamicSamplingContext;
8898
}
8999

90-
if (props.pageProps._sentryTraceData) {
91-
nextDataTagInfo.traceParentData = extractTraceparentData(props.pageProps._sentryTraceData);
100+
if (traceparentData) {
101+
nextDataTagInfo.traceParentData = traceparentData;
92102
}
103+
104+
getCurrentHub().getScope().setPropagationContext(propagationContext);
93105
}
94106

95107
return nextDataTagInfo;
@@ -121,13 +133,11 @@ export function nextRouterInstrumentation(
121133
startTransactionOnPageLoad: boolean = true,
122134
startTransactionOnLocationChange: boolean = true,
123135
): void {
124-
const { route, traceParentData, baggage, params } = extractNextDataTagInformation();
136+
const { route, traceParentData, dynamicSamplingContext, params } = extractNextDataTagInformation();
125137
prevLocationName = route || globalObject.location.pathname;
126138

127139
if (startTransactionOnPageLoad) {
128140
const source = route ? 'route' : 'url';
129-
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggage);
130-
131141
activeTransaction = startTransactionCb({
132142
name: prevLocationName,
133143
op: 'pageload',

packages/nextjs/src/edge/utils/edgeWrapperUtils.ts

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
import { captureException, getCurrentHub, hasTracingEnabled, startTransaction } from '@sentry/core';
22
import type { Span } from '@sentry/types';
3-
import {
4-
addExceptionMechanism,
5-
baggageHeaderToDynamicSamplingContext,
6-
extractTraceparentData,
7-
logger,
8-
objectify,
9-
} from '@sentry/utils';
3+
import { addExceptionMechanism, logger, objectify, tracingContextFromHeaders } from '@sentry/utils';
104

115
import type { EdgeRouteHandler } from '../types';
126
import { flush } from './flush';
@@ -32,17 +26,17 @@ export function withEdgeWrapping<H extends EdgeRouteHandler>(
3226
op: options.spanOp,
3327
});
3428
} else if (req instanceof Request) {
35-
// If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
36-
let traceparentData;
37-
38-
const sentryTraceHeader = req.headers.get('sentry-trace');
39-
if (sentryTraceHeader) {
40-
traceparentData = extractTraceparentData(sentryTraceHeader);
41-
__DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`);
29+
const sentryTrace = req.headers.get('sentry-trace') || '';
30+
const baggage = req.headers.get('baggage');
31+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
32+
sentryTrace,
33+
baggage,
34+
);
35+
currentScope.setPropagationContext(propagationContext);
36+
if (traceparentData) {
37+
__DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData.traceId}.`);
4238
}
4339

44-
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(req.headers.get('baggage'));
45-
4640
span = startTransaction({
4741
name: options.spanDescription,
4842
op: options.spanOp,

packages/nextjs/src/server/utils/wrapperUtils.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
startTransaction,
77
} from '@sentry/core';
88
import type { Span, Transaction } from '@sentry/types';
9-
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils';
9+
import { isString, tracingContextFromHeaders } from '@sentry/utils';
1010
import type { IncomingMessage, ServerResponse } from 'http';
1111

1212
import { platformSupportsStreaming } from './platformSupportsStreaming';
@@ -38,6 +38,7 @@ function setTransactionOnRequest(transaction: Transaction, req: IncomingMessage)
3838
*
3939
* Note: This function turns the wrapped function into an asynchronous one.
4040
*/
41+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
4142
export function withErrorInstrumentation<F extends (...args: any[]) => any>(
4243
origFunction: F,
4344
): (...params: Parameters<F>) => Promise<ReturnType<F>> {
@@ -66,6 +67,7 @@ export function withErrorInstrumentation<F extends (...args: any[]) => any>(
6667
* @param options Options providing details for the created transaction and span.
6768
* @returns what the data fetching method call returned.
6869
*/
70+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
6971
export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Promise<any> | any>(
7072
origDataFetcher: F,
7173
req: IncomingMessage,
@@ -86,12 +88,14 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
8688
const previousSpan: Span | undefined = getTransactionFromRequest(req) ?? scope.getSpan();
8789
let dataFetcherSpan;
8890

89-
const sentryTraceHeader = req.headers['sentry-trace'];
90-
const rawBaggageString = req.headers && req.headers.baggage;
91-
const traceparentData =
92-
typeof sentryTraceHeader === 'string' ? extractTraceparentData(sentryTraceHeader) : undefined;
93-
94-
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(rawBaggageString);
91+
const sentryTrace =
92+
req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined;
93+
const baggage = req.headers?.baggage;
94+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
95+
sentryTrace,
96+
baggage,
97+
);
98+
scope.setPropagationContext(propagationContext);
9599

96100
if (platformSupportsStreaming()) {
97101
let spanToContinue: Span;

packages/nextjs/src/server/wrapApiHandlerWithSentry.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@ import { captureException, startTransaction } from '@sentry/node';
33
import type { Transaction } from '@sentry/types';
44
import {
55
addExceptionMechanism,
6-
baggageHeaderToDynamicSamplingContext,
7-
extractTraceparentData,
86
isString,
97
logger,
108
objectify,
119
stripUrlQueryAndFragment,
10+
tracingContextFromHeaders,
1211
} from '@sentry/utils';
1312

1413
import type { AugmentedNextApiRequest, AugmentedNextApiResponse, NextApiHandler } from './types';
@@ -73,15 +72,18 @@ export function withSentry(apiHandler: NextApiHandler, parameterizedRoute?: stri
7372
currentScope.setSDKProcessingMetadata({ request: req });
7473

7574
if (hasTracingEnabled(options) && options?.instrumenter === 'sentry') {
76-
// If there is a trace header set, extract the data from it (parentSpanId, traceId, and sampling decision)
77-
let traceparentData;
78-
if (req.headers && isString(req.headers['sentry-trace'])) {
79-
traceparentData = extractTraceparentData(req.headers['sentry-trace']);
80-
__DEBUG_BUILD__ && logger.log(`[Tracing] Continuing trace ${traceparentData?.traceId}.`);
81-
}
75+
const sentryTrace =
76+
req.headers && isString(req.headers['sentry-trace']) ? req.headers['sentry-trace'] : undefined;
77+
const baggage = req.headers?.baggage;
78+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
79+
sentryTrace,
80+
baggage,
81+
);
82+
hub.getScope().setPropagationContext(propagationContext);
8283

83-
const baggageHeader = req.headers && req.headers.baggage;
84-
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(baggageHeader);
84+
if (__DEBUG_BUILD__ && traceparentData) {
85+
logger.log(`[Tracing] Continuing trace ${traceparentData.traceId}.`);
86+
}
8587

8688
// prefer the parameterized route, if we have it (which we will if we've auto-wrapped the route handler)
8789
let reqPath = parameterizedRoute;

packages/nextjs/src/server/wrapServerComponentWithSentry.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,15 @@ import {
55
runWithAsyncContext,
66
startTransaction,
77
} from '@sentry/core';
8-
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils';
8+
import { tracingContextFromHeaders } from '@sentry/utils';
99

1010
import { isNotFoundNavigationError, isRedirectNavigationError } from '../common/nextNavigationErrorUtils';
1111
import type { ServerComponentContext } from '../common/types';
1212

1313
/**
1414
* Wraps an `app` directory server component with Sentry error instrumentation.
1515
*/
16+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
1617
export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>(
1718
appDirComponent: F,
1819
context: ServerComponentContext,
@@ -28,14 +29,15 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
2829
apply: (originalFunction, thisArg, args) => {
2930
return runWithAsyncContext(() => {
3031
const hub = getCurrentHub();
32+
const currentScope = hub.getScope();
3133

3234
let maybePromiseResult;
3335

34-
const traceparentData = context.sentryTraceHeader
35-
? extractTraceparentData(context.sentryTraceHeader)
36-
: undefined;
37-
38-
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(context.baggageHeader);
36+
const { traceparentData, dynamicSamplingContext, propagationContext } = tracingContextFromHeaders(
37+
context.sentryTraceHeader,
38+
context.baggageHeader,
39+
);
40+
currentScope.setPropagationContext(propagationContext);
3941

4042
const transaction = startTransaction({
4143
op: 'function.nextjs',
@@ -48,7 +50,6 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
4850
},
4951
});
5052

51-
const currentScope = hub.getScope();
5253
if (currentScope) {
5354
currentScope.setSpan(transaction);
5455
}

0 commit comments

Comments
 (0)