@@ -57,9 +57,42 @@ const INTEGRATION_NAME_V3 = 'Fastify-V3';
5757
5858export const instrumentFastifyV3 = generateInstrumentOnce ( INTEGRATION_NAME_V3 , ( ) => new FastifyInstrumentationV3 ( ) ) ;
5959
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+
6091export const instrumentFastify = generateInstrumentOnce ( INTEGRATION_NAME , ( ) => {
6192 const fastifyOtelInstrumentationInstance = new FastifyOtelInstrumentation ( ) ;
6293 const plugin = fastifyOtelInstrumentationInstance . plugin ( ) ;
94+ const options = fastifyOtelInstrumentationInstance . getConfig ( ) ;
95+ const shouldHandleError = ( options as FastifyHandlerOptions ) ?. shouldHandleError || defaultShouldHandleError ;
6396
6497 // This message handler works for Fastify versions 3, 4 and 5
6598 diagnosticsChannel . subscribe ( 'fastify.initialization' , message => {
@@ -78,8 +111,20 @@ export const instrumentFastify = generateInstrumentOnce(INTEGRATION_NAME, () =>
78111 } ) ;
79112 } ) ;
80113
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+
81126 // 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 > ;
83128} ) ;
84129
85130const _fastifyIntegration = ( ( ) => {
@@ -143,15 +188,11 @@ function defaultShouldHandleError(_error: Error, _request: FastifyRequest, reply
143188 */
144189export function setupFastifyErrorHandler ( fastify : FastifyInstance , options ?: Partial < FastifyHandlerOptions > ) : void {
145190 const shouldHandleError = options ?. shouldHandleError || defaultShouldHandleError ;
146-
147191 const plugin = Object . assign (
148192 function ( fastify : FastifyInstance , _options : unknown , done : ( ) => void ) : void {
149193 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' ) ;
153195 } ) ;
154-
155196 done ( ) ;
156197 } ,
157198 {
0 commit comments