Skip to content

Commit 08a3b7c

Browse files
authored
feat(nextjs): Set transaction names on scope for route handlers and generation functions (#11869)
for server components: #11850
1 parent ac59e7e commit 08a3b7c

File tree

2 files changed

+94
-90
lines changed

2 files changed

+94
-90
lines changed

packages/nextjs/src/common/wrapGenerationFunctionWithSentry.ts

Lines changed: 45 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import {
44
SPAN_STATUS_OK,
55
captureException,
66
getClient,
7-
getCurrentScope,
87
handleCallbackErrors,
98
startSpanManual,
109
withIsolationScope,
10+
withScope,
1111
} from '@sentry/core';
1212
import type { WebFetchHeaders } from '@sentry/types';
1313
import { propagationContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils';
@@ -59,51 +59,53 @@ export function wrapGenerationFunctionWithSentry<F extends (...args: any[]) => a
5959
const propagationContext = commonObjectToPropagationContext(headers, incomingPropagationContext);
6060

6161
return withIsolationScope(isolationScope, () => {
62-
isolationScope.setTransactionName(`${componentType}.${generationFunctionIdentifier} (${componentRoute})`);
63-
isolationScope.setSDKProcessingMetadata({
64-
request: {
65-
headers: headers ? winterCGHeadersToDict(headers) : undefined,
66-
},
67-
});
62+
return withScope(scope => {
63+
scope.setTransactionName(`${componentType}.${generationFunctionIdentifier} (${componentRoute})`);
64+
isolationScope.setSDKProcessingMetadata({
65+
request: {
66+
headers: headers ? winterCGHeadersToDict(headers) : undefined,
67+
},
68+
});
6869

69-
getCurrentScope().setExtra('route_data', data);
70-
getCurrentScope().setPropagationContext(propagationContext);
70+
scope.setExtra('route_data', data);
71+
scope.setPropagationContext(propagationContext);
7172

72-
return startSpanManual(
73-
{
74-
op: 'function.nextjs',
75-
name: `${componentType}.${generationFunctionIdentifier} (${componentRoute})`,
76-
forceTransaction: true,
77-
attributes: {
78-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
79-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
80-
},
81-
},
82-
span => {
83-
return handleCallbackErrors(
84-
() => originalFunction.apply(thisArg, args),
85-
err => {
86-
if (isNotFoundNavigationError(err)) {
87-
// We don't want to report "not-found"s
88-
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'not_found' });
89-
} else if (isRedirectNavigationError(err)) {
90-
// We don't want to report redirects
91-
span.setStatus({ code: SPAN_STATUS_OK });
92-
} else {
93-
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
94-
captureException(err, {
95-
mechanism: {
96-
handled: false,
97-
},
98-
});
99-
}
100-
},
101-
() => {
102-
span.end();
73+
return startSpanManual(
74+
{
75+
op: 'function.nextjs',
76+
name: `${componentType}.${generationFunctionIdentifier} (${componentRoute})`,
77+
forceTransaction: true,
78+
attributes: {
79+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
80+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
10381
},
104-
);
105-
},
106-
);
82+
},
83+
span => {
84+
return handleCallbackErrors(
85+
() => originalFunction.apply(thisArg, args),
86+
err => {
87+
if (isNotFoundNavigationError(err)) {
88+
// We don't want to report "not-found"s
89+
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'not_found' });
90+
} else if (isRedirectNavigationError(err)) {
91+
// We don't want to report redirects
92+
span.setStatus({ code: SPAN_STATUS_OK });
93+
} else {
94+
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
95+
captureException(err, {
96+
mechanism: {
97+
handled: false,
98+
},
99+
});
100+
}
101+
},
102+
() => {
103+
span.end();
104+
},
105+
);
106+
},
107+
);
108+
});
107109
});
108110
});
109111
},

packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts

Lines changed: 49 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import {
44
SEMANTIC_ATTRIBUTE_SENTRY_SOURCE,
55
SPAN_STATUS_ERROR,
66
captureException,
7-
getCurrentScope,
87
handleCallbackErrors,
98
setHttpStatus,
109
startSpan,
1110
withIsolationScope,
11+
withScope,
1212
} from '@sentry/core';
1313
import { propagationContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils';
1414
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
@@ -51,57 +51,59 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
5151

5252
const propagationContext = commonObjectToPropagationContext(headers, incomingPropagationContext);
5353

54-
return withIsolationScope(isolationScope, async () => {
55-
isolationScope.setTransactionName(`${method} ${parameterizedRoute}`);
56-
getCurrentScope().setPropagationContext(propagationContext);
57-
try {
58-
return startSpan(
59-
{
60-
name: `${method} ${parameterizedRoute}`,
61-
attributes: {
62-
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
63-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',
64-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
65-
},
66-
forceTransaction: true,
67-
},
68-
async span => {
69-
const response: Response = await handleCallbackErrors(
70-
() => originalFunction.apply(thisArg, args),
71-
error => {
72-
// Next.js throws errors when calling `redirect()`. We don't wanna report these.
73-
if (isRedirectNavigationError(error)) {
74-
// Don't do anything
75-
} else if (isNotFoundNavigationError(error) && span) {
76-
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'not_found' });
77-
} else {
78-
captureException(error, {
79-
mechanism: {
80-
handled: false,
81-
},
82-
});
83-
}
54+
return withIsolationScope(isolationScope, () => {
55+
return withScope(async scope => {
56+
scope.setTransactionName(`${method} ${parameterizedRoute}`);
57+
scope.setPropagationContext(propagationContext);
58+
try {
59+
return startSpan(
60+
{
61+
name: `${method} ${parameterizedRoute}`,
62+
attributes: {
63+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route',
64+
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.server',
65+
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs',
8466
},
85-
);
67+
forceTransaction: true,
68+
},
69+
async span => {
70+
const response: Response = await handleCallbackErrors(
71+
() => originalFunction.apply(thisArg, args),
72+
error => {
73+
// Next.js throws errors when calling `redirect()`. We don't wanna report these.
74+
if (isRedirectNavigationError(error)) {
75+
// Don't do anything
76+
} else if (isNotFoundNavigationError(error) && span) {
77+
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'not_found' });
78+
} else {
79+
captureException(error, {
80+
mechanism: {
81+
handled: false,
82+
},
83+
});
84+
}
85+
},
86+
);
8687

87-
try {
88-
if (span && response.status) {
89-
setHttpStatus(span, response.status);
88+
try {
89+
if (span && response.status) {
90+
setHttpStatus(span, response.status);
91+
}
92+
} catch {
93+
// best effort - response may be undefined?
9094
}
91-
} catch {
92-
// best effort - response may be undefined?
93-
}
9495

95-
return response;
96-
},
97-
);
98-
} finally {
99-
if (!platformSupportsStreaming() || process.env.NEXT_RUNTIME === 'edge') {
100-
// 1. Edge transport requires manual flushing
101-
// 2. Lambdas require manual flushing to prevent execution freeze before the event is sent
102-
await flushQueue();
96+
return response;
97+
},
98+
);
99+
} finally {
100+
if (!platformSupportsStreaming() || process.env.NEXT_RUNTIME === 'edge') {
101+
// 1. Edge transport requires manual flushing
102+
// 2. Lambdas require manual flushing to prevent execution freeze before the event is sent
103+
await flushQueue();
104+
}
103105
}
104-
}
106+
});
105107
});
106108
});
107109
},

0 commit comments

Comments
 (0)