1+ import type * as http from 'http' ;
12import { registerInstrumentations } from '@opentelemetry/instrumentation' ;
23import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express' ;
34import { defineIntegration } from '@sentry/core' ;
5+ import { captureException , getClient , getIsolationScope } from '@sentry/core' ;
46import type { IntegrationFn } from '@sentry/types' ;
57
8+ import type { NodeClient } from '../../sdk/client' ;
69import { addOriginToSpan } from '../../utils/addOriginToSpan' ;
710
811const _expressIntegration = ( ( ) => {
@@ -26,5 +29,89 @@ const _expressIntegration = (() => {
2629 * Express integration
2730 *
2831 * 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.
2933 */
3034export 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