Skip to content

Commit bd60b74

Browse files
author
Luca Forstner
committed
This fix is actually stupid
1 parent 0659e96 commit bd60b74

File tree

3 files changed

+60
-72
lines changed

3 files changed

+60
-72
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ test('Should record exceptions and transactions for faulty route handlers', asyn
6161
expect(routehandlerError.exception?.values?.[0].value).toBe('route-handler-error');
6262

6363
expect(routehandlerError.request?.method).toBe('PUT');
64-
expect(routehandlerError.request?.url).toContain('/route-handlers/[param]/error');
64+
expect(routehandlerError.request?.url).toContain('/route-handlers/baz/error');
6565

6666
expect(routehandlerError.transaction).toBe('PUT /route-handlers/[param]/error');
6767
});

packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts

Lines changed: 58 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
captureException,
66
getActiveSpan,
77
getCapturedScopesOnSpan,
8+
getIsolationScope,
89
getRootSpan,
910
handleCallbackErrors,
1011
setCapturedScopesOnSpan,
@@ -16,9 +17,8 @@ import {
1617
import type { RouteHandlerContext } from './types';
1718

1819
import { propagationContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils';
19-
2020
import { isNotFoundNavigationError, isRedirectNavigationError } from './nextNavigationErrorUtils';
21-
import { commonObjectToIsolationScope, commonObjectToPropagationContext } from './utils/tracingUtils';
21+
import { commonObjectToIsolationScope } from './utils/tracingUtils';
2222

2323
/**
2424
* Wraps a Next.js App Router Route handler with Sentry error and performance instrumentation.
@@ -34,82 +34,77 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
3434

3535
return new Proxy(routeHandler, {
3636
apply: async (originalFunction, thisArg, args) => {
37-
const isolationScope = commonObjectToIsolationScope(headers);
38-
39-
const completeHeadersDict: Record<string, string> = headers ? winterCGHeadersToDict(headers) : {};
40-
41-
isolationScope.setSDKProcessingMetadata({
42-
request: {
43-
url: parameterizedRoute,
44-
method,
45-
headers: completeHeadersDict,
46-
},
47-
});
48-
49-
const incomingPropagationContext = propagationContextFromHeaders(
50-
completeHeadersDict['sentry-trace'],
51-
completeHeadersDict['baggage'],
52-
);
53-
54-
const propagationContext = commonObjectToPropagationContext(headers, incomingPropagationContext);
55-
5637
const activeSpan = getActiveSpan();
5738
const rootSpan = activeSpan ? getRootSpan(activeSpan) : undefined;
58-
if (rootSpan) {
39+
40+
let edgeRuntimeIsolationScopeOverride: Scope | undefined;
41+
if (rootSpan && process.env.NEXT_RUNTIME === 'edge') {
42+
const isolationScope = commonObjectToIsolationScope(headers);
5943
const { scope } = getCapturedScopesOnSpan(rootSpan);
6044
setCapturedScopesOnSpan(rootSpan, scope ?? new Scope(), isolationScope);
6145

62-
if (process.env.NEXT_RUNTIME === 'edge') {
63-
rootSpan.updateName(`${method} ${parameterizedRoute}`);
64-
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
65-
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.server');
66-
}
46+
edgeRuntimeIsolationScopeOverride = isolationScope;
47+
48+
rootSpan.updateName(`${method} ${parameterizedRoute}`);
49+
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, 'route');
50+
rootSpan.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.server');
6751
}
6852

69-
return withIsolationScope(isolationScope, () => {
70-
return withScope(async scope => {
71-
scope.setTransactionName(`${method} ${parameterizedRoute}`);
72-
scope.setPropagationContext(propagationContext);
53+
return withIsolationScope(
54+
process.env.NEXT_RUNTIME === 'edge' ? edgeRuntimeIsolationScopeOverride : getIsolationScope(),
55+
() => {
56+
return withScope(async scope => {
57+
scope.setTransactionName(`${method} ${parameterizedRoute}`);
7358

74-
const response: Response = await handleCallbackErrors(
75-
() => originalFunction.apply(thisArg, args),
76-
error => {
77-
// Next.js throws errors when calling `redirect()`. We don't wanna report these.
78-
if (isRedirectNavigationError(error)) {
79-
// Don't do anything
80-
} else if (isNotFoundNavigationError(error)) {
59+
if (process.env.NEXT_RUNTIME === 'edge') {
60+
const completeHeadersDict: Record<string, string> = headers ? winterCGHeadersToDict(headers) : {};
61+
const incomingPropagationContext = propagationContextFromHeaders(
62+
completeHeadersDict['sentry-trace'],
63+
completeHeadersDict['baggage'],
64+
);
65+
scope.setPropagationContext(incomingPropagationContext);
66+
}
67+
68+
const response: Response = await handleCallbackErrors(
69+
() => originalFunction.apply(thisArg, args),
70+
error => {
71+
// Next.js throws errors when calling `redirect()`. We don't wanna report these.
72+
if (isRedirectNavigationError(error)) {
73+
// Don't do anything
74+
} else if (isNotFoundNavigationError(error)) {
75+
if (activeSpan) {
76+
setHttpStatus(activeSpan, 404);
77+
}
78+
if (rootSpan) {
79+
setHttpStatus(rootSpan, 404);
80+
}
81+
} else {
82+
captureException(error, {
83+
mechanism: {
84+
handled: false,
85+
},
86+
});
87+
}
88+
},
89+
);
90+
91+
try {
92+
if (response.status) {
8193
if (activeSpan) {
82-
setHttpStatus(activeSpan, 404);
94+
setHttpStatus(activeSpan, response.status);
8395
}
8496
if (rootSpan) {
85-
setHttpStatus(rootSpan, 404);
97+
setHttpStatus(rootSpan, response.status);
8698
}
87-
} else {
88-
captureException(error, {
89-
mechanism: {
90-
handled: false,
91-
},
92-
});
93-
}
94-
},
95-
);
96-
97-
try {
98-
if (response.status) {
99-
if (activeSpan) {
100-
setHttpStatus(activeSpan, response.status);
101-
}
102-
if (rootSpan) {
103-
setHttpStatus(rootSpan, response.status);
10499
}
100+
} catch {
101+
// best effort - response may be undefined?
105102
}
106-
} catch {
107-
// best effort - response may be undefined?
108-
}
109103

110-
return response;
111-
});
112-
});
104+
return response;
105+
});
106+
},
107+
);
113108
},
114109
});
115110
}

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

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,27 +38,20 @@ function wrapHandler<T>(handler: T, method: 'GET' | 'POST' | 'PUT' | 'PATCH' | '
3838

3939
return new Proxy(handler, {
4040
apply: (originalFunction, thisArg, args) => {
41-
let sentryTraceHeader: string | undefined | null = undefined;
42-
let baggageHeader: string | undefined | null = undefined;
4341
let headers: WebFetchHeaders | undefined = undefined;
4442

4543
// We try-catch here just in case the API around `requestAsyncStorage` changes unexpectedly since it is not public API
4644
try {
47-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
4845
const requestAsyncStore = requestAsyncStorage?.getStore() as ReturnType<RequestAsyncStorage['getStore']>;
49-
sentryTraceHeader = requestAsyncStore?.headers.get('sentry-trace') ?? undefined;
50-
baggageHeader = requestAsyncStore?.headers.get('baggage') ?? undefined;
5146
headers = requestAsyncStore?.headers;
5247
} catch (e) {
5348
/** empty */
5449
}
5550

56-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
51+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
5752
return Sentry.wrapRouteHandlerWithSentry(originalFunction as any, {
5853
method,
5954
parameterizedRoute: '__ROUTE__',
60-
sentryTraceHeader,
61-
baggageHeader,
6255
headers,
6356
}).apply(thisArg, args);
6457
},

0 commit comments

Comments
 (0)