@@ -57,9 +57,42 @@ const INTEGRATION_NAME_V3 = 'Fastify-V3';
57
57
58
58
export const instrumentFastifyV3 = generateInstrumentOnce ( INTEGRATION_NAME_V3 , ( ) => new FastifyInstrumentationV3 ( ) ) ;
59
59
60
+ function handleFastifyError (
61
+ this : {
62
+ diagnosticsChannelExists ?: boolean ;
63
+ } ,
64
+ error : Error ,
65
+ request : FastifyRequest & { opentelemetry ?: ( ) => { span ?: Span } } ,
66
+ reply : FastifyReply ,
67
+ shouldHandleError : ( error : Error , request : FastifyRequest , reply : FastifyReply ) => boolean ,
68
+ handlerOrigin : 'diagnostics-channel' | 'onError-hook' ,
69
+ ) : void {
70
+ // Diagnostics channel runs before the onError hook, so we can use it to check if the handler was already registered
71
+ if ( handlerOrigin === 'diagnostics-channel' ) {
72
+ this . diagnosticsChannelExists = true ;
73
+ }
74
+
75
+ if ( this . diagnosticsChannelExists && handlerOrigin === 'onError-hook' ) {
76
+ DEBUG_BUILD &&
77
+ logger . warn (
78
+ 'Fastify error handler was already registered via diagnostics channel.' ,
79
+ 'You can safely remove `setupFastifyErrorHandler` call.' ,
80
+ ) ;
81
+
82
+ // If the diagnostics channel already exists, we don't need to handle the error again
83
+ return ;
84
+ }
85
+
86
+ if ( shouldHandleError ( error , request , reply ) ) {
87
+ captureException ( error ) ;
88
+ }
89
+ }
90
+
60
91
export const instrumentFastify = generateInstrumentOnce ( INTEGRATION_NAME , ( ) => {
61
92
const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation ( ) ;
62
93
const plugin = fastifyOtelInstrumentationInstance . plugin ( ) ;
94
+ const options = fastifyOtelInstrumentationInstance . getConfig ( ) ;
95
+ const shouldHandleError = ( options as FastifyHandlerOptions ) ?. shouldHandleError || defaultShouldHandleError ;
63
96
64
97
// This message handler works for Fastify versions 3, 4 and 5
65
98
diagnosticsChannel . subscribe ( 'fastify.initialization' , message => {
@@ -78,8 +111,20 @@ export const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () =>
78
111
} ) ;
79
112
} ) ;
80
113
114
+ // This diagnostics channel only works on Fastify version 5
115
+ // For versions 3 and 4, we use `setupFastifyErrorHandler` instead
116
+ diagnosticsChannel . subscribe ( 'tracing:fastify.request.handler:error' , message => {
117
+ const { error, request, reply } = message as {
118
+ error : Error ;
119
+ request : FastifyRequest & { opentelemetry ?: ( ) => { span ?: Span } } ;
120
+ reply : FastifyReply ;
121
+ } ;
122
+
123
+ handleFastifyError . call ( handleFastifyError , error , request , reply , shouldHandleError , 'diagnostics-channel' ) ;
124
+ } ) ;
125
+
81
126
// Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation
82
- return fastifyOtelInstrumentationInstance as Instrumentation < InstrumentationConfig > ;
127
+ return fastifyOtelInstrumentationInstance as Instrumentation < InstrumentationConfig & FastifyHandlerOptions > ;
83
128
} ) ;
84
129
85
130
const _fastifyIntegration = ( ( ) => {
@@ -143,15 +188,11 @@ function defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply
143
188
*/
144
189
export function setupFastifyErrorHandler ( fastify : FastifyInstance , options ?: Partial < FastifyHandlerOptions > ) : void {
145
190
const shouldHandleError = options ?. shouldHandleError || defaultShouldHandleError ;
146
-
147
191
const plugin = Object . assign (
148
192
function ( fastify : FastifyInstance , _options : unknown , done : ( ) => void ) : void {
149
193
fastify . addHook ( 'onError' , async ( request , reply , error ) => {
150
- if ( shouldHandleError ( error , request , reply ) ) {
151
- captureException ( error ) ;
152
- }
194
+ handleFastifyError . call ( handleFastifyError , error , request , reply , shouldHandleError , 'onError-hook' ) ;
153
195
} ) ;
154
-
155
196
done ( ) ;
156
197
} ,
157
198
{
0 commit comments