Skip to content

Commit b5cd99d

Browse files
Luca ForstnerAbhiPrasad
authored andcommitted
feat(nextjs): Connect traces for server components
1 parent bed3465 commit b5cd99d

File tree

4 files changed

+39
-4
lines changed

4 files changed

+39
-4
lines changed

packages/nextjs/src/common/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export type ServerComponentContext = {
22
componentRoute: string;
33
componentType: string;
4+
sentryTraceHeader?: string;
5+
baggageHeader?: string;
46
};

packages/nextjs/src/config/loaders/wrappingLoader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export default function wrappingLoader(
119119
// https://github.com/vercel/next.js/blob/295f9da393f7d5a49b0c2e15a2f46448dbdc3895/packages/next/build/analysis/get-page-static-info.ts#L37
120120
// https://github.com/vercel/next.js/blob/a1c15d84d906a8adf1667332a3f0732be615afa0/packages/next-swc/crates/core/src/react_server_components.rs#L247
121121
// We do not want to wrap client components
122-
if (userCode.includes('/* __next_internal_client_entry_do_not_use__ */')) {
122+
if (userCode.includes('__next_internal_client_entry_do_not_use__')) {
123123
this.callback(null, userCode, userModuleSourceMap);
124124
return;
125125
}

packages/nextjs/src/config/templates/serverComponentWrapperTemplate.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import * as wrapee from '__SENTRY_WRAPPING_TARGET_FILE__';
1212
// eslint-disable-next-line import/no-extraneous-dependencies
1313
import * as Sentry from '@sentry/nextjs';
14+
// @ts-ignore TODO
15+
// eslint-disable-next-line import/no-unresolved
16+
import { headers } from 'next/headers';
1417

1518
type ServerComponentModule = {
1619
default: unknown;
@@ -22,9 +25,30 @@ const serverComponent = serverComponentModule.default;
2225

2326
let wrappedServerComponent;
2427
if (typeof serverComponent === 'function') {
25-
wrappedServerComponent = Sentry.wrapServerComponentWithSentry(serverComponent, {
26-
componentRoute: '__ROUTE__',
27-
componentType: '__COMPONENT_TYPE__',
28+
// TODO: explain why this code needs to be in this file
29+
wrappedServerComponent = new Proxy(serverComponent, {
30+
apply: (originalFunction, thisArg, args) => {
31+
let sentryTraceHeader: string | undefined = undefined;
32+
let baggageHeader: string | undefined = undefined;
33+
34+
if (process.env.NEXT_PHASE !== 'phase-production-build') {
35+
// @ts-ignore TODO
36+
// eslint-disable-next-line import/no-unresolved
37+
// const { headers } = await import('next/headers');
38+
const headersList = headers();
39+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
40+
sentryTraceHeader = headersList.get('sentry-trace');
41+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
42+
baggageHeader = headersList.get('baggage');
43+
}
44+
45+
return Sentry.wrapServerComponentWithSentry(originalFunction, {
46+
componentRoute: '__ROUTE__',
47+
componentType: '__COMPONENT_TYPE__',
48+
sentryTraceHeader,
49+
baggageHeader,
50+
}).apply(thisArg, args);
51+
},
2852
});
2953
} else {
3054
wrappedServerComponent = serverComponent;

packages/nextjs/src/server/wrapServerComponentWithSentry.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { captureException, getCurrentHub, startTransaction } from '@sentry/core';
2+
import { baggageHeaderToDynamicSamplingContext, extractTraceparentData } from '@sentry/utils';
23
import * as domain from 'domain';
34

45
import type { ServerComponentContext } from '../common/types';
@@ -20,12 +21,20 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
2021
return domain.create().bind(() => {
2122
let maybePromiseResult;
2223

24+
const traceparentData = context.sentryTraceHeader
25+
? extractTraceparentData(context.sentryTraceHeader)
26+
: undefined;
27+
28+
const dynamicSamplingContext = baggageHeaderToDynamicSamplingContext(context.baggageHeader);
29+
2330
const transaction = startTransaction({
2431
op: 'function.nextjs',
2532
name: `${componentType} Server Component (${componentRoute})`,
2633
status: 'ok',
34+
...traceparentData,
2735
metadata: {
2836
source: 'component',
37+
dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext,
2938
},
3039
});
3140

0 commit comments

Comments
 (0)