1
1
import {
2
+ SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN ,
3
+ SEMANTIC_ATTRIBUTE_SENTRY_SOURCE ,
2
4
SPAN_STATUS_ERROR ,
3
5
addTracingExtensions ,
4
6
captureException ,
5
7
getActiveSpan ,
6
8
getRootSpan ,
7
9
handleCallbackErrors ,
8
10
setHttpStatus ,
11
+ startSpan ,
9
12
withIsolationScope ,
10
13
} from '@sentry/core' ;
14
+ import type { Span } from '@sentry/types' ;
11
15
import { winterCGHeadersToDict } from '@sentry/utils' ;
12
16
import { isNotFoundNavigationError , isRedirectNavigationError } from './nextNavigationErrorUtils' ;
13
17
import type { RouteHandlerContext } from './types' ;
14
18
import { platformSupportsStreaming } from './utils/platformSupportsStreaming' ;
15
19
import { flushQueue } from './utils/responseEnd' ;
16
20
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
+
17
59
/**
18
60
* Wraps a Next.js route handler with performance and error instrumentation.
19
61
*/
@@ -24,10 +66,10 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
24
66
) : ( ...args : Parameters < F > ) => ReturnType < F > extends Promise < unknown > ? ReturnType < F > : Promise < ReturnType < F > > {
25
67
addTracingExtensions ( ) ;
26
68
27
- const { headers } = context ;
69
+ const { method , parameterizedRoute , headers } = context ;
28
70
29
71
return new Proxy ( routeHandler , {
30
- apply : async ( originalFunction , thisArg , args ) => {
72
+ apply : ( originalFunction , thisArg , args ) => {
31
73
return withIsolationScope ( async isolationScope => {
32
74
isolationScope . setSDKProcessingMetadata ( {
33
75
request : {
@@ -39,33 +81,24 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
39
81
const activeSpan = getActiveSpan ( ) ;
40
82
const rootSpan = activeSpan && getRootSpan ( activeSpan ) ;
41
83
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
+ ) ;
66
101
}
67
-
68
- return response ;
69
102
} finally {
70
103
if ( ! platformSupportsStreaming ( ) || process . env . NEXT_RUNTIME === 'edge' ) {
71
104
// 1. Edge transport requires manual flushing
0 commit comments