UNPKG

@opentelemetry/instrumentation-restify

Version:
160 lines 7.16 kB
"use strict"; /* * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.RestifyInstrumentation = void 0; const api = require("@opentelemetry/api"); const types_1 = require("./types"); const AttributeNames_1 = require("./enums/AttributeNames"); /** @knipignore */ const version_1 = require("./version"); const constants = require("./constants"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); const utils_1 = require("./utils"); const core_1 = require("@opentelemetry/core"); const supportedVersions = ['>=4.1.0 <12']; class RestifyInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(version_1.PACKAGE_NAME, version_1.PACKAGE_VERSION, config); } _moduleVersion; _isDisabled = false; init() { const module = new instrumentation_1.InstrumentationNodeModuleDefinition(constants.MODULE_NAME, supportedVersions, (moduleExports, moduleVersion) => { this._moduleVersion = moduleVersion; return moduleExports; }); module.files.push(new instrumentation_1.InstrumentationNodeModuleFile('restify/lib/server.js', supportedVersions, moduleExports => { this._isDisabled = false; const Server = moduleExports; for (const name of constants.RESTIFY_METHODS) { if ((0, instrumentation_1.isWrapped)(Server.prototype[name])) { this._unwrap(Server.prototype, name); } this._wrap(Server.prototype, name, this._methodPatcher.bind(this)); } for (const name of constants.RESTIFY_MW_METHODS) { if ((0, instrumentation_1.isWrapped)(Server.prototype[name])) { this._unwrap(Server.prototype, name); } this._wrap(Server.prototype, name, this._middlewarePatcher.bind(this)); } return moduleExports; }, moduleExports => { this._isDisabled = true; if (moduleExports) { const Server = moduleExports; for (const name of constants.RESTIFY_METHODS) { this._unwrap(Server.prototype, name); } for (const name of constants.RESTIFY_MW_METHODS) { this._unwrap(Server.prototype, name); } } })); return module; } _middlewarePatcher(original, methodName) { const instrumentation = this; return function (...handler) { return original.call(this, instrumentation._handlerPatcher({ type: types_1.LayerType.MIDDLEWARE, methodName }, handler)); }; } _methodPatcher(original, methodName) { const instrumentation = this; return function (path, ...handler) { return original.call(this, path, ...instrumentation._handlerPatcher({ type: types_1.LayerType.REQUEST_HANDLER, path, methodName }, handler)); }; } // will return the same type as `handler`, but all functions recursively patched _handlerPatcher(metadata, handler) { if (Array.isArray(handler)) { return handler.map(handler => this._handlerPatcher(metadata, handler)); } if (typeof handler === 'function') { return (req, res, next) => { if (this._isDisabled) { return handler(req, res, next); } const route = typeof req.getRoute === 'function' ? req.getRoute()?.path : req.route?.path; // replace HTTP instrumentations name with one that contains a route const httpMetadata = (0, core_1.getRPCMetadata)(api.context.active()); if (httpMetadata?.type === core_1.RPCType.HTTP) { httpMetadata.route = route; } const fnName = handler.name || undefined; const spanName = metadata.type === types_1.LayerType.REQUEST_HANDLER ? `request handler - ${route}` : `middleware - ${fnName || 'anonymous'}`; const attributes = { [AttributeNames_1.AttributeNames.NAME]: fnName, [AttributeNames_1.AttributeNames.VERSION]: this._moduleVersion || 'n/a', [AttributeNames_1.AttributeNames.TYPE]: metadata.type, [AttributeNames_1.AttributeNames.METHOD]: metadata.methodName, [semantic_conventions_1.ATTR_HTTP_ROUTE]: route, }; const span = this.tracer.startSpan(spanName, { attributes, }, api.context.active()); const instrumentation = this; const { requestHook } = instrumentation.getConfig(); if (requestHook) { (0, instrumentation_1.safeExecuteInTheMiddle)(() => { return requestHook(span, { request: req, layerType: metadata.type, }); }, e => { if (e) { instrumentation._diag.error('request hook failed', e); } }, true); } const patchedNext = (err) => { span.end(); next(err); }; patchedNext.ifError = next.ifError; const wrapPromise = (promise) => { return promise .then(value => { span.end(); return value; }) .catch(err => { span.recordException(err); span.end(); throw err; }); }; const newContext = api.trace.setSpan(api.context.active(), span); return api.context.with(newContext, (req, res, next) => { if ((0, utils_1.isAsyncFunction)(handler)) { return wrapPromise(handler(req, res, next)); } try { const result = handler(req, res, next); if ((0, utils_1.isPromise)(result)) { return wrapPromise(result); } span.end(); return result; } catch (err) { span.recordException(err); span.end(); throw err; } }, this, req, res, patchedNext); }; } return handler; } } exports.RestifyInstrumentation = RestifyInstrumentation; //# sourceMappingURL=instrumentation.js.map