11import {
2+ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
3+ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
24 SPAN_STATUS_ERROR ,
35 addTracingExtensions ,
46 captureException ,
57 getActiveSpan ,
68 getRootSpan ,
79 handleCallbackErrors ,
810 setHttpStatus ,
11+ startSpan ,
912 withIsolationScope ,
1013} from '@sentry/core' ;
14+ import type { Span } from '@sentry/types' ;
1115import { winterCGHeadersToDict } from '@sentry/utils' ;
1216import { isNotFoundNavigationError , isRedirectNavigationError } from './nextNavigationErrorUtils' ;
1317import type { RouteHandlerContext } from './types' ;
1418import { platformSupportsStreaming } from './utils/platformSupportsStreaming' ;
1519import { flushQueue } from './utils/responseEnd' ;
1620import { withIsolationScopeOrReuseFromRootSpan } from './utils/withIsolationScopeOrReuseFromRootSpan' ;
1721
22+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
23+ async function addSpanAttributes < F extends ( ...args : any [ ] ) => any > (
24+ originalFunction : F ,
25+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26+ thisArg : any ,
27+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28+ args : any [ ] ,
29+ rootSpan ?: Span ,
30+ ) : Promise < Response > {
31+ const response : Response = await handleCallbackErrors (
32+ ( ) => originalFunction . apply ( thisArg , args ) ,
33+ error => {
34+ // Next.js throws errors when calling `redirect()`. We don't wanna report these.
35+ if ( isRedirectNavigationError ( error ) ) {
36+ // Don't do anything
37+ } else if ( isNotFoundNavigationError ( error ) && rootSpan ) {
38+ rootSpan . setStatus ( { code : SPAN_STATUS_ERROR , message : 'not_found' } ) ;
39+ } else {
40+ captureException ( error , {
41+ mechanism : {
42+ handled : false ,
43+ } ,
44+ } ) ;
45+ }
46+ } ,
47+ ) ;
48+
49+ try {
50+ if ( rootSpan && response . status ) {
51+ setHttpStatus ( rootSpan , response . status ) ;
52+ }
53+ } catch {
54+ // best effort - response may be undefined?
55+ }
56+
57+ return response ;
58+ }
59+
1860/**
1961 * Wraps a Next.js route handler with performance and error instrumentation.
2062 */
@@ -25,10 +67,10 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
2567) : ( ...args : Parameters < F > ) => ReturnType < F > extends Promise < unknown > ? ReturnType < F > : Promise < ReturnType < F > > {
2668 addTracingExtensions ( ) ;
2769
28- const { headers } = context ;
70+ const { method , parameterizedRoute , headers } = context ;
2971
3072 return new Proxy ( routeHandler , {
31- apply : async ( originalFunction , thisArg , args ) => {
73+ apply : ( originalFunction , thisArg , args ) => {
3274 return withIsolationScope ( async isolationScope => {
3375 isolationScope . setSDKProcessingMetadata ( {
3476 request : {
@@ -40,33 +82,24 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
4082 const activeSpan = getActiveSpan ( ) ;
4183 const rootSpan = activeSpan && getRootSpan ( activeSpan ) ;
4284
43- const response : Response = await handleCallbackErrors (
44- ( ) => originalFunction . apply ( thisArg , args ) ,
45- error => {
46- // Next.js throws errors when calling `redirect()`. We don't wanna report these.
47- if ( isRedirectNavigationError ( error ) ) {
48- // Don't do anything
49- } else if ( isNotFoundNavigationError ( error ) && rootSpan ) {
50- rootSpan . setStatus ( { code : SPAN_STATUS_ERROR , message : 'not_found' } ) ;
51- } else {
52- captureException ( error , {
53- mechanism : {
54- handled : false ,
55- } ,
56- } ) ;
57- }
58- } ,
59- ) ;
60-
61- try {
62- if ( rootSpan && response . status ) {
63- setHttpStatus ( rootSpan , response . status ) ;
64- }
65- } catch {
66- // best effort - response may be undefined?
85+ if ( rootSpan ) {
86+ return await addSpanAttributes < F > ( originalFunction , thisArg , args , rootSpan ) ;
87+ } else {
88+ /** As our own HTTP integration is disabled (src/server/index.ts) the rootSpan comes from Next.js.
89+ * In case there is not root span, we start a new one. */
90+ return await startSpan (
91+ {
92+ op : 'http.server' ,
93+ name : `${ method } ${ parameterizedRoute } ` ,
94+ forceTransaction : true ,
95+ attributes : {
96+ [ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ] : 'route' ,
97+ [ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ] : 'auto.function.nextjs' ,
98+ } ,
99+ } ,
100+ async span => addSpanAttributes ( originalFunction , thisArg , args , span ) ,
101+ ) ;
67102 }
68-
69- return response ;
70103 } finally {
71104 if ( ! platformSupportsStreaming ( ) || process . env . NEXT_RUNTIME === 'edge' ) {
72105 // 1. Edge transport requires manual flushing
0 commit comments