UNPKG

@pw-tech/instrumentation-hyper-express

Version:
235 lines 11.4 kB
"use strict"; /* * Copyright The OpenTelemetry Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.HyperExpressInstrumentation = void 0; const api = require("@opentelemetry/api"); const types_1 = require("./types"); const AttributeNames_1 = require("./enums/AttributeNames"); // import { VERSION } from './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 api_1 = require("@opentelemetry/api"); const APM_TYPE = process.env.APM_TYPE || "ELASTIC"; class HyperExpressInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super(`@opentelemetry/instrumentation-${constants.MODULE_NAME}`, "0.38.0", config); this._isDisabled = false; } setConfig(config = {}) { this._config = Object.assign({}, config); } getConfig() { return this._config; } init() { const module = new instrumentation_1.InstrumentationNodeModuleDefinition(constants.MODULE_NAME, constants.SUPPORTED_VERSIONS, (moduleExports, moduleVersion) => { this._moduleVersion = moduleVersion; return moduleExports; }); module.files.push(new instrumentation_1.InstrumentationNodeModuleFile('hyper-express/src/components/router/Router.js', constants.SUPPORTED_VERSIONS, moduleExports => { const Router = moduleExports; // Wrap router methods (assuming HYPER_EXPRESS_METHODS covers route methods) for (const name of constants.HYPER_EXPRESS_METHODS) { if ((0, instrumentation_1.isWrapped)(Router.prototype[name])) { this._unwrap(Router.prototype, name); } this._wrap(Router.prototype, name, this._methodPatcher.bind(this)); } return moduleExports; }, moduleExports => { if (moduleExports) { const Router = moduleExports; for (const name of constants.HYPER_EXPRESS_METHODS) { this._unwrap(Router.prototype, name); } } })); module.files.push(new instrumentation_1.InstrumentationNodeModuleFile('hyper-express/src/components/Server.js', constants.SUPPORTED_VERSIONS, moduleExports => { this._isDisabled = false; const Server = moduleExports; // for (const name of constants.HYPER_EXPRESS_METHODS) { // if (isWrapped(Server.prototype[name])) { // this._unwrap(Server.prototype, name); // } // this._wrap( // Server.prototype, // name as keyof Server, // this._methodPatcher.bind(this) // ); // } for (const name of constants.HYPER_EXPRESS_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.HYPER_EXPRESS_METHODS) { // this._unwrap(Server.prototype, name as keyof Server); // } for (const name of constants.HYPER_EXPRESS_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, 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) => { const fnName = handler.name || undefined; const isAnnoymousMiddleware = !fnName && metadata.type !== types_1.LayerType.REQUEST; if (isAnnoymousMiddleware || this._isDisabled) { return handler(req, res, next); } // const route = // typeof req.getRoute === 'function' // ? req.getRoute()?.path // : req.route?.path; //@ts-ignore const reqRoute = req.route; const route = reqRoute.pattern; // replace HTTP instrumentations name with one that contains a route // const httpMetadata = getRPCMetadata(api.context.active()); // if (httpMetadata?.type === RPCType.HTTP) { // httpMetadata.route = route; // } const resource = metadata.type === types_1.LayerType.REQUEST ? `${reqRoute.method} ${route}` : `middleware - ${fnName || 'anonymous'}`; let spanName = ''; switch (APM_TYPE) { case 'DD': spanName = metadata.type === types_1.LayerType.REQUEST ? AttributeNames_1.SpanName.REQUEST : AttributeNames_1.SpanName.MIDDLEWARE; break; case 'ELASTIC': spanName = resource; break; } let attributes = {}; if (metadata.type === types_1.LayerType.REQUEST) { //@ts-ignore Object.assign(attributes, { [semantic_conventions_1.SEMATTRS_HTTP_ROUTE]: route, [semantic_conventions_1.SEMATTRS_HTTP_HOST]: req.headers.host, [semantic_conventions_1.SEMATTRS_HTTP_METHOD]: reqRoute.method, [semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT]: req.headers['user-agent'], [semantic_conventions_1.SEMATTRS_HTTP_TARGET]: route, [semantic_conventions_1.SEMATTRS_HTTP_URL]: `${(0, utils_1.getScheme)(req.app)}${req.headers.host}${req.url}`, // [SEMATTRS_HTTP_CLIENT_IP]: req.ip, }); } const span = this.tracer.startSpan(spanName, { attributes, kind: metadata.type === types_1.LayerType.REQUEST ? 1 : 0, }, api.context.active()); const instrumentation = this; const requestHook = instrumentation.getConfig().requestHook; 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; // todo: fix me const wrapPromise = (promise) => { return promise .then(value => { var _a, _b, _c; //@ts-ignore if (((_c = (_b = (_a = span === null || span === void 0 ? void 0 : span._span) === null || _a === void 0 ? void 0 : _a._agent) === null || _b === void 0 ? void 0 : _b.currentTransaction) === null || _c === void 0 ? void 0 : _c._result) && res.statusCode && APM_TYPE === 'ELASTIC') { const statusCodeStartNumber = res.statusCode.toString().charAt(0); //@ts-ignore span._span._agent.currentTransaction._result = `HTTP ${statusCodeStartNumber}xx`; } if (metadata.type === types_1.LayerType.REQUEST) { // span.setAttribute(SEMATTRS_HTTP_STATUS_CODE, res.statusCode); span.setAttributes({ [semantic_conventions_1.SEMATTRS_HTTP_STATUS_CODE]: res.statusCode, }); span.setStatus({ code: (0, utils_1.parseResponseStatus)(api_1.SpanKind.SERVER, res.statusCode), }); } // span.setAttribute(AttributeNames.STATUS_CODE, res.statusCode); 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.HyperExpressInstrumentation = HyperExpressInstrumentation; //# sourceMappingURL=instrumentation.js.map