1
+ import type * as http from 'http' ;
1
2
import { registerInstrumentations } from '@opentelemetry/instrumentation' ;
2
3
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express' ;
3
4
import { defineIntegration } from '@sentry/core' ;
5
+ import { captureException , getClient , getIsolationScope } from '@sentry/core' ;
4
6
import type { IntegrationFn } from '@sentry/types' ;
5
7
8
+ import type { NodeClient } from '../../sdk/client' ;
6
9
import { addOriginToSpan } from '../../utils/addOriginToSpan' ;
7
10
8
11
const _expressIntegration = ( ( ) => {
@@ -26,5 +29,89 @@ const _expressIntegration = (() => {
26
29
* Express integration
27
30
*
28
31
* Capture tracing data for express.
32
+ * In order to capture exceptions, you have to call `setupExpressErrorHandler(app)` before any other middleware and after all controllers.
29
33
*/
30
34
export const expressIntegration = defineIntegration ( _expressIntegration ) ;
35
+
36
+ interface MiddlewareError extends Error {
37
+ status ?: number | string ;
38
+ statusCode ?: number | string ;
39
+ status_code ?: number | string ;
40
+ output ?: {
41
+ statusCode ?: number | string ;
42
+ } ;
43
+ }
44
+
45
+ type ExpressMiddleware = (
46
+ error : MiddlewareError ,
47
+ req : http . IncomingMessage ,
48
+ res : http . ServerResponse ,
49
+ next : ( error : MiddlewareError ) => void ,
50
+ ) => void ;
51
+
52
+ /**
53
+ * An Express-compatible error handler.
54
+ */
55
+ export function expressErrorHandler ( options ?: {
56
+ /**
57
+ * Callback method deciding whether error should be captured and sent to Sentry
58
+ * @param error Captured middleware error
59
+ */
60
+ shouldHandleError ?( this : void , error : MiddlewareError ) : boolean ;
61
+ } ) : ExpressMiddleware {
62
+ return function sentryErrorMiddleware (
63
+ error : MiddlewareError ,
64
+ _req : http . IncomingMessage ,
65
+ res : http . ServerResponse ,
66
+ next : ( error : MiddlewareError ) => void ,
67
+ ) : void {
68
+ const shouldHandleError = options ?. shouldHandleError || defaultShouldHandleError ;
69
+
70
+ if ( shouldHandleError ( error ) ) {
71
+ const client = getClient < NodeClient > ( ) ;
72
+ if ( client && client . getOptions ( ) . autoSessionTracking ) {
73
+ // Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the
74
+ // `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only
75
+ // instantiated when the the`requestHandler` middleware is initialised, which indicates that we should be
76
+ // running in SessionAggregates mode
77
+ const isSessionAggregatesMode = client [ '_sessionFlusher' ] !== undefined ;
78
+ if ( isSessionAggregatesMode ) {
79
+ const requestSession = getIsolationScope ( ) . getRequestSession ( ) ;
80
+ // If an error bubbles to the `errorHandler`, then this is an unhandled error, and should be reported as a
81
+ // Crashed session. The `_requestSession.status` is checked to ensure that this error is happening within
82
+ // the bounds of a request, and if so the status is updated
83
+ if ( requestSession && requestSession . status !== undefined ) {
84
+ requestSession . status = 'crashed' ;
85
+ }
86
+ }
87
+ }
88
+
89
+ const eventId = captureException ( error , { mechanism : { type : 'middleware' , handled : false } } ) ;
90
+ ( res as { sentry ?: string } ) . sentry = eventId ;
91
+ next ( error ) ;
92
+
93
+ return ;
94
+ }
95
+
96
+ next ( error ) ;
97
+ } ;
98
+ }
99
+
100
+ /**
101
+ * Setup an error handler for Express.
102
+ * The error handler must be before any other middleware and after all controllers.
103
+ */
104
+ export function setupExpressErrorHandler ( app : { use : ( middleware : ExpressMiddleware ) => unknown } ) : void {
105
+ app . use ( expressErrorHandler ( ) ) ;
106
+ }
107
+
108
+ function getStatusCodeFromResponse ( error : MiddlewareError ) : number {
109
+ const statusCode = error . status || error . statusCode || error . status_code || ( error . output && error . output . statusCode ) ;
110
+ return statusCode ? parseInt ( statusCode as string , 10 ) : 500 ;
111
+ }
112
+
113
+ /** Returns true if response code is internal server error */
114
+ function defaultShouldHandleError ( error : MiddlewareError ) : boolean {
115
+ const status = getStatusCodeFromResponse ( error ) ;
116
+ return status >= 500 ;
117
+ }
0 commit comments