From d3bbf9f9bcbb640f4fe12c37f39dd743159e2b1c Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Fri, 20 Feb 2026 11:15:28 +0100 Subject: [PATCH 1/2] feat(node): Bump to latest @fastify/otel This PR unvendors `@fastify/otel` and bumps to the latest version. We can do this now because the newest `minimatch` expanded node support to node 18. Closes: #19450 --- packages/node/package.json | 4 +- .../tracing/fastify/fastify-otel/.eslintrc.js | 9 - .../tracing/fastify/fastify-otel/index.d.ts | 29 -- .../tracing/fastify/fastify-otel/index.js | 487 ------------------ .../src/integrations/tracing/fastify/index.ts | 6 +- yarn.lock | 50 +- 6 files changed, 48 insertions(+), 537 deletions(-) delete mode 100644 packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js delete mode 100644 packages/node/src/integrations/tracing/fastify/fastify-otel/index.d.ts delete mode 100644 packages/node/src/integrations/tracing/fastify/fastify-otel/index.js diff --git a/packages/node/package.json b/packages/node/package.json index 622cd2b4ecc4..fc8e038bc176 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -95,11 +95,11 @@ "@opentelemetry/sdk-trace-base": "^2.5.0", "@opentelemetry/semantic-conventions": "^1.39.0", "@prisma/instrumentation": "7.2.0", + "@fastify/otel": "0.16.0", "@sentry/core": "10.39.0", "@sentry/node-core": "10.39.0", "@sentry/opentelemetry": "10.39.0", - "import-in-the-middle": "^2.0.6", - "minimatch": "^9.0.0" + "import-in-the-middle": "^2.0.6" }, "devDependencies": { "@types/node": "^18.19.1" diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js b/packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js deleted file mode 100644 index 9b2b2d51af09..000000000000 --- a/packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js +++ /dev/null @@ -1,9 +0,0 @@ -module.exports = { - env: { - node: true, - }, - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - }, -}; diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.d.ts b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.d.ts deleted file mode 100644 index 48b5297e074c..000000000000 --- a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.d.ts +++ /dev/null @@ -1,29 +0,0 @@ -/* eslint-disable @typescript-eslint/explicit-member-accessibility */ -/// - -import type { InstrumentationNodeModuleDefinition } from '@opentelemetry/instrumentation'; -import { InstrumentationBase } from '@opentelemetry/instrumentation'; -import type { FastifyPluginCallback } from 'fastify'; -import type { FastifyOtelInstrumentationOpts, FastifyOtelOptions, FastifyOtelRequestContext } from './types'; - -declare module 'fastify' { - interface FastifyRequest { - opentelemetry(): FastifyOtelRequestContext; - } -} - -declare class FastifyOtelInstrumentation< - Config extends FastifyOtelInstrumentationOpts = FastifyOtelInstrumentationOpts, -> extends InstrumentationBase { - servername: string; - constructor(config?: FastifyOtelInstrumentationOpts); - init(): InstrumentationNodeModuleDefinition[]; - plugin(): FastifyPluginCallback; -} - -declare namespace exported { - export type { FastifyOtelInstrumentationOpts }; - export { FastifyOtelInstrumentation }; -} - -export = exported; diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js deleted file mode 100644 index a3021cba20e0..000000000000 --- a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js +++ /dev/null @@ -1,487 +0,0 @@ -/* -Vendored in and modified from @fastify/otel version 0.8.0 -https://github.com/fastify/otel/releases/tag/v0.8.0 - -Tried not to modify the original code too much keeping it as a JavaScript CJS module to make it easier to update when required - -Modifications include: -- Removed reading of package.json to get the version and package name - -MIT License - -Copyright (c) 2024 Fastify - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. -*/ - -/* eslint-disable jsdoc/require-jsdoc */ -/* eslint-disable max-lines */ -/* eslint-disable no-param-reassign */ -/* eslint-disable import/named */ -import dc from 'node:diagnostics_channel'; -import { context, diag, propagation, SpanStatusCode, trace } from '@opentelemetry/api'; -import { getRPCMetadata, RPCType } from '@opentelemetry/core'; -import { InstrumentationBase } from '@opentelemetry/instrumentation'; -import { - ATTR_HTTP_REQUEST_METHOD, - ATTR_HTTP_RESPONSE_STATUS_CODE, - ATTR_HTTP_ROUTE, - ATTR_SERVICE_NAME, -} from '@opentelemetry/semantic-conventions'; -import * as minimatch from 'minimatch'; - -// SENTRY VENDOR NOTE -// Instead of using the package.json file, we hard code the package name and version here. -const PACKAGE_NAME = '@fastify/otel'; -const PACKAGE_VERSION = '0.8.0'; - -// Constants -const SUPPORTED_VERSIONS = '>=4.0.0 <6'; -const FASTIFY_HOOKS = [ - 'onRequest', - 'preParsing', - 'preValidation', - 'preHandler', - 'preSerialization', - 'onSend', - 'onResponse', - 'onError', -]; -const ATTRIBUTE_NAMES = { - HOOK_NAME: 'hook.name', - FASTIFY_TYPE: 'fastify.type', - HOOK_CALLBACK_NAME: 'hook.callback.name', - ROOT: 'fastify.root', -}; -const HOOK_TYPES = { - ROUTE: 'route-hook', - INSTANCE: 'hook', - HANDLER: 'request-handler', -}; -const ANONYMOUS_FUNCTION_NAME = 'anonymous'; - -// Symbols -const kInstrumentation = Symbol('fastify otel instance'); -const kRequestSpan = Symbol('fastify otel request spans'); -const kRequestContext = Symbol('fastify otel request context'); -const kAddHookOriginal = Symbol('fastify otel addhook original'); -const kSetNotFoundOriginal = Symbol('fastify otel setnotfound original'); -const kIgnorePaths = Symbol('fastify otel ignore path'); - -export class FastifyOtelInstrumentation extends InstrumentationBase { - constructor(config) { - super(PACKAGE_NAME, PACKAGE_VERSION, config); - this.servername = config?.servername ?? process.env.OTEL_SERVICE_NAME ?? 'fastify'; - this[kIgnorePaths] = null; - this._logger = diag.createComponentLogger({ namespace: PACKAGE_NAME }); - - if (config?.ignorePaths != null || process.env.OTEL_FASTIFY_IGNORE_PATHS != null) { - const ignorePaths = config?.ignorePaths ?? process.env.OTEL_FASTIFY_IGNORE_PATHS; - - if ((typeof ignorePaths !== 'string' || ignorePaths.length === 0) && typeof ignorePaths !== 'function') { - throw new TypeError('ignorePaths must be a string or a function'); - } - - const globMatcher = minimatch.minimatch; - - this[kIgnorePaths] = routeOptions => { - if (typeof ignorePaths === 'function') { - return ignorePaths(routeOptions); - } else { - return globMatcher(routeOptions.url, ignorePaths); - } - }; - } - } - - enable() { - if (this._handleInitialization === undefined && this.getConfig().registerOnInitialization) { - const FastifyInstrumentationPlugin = this.plugin(); - this._handleInitialization = message => { - message.fastify.register(FastifyInstrumentationPlugin); - }; - dc.subscribe('fastify.initialization', this._handleInitialization); - } - return super.enable(); - } - - disable() { - if (this._handleInitialization) { - dc.unsubscribe('fastify.initialization', this._handleInitialization); - this._handleInitialization = undefined; - } - return super.disable(); - } - - // We do not do patching in this instrumentation - init() { - return []; - } - - plugin() { - const instrumentation = this; - - FastifyInstrumentationPlugin[Symbol.for('skip-override')] = true; - FastifyInstrumentationPlugin[Symbol.for('fastify.display-name')] = '@fastify/otel'; - FastifyInstrumentationPlugin[Symbol.for('plugin-meta')] = { - fastify: SUPPORTED_VERSIONS, - name: '@fastify/otel', - }; - - return FastifyInstrumentationPlugin; - - function FastifyInstrumentationPlugin(instance, opts, done) { - instance.decorate(kInstrumentation, instrumentation); - // addHook and notfoundHandler are essentially inherited from the prototype - // what is important is to bound it to the right instance - instance.decorate(kAddHookOriginal, instance.addHook); - instance.decorate(kSetNotFoundOriginal, instance.setNotFoundHandler); - instance.decorateRequest('opentelemetry', function openetelemetry() { - const ctx = this[kRequestContext]; - const span = this[kRequestSpan]; - return { - span, - tracer: instrumentation.tracer, - context: ctx, - inject: (carrier, setter) => { - return propagation.inject(ctx, carrier, setter); - }, - extract: (carrier, getter) => { - return propagation.extract(ctx, carrier, getter); - }, - }; - }); - instance.decorateRequest(kRequestSpan, null); - instance.decorateRequest(kRequestContext, null); - - instance.addHook('onRoute', function (routeOptions) { - if (instrumentation[kIgnorePaths]?.(routeOptions) === true) { - instrumentation._logger.debug( - `Ignoring route instrumentation ${routeOptions.method} ${routeOptions.url} because it matches the ignore path`, - ); - return; - } - - for (const hook of FASTIFY_HOOKS) { - if (routeOptions[hook] != null) { - const handlerLike = routeOptions[hook]; - - if (typeof handlerLike === 'function') { - routeOptions[hook] = handlerWrapper(handlerLike, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - route -> ${hook}`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.ROUTE, - [ATTR_HTTP_ROUTE]: routeOptions.url, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - handlerLike.name?.length > 0 ? handlerLike.name : ANONYMOUS_FUNCTION_NAME /* c8 ignore next */, - }); - } else if (Array.isArray(handlerLike)) { - const wrappedHandlers = []; - - for (const handler of handlerLike) { - wrappedHandlers.push( - handlerWrapper(handler, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - route -> ${hook}`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.ROUTE, - [ATTR_HTTP_ROUTE]: routeOptions.url, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME, - }), - ); - } - - routeOptions[hook] = wrappedHandlers; - } - } - } - - // We always want to add the onSend hook to the route to be executed last - if (routeOptions.onSend != null) { - routeOptions.onSend = Array.isArray(routeOptions.onSend) - ? [...routeOptions.onSend, onSendHook] - : [routeOptions.onSend, onSendHook]; - } else { - routeOptions.onSend = onSendHook; - } - - // We always want to add the onError hook to the route to be executed last - if (routeOptions.onError != null) { - routeOptions.onError = Array.isArray(routeOptions.onError) - ? [...routeOptions.onError, onErrorHook] - : [routeOptions.onError, onErrorHook]; - } else { - routeOptions.onError = onErrorHook; - } - - routeOptions.handler = handlerWrapper(routeOptions.handler, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - route-handler`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.HANDLER, - [ATTR_HTTP_ROUTE]: routeOptions.url, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - routeOptions.handler.name.length > 0 ? routeOptions.handler.name : ANONYMOUS_FUNCTION_NAME, - }); - }); - - instance.addHook('onRequest', function (request, _reply, hookDone) { - if (this[kInstrumentation].isEnabled() === false) { - return hookDone(); - } else if ( - this[kInstrumentation][kIgnorePaths]?.({ - url: request.url, - method: request.method, - }) === true - ) { - this[kInstrumentation]._logger.debug( - `Ignoring request ${request.method} ${request.url} because it matches the ignore path`, - ); - return hookDone(); - } - - let ctx = context.active(); - - if (trace.getSpan(ctx) == null) { - ctx = propagation.extract(ctx, request.headers); - } - - const rpcMetadata = getRPCMetadata(ctx); - - if (request.routeOptions.url != null && rpcMetadata?.type === RPCType.HTTP) { - rpcMetadata.route = request.routeOptions.url; - } - - /** @type {import('@opentelemetry/api').Span} */ - const span = this[kInstrumentation].tracer.startSpan( - 'request', - { - attributes: { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.ROOT]: '@fastify/otel', - [ATTR_HTTP_ROUTE]: request.url, - [ATTR_HTTP_REQUEST_METHOD]: request.method, - }, - }, - ctx, - ); - - request[kRequestContext] = trace.setSpan(ctx, span); - request[kRequestSpan] = span; - - context.with(request[kRequestContext], () => { - hookDone(); - }); - }); - - // onResponse is the last hook to be executed, only added for 404 handlers - instance.addHook('onResponse', function (request, reply, hookDone) { - const span = request[kRequestSpan]; - - if (span != null) { - span.setStatus({ - code: SpanStatusCode.OK, - message: 'OK', - }); - span.setAttributes({ - [ATTR_HTTP_RESPONSE_STATUS_CODE]: 404, - }); - span.end(); - } - - request[kRequestSpan] = null; - - hookDone(); - }); - - instance.addHook = addHookPatched; - instance.setNotFoundHandler = setNotFoundHandlerPatched; - - done(); - - function onSendHook(request, reply, payload, hookDone) { - /** @type {import('@opentelemetry/api').Span} */ - const span = request[kRequestSpan]; - - if (span != null) { - if (reply.statusCode < 500) { - span.setStatus({ - code: SpanStatusCode.OK, - message: 'OK', - }); - } - - span.setAttributes({ - [ATTR_HTTP_RESPONSE_STATUS_CODE]: reply.statusCode, - }); - span.end(); - } - - request[kRequestSpan] = null; - - hookDone(null, payload); - } - - function onErrorHook(request, reply, error, hookDone) { - /** @type {Span} */ - const span = request[kRequestSpan]; - - if (span != null) { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message, - }); - span.recordException(error); - } - - hookDone(); - } - - function addHookPatched(name, hook) { - const addHookOriginal = this[kAddHookOriginal]; - - if (FASTIFY_HOOKS.includes(name)) { - return addHookOriginal.call( - this, - name, - handlerWrapper(hook, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - ${name}`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.INSTANCE, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - hook.name?.length > 0 ? hook.name : ANONYMOUS_FUNCTION_NAME /* c8 ignore next */, - }), - ); - } else { - return addHookOriginal.call(this, name, hook); - } - } - - function setNotFoundHandlerPatched(hooks, handler) { - const setNotFoundHandlerOriginal = this[kSetNotFoundOriginal]; - if (typeof hooks === 'function') { - handler = handlerWrapper(hooks, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - not-found-handler`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.INSTANCE, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - hooks.name?.length > 0 ? hooks.name : ANONYMOUS_FUNCTION_NAME /* c8 ignore next */, - }); - setNotFoundHandlerOriginal.call(this, handler); - } else { - if (hooks.preValidation != null) { - hooks.preValidation = handlerWrapper(hooks.preValidation, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - not-found-handler - preValidation`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.INSTANCE, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - hooks.preValidation.name?.length > 0 - ? hooks.preValidation.name - : ANONYMOUS_FUNCTION_NAME /* c8 ignore next */, - }); - } - - if (hooks.preHandler != null) { - hooks.preHandler = handlerWrapper(hooks.preHandler, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - not-found-handler - preHandler`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.INSTANCE, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - hooks.preHandler.name?.length > 0 - ? hooks.preHandler.name - : ANONYMOUS_FUNCTION_NAME /* c8 ignore next */, - }); - } - - handler = handlerWrapper(handler, { - [ATTR_SERVICE_NAME]: instance[kInstrumentation].servername, - [ATTRIBUTE_NAMES.HOOK_NAME]: `${this.pluginName} - not-found-handler`, - [ATTRIBUTE_NAMES.FASTIFY_TYPE]: HOOK_TYPES.INSTANCE, - [ATTRIBUTE_NAMES.HOOK_CALLBACK_NAME]: - handler.name?.length > 0 ? handler.name : ANONYMOUS_FUNCTION_NAME /* c8 ignore next */, - }); - setNotFoundHandlerOriginal.call(this, hooks, handler); - } - } - - function handlerWrapper(handler, spanAttributes = {}) { - return function handlerWrapped(...args) { - /** @type {FastifyOtelInstrumentation} */ - const instrumentation = this[kInstrumentation]; - const [request] = args; - - if (instrumentation.isEnabled() === false) { - return handler.call(this, ...args); - } - - const ctx = request[kRequestContext] ?? context.active(); - const span = instrumentation.tracer.startSpan( - `handler - ${ - handler.name?.length > 0 - ? handler.name - : (this.pluginName /* c8 ignore next */ ?? ANONYMOUS_FUNCTION_NAME) /* c8 ignore next */ - }`, - { - attributes: spanAttributes, - }, - ctx, - ); - - return context.with( - trace.setSpan(ctx, span), - function () { - try { - const res = handler.call(this, ...args); - - if (typeof res?.then === 'function') { - return res.then( - result => { - span.end(); - return result; - }, - error => { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message, - }); - span.recordException(error); - span.end(); - return Promise.reject(error); - }, - ); - } - - span.end(); - return res; - } catch (error) { - span.setStatus({ - code: SpanStatusCode.ERROR, - message: error.message, - }); - span.recordException(error); - span.end(); - throw error; - } - }, - this, - ); - }; - } - } - } -} diff --git a/packages/node/src/integrations/tracing/fastify/index.ts b/packages/node/src/integrations/tracing/fastify/index.ts index b84875b906e8..e3319fb62b51 100644 --- a/packages/node/src/integrations/tracing/fastify/index.ts +++ b/packages/node/src/integrations/tracing/fastify/index.ts @@ -1,5 +1,5 @@ import * as diagnosticsChannel from 'node:diagnostics_channel'; -import type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation'; +import { FastifyOtelInstrumentation } from '@fastify/otel'; import type { IntegrationFn, Span } from '@sentry/core'; import { captureException, @@ -13,7 +13,6 @@ import { } from '@sentry/core'; import { generateInstrumentOnce } from '@sentry/node-core'; import { DEBUG_BUILD } from '../../../debug-build'; -import { FastifyOtelInstrumentation } from './fastify-otel/index'; import type { FastifyInstance, FastifyMinimal, FastifyReply, FastifyRequest } from './types'; import { FastifyInstrumentationV3 } from './v3/instrumentation'; @@ -169,8 +168,7 @@ export const instrumentFastify = generateInstrumentOnce(`${INTEGRATION_NAME}.v5` handleFastifyError.call(handleFastifyError, error, request, reply, 'diagnostics-channel'); }); - // Returning this as unknown not to deal with the internal types of the FastifyOtelInstrumentation - return fastifyOtelInstrumentationInstance as Instrumentation; + return fastifyOtelInstrumentationInstance; }); const _fastifyIntegration = (({ shouldHandleError }: Partial) => { diff --git a/yarn.lock b/yarn.lock index 7a408e4a9a7a..9d136859213d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4782,6 +4782,16 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8" integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ== +"@fastify/otel@0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@fastify/otel/-/otel-0.16.0.tgz#e003c9b81039490af9141a7f1397de6b05baa768" + integrity sha512-2304BdM5Q/kUvQC9qJO1KZq3Zn1WWsw+WWkVmFEaj1UE2hEIiuFqrPeglQOwEtw/ftngisqfQ3v70TWMmwhhHA== + dependencies: + "@opentelemetry/core" "^2.0.0" + "@opentelemetry/instrumentation" "^0.208.0" + "@opentelemetry/semantic-conventions" "^1.28.0" + minimatch "^10.0.3" + "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -6459,6 +6469,13 @@ dependencies: "@opentelemetry/api" "^1.3.0" +"@opentelemetry/api-logs@0.208.0": + version "0.208.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz#56d3891010a1fa1cf600ba8899ed61b43ace511c" + integrity sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg== + dependencies: + "@opentelemetry/api" "^1.3.0" + "@opentelemetry/api-logs@0.211.0": version "0.211.0" resolved "https://registry.yarnpkg.com/@opentelemetry/api-logs/-/api-logs-0.211.0.tgz#32d9ed98939956a84d4e2ff5e01598cb9d28d744" @@ -6709,6 +6726,15 @@ import-in-the-middle "^2.0.0" require-in-the-middle "^8.0.0" +"@opentelemetry/instrumentation@^0.208.0": + version "0.208.0" + resolved "https://registry.yarnpkg.com/@opentelemetry/instrumentation/-/instrumentation-0.208.0.tgz#d764f8e4329dad50804e2e98f010170c14c4ce8f" + integrity sha512-Eju0L4qWcQS+oXxi6pgh7zvE2byogAkcsVv0OjHF/97iOz1N/aKE6etSGowYkie+YA1uo6DNwdSxaaNnLvcRlA== + dependencies: + "@opentelemetry/api-logs" "0.208.0" + import-in-the-middle "^2.0.0" + require-in-the-middle "^8.0.0" + "@opentelemetry/redis-common@^0.38.2": version "0.38.2" resolved "https://registry.yarnpkg.com/@opentelemetry/redis-common/-/redis-common-0.38.2.tgz#cefa4f3e79db1cd54f19e233b7dfb56621143955" @@ -6731,7 +6757,7 @@ "@opentelemetry/resources" "2.5.0" "@opentelemetry/semantic-conventions" "^1.29.0" -"@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.37.0", "@opentelemetry/semantic-conventions@^1.39.0": +"@opentelemetry/semantic-conventions@^1.24.0", "@opentelemetry/semantic-conventions@^1.27.0", "@opentelemetry/semantic-conventions@^1.28.0", "@opentelemetry/semantic-conventions@^1.29.0", "@opentelemetry/semantic-conventions@^1.30.0", "@opentelemetry/semantic-conventions@^1.33.0", "@opentelemetry/semantic-conventions@^1.33.1", "@opentelemetry/semantic-conventions@^1.34.0", "@opentelemetry/semantic-conventions@^1.36.0", "@opentelemetry/semantic-conventions@^1.37.0", "@opentelemetry/semantic-conventions@^1.39.0": version "1.39.0" resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz#f653b2752171411feb40310b8a8953d7e5c543b7" integrity sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg== @@ -12465,6 +12491,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +balanced-match@^4.0.2: + version "4.0.3" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-4.0.3.tgz#6337a2f23e0604a30481423432f99eac603599f9" + integrity sha512-1pHv8LX9CpKut1Zp4EXey7Z8OfH11ONNH6Dhi2WDUt31VVZFXZzKwXcysBgqSumFCmR+0dqjMK5v5JiFHzi0+g== + bare-events@^2.2.0, bare-events@^2.5.4: version "2.5.4" resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.5.4.tgz#16143d435e1ed9eafd1ab85f12b89b3357a41745" @@ -12792,6 +12823,13 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" +brace-expansion@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-5.0.2.tgz#b6c16d0791087af6c2bc463f52a8142046c06b6f" + integrity sha512-Pdk8c9poy+YhOgVWw1JNN22/HcivgKWwpxKq04M/jTmHyCZn12WPJebZxdjSa5TmBqISrUSgNYU3eRORljfCCw== + dependencies: + balanced-match "^4.0.2" + braces@^2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" @@ -22508,12 +22546,12 @@ minimatch@5.1.0, minimatch@^5.0.1, minimatch@^5.1.0: dependencies: brace-expansion "^2.0.1" -minimatch@^10.1.2: - version "10.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.1.2.tgz#6c3f289f9de66d628fa3feb1842804396a43d81c" - integrity sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw== +minimatch@^10.0.3, minimatch@^10.1.2: + version "10.2.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-10.2.2.tgz#361603ee323cfb83496fea2ae17cc44ea4e1f99f" + integrity sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw== dependencies: - "@isaacs/brace-expansion" "^5.0.1" + brace-expansion "^5.0.2" minimatch@^7.4.1: version "7.4.6" From 7d4d732fbdc71a21e9fad2863a8452679479c092 Mon Sep 17 00:00:00 2001 From: Andrei Borza Date: Fri, 20 Feb 2026 13:09:40 +0100 Subject: [PATCH 2/2] fix(node): Fix CI failures from @fastify/otel bump - Cast instrumentFastify return type to prevent @fastify/otel types from leaking into the public API (fixes TS 3.8 and TS 5.0 compatibility) - Remove `service.name` span assertions no longer emitted by @fastify/otel@0.16.0 - Bump @sentry/node size limit from 167KB to 171KB --- .size-limit.js | 2 +- .../nestjs-fastify/tests/transactions.test.ts | 2 -- .../node-fastify-4/tests/transactions.test.ts | 1 - .../node-fastify-5/tests/transactions.test.ts | 1 - packages/node/src/integrations/tracing/fastify/index.ts | 4 +++- yarn.lock | 2 +- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.size-limit.js b/.size-limit.js index f4bf45b47b40..cb05cb876adc 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -326,7 +326,7 @@ module.exports = [ import: createImport('init'), ignore: [...builtinModules, ...nodePrefixedBuiltinModules], gzip: true, - limit: '167 KB', + limit: '171 KB', }, { name: '@sentry/node - without tracing', diff --git a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts index b7a8dd5839bd..093375e11e06 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs-fastify/tests/transactions.test.ts @@ -62,7 +62,6 @@ test('Sends an API route transaction', async ({ baseURL }) => { data: { 'sentry.origin': 'auto.http.otel.fastify', 'sentry.op': 'hook.fastify', - 'service.name': 'fastify', 'hook.name': 'fastify -> @fastify/otel -> @fastify/middie - onRequest', 'fastify.type': 'hook', 'hook.callback.name': 'runMiddie', @@ -81,7 +80,6 @@ test('Sends an API route transaction', async ({ baseURL }) => { data: { 'sentry.origin': 'auto.http.otel.fastify', 'sentry.op': 'request_handler.fastify', - 'service.name': 'fastify', 'hook.name': 'fastify -> @fastify/otel -> @fastify/middie - route-handler', 'fastify.type': 'request-handler', 'http.route': '/test-transaction', diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-4/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-fastify-4/tests/transactions.test.ts index eadf89abe7ae..a6a863104039 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-4/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-fastify-4/tests/transactions.test.ts @@ -72,7 +72,6 @@ test('Sends an API route transaction', async ({ baseURL }) => { 'hook.name': 'fastify -> @fastify/otel - onRequest', 'sentry.op': 'hook.fastify', 'sentry.origin': 'auto.http.otel.fastify', - 'service.name': 'fastify', }, description: '@fastify/otel - onRequest', op: 'hook.fastify', diff --git a/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/transactions.test.ts b/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/transactions.test.ts index 3a00e0616f57..98199f126ea2 100644 --- a/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/transactions.test.ts +++ b/dev-packages/e2e-tests/test-applications/node-fastify-5/tests/transactions.test.ts @@ -72,7 +72,6 @@ test('Sends an API route transaction', async ({ baseURL }) => { 'hook.name': 'fastify -> @fastify/otel - onRequest', 'sentry.op': 'hook.fastify', 'sentry.origin': 'auto.http.otel.fastify', - 'service.name': 'fastify', }, description: '@fastify/otel - onRequest', op: 'hook.fastify', diff --git a/packages/node/src/integrations/tracing/fastify/index.ts b/packages/node/src/integrations/tracing/fastify/index.ts index e3319fb62b51..d5cc7cebd423 100644 --- a/packages/node/src/integrations/tracing/fastify/index.ts +++ b/packages/node/src/integrations/tracing/fastify/index.ts @@ -1,5 +1,6 @@ import * as diagnosticsChannel from 'node:diagnostics_channel'; import { FastifyOtelInstrumentation } from '@fastify/otel'; +import type { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation'; import type { IntegrationFn, Span } from '@sentry/core'; import { captureException, @@ -168,7 +169,8 @@ export const instrumentFastify = generateInstrumentOnce(`${INTEGRATION_NAME}.v5` handleFastifyError.call(handleFastifyError, error, request, reply, 'diagnostics-channel'); }); - return fastifyOtelInstrumentationInstance; + // Returning this as Instrumentation to avoid leaking @fastify/otel types into the public API + return fastifyOtelInstrumentationInstance as unknown as Instrumentation; }); const _fastifyIntegration = (({ shouldHandleError }: Partial) => { diff --git a/yarn.lock b/yarn.lock index 9d136859213d..422c631a0a73 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5437,7 +5437,7 @@ resolved "https://registry.yarnpkg.com/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz#3081dadbc3460661b751e7591d7faea5df39dd29" integrity sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ== -"@isaacs/brace-expansion@^5.0.0", "@isaacs/brace-expansion@^5.0.1": +"@isaacs/brace-expansion@^5.0.0": version "5.0.1" resolved "https://registry.yarnpkg.com/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz#0ef5a92d91f2fff2a37646ce54da9e5f599f6eff" integrity sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==