From b2855dc6e3bb5638eb02ec8b7ffd1c674b56e21c Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Tue, 28 Jul 2026 12:17:50 +0200 Subject: [PATCH 1/2] feat(node)!: Drop support for Fastify `<3.21.0` Removes `FastifyInstrumentationV3` (and `instrumentFastifyV3`), the OpenTelemetry instrumentation that covered the early Fastify v3 range `>=3.0.0 <3.21.0`. The channel-based `fastifyIntegration` from `@sentry/server-utils` covers Fastify `>=3.21.0 <6`, so the node wrapper is reduced to a pass-through. Ref: #22346 Co-Authored-By: Claude Opus 4.8 --- .../src/integrations/tracing/fastify/index.ts | 23 +- .../tracing/fastify/v3/constants.ts | 34 -- .../fastify/v3/enums/AttributeNames.ts | 34 -- .../tracing/fastify/v3/instrumentation.ts | 334 ------------------ .../tracing/fastify/v3/internal-types.ts | 24 -- .../integrations/tracing/fastify/v3/types.ts | 41 --- .../integrations/tracing/fastify/v3/utils.ts | 137 ------- .../node/src/integrations/tracing/index.ts | 11 +- 8 files changed, 5 insertions(+), 633 deletions(-) delete mode 100644 packages/node/src/integrations/tracing/fastify/v3/constants.ts delete mode 100644 packages/node/src/integrations/tracing/fastify/v3/enums/AttributeNames.ts delete mode 100644 packages/node/src/integrations/tracing/fastify/v3/instrumentation.ts delete mode 100644 packages/node/src/integrations/tracing/fastify/v3/internal-types.ts delete mode 100644 packages/node/src/integrations/tracing/fastify/v3/types.ts delete mode 100644 packages/node/src/integrations/tracing/fastify/v3/utils.ts diff --git a/packages/node/src/integrations/tracing/fastify/index.ts b/packages/node/src/integrations/tracing/fastify/index.ts index 5b52965144aa..3b4977416908 100644 --- a/packages/node/src/integrations/tracing/fastify/index.ts +++ b/packages/node/src/integrations/tracing/fastify/index.ts @@ -1,8 +1,6 @@ -import type { Integration, IntegrationFn } from '@sentry/core'; -import { defineIntegration, extendIntegration, getClient } from '@sentry/core'; -import { generateInstrumentOnce } from '../../../otel/instrument'; +import type { Integration } from '@sentry/core'; +import { defineIntegration, getClient } from '@sentry/core'; import type { FastifyInstance, FastifyMinimal, FastifyReply, FastifyRequest } from './types'; -import { FastifyInstrumentationV3 } from './v3/instrumentation'; import { fastifyIntegration as serverUtilsFastifyIntegration, instrumentFastify, @@ -94,11 +92,6 @@ interface FastifyHandlerOptions { const INTEGRATION_NAME = 'Fastify' as const; -export const instrumentFastifyV3 = generateInstrumentOnce( - `${INTEGRATION_NAME}.v3`, - () => new FastifyInstrumentationV3(), -); - function getFastifyIntegration(): FastifyIntegration | undefined { const client = getClient(); if (!client) { @@ -108,16 +101,6 @@ function getFastifyIntegration(): FastifyIntegration | undefined { } } -const _fastifyIntegration = ((options: Partial) => { - const parentIntegration = serverUtilsFastifyIntegration(options) as FastifyIntegration; - - return extendIntegration(parentIntegration, { - setupOnce() { - instrumentFastifyV3(); - }, - }); -}) satisfies IntegrationFn; - /** * Adds Sentry tracing instrumentation for [Fastify](https://fastify.dev/). * @@ -135,7 +118,7 @@ const _fastifyIntegration = ((options: Partial) => { * ``` */ export const fastifyIntegration = defineIntegration((options: Partial = {}) => - _fastifyIntegration(options), + serverUtilsFastifyIntegration(options), ); /** diff --git a/packages/node/src/integrations/tracing/fastify/v3/constants.ts b/packages/node/src/integrations/tracing/fastify/v3/constants.ts deleted file mode 100644 index 10a64ed3b420..000000000000 --- a/packages/node/src/integrations/tracing/fastify/v3/constants.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Vendored from https://github.com/open-telemetry/opentelemetry-js-contrib/blob/407f61591ba69a39a6908264379d4d98a48dbec4/plugins/node/opentelemetry-instrumentation-fastify/src/constants.ts -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export const spanRequestSymbol = Symbol('opentelemetry.instrumentation.fastify.request_active_span'); - -// The instrumentation creates a span for invocations of lifecycle hook handlers -// that take `(request, reply, ...[, done])` arguments. Currently this is all -// lifecycle hooks except `onRequestAbort`. -// https://fastify.dev/docs/latest/Reference/Hooks -export const hooksNamesToWrap = new Set([ - 'onTimeout', - 'onRequest', - 'preParsing', - 'preValidation', - 'preSerialization', - 'preHandler', - 'onSend', - 'onResponse', - 'onError', -]); diff --git a/packages/node/src/integrations/tracing/fastify/v3/enums/AttributeNames.ts b/packages/node/src/integrations/tracing/fastify/v3/enums/AttributeNames.ts deleted file mode 100644 index 343b68747f06..000000000000 --- a/packages/node/src/integrations/tracing/fastify/v3/enums/AttributeNames.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Vendored from https://github.com/open-telemetry/opentelemetry-js-contrib/blob/407f61591ba69a39a6908264379d4d98a48dbec4/plugins/node/opentelemetry-instrumentation-fastify/src/enums/AttributeNames.ts -// -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -export enum AttributeNames { - FASTIFY_NAME = 'fastify.name', - FASTIFY_TYPE = 'fastify.type', - HOOK_NAME = 'hook.name', - PLUGIN_NAME = 'plugin.name', -} - -export enum FastifyTypes { - MIDDLEWARE = 'middleware', - REQUEST_HANDLER = 'request_handler', -} - -export enum FastifyNames { - MIDDLEWARE = 'middleware', - REQUEST_HANDLER = 'request handler', -} diff --git a/packages/node/src/integrations/tracing/fastify/v3/instrumentation.ts b/packages/node/src/integrations/tracing/fastify/v3/instrumentation.ts deleted file mode 100644 index d464b3d4d8cf..000000000000 --- a/packages/node/src/integrations/tracing/fastify/v3/instrumentation.ts +++ /dev/null @@ -1,334 +0,0 @@ -// Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/407f61591ba69a39a6908264379d4d98a48dbec4/plugins/node/opentelemetry-instrumentation-fastify/src/instrumentation.ts -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-this-alias */ -/* eslint-disable jsdoc/require-jsdoc */ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ - -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Attributes, context, SpanStatusCode, trace } from '@opentelemetry/api'; -import { - InstrumentationBase, - InstrumentationNodeModuleDefinition, - safeExecuteInTheMiddle, -} from '@opentelemetry/instrumentation'; -import { HTTP_ROUTE } from '@sentry/conventions/attributes'; -import type { Span } from '@sentry/core'; -import { - getClient, - getIsolationScope, - SDK_VERSION, - SEMANTIC_ATTRIBUTE_SENTRY_OP, - SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, - spanToJSON, -} from '@sentry/core'; -import { setHttpServerSpanRouteAttribute } from '../../../../utils/setHttpServerSpanRouteAttribute'; -import type { - FastifyErrorCodes, - FastifyInstance, - FastifyReply, - FastifyRequest, - HandlerOriginal, - HookHandlerDoneFunction, -} from '../types'; -import { AttributeNames, FastifyNames, FastifyTypes } from './enums/AttributeNames'; -import type { PluginFastifyReply } from './internal-types'; -import type { FastifyInstrumentationConfig } from './types'; -import { endSpan, safeExecuteInTheMiddleMaybePromise, startSpan } from './utils'; -/** @knipignore */ - -const PACKAGE_NAME = '@sentry/instrumentation-fastify-v3'; -const ANONYMOUS_NAME = 'anonymous'; - -// The instrumentation creates a span for invocations of lifecycle hook handlers -// that take `(request, reply, ...[, done])` arguments. Currently this is all -// lifecycle hooks except `onRequestAbort`. -// https://fastify.dev/docs/latest/Reference/Hooks -const hooksNamesToWrap = new Set([ - 'onTimeout', - 'onRequest', - 'preParsing', - 'preValidation', - 'preSerialization', - 'preHandler', - 'onSend', - 'onResponse', - 'onError', -]); - -/** - * Fastify instrumentation for OpenTelemetry - */ -export class FastifyInstrumentationV3 extends InstrumentationBase { - public constructor(config: FastifyInstrumentationConfig = {}) { - super(PACKAGE_NAME, SDK_VERSION, config); - } - - public init(): InstrumentationNodeModuleDefinition[] { - return [ - new InstrumentationNodeModuleDefinition('fastify', ['>=3.0.0 <3.21.0'], moduleExports => { - return this._patchConstructor(moduleExports); - }), - ]; - } - - private _hookOnRequest() { - const instrumentation = this; - - return function onRequest(request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) { - if (!instrumentation.isEnabled()) { - return done(); - } - instrumentation._wrap(reply, 'send', instrumentation._patchSend()); - - const anyRequest = request as any; - - const routeName = anyRequest.routeOptions - ? anyRequest.routeOptions.url // since fastify@4.10.0 - : request.routerPath; - if (routeName) { - setHttpServerSpanRouteAttribute(routeName); - } - - const method = request.method || 'GET'; - - getIsolationScope().setTransactionName(`${method} ${routeName}`); - done(); - }; - } - - private _wrapHandler( - pluginName: string, - hookName: string, - original: HandlerOriginal, - syncFunctionWithDone: boolean, - ): () => Promise { - const instrumentation = this; - this._diag.debug('Patching fastify route.handler function'); - - return function (this: any, ...args: unknown[]): Promise { - if (!instrumentation.isEnabled()) { - return original.apply(this, args); - } - - const name = original.name || pluginName || ANONYMOUS_NAME; - const spanName = `${FastifyNames.MIDDLEWARE} - ${name}`; - - const reply = args[1] as PluginFastifyReply; - - const span = startSpan(reply, instrumentation.tracer, spanName, { - [AttributeNames.FASTIFY_TYPE]: FastifyTypes.MIDDLEWARE, - [AttributeNames.PLUGIN_NAME]: pluginName, - [AttributeNames.HOOK_NAME]: hookName, - }); - - const origDone = syncFunctionWithDone && (args[args.length - 1] as HookHandlerDoneFunction); - if (origDone) { - args[args.length - 1] = function (...doneArgs: Parameters) { - endSpan(reply); - origDone.apply(this, doneArgs); - }; - } - - return context.with(trace.setSpan(context.active(), span), () => { - return safeExecuteInTheMiddleMaybePromise( - () => { - return original.apply(this, args); - }, - err => { - if (err instanceof Error) { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }); - span.recordException(err); - } - // async hooks should end the span as soon as the promise is resolved - if (!syncFunctionWithDone) { - endSpan(reply); - } - }, - ); - }); - }; - } - - private _wrapAddHook(): (original: FastifyInstance['addHook']) => () => FastifyInstance { - const instrumentation = this; - this._diag.debug('Patching fastify server.addHook function'); - - // biome-ignore lint/complexity/useArrowFunction: - return function (original: FastifyInstance['addHook']): () => FastifyInstance { - return function wrappedAddHook(this: any, ...args: any) { - const name = args[0] as string; - const handler = args[1] as HandlerOriginal; - const pluginName = this.pluginName; - if (!hooksNamesToWrap.has(name)) { - return original.apply(this, args); - } - - const syncFunctionWithDone = - typeof args[args.length - 1] === 'function' && handler.constructor.name !== 'AsyncFunction'; - - return original.apply(this, [ - name, - instrumentation._wrapHandler(pluginName, name, handler, syncFunctionWithDone), - ] as never); - }; - }; - } - - private _patchConstructor(moduleExports: { - fastify: () => FastifyInstance; - errorCodes: FastifyErrorCodes | undefined; - }): () => FastifyInstance { - const instrumentation = this; - - function fastify(this: FastifyInstance, ...args: any) { - const app: FastifyInstance = moduleExports.fastify.apply(this, args); - app.addHook('onRequest', instrumentation._hookOnRequest()); - app.addHook('preHandler', instrumentation._hookPreHandler()); - - instrumentClient(); - - instrumentation._wrap(app, 'addHook', instrumentation._wrapAddHook()); - - return app; - } - - if (moduleExports.errorCodes !== undefined) { - fastify.errorCodes = moduleExports.errorCodes; - } - fastify.fastify = fastify; - fastify.default = fastify; - return fastify; - } - - private _patchSend() { - const instrumentation = this; - this._diag.debug('Patching fastify reply.send function'); - - return function patchSend(original: () => FastifyReply): () => FastifyReply { - return function send(this: FastifyReply, ...args: any) { - const maybeError: any = args[0]; - - if (!instrumentation.isEnabled()) { - return original.apply(this, args); - } - - return safeExecuteInTheMiddle( - () => { - return original.apply(this, args); - }, - err => { - if (!err && maybeError instanceof Error) { - // eslint-disable-next-line no-param-reassign - err = maybeError; - } - endSpan(this, err); - }, - ); - }; - }; - } - - private _hookPreHandler() { - const instrumentation = this; - this._diag.debug('Patching fastify preHandler function'); - - return function preHandler(this: any, request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) { - if (!instrumentation.isEnabled()) { - return done(); - } - const anyRequest = request as any; - - const handler = anyRequest.routeOptions?.handler || anyRequest.context?.handler; - const handlerName = handler?.name.startsWith('bound ') ? handler.name.substring(6) : handler?.name; - const spanName = `${FastifyNames.REQUEST_HANDLER} - ${handlerName || this.pluginName || ANONYMOUS_NAME}`; - - const spanAttributes: Attributes = { - [AttributeNames.PLUGIN_NAME]: this.pluginName, - [AttributeNames.FASTIFY_TYPE]: FastifyTypes.REQUEST_HANDLER, - // eslint-disable-next-line typescript/no-deprecated - [HTTP_ROUTE]: anyRequest.routeOptions - ? anyRequest.routeOptions.url // since fastify@4.10.0 - : request.routerPath, - }; - if (handlerName) { - spanAttributes[AttributeNames.FASTIFY_NAME] = handlerName; - } - const span = startSpan(reply, instrumentation.tracer, spanName, spanAttributes); - - addFastifyV3SpanAttributes(span); - - const { requestHook } = instrumentation.getConfig(); - if (requestHook) { - safeExecuteInTheMiddle( - () => requestHook(span, { request }), - e => { - if (e) { - instrumentation._diag.error('request hook failed', e); - } - }, - true, - ); - } - - return context.with(trace.setSpan(context.active(), span), () => { - done(); - }); - }; - } -} - -function instrumentClient(): void { - const client = getClient(); - if (client) { - client.on('spanStart', (span: Span) => { - addFastifyV3SpanAttributes(span); - }); - } -} - -function addFastifyV3SpanAttributes(span: Span): void { - const attributes = spanToJSON(span).data; - - // this is one of: middleware, request_handler - const type = attributes['fastify.type']; - - // If this is already set, or we have no fastify span, no need to process again... - if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) { - return; - } - - span.setAttributes({ - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.fastify', - [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.fastify`, - }); - - // Also update the name, we don't need to "middleware - " prefix - const name = attributes['fastify.name'] || attributes['plugin.name'] || attributes['hook.name']; - if (typeof name === 'string') { - // Try removing `fastify -> ` and `@fastify/otel -> ` prefixes - // This is a bit of a hack, and not always working for all spans - // But it's the best we can do without a proper API - const updatedName = name.replace(/^fastify -> /, '').replace(/^@fastify\/otel -> /, ''); - - span.updateName(updatedName); - } -} diff --git a/packages/node/src/integrations/tracing/fastify/v3/internal-types.ts b/packages/node/src/integrations/tracing/fastify/v3/internal-types.ts deleted file mode 100644 index f71a92df0685..000000000000 --- a/packages/node/src/integrations/tracing/fastify/v3/internal-types.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/407f61591ba69a39a6908264379d4d98a48dbec4/plugins/node/opentelemetry-instrumentation-fastify/src/internal-types.ts -import type { Span } from '@opentelemetry/api'; -import type { FastifyReply } from '../types'; -import type { spanRequestSymbol } from './constants'; - -export type PluginFastifyReply = FastifyReply & { - [spanRequestSymbol]?: Span[]; -}; diff --git a/packages/node/src/integrations/tracing/fastify/v3/types.ts b/packages/node/src/integrations/tracing/fastify/v3/types.ts deleted file mode 100644 index 4d41a729f9d0..000000000000 --- a/packages/node/src/integrations/tracing/fastify/v3/types.ts +++ /dev/null @@ -1,41 +0,0 @@ -// Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/407f61591ba69a39a6908264379d4d98a48dbec4/plugins/node/opentelemetry-instrumentation-fastify/src/types.ts -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Span } from '@opentelemetry/api'; -import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; - -export interface FastifyRequestInfo { - request: any; // FastifyRequest object from fastify package -} - -/** - * Function that can be used to add custom attributes to the current span - * @param span - The Fastify handler span. - * @param info - The Fastify request info object. - */ -export interface FastifyCustomAttributeFunction { - (span: Span, info: FastifyRequestInfo): void; -} - -/** - * Options available for the Fastify Instrumentation - */ -export interface FastifyInstrumentationConfig extends InstrumentationConfig { - /** Function for adding custom attributes to each handler span */ - requestHook?: FastifyCustomAttributeFunction; -} diff --git a/packages/node/src/integrations/tracing/fastify/v3/utils.ts b/packages/node/src/integrations/tracing/fastify/v3/utils.ts deleted file mode 100644 index 26f0014a67e1..000000000000 --- a/packages/node/src/integrations/tracing/fastify/v3/utils.ts +++ /dev/null @@ -1,137 +0,0 @@ -// Vendored from: https://github.com/open-telemetry/opentelemetry-js-contrib/blob/407f61591ba69a39a6908264379d4d98a48dbec4/plugins/node/opentelemetry-instrumentation-fastify/src/utils.ts -/* eslint-disable jsdoc/require-jsdoc */ -/* eslint-disable @typescript-eslint/no-dynamic-delete */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/explicit-function-return-type */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* - * Copyright The OpenTelemetry Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { type Attributes, type Span, SpanStatusCode, type Tracer } from '@opentelemetry/api'; -import { spanRequestSymbol } from './constants'; -import type { PluginFastifyReply } from './internal-types'; - -/** - * Starts Span - * @param reply - reply function - * @param tracer - tracer - * @param spanName - span name - * @param spanAttributes - span attributes - */ -export function startSpan( - reply: PluginFastifyReply, - tracer: Tracer, - spanName: string, - spanAttributes: Attributes = {}, -) { - const span = tracer.startSpan(spanName, { attributes: spanAttributes }); - - const spans: Span[] = reply[spanRequestSymbol] || []; - spans.push(span); - - Object.defineProperty(reply, spanRequestSymbol, { - enumerable: false, - configurable: true, - value: spans, - }); - - return span; -} - -/** - * Ends span - * @param reply - reply function - * @param err - error - */ -export function endSpan(reply: PluginFastifyReply, err?: any) { - const spans = reply[spanRequestSymbol] || []; - // there is no active span, or it has already ended - if (!spans.length) { - return; - } - // biome-ignore lint/complexity/noForEach: - spans.forEach((span: Span) => { - if (err) { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: err.message, - }); - span.recordException(err); - } - span.end(); - }); - delete reply[spanRequestSymbol]; -} - -// @TODO after approve add this to instrumentation package and replace usage -// when it will be released - -/** - * This function handles the missing case from instrumentation package when - * execute can either return a promise or void. And using async is not an - * option as it is producing unwanted side effects. - * @param execute - function to be executed - * @param onFinish - function called when function executed - * @param preventThrowingError - prevent to throw error when execute - * function fails - */ -export function safeExecuteInTheMiddleMaybePromise( - execute: () => Promise, - onFinish: (e: unknown, result?: T) => void, - preventThrowingError?: boolean, -): Promise; -export function safeExecuteInTheMiddleMaybePromise( - execute: () => T, - onFinish: (e: unknown, result?: T) => void, - preventThrowingError?: boolean, -): T; -export function safeExecuteInTheMiddleMaybePromise( - execute: () => T | Promise, - onFinish: (e: unknown, result?: T) => void, - preventThrowingError?: boolean, -): T | Promise | undefined { - let error: unknown; - let result: T | Promise | undefined = undefined; - try { - result = execute(); - - if (isPromise(result)) { - result.then( - res => onFinish(undefined, res), - err => onFinish(err), - ); - } - } catch (e) { - error = e; - } finally { - if (!isPromise(result)) { - onFinish(error, result); - if (error && !preventThrowingError) { - // eslint-disable-next-line no-unsafe-finally - throw error; - } - } - // eslint-disable-next-line no-unsafe-finally - return result; - } -} - -function isPromise(val: T | Promise): val is Promise { - return ( - (typeof val === 'object' && val && typeof Object.getOwnPropertyDescriptor(val, 'then')?.value === 'function') || - false - ); -} diff --git a/packages/node/src/integrations/tracing/index.ts b/packages/node/src/integrations/tracing/index.ts index 337df789c1cc..8f87b0e69d9b 100644 --- a/packages/node/src/integrations/tracing/index.ts +++ b/packages/node/src/integrations/tracing/index.ts @@ -25,14 +25,12 @@ import { vercelAiIntegration, } from '@sentry/server-utils/orchestrion'; import { instrumentSentryHttp } from '../http'; -import { fastifyIntegration, instrumentFastifyV3 } from './fastify'; +import { fastifyIntegration } from './fastify'; import { redisIntegration } from './redis'; export function getAutoPerformanceIntegrations(): Integration[] { return [ expressIntegration(), - // Fastify keeps the node wrapper: the streamlined integration covers fastify `>=3.21.0 <6`, and - // the wrapper adds `instrumentFastifyV3` for the remaining early-v3 range (`>=3.0.0 <3.21.0`). fastifyIntegration(), graphqlDiagnosticsIntegration(), mongodbIntegration(), @@ -69,10 +67,5 @@ export function getAutoPerformanceIntegrations(): Integration[] { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => void) & { id: string })[] { - return [ - instrumentSentryHttp, - // The streamlined `Fastify` integration covers fastify `>=3.21.0 <6`; `instrumentFastifyV3` - // fills the remaining early-v3 gap (`>=3.0.0 <3.21.0`), so it stays preloaded here. - instrumentFastifyV3, - ]; + return [instrumentSentryHttp]; } From 341a3675a15b5ab58f2f80c7c7e8f6315d739be6 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Tue, 28 Jul 2026 13:19:37 +0200 Subject: [PATCH 2/2] Remove fastify 3.20 e2e variant The channel-based fastify integration only covers `>=3.21.0 <6`, so the `node-fastify-3` app's `fastify@~3.20.0` variant no longer has instrumentation and its span assertions would fail. The default `fastify@3.29.5` dependency keeps v3 covered. --- .../test-applications/node-fastify-3/package.json | 9 --------- .../node-fastify-3/tests/transactions.test.ts | 1 - 2 files changed, 10 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json b/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json index 49a15f399f4d..3fa36adbbbd5 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json +++ b/dev-packages/e2e-tests/test-applications/node-fastify-3/package.json @@ -10,7 +10,6 @@ "clean": "npx rimraf node_modules pnpm-lock.yaml", "typecheck": "tsc", "test:build": "pnpm install && pnpm run typecheck", - "test:build-3_20": "pnpm install && pnpm add fastify@~3.20.0 && pnpm run typecheck", "test:assert": "pnpm test && pnpm test:override" }, "dependencies": { @@ -26,13 +25,5 @@ }, "volta": { "extends": "../../package.json" - }, - "sentryTest": { - "variants": [ - { - "build-command": "pnpm test:build-3_20", - "label": "fastify v3.20.0" - } - ] } } diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-3/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-fastify-3/tests/transactions.test.ts index b5c72b180d96..2912f27eec34 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-3/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-fastify-3/tests/transactions.test.ts @@ -98,7 +98,6 @@ test.skip('Sends an API route transaction', async ({ baseURL }) => { data: expect.objectContaining({ 'sentry.origin': 'auto.http.otel.fastify', 'sentry.op': 'request_handler.fastify', - // format is slightly different in v3.20.0 and v3.21.0 'fastify.type': expect.stringMatching(/request[-_]handler/), 'http.route': '/test-transaction', }),