From 34ecefafc8e46a44d85a61862a3e6d8478f929ee Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Mon, 19 May 2025 14:50:08 +0100 Subject: [PATCH 1/5] feat(node): Vendor in `@fastify/otel` --- packages/node/package.json | 1 - packages/node/rollup.npm.config.mjs | 2 + .../tracing/fastify/fastify-otel/index.d.ts | 29 ++ .../tracing/fastify/fastify-otel/index.js | 493 ++++++++++++++++++ .../src/integrations/tracing/fastify/index.ts | 2 +- .../tracing/fastify/tsconfig.json | 6 + yarn.lock | 11 +- 7 files changed, 532 insertions(+), 12 deletions(-) create mode 100644 packages/node/src/integrations/tracing/fastify/fastify-otel/index.d.ts create mode 100644 packages/node/src/integrations/tracing/fastify/fastify-otel/index.js create mode 100644 packages/node/src/integrations/tracing/fastify/tsconfig.json diff --git a/packages/node/package.json b/packages/node/package.json index a2da87ce2400..2d35510e569d 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -65,7 +65,6 @@ "access": "public" }, "dependencies": { - "@fastify/otel": "https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb", "@opentelemetry/api": "^1.9.0", "@opentelemetry/context-async-hooks": "^1.30.1", "@opentelemetry/core": "^1.30.1", diff --git a/packages/node/rollup.npm.config.mjs b/packages/node/rollup.npm.config.mjs index e0483c673d1c..4d0ae8e0a24b 100644 --- a/packages/node/rollup.npm.config.mjs +++ b/packages/node/rollup.npm.config.mjs @@ -1,3 +1,4 @@ +import commonjs from '@rollup/plugin-commonjs'; import replace from '@rollup/plugin-replace'; import { makeBaseNPMConfig, makeNPMConfigVariants, makeOtelLoaders } from '@sentry-internal/rollup-utils'; import { createWorkerCodeBuilder } from './rollup.anr-worker.config.mjs'; @@ -27,6 +28,7 @@ export default [ preserveModules: true, }, plugins: [ + commonjs(), replace({ delimiters: ['###', '###'], // removes some rollup warnings 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 new file mode 100644 index 000000000000..48b5297e074c --- /dev/null +++ b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.d.ts @@ -0,0 +1,29 @@ +/* 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 new file mode 100644 index 000000000000..6fff247a8987 --- /dev/null +++ b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js @@ -0,0 +1,493 @@ +/* +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 `logger` as it created problems with TypesScript +- 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 @typescript-eslint/explicit-member-accessibility */ +'use strict'; +const dc = require('node:diagnostics_channel'); +const { context, trace, SpanStatusCode, propagation, diag } = require('@opentelemetry/api'); +const { getRPCMetadata, RPCType } = require('@opentelemetry/core'); +const { + ATTR_HTTP_ROUTE, + ATTR_HTTP_RESPONSE_STATUS_CODE, + ATTR_HTTP_REQUEST_METHOD, + ATTR_SERVICE_NAME, +} = require('@opentelemetry/semantic-conventions'); +const { InstrumentationBase } = require('@opentelemetry/instrumentation'); + +// 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'); + +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; + + 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'); + } + + let globMatcher = null; + + this[kIgnorePaths] = routeOptions => { + if (typeof ignorePaths === 'function') { + return ignorePaths(routeOptions); + } else { + // Using minimatch to match the path until path.matchesGlob is out of experimental + // path.matchesGlob uses minimatch internally + if (globMatcher == null) { + globMatcher = require('minimatch').minimatch; + } + + 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, + ); + }; + } + } + } +} + +module.exports = FastifyOtelInstrumentation; +module.exports.FastifyOtelInstrumentation = FastifyOtelInstrumentation; diff --git a/packages/node/src/integrations/tracing/fastify/index.ts b/packages/node/src/integrations/tracing/fastify/index.ts index 49140f46edda..13805e18d575 100644 --- a/packages/node/src/integrations/tracing/fastify/index.ts +++ b/packages/node/src/integrations/tracing/fastify/index.ts @@ -1,5 +1,4 @@ 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 { @@ -14,6 +13,7 @@ import { } from '@sentry/core'; import { DEBUG_BUILD } from '../../../debug-build'; import { generateInstrumentOnce } from '../../../otel/instrument'; +import { FastifyOtelInstrumentation } from './fastify-otel/index'; import type { FastifyInstance, FastifyReply, FastifyRequest } from './types'; import { FastifyInstrumentationV3 } from './v3/instrumentation'; diff --git a/packages/node/src/integrations/tracing/fastify/tsconfig.json b/packages/node/src/integrations/tracing/fastify/tsconfig.json new file mode 100644 index 000000000000..a665da70537f --- /dev/null +++ b/packages/node/src/integrations/tracing/fastify/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "allowSyntheticDefaultImports": true, + }, +} diff --git a/yarn.lock b/yarn.lock index 78c0f0fb30a3..b9faa5f80dab 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3912,15 +3912,6 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.0.0.tgz#f22824caff3ae506b18207bad4126dbc6ccdb6b8" integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ== -"@fastify/otel@https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb": - version "0.8.0" - resolved "https://codeload.github.com/getsentry/fastify-otel/tar.gz/ae3088d65e286bdc94ac5d722573537d6a6671bb#1632d3df7ebf8cd86996a50e9e42721aea05b39c" - dependencies: - "@opentelemetry/core" "^1.30.1" - "@opentelemetry/instrumentation" "^0.57.2" - "@opentelemetry/semantic-conventions" "^1.28.0" - minimatch "^9" - "@gar/promisify@^1.1.3": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -20856,7 +20847,7 @@ minimatch@^8.0.2: dependencies: brace-expansion "^2.0.1" -minimatch@^9, minimatch@^9.0.0, minimatch@^9.0.4: +minimatch@^9.0.0, minimatch@^9.0.4: version "9.0.5" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== From d2763a131ab04ae20e7254ba90af27e0ebd87dde Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Mon, 19 May 2025 15:58:21 +0100 Subject: [PATCH 2/5] Remove commonjs plugin, configure eslint --- packages/node/package.json | 3 ++- packages/node/rollup.npm.config.mjs | 2 -- .../tracing/fastify/fastify-otel/.eslintrc.js | 9 +++++++ .../tracing/fastify/fastify-otel/index.js | 25 +++++++++---------- 4 files changed, 23 insertions(+), 16 deletions(-) create mode 100644 packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js diff --git a/packages/node/package.json b/packages/node/package.json index 2d35510e569d..e029554d52f1 100644 --- a/packages/node/package.json +++ b/packages/node/package.json @@ -97,7 +97,8 @@ "@prisma/instrumentation": "6.7.0", "@sentry/core": "9.20.0", "@sentry/opentelemetry": "9.20.0", - "import-in-the-middle": "^1.13.1" + "import-in-the-middle": "^1.13.1", + "minimatch": "^9.0.0" }, "devDependencies": { "@types/node": "^18.19.1" diff --git a/packages/node/rollup.npm.config.mjs b/packages/node/rollup.npm.config.mjs index 4d0ae8e0a24b..e0483c673d1c 100644 --- a/packages/node/rollup.npm.config.mjs +++ b/packages/node/rollup.npm.config.mjs @@ -1,4 +1,3 @@ -import commonjs from '@rollup/plugin-commonjs'; import replace from '@rollup/plugin-replace'; import { makeBaseNPMConfig, makeNPMConfigVariants, makeOtelLoaders } from '@sentry-internal/rollup-utils'; import { createWorkerCodeBuilder } from './rollup.anr-worker.config.mjs'; @@ -28,7 +27,6 @@ export default [ preserveModules: true, }, plugins: [ - commonjs(), replace({ delimiters: ['###', '###'], // removes some rollup warnings diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js b/packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js new file mode 100644 index 000000000000..9b2b2d51af09 --- /dev/null +++ b/packages/node/src/integrations/tracing/fastify/fastify-otel/.eslintrc.js @@ -0,0 +1,9 @@ +module.exports = { + env: { + node: true, + }, + parserOptions: { + sourceType: 'module', + ecmaVersion: 2020, + }, +}; diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js index 6fff247a8987..3ff143652354 100644 --- a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js +++ b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js @@ -32,17 +32,19 @@ SOFTWARE. */ /* eslint-disable @typescript-eslint/explicit-member-accessibility */ -'use strict'; -const dc = require('node:diagnostics_channel'); -const { context, trace, SpanStatusCode, propagation, diag } = require('@opentelemetry/api'); -const { getRPCMetadata, RPCType } = require('@opentelemetry/core'); -const { - ATTR_HTTP_ROUTE, - ATTR_HTTP_RESPONSE_STATUS_CODE, +/* eslint-disable jsdoc/require-jsdoc */ +/* eslint-disable max-lines */ +/* eslint-disable no-param-reassign */ +import dc from 'node:diagnostics_channel'; +import { context, 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, -} = require('@opentelemetry/semantic-conventions'); -const { InstrumentationBase } = require('@opentelemetry/instrumentation'); +} from '@opentelemetry/semantic-conventions'; // SENTRY VENDOR NOTE // Instead of using the package.json file, we hard code the package name and version here. @@ -82,7 +84,7 @@ const kAddHookOriginal = Symbol('fastify otel addhook original'); const kSetNotFoundOriginal = Symbol('fastify otel setnotfound original'); const kIgnorePaths = Symbol('fastify otel ignore path'); -class FastifyOtelInstrumentation extends InstrumentationBase { +export class FastifyOtelInstrumentation extends InstrumentationBase { constructor(config) { super(PACKAGE_NAME, PACKAGE_VERSION, config); this.servername = config?.servername ?? process.env.OTEL_SERVICE_NAME ?? 'fastify'; @@ -488,6 +490,3 @@ class FastifyOtelInstrumentation extends InstrumentationBase { } } } - -module.exports = FastifyOtelInstrumentation; -module.exports.FastifyOtelInstrumentation = FastifyOtelInstrumentation; From 9f974df521f5f5625a9f67cf752147e12c13c1ee Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Mon, 19 May 2025 16:23:37 +0100 Subject: [PATCH 3/5] Fix formatting --- .../node/src/integrations/tracing/fastify/fastify-otel/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js index 3ff143652354..a16e6c61cd9f 100644 --- a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js +++ b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js @@ -36,7 +36,7 @@ SOFTWARE. /* eslint-disable max-lines */ /* eslint-disable no-param-reassign */ import dc from 'node:diagnostics_channel'; -import { context, propagation,SpanStatusCode, trace } from '@opentelemetry/api'; +import { context, propagation, SpanStatusCode, trace } from '@opentelemetry/api'; import { getRPCMetadata, RPCType } from '@opentelemetry/core'; import { InstrumentationBase } from '@opentelemetry/instrumentation'; import { From 0db99e06d0b473d9532013d75b1e392d76240ccf Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Mon, 19 May 2025 16:46:36 +0100 Subject: [PATCH 4/5] Remove local `tsconfig` --- .../node/src/integrations/tracing/fastify/tsconfig.json | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 packages/node/src/integrations/tracing/fastify/tsconfig.json diff --git a/packages/node/src/integrations/tracing/fastify/tsconfig.json b/packages/node/src/integrations/tracing/fastify/tsconfig.json deleted file mode 100644 index a665da70537f..000000000000 --- a/packages/node/src/integrations/tracing/fastify/tsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "extends": "../../../../tsconfig.json", - "compilerOptions": { - "allowSyntheticDefaultImports": true, - }, -} From 0a4e6de32b166cf6b970179a495cbdd28f068006 Mon Sep 17 00:00:00 2001 From: Onur Temizkan Date: Tue, 20 May 2025 10:25:00 +0100 Subject: [PATCH 5/5] Use `instrumentation._logger` instead --- .../tracing/fastify/fastify-otel/index.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js index a16e6c61cd9f..d4f0638cb30a 100644 --- a/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js +++ b/packages/node/src/integrations/tracing/fastify/fastify-otel/index.js @@ -5,7 +5,6 @@ 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 `logger` as it created problems with TypesScript - Removed reading of package.json to get the version and package name MIT License @@ -36,7 +35,7 @@ SOFTWARE. /* eslint-disable max-lines */ /* eslint-disable no-param-reassign */ import dc from 'node:diagnostics_channel'; -import { context, propagation, SpanStatusCode, trace } from '@opentelemetry/api'; +import { context, diag, propagation, SpanStatusCode, trace } from '@opentelemetry/api'; import { getRPCMetadata, RPCType } from '@opentelemetry/core'; import { InstrumentationBase } from '@opentelemetry/instrumentation'; import { @@ -89,6 +88,7 @@ export class FastifyOtelInstrumentation extends InstrumentationBase { 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; @@ -177,9 +177,9 @@ export class FastifyOtelInstrumentation extends InstrumentationBase { 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`, - // ); + instrumentation._logger.debug( + `Ignoring route instrumentation ${routeOptions.method} ${routeOptions.url} because it matches the ignore path`, + ); return; } @@ -254,9 +254,9 @@ export class FastifyOtelInstrumentation extends InstrumentationBase { method: request.method, }) === true ) { - // this[kInstrumentation].logger.debug( - // `Ignoring request ${request.method} ${request.url} because it matches the ignore path`, - // ); + this[kInstrumentation]._logger.debug( + `Ignoring request ${request.method} ${request.url} because it matches the ignore path`, + ); return hookDone(); }