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