UNPKG

@sentry/node

Version:

Sentry Node SDK using OpenTelemetry for performance instrumentation

141 lines (137 loc) 5.53 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); const api = require('@opentelemetry/api'); const instrumentation = require('@opentelemetry/instrumentation'); const types = require('./types.js'); const core = require('@sentry/core'); const utils = require('./utils.js'); const core$1 = require('@opentelemetry/core'); const internalTypes = require('./internal-types.js'); const PACKAGE_NAME = "@sentry/instrumentation-koa"; class KoaInstrumentation extends instrumentation.InstrumentationBase { constructor(config = {}) { super(PACKAGE_NAME, core.SDK_VERSION, config); } init() { return new instrumentation.InstrumentationNodeModuleDefinition( "koa", [">=2.0.0 <4"], (module) => { const moduleExports = module[Symbol.toStringTag] === "Module" ? module.default : module; if (moduleExports == null) { return moduleExports; } if (instrumentation.isWrapped(moduleExports.prototype.use)) { this._unwrap(moduleExports.prototype, "use"); } this._wrap(moduleExports.prototype, "use", this._getKoaUsePatch.bind(this)); return module; }, (module) => { const moduleExports = module[Symbol.toStringTag] === "Module" ? module.default : module; if (instrumentation.isWrapped(moduleExports.prototype.use)) { this._unwrap(moduleExports.prototype, "use"); } } ); } /** * Patches the Koa.use function in order to instrument each original * middleware layer which is introduced * @param {KoaMiddleware} middleware - the original middleware function */ _getKoaUsePatch(original) { const plugin = this; return function use(middlewareFunction) { let patchedFunction; if (middlewareFunction.router) { patchedFunction = plugin._patchRouterDispatch(middlewareFunction); } else { patchedFunction = plugin._patchLayer(middlewareFunction, false); } return original.apply(this, [patchedFunction]); }; } /** * Patches the dispatch function used by @koa/router. This function * goes through each routed middleware and adds instrumentation via a call * to the @function _patchLayer function. * @param {KoaMiddleware} dispatchLayer - the original dispatch function which dispatches * routed middleware */ _patchRouterDispatch(dispatchLayer) { api.diag.debug("Patching @koa/router dispatch"); const router = dispatchLayer.router; const routesStack = router?.stack ?? []; for (const pathLayer of routesStack) { const path = pathLayer.path; const pathStack = pathLayer.stack; for (let j = 0; j < pathStack.length; j++) { const routedMiddleware = pathStack[j]; pathStack[j] = this._patchLayer(routedMiddleware, true, path); } } return dispatchLayer; } /** * Patches each individual @param middlewareLayer function in order to create the * span and propagate context. It does not create spans when there is no parent span. * @param {KoaMiddleware} middlewareLayer - the original middleware function. * @param {boolean} isRouter - tracks whether the original middleware function * was dispatched by the router originally * @param {string?} layerPath - if present, provides additional data from the * router about the routed path which the middleware is attached to */ _patchLayer(middlewareLayer, isRouter, layerPath) { const layerType = isRouter ? types.KoaLayerType.ROUTER : types.KoaLayerType.MIDDLEWARE; if (middlewareLayer[internalTypes.kLayerPatched] === true || utils.isLayerIgnored(layerType, this.getConfig())) return middlewareLayer; if (middlewareLayer.constructor.name === "GeneratorFunction" || middlewareLayer.constructor.name === "AsyncGeneratorFunction") { api.diag.debug("ignoring generator-based Koa middleware layer"); return middlewareLayer; } middlewareLayer[internalTypes.kLayerPatched] = true; api.diag.debug("patching Koa middleware layer"); return async (context, next) => { const parent = api.trace.getSpan(api.context.active()); if (parent === void 0) { return middlewareLayer(context, next); } const metadata = utils.getMiddlewareMetadata(context, middlewareLayer, isRouter, layerPath); const span = this.tracer.startSpan(metadata.name, { attributes: metadata.attributes }); const rpcMetadata = core$1.getRPCMetadata(api.context.active()); if (rpcMetadata?.type === core$1.RPCType.HTTP && context._matchedRoute) { rpcMetadata.route = context._matchedRoute.toString(); } const { requestHook } = this.getConfig(); if (requestHook) { instrumentation.safeExecuteInTheMiddle( () => requestHook(span, { context, middlewareLayer, layerType }), (e) => { if (e) { api.diag.error("koa instrumentation: request hook failed", e); } }, true ); } const newContext = api.trace.setSpan(api.context.active(), span); return api.context.with(newContext, async () => { try { return await middlewareLayer(context, next); } catch (err) { span.recordException(err); throw err; } finally { span.end(); } }); }; } } exports.KoaInstrumentation = KoaInstrumentation; //# sourceMappingURL=instrumentation.js.map