UNPKG

@sentry/node

Version:

Sentry Node SDK using OpenTelemetry for performance instrumentation

223 lines (219 loc) 8.13 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const api = require('@opentelemetry/api'); const core = require('@opentelemetry/core'); const instrumentation = require('@opentelemetry/instrumentation'); const semanticConventions = require('@opentelemetry/semantic-conventions'); const core$1 = require('@sentry/core'); const AttributeNames = require('./enums/AttributeNames.js'); const utils = require('./utils.js'); const PACKAGE_VERSION = "0.1.0"; const PACKAGE_NAME = "@sentry/instrumentation-fastify-v3"; const ANONYMOUS_NAME = "anonymous"; const hooksNamesToWrap = /* @__PURE__ */ new Set([ "onTimeout", "onRequest", "preParsing", "preValidation", "preSerialization", "preHandler", "onSend", "onResponse", "onError" ]); class FastifyInstrumentationV3 extends instrumentation.InstrumentationBase { constructor(config = {}) { super(PACKAGE_NAME, PACKAGE_VERSION, config); } init() { return [ new instrumentation.InstrumentationNodeModuleDefinition("fastify", [">=3.0.0 <4"], (moduleExports) => { return this._patchConstructor(moduleExports); }) ]; } _hookOnRequest() { const instrumentation = this; return function onRequest(request, reply, done) { if (!instrumentation.isEnabled()) { return done(); } instrumentation._wrap(reply, "send", instrumentation._patchSend()); const anyRequest = request; const rpcMetadata = core.getRPCMetadata(api.context.active()); const routeName = anyRequest.routeOptions ? anyRequest.routeOptions.url : request.routerPath; if (routeName && rpcMetadata?.type === core.RPCType.HTTP) { rpcMetadata.route = routeName; } const method = request.method || "GET"; core$1.getIsolationScope().setTransactionName(`${method} ${routeName}`); done(); }; } _wrapHandler(pluginName, hookName, original, syncFunctionWithDone) { const instrumentation = this; this._diag.debug("Patching fastify route.handler function"); return function(...args) { if (!instrumentation.isEnabled()) { return original.apply(this, args); } const name = original.name || pluginName || ANONYMOUS_NAME; const spanName = `${AttributeNames.FastifyNames.MIDDLEWARE} - ${name}`; const reply = args[1]; const span = utils.startSpan(reply, instrumentation.tracer, spanName, { [AttributeNames.AttributeNames.FASTIFY_TYPE]: AttributeNames.FastifyTypes.MIDDLEWARE, [AttributeNames.AttributeNames.PLUGIN_NAME]: pluginName, [AttributeNames.AttributeNames.HOOK_NAME]: hookName }); const origDone = syncFunctionWithDone && args[args.length - 1]; if (origDone) { args[args.length - 1] = function(...doneArgs) { utils.endSpan(reply); origDone.apply(this, doneArgs); }; } return api.context.with(api.trace.setSpan(api.context.active(), span), () => { return utils.safeExecuteInTheMiddleMaybePromise( () => { return original.apply(this, args); }, (err) => { if (err instanceof Error) { span.setStatus({ code: api.SpanStatusCode.ERROR, message: err.message }); span.recordException(err); } if (!syncFunctionWithDone) { utils.endSpan(reply); } } ); }); }; } _wrapAddHook() { const instrumentation = this; this._diag.debug("Patching fastify server.addHook function"); return function(original) { return function wrappedAddHook(...args) { const name = args[0]; const handler = args[1]; 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) ]); }; }; } _patchConstructor(moduleExports) { const instrumentation = this; function fastify(...args) { const app = 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 !== void 0) { fastify.errorCodes = moduleExports.errorCodes; } fastify.fastify = fastify; fastify.default = fastify; return fastify; } _patchSend() { const instrumentation$1 = this; this._diag.debug("Patching fastify reply.send function"); return function patchSend(original) { return function send(...args) { const maybeError = args[0]; if (!instrumentation$1.isEnabled()) { return original.apply(this, args); } return instrumentation.safeExecuteInTheMiddle( () => { return original.apply(this, args); }, (err) => { if (!err && maybeError instanceof Error) { err = maybeError; } utils.endSpan(this, err); } ); }; }; } _hookPreHandler() { const instrumentation$1 = this; this._diag.debug("Patching fastify preHandler function"); return function preHandler(request, reply, done) { if (!instrumentation$1.isEnabled()) { return done(); } const anyRequest = request; const handler = anyRequest.routeOptions?.handler || anyRequest.context?.handler; const handlerName = handler?.name.startsWith("bound ") ? handler.name.substring(6) : handler?.name; const spanName = `${AttributeNames.FastifyNames.REQUEST_HANDLER} - ${handlerName || this.pluginName || ANONYMOUS_NAME}`; const spanAttributes = { [AttributeNames.AttributeNames.PLUGIN_NAME]: this.pluginName, [AttributeNames.AttributeNames.FASTIFY_TYPE]: AttributeNames.FastifyTypes.REQUEST_HANDLER, // eslint-disable-next-line deprecation/deprecation [semanticConventions.SEMATTRS_HTTP_ROUTE]: anyRequest.routeOptions ? anyRequest.routeOptions.url : request.routerPath }; if (handlerName) { spanAttributes[AttributeNames.AttributeNames.FASTIFY_NAME] = handlerName; } const span = utils.startSpan(reply, instrumentation$1.tracer, spanName, spanAttributes); addFastifyV3SpanAttributes(span); const { requestHook } = instrumentation$1.getConfig(); if (requestHook) { instrumentation.safeExecuteInTheMiddle( () => requestHook(span, { request }), (e) => { if (e) { instrumentation$1._diag.error("request hook failed", e); } }, true ); } return api.context.with(api.trace.setSpan(api.context.active(), span), () => { done(); }); }; } } function instrumentClient() { const client = core$1.getClient(); if (client) { client.on("spanStart", (span) => { addFastifyV3SpanAttributes(span); }); } } function addFastifyV3SpanAttributes(span) { const attributes = core$1.spanToJSON(span).data; const type = attributes["fastify.type"]; if (attributes[core$1.SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) { return; } span.setAttributes({ [core$1.SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.http.otel.fastify", [core$1.SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.fastify` }); const name = attributes["fastify.name"] || attributes["plugin.name"] || attributes["hook.name"]; if (typeof name === "string") { const updatedName = name.replace(/^fastify -> /, "").replace(/^@fastify\/otel -> /, ""); span.updateName(updatedName); } } exports.FastifyInstrumentationV3 = FastifyInstrumentationV3; //# sourceMappingURL=instrumentation.js.map