Skip to content

Commit 5b03785

Browse files
committed
return of the scopes
1 parent 196ee63 commit 5b03785

File tree

3 files changed

+67
-19
lines changed

3 files changed

+67
-19
lines changed

dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/route-handlers.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ test('Should record exceptions and transactions for faulty route handlers', asyn
5454
expect(routehandlerError.tags?.['my-isolated-tag']).toBe(true);
5555
expect(routehandlerError.tags?.['my-global-scope-isolated-tag']).not.toBeDefined();
5656

57-
expect(routehandlerTransaction.contexts?.trace?.status).toBe('unknown_error');
57+
expect(routehandlerTransaction.contexts?.trace?.status).toBe('internal_error');
5858
expect(routehandlerTransaction.contexts?.trace?.op).toBe('http.server');
59-
expect(routehandlerTransaction.contexts?.trace?.origin).toBe('auto.function.nextjs');
59+
expect(routehandlerTransaction.contexts?.trace?.origin).toBe('auto');
6060

6161
expect(routehandlerError.exception?.values?.[0].value).toBe('route-handler-error');
6262

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1-
import { Scope, getActiveSpan, getCapturedScopesOnSpan, getRootSpan, setCapturedScopesOnSpan } from '@sentry/core';
1+
import {
2+
Scope,
3+
captureException,
4+
getActiveSpan,
5+
getCapturedScopesOnSpan,
6+
getRootSpan,
7+
handleCallbackErrors,
8+
setCapturedScopesOnSpan,
9+
withIsolationScope,
10+
withScope,
11+
} from '@sentry/core';
212

313
import type { RouteHandlerContext } from './types';
414

5-
import { commonObjectToIsolationScope } from './utils/tracingUtils';
15+
import { propagationContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils';
16+
17+
import { isRedirectNavigationError } from './nextNavigationErrorUtils';
18+
import { commonObjectToIsolationScope, commonObjectToPropagationContext } from './utils/tracingUtils';
619

720
/**
821
* Wraps a Next.js App Router Route handler with Sentry error and performance instrumentation.
@@ -14,24 +27,56 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
1427
routeHandler: F,
1528
context: RouteHandlerContext,
1629
): (...args: Parameters<F>) => ReturnType<F> extends Promise<unknown> ? ReturnType<F> : Promise<ReturnType<F>> {
17-
const { headers } = context;
30+
const { method, parameterizedRoute, headers } = context;
1831

1932
return new Proxy(routeHandler, {
20-
apply: (originalFunction, thisArg, args) => {
33+
apply: async (originalFunction, thisArg, args) => {
2134
const isolationScope = commonObjectToIsolationScope(headers);
2235

36+
const completeHeadersDict: Record<string, string> = headers ? winterCGHeadersToDict(headers) : {};
37+
38+
isolationScope.setSDKProcessingMetadata({
39+
request: {
40+
headers: completeHeadersDict,
41+
},
42+
});
43+
44+
const incomingPropagationContext = propagationContextFromHeaders(
45+
completeHeadersDict['sentry-trace'],
46+
completeHeadersDict['baggage'],
47+
);
48+
49+
const propagationContext = commonObjectToPropagationContext(headers, incomingPropagationContext);
50+
2351
const activeSpan = getActiveSpan();
2452
if (activeSpan) {
2553
const rootSpan = getRootSpan(activeSpan);
54+
rootSpan.setAttribute('sentry.route_handler', true);
2655
const { scope } = getCapturedScopesOnSpan(rootSpan);
2756
setCapturedScopesOnSpan(rootSpan, scope ?? new Scope(), isolationScope);
28-
29-
// We mark the root span as an app route handler span so we can allow-list it in our span processor
30-
// that would normally filter out all Next.js transactions/spans
31-
rootSpan.setAttribute('sentry.route_handler', true);
3257
}
3358

34-
return originalFunction.apply(thisArg, args);
59+
return withIsolationScope(isolationScope, () => {
60+
return withScope(scope => {
61+
scope.setTransactionName(`${method} ${parameterizedRoute}`);
62+
scope.setPropagationContext(propagationContext);
63+
return handleCallbackErrors(
64+
() => originalFunction.apply(thisArg, args),
65+
error => {
66+
// Next.js throws errors when calling `redirect()`. We don't wanna report these.
67+
if (isRedirectNavigationError(error)) {
68+
// Don't do anything
69+
} else {
70+
captureException(error, {
71+
mechanism: {
72+
handled: false,
73+
},
74+
});
75+
}
76+
},
77+
);
78+
});
79+
});
3580
},
3681
});
3782
}

packages/nextjs/src/server/index.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,10 @@ export function init(options: NodeOptions): NodeClient | undefined {
196196
// We want to rename these spans because they look like "GET /path/to/route" and we already emit spans that look
197197
// like this with our own http instrumentation.
198198
if (spanAttributes?.['next.span_type'] === 'BaseServer.handleRequest') {
199-
span.updateName('next server handler'); // This is all lowercase because the spans that Next.js emits by itself generally look like this.
199+
const rootSpan = getRootSpan(span);
200+
if (span !== rootSpan) {
201+
span.updateName('next server handler'); // This is all lowercase because the spans that Next.js emits by itself generally look like this.
202+
}
200203
}
201204
});
202205

@@ -307,16 +310,16 @@ export function init(options: NodeOptions): NodeClient | undefined {
307310
}
308311

309312
// Enhance route handler transactions
310-
if (
311-
event.type === 'transaction' &&
312-
event.transaction?.match(/^(GET|HEAD|POST|PUT|DELETE|CONNECT|OPTIONS|TRACE|PATCH) .*\/route$/) &&
313-
event.contexts?.trace?.data?.['sentry.route_handler'] === true &&
314-
!event.contexts.trace.op
315-
) {
313+
if (event.type === 'transaction' && event.contexts?.trace?.data?.['sentry.route_handler'] === true) {
316314
event.contexts.trace.data = event.contexts.trace.data || {};
317315
event.contexts.trace.data[SEMANTIC_ATTRIBUTE_SENTRY_OP] = 'http.server';
318316
event.contexts.trace.op = 'http.server';
319-
event.transaction = event.transaction.replace(/\/route$/, '');
317+
if (typeof event.contexts.trace.data[ATTR_HTTP_ROUTE] === 'string') {
318+
// eslint-disable-next-line deprecation/deprecation
319+
event.transaction = `${event.contexts.trace.data[SEMATTRS_HTTP_METHOD]} ${event.contexts.trace.data[
320+
ATTR_HTTP_ROUTE
321+
].replace(/\/route$/, '')}`;
322+
}
320323
}
321324

322325
return event;

0 commit comments

Comments
 (0)