Skip to content

Commit 043ae7e

Browse files
authored
feat(nextjs): Always transmit trace data to the client (#13337)
1 parent 3871892 commit 043ae7e

7 files changed

+89
-100
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SPAN_STATUS_OK,
77
captureException,
88
continueTrace,
9+
getTraceData,
910
startInactiveSpan,
1011
startSpan,
1112
startSpanManual,
@@ -88,8 +89,11 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
8889
/** Name of the data fetching method - will be used for describing the data fetcher's span. */
8990
dataFetchingMethodName: string;
9091
},
91-
): (...params: Parameters<F>) => Promise<ReturnType<F>> {
92-
return async function (this: unknown, ...args: Parameters<F>): Promise<ReturnType<F>> {
92+
): (...params: Parameters<F>) => Promise<{ data: ReturnType<F>; sentryTrace?: string; baggage?: string }> {
93+
return async function (
94+
this: unknown,
95+
...args: Parameters<F>
96+
): Promise<{ data: ReturnType<F>; sentryTrace?: string; baggage?: string }> {
9397
return escapeNextjsTracing(() => {
9498
const isolationScope = commonObjectToIsolationScope(req);
9599
return withIsolationScope(isolationScope, () => {
@@ -116,8 +120,13 @@ export function withTracedServerSideDataFetcher<F extends (...args: any[]) => Pr
116120
},
117121
async dataFetcherSpan => {
118122
dataFetcherSpan.setStatus({ code: SPAN_STATUS_OK });
123+
const { 'sentry-trace': sentryTrace, baggage } = getTraceData();
119124
try {
120-
return await origDataFetcher.apply(this, args);
125+
return {
126+
sentryTrace: sentryTrace,
127+
baggage: baggage,
128+
data: await origDataFetcher.apply(this, args),
129+
};
121130
} catch (e) {
122131
dataFetcherSpan.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
123132
requestSpan?.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });

packages/nextjs/src/common/wrapAppGetInitialPropsWithSentry.ts

Lines changed: 21 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { getActiveSpan, getDynamicSamplingContextFromSpan, getRootSpan, spanToTraceHeader } from '@sentry/core';
2-
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
31
import type App from 'next/app';
42

53
import { isBuild } from './utils/isBuild';
6-
import { getSpanFromRequest, withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
4+
import { withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
75

86
type AppGetInitialProps = (typeof App)['getInitialProps'];
97

@@ -26,6 +24,7 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI
2624
const { req, res } = context.ctx;
2725

2826
const errorWrappedAppGetInitialProps = withErrorInstrumentation(wrappingTarget);
27+
2928
// Generally we can assume that `req` and `res` are always defined on the server:
3029
// https://nextjs.org/docs/api-reference/data-fetching/get-initial-props#context-object
3130
// This does not seem to be the case in dev mode. Because we have no clean way of associating the the data fetcher
@@ -37,16 +36,21 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI
3736
dataFetchingMethodName: 'getInitialProps',
3837
});
3938

40-
const appGetInitialProps: {
41-
pageProps: {
42-
_sentryTraceData?: string;
43-
_sentryBaggage?: string;
39+
const {
40+
data: appGetInitialProps,
41+
sentryTrace,
42+
baggage,
43+
}: {
44+
data: {
45+
pageProps: {
46+
_sentryTraceData?: string;
47+
_sentryBaggage?: string;
48+
};
4449
};
50+
sentryTrace?: string;
51+
baggage?: string;
4552
} = await tracedGetInitialProps.apply(thisArg, args);
4653

47-
const activeSpan = getActiveSpan();
48-
const requestSpan = getSpanFromRequest(req) ?? (activeSpan ? getRootSpan(activeSpan) : undefined);
49-
5054
// Per definition, `pageProps` is not optional, however an increased amount of users doesn't seem to call
5155
// `App.getInitialProps(appContext)` in their custom `_app` pages which is required as per
5256
// https://nextjs.org/docs/advanced-features/custom-app - resulting in missing `pageProps`.
@@ -55,21 +59,14 @@ export function wrapAppGetInitialPropsWithSentry(origAppGetInitialProps: AppGetI
5559
appGetInitialProps.pageProps = {};
5660
}
5761

58-
if (requestSpan) {
59-
const sentryTrace = spanToTraceHeader(requestSpan);
60-
61-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
62-
if (sentryTrace) {
63-
appGetInitialProps.pageProps._sentryTraceData = sentryTrace;
64-
}
65-
66-
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(requestSpan);
67-
const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
62+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
63+
if (sentryTrace) {
64+
appGetInitialProps.pageProps._sentryTraceData = sentryTrace;
65+
}
6866

69-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
70-
if (baggage) {
71-
appGetInitialProps.pageProps._sentryBaggage = baggage;
72-
}
67+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
68+
if (baggage) {
69+
appGetInitialProps.pageProps._sentryBaggage = baggage;
7370
}
7471

7572
return appGetInitialProps;

packages/nextjs/src/common/wrapDocumentGetInitialPropsWithSentry.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function wrapDocumentGetInitialPropsWithSentry(
1717
origDocumentGetInitialProps: DocumentGetInitialProps,
1818
): DocumentGetInitialProps {
1919
return new Proxy(origDocumentGetInitialProps, {
20-
apply: (wrappingTarget, thisArg, args: Parameters<DocumentGetInitialProps>) => {
20+
apply: async (wrappingTarget, thisArg, args: Parameters<DocumentGetInitialProps>) => {
2121
if (isBuild()) {
2222
return wrappingTarget.apply(thisArg, args);
2323
}
@@ -37,7 +37,8 @@ export function wrapDocumentGetInitialPropsWithSentry(
3737
dataFetchingMethodName: 'getInitialProps',
3838
});
3939

40-
return tracedGetInitialProps.apply(thisArg, args);
40+
const { data } = await tracedGetInitialProps.apply(thisArg, args);
41+
return data;
4142
} else {
4243
return errorWrappedGetInitialProps.apply(thisArg, args);
4344
}

packages/nextjs/src/common/wrapErrorGetInitialPropsWithSentry.ts

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import { getActiveSpan, getDynamicSamplingContextFromSpan, getRootSpan, spanToTraceHeader } from '@sentry/core';
2-
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
31
import type { NextPageContext } from 'next';
42
import type { ErrorProps } from 'next/error';
53

64
import { isBuild } from './utils/isBuild';
7-
import { getSpanFromRequest, withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
5+
import { withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
86

97
type ErrorGetInitialProps = (context: NextPageContext) => Promise<ErrorProps>;
108

@@ -40,29 +38,27 @@ export function wrapErrorGetInitialPropsWithSentry(
4038
dataFetchingMethodName: 'getInitialProps',
4139
});
4240

43-
const errorGetInitialProps: ErrorProps & {
44-
_sentryTraceData?: string;
45-
_sentryBaggage?: string;
41+
const {
42+
data: errorGetInitialProps,
43+
baggage,
44+
sentryTrace,
45+
}: {
46+
data: ErrorProps & {
47+
_sentryTraceData?: string;
48+
_sentryBaggage?: string;
49+
};
50+
baggage?: string;
51+
sentryTrace?: string;
4652
} = await tracedGetInitialProps.apply(thisArg, args);
4753

48-
const activeSpan = getActiveSpan();
49-
const requestSpan = getSpanFromRequest(req) ?? (activeSpan ? getRootSpan(activeSpan) : undefined);
50-
51-
if (requestSpan) {
52-
const sentryTrace = spanToTraceHeader(requestSpan);
53-
54-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
55-
if (sentryTrace) {
56-
errorGetInitialProps._sentryTraceData = sentryTrace;
57-
}
58-
59-
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(requestSpan);
60-
const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
54+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
55+
if (sentryTrace) {
56+
errorGetInitialProps._sentryTraceData = sentryTrace;
57+
}
6158

62-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
63-
if (baggage) {
64-
errorGetInitialProps._sentryBaggage = baggage;
65-
}
59+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
60+
if (baggage) {
61+
errorGetInitialProps._sentryBaggage = baggage;
6662
}
6763

6864
return errorGetInitialProps;

packages/nextjs/src/common/wrapGetInitialPropsWithSentry.ts

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { getActiveSpan, getDynamicSamplingContextFromSpan, getRootSpan, spanToTraceHeader } from '@sentry/core';
2-
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
31
import type { NextPage } from 'next';
42

53
import { isBuild } from './utils/isBuild';
6-
import { getSpanFromRequest, withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
4+
import { withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
75

86
type GetInitialProps = Required<NextPage>['getInitialProps'];
97

@@ -36,29 +34,27 @@ export function wrapGetInitialPropsWithSentry(origGetInitialProps: GetInitialPro
3634
dataFetchingMethodName: 'getInitialProps',
3735
});
3836

39-
const initialProps: {
40-
_sentryTraceData?: string;
41-
_sentryBaggage?: string;
37+
const {
38+
data: initialProps,
39+
baggage,
40+
sentryTrace,
41+
}: {
42+
data: {
43+
_sentryTraceData?: string;
44+
_sentryBaggage?: string;
45+
};
46+
baggage?: string;
47+
sentryTrace?: string;
4248
} = (await tracedGetInitialProps.apply(thisArg, args)) ?? {}; // Next.js allows undefined to be returned from a getInitialPropsFunction.
4349

44-
const activeSpan = getActiveSpan();
45-
const requestSpan = getSpanFromRequest(req) ?? (activeSpan ? getRootSpan(activeSpan) : undefined);
46-
47-
if (requestSpan) {
48-
const sentryTrace = spanToTraceHeader(requestSpan);
49-
50-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
51-
if (sentryTrace) {
52-
initialProps._sentryTraceData = sentryTrace;
53-
}
54-
55-
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(requestSpan);
56-
const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
50+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
51+
if (sentryTrace) {
52+
initialProps._sentryTraceData = sentryTrace;
53+
}
5754

58-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
59-
if (baggage) {
60-
initialProps._sentryBaggage = baggage;
61-
}
55+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
56+
if (baggage) {
57+
initialProps._sentryBaggage = baggage;
6258
}
6359

6460
return initialProps;

packages/nextjs/src/common/wrapGetServerSidePropsWithSentry.ts

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
import { getActiveSpan, getDynamicSamplingContextFromSpan, getRootSpan, spanToTraceHeader } from '@sentry/core';
2-
import { dynamicSamplingContextToSentryBaggageHeader } from '@sentry/utils';
31
import type { GetServerSideProps } from 'next';
42

53
import { isBuild } from './utils/isBuild';
6-
import { getSpanFromRequest, withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
4+
import { withErrorInstrumentation, withTracedServerSideDataFetcher } from './utils/wrapperUtils';
75

86
/**
97
* Create a wrapped version of the user's exported `getServerSideProps` function
@@ -32,27 +30,21 @@ export function wrapGetServerSidePropsWithSentry(
3230
dataFetchingMethodName: 'getServerSideProps',
3331
});
3432

35-
const serverSideProps = await (tracedGetServerSideProps.apply(thisArg, args) as ReturnType<
36-
typeof tracedGetServerSideProps
37-
>);
33+
const {
34+
data: serverSideProps,
35+
baggage,
36+
sentryTrace,
37+
} = await (tracedGetServerSideProps.apply(thisArg, args) as ReturnType<typeof tracedGetServerSideProps>);
3838

3939
if (serverSideProps && 'props' in serverSideProps) {
40-
const activeSpan = getActiveSpan();
41-
const requestSpan = getSpanFromRequest(req) ?? (activeSpan ? getRootSpan(activeSpan) : undefined);
42-
if (requestSpan) {
43-
const sentryTrace = spanToTraceHeader(requestSpan);
44-
45-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
46-
if (sentryTrace) {
47-
(serverSideProps.props as Record<string, unknown>)._sentryTraceData = sentryTrace;
48-
}
49-
50-
const dynamicSamplingContext = getDynamicSamplingContextFromSpan(requestSpan);
51-
const baggage = dynamicSamplingContextToSentryBaggageHeader(dynamicSamplingContext);
52-
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
53-
if (baggage) {
54-
(serverSideProps.props as Record<string, unknown>)._sentryBaggage = baggage;
55-
}
40+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
41+
if (sentryTrace) {
42+
(serverSideProps.props as Record<string, unknown>)._sentryTraceData = sentryTrace;
43+
}
44+
45+
// The Next.js serializer throws on undefined values so we need to guard for it (#12102)
46+
if (baggage) {
47+
(serverSideProps.props as Record<string, unknown>)._sentryBaggage = baggage;
5648
}
5749
}
5850

packages/nextjs/src/common/wrapGetStaticPropsWithSentry.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export function wrapGetStaticPropsWithSentry(
1717
parameterizedRoute: string,
1818
): GetStaticProps<Props> {
1919
return new Proxy(origGetStaticPropsa, {
20-
apply: (wrappingTarget, thisArg, args: Parameters<GetStaticProps<Props>>) => {
20+
apply: async (wrappingTarget, thisArg, args: Parameters<GetStaticProps<Props>>) => {
2121
if (isBuild()) {
2222
return wrappingTarget.apply(thisArg, args);
2323
}
@@ -27,8 +27,6 @@ export function wrapGetStaticPropsWithSentry(
2727
parameterizedRoute,
2828
dataFetchingMethodName: 'getStaticProps',
2929
});
30-
31-
return errorWrappedGetStaticProps.apply(thisArg, args);
3230
},
3331
});
3432
}

0 commit comments

Comments
 (0)