UNPKG

@forklaunch/opentelemetry-instrumentation-hyper-express

Version:
228 lines 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HyperExpressInstrumentation = void 0; const api_1 = require("@opentelemetry/api"); const instrumentation_1 = require("@opentelemetry/instrumentation"); const semantic_conventions_1 = require("@opentelemetry/semantic-conventions"); class HyperExpressInstrumentation extends instrumentation_1.InstrumentationBase { constructor(config = {}) { super("opentelemetry-instrumentation-hyperexpress", "0.0.1", config); this.HTTP_METHODS = [ "get", "post", "put", "delete", "patch", "head", "options", ]; } init() { return [ new instrumentation_1.InstrumentationNodeModuleDefinition("@forklaunch/hyper-express-fork", ["*"], (moduleExports) => { this._wrap(moduleExports.Router.prototype, "use", this._patchUse.bind(this)); for (const method of this.HTTP_METHODS) { this._wrap(moduleExports.Router.prototype, method, this._patchRoute.bind(this)); } return moduleExports; }, (moduleExports) => { if (moduleExports === undefined) return; this._unwrap(moduleExports.Router.prototype, "use"); for (const method of this.HTTP_METHODS) { this._unwrap(moduleExports.Router.prototype, method); } }), ]; } _createTopLevelSpan(req, res) { var _a, _b; if (!req.span) { req.span = this.tracer.startSpan(`${req.method} ${req.path}`, { root: true, attributes: { [semantic_conventions_1.ATTR_SERVICE_NAME]: (_b = (_a = process.env.OTEL_SERVICE_NAME) !== null && _a !== void 0 ? _a : process.env.SERVICE_NAME) !== null && _b !== void 0 ? _b : "unknown", [semantic_conventions_1.SEMATTRS_HTTP_FLAVOR]: "1.1", [semantic_conventions_1.SEMATTRS_HTTP_HOST]: req.headers.host, [semantic_conventions_1.SEMATTRS_HTTP_METHOD]: req.method, [semantic_conventions_1.SEMATTRS_HTTP_REQUEST_CONTENT_LENGTH]: req.headers["content-length"], [semantic_conventions_1.SEMATTRS_HTTP_URL]: `${req.protocol}://${req.headers.host}${req.url}`, [semantic_conventions_1.SEMATTRS_HTTP_TARGET]: req.path, [semantic_conventions_1.SEMATTRS_HTTP_USER_AGENT]: req.headers["user-agent"], }, }); res.on("finish", () => { if (req.span) { req.span.setAttributes({ [semantic_conventions_1.ATTR_HTTP_ROUTE]: req.originalPath, [semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode, "http.status_text": res.statusMessage, "error.message": res.locals.errorMessage || "Unknown error", }); if (res.statusCode && res.statusCode >= 400) { req.span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: `HTTP ${res.statusCode} error occurred`, }); req.span.addEvent("error", { [semantic_conventions_1.ATTR_HTTP_ROUTE]: req.originalPath, [semantic_conventions_1.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode, "http.status_text": res.statusMessage, "error.message": res.locals.errorMessage || "Unknown error", }); } req.span.end(); } }); } } _wrapMiddleware(middleware) { return (req, res, next) => { this._createTopLevelSpan(req, res); return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), req.span), () => { var _a, _b, _c; const span = this.tracer.startSpan(`middleware - ${middleware.name || "<anonymous>"}`); const attributes = { "api.name": (_b = (_a = req.contractDetails) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "undefined", "correlation.id": (_c = req.context) === null || _c === void 0 ? void 0 : _c.correlationId, [semantic_conventions_1.SEMATTRS_HTTP_METHOD]: req.method, [semantic_conventions_1.SEMATTRS_HTTP_URL]: `${req.protocol}://${req.headers.host}${req.url}`, [semantic_conventions_1.SEMATTRS_HTTP_TARGET]: req.path, }; span.setAttributes(attributes); return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => { const result = middleware(req, res, (err) => { if (err) { span.recordException(err); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: err.message, }); span.addEvent("error", { "error.message": err.message, "error.stack": err.stack, }); } span.end(); next(err); }); span.end(); return result; }); }); }; } _wrapHandler(path, handler) { return (req, res, next) => { this._createTopLevelSpan(req, res); return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), req.span), () => { var _a, _b, _c, _d, _e, _f, _g; (_a = req.span) === null || _a === void 0 ? void 0 : _a.setAttributes({ "api.name": (_c = (_b = req.contractDetails) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : "undefined", "correlation.id": (_d = req.context) === null || _d === void 0 ? void 0 : _d.correlationId, }); const span = this.tracer.startSpan(`request handler - ${path}`); const attributes = { "api.name": (_f = (_e = req.contractDetails) === null || _e === void 0 ? void 0 : _e.name) !== null && _f !== void 0 ? _f : "undefined", "correlation.id": (_g = req.context) === null || _g === void 0 ? void 0 : _g.correlationId, [semantic_conventions_1.SEMATTRS_HTTP_METHOD]: req.method, [semantic_conventions_1.SEMATTRS_HTTP_URL]: `${req.protocol}://${req.headers.host}${req.url}`, [semantic_conventions_1.SEMATTRS_HTTP_TARGET]: req.path, [semantic_conventions_1.SEMATTRS_HTTP_ROUTE]: path, }; span.setAttributes(attributes); return api_1.context.with(api_1.trace.setSpan(api_1.context.active(), span), () => { try { const result = handler(req, res, next); if (result instanceof Promise) { return result.finally(() => { span.end(); }); } span.end(); return result; } catch (error) { span.recordException(error); span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message, }); span.addEvent("error", { "error.message": error.message, "error.stack": error.stack, }); span.end(); throw error; } }); }); }; } _patchUse(original) { const instrumentation = this; return function (...args) { return original.apply(this, args.map((maybeMiddleware) => { switch (typeof maybeMiddleware) { case "function": { if (Array.isArray(maybeMiddleware)) { if (maybeMiddleware.length === 0) { return maybeMiddleware; } else { return maybeMiddleware.map((innerMiddleware) => instrumentation._wrapMiddleware(innerMiddleware)); } } return instrumentation._wrapMiddleware(maybeMiddleware); } case "object": { if (maybeMiddleware.constructor.name === "Router") { return maybeMiddleware; } else if ("middleware" in maybeMiddleware && typeof maybeMiddleware .middleware === "function") { return instrumentation._wrapMiddleware(maybeMiddleware .middleware); } return maybeMiddleware; } default: return maybeMiddleware; } })); }; } _patchRoute(original) { const instrumentation = this; return function (path, ...args) { const handler = args.pop(); return original.apply(this, [ path, ...args.map((arg) => { switch (typeof arg) { case "function": if (Array.isArray(arg)) { return arg.map((innerMiddleware) => instrumentation._wrapMiddleware(innerMiddleware)); } return instrumentation._wrapMiddleware(arg); default: return arg; } }), Array.isArray(handler) ? [ ...handler .slice(0, -1) .map((innerHandler) => instrumentation._wrapMiddleware(innerHandler)), instrumentation._wrapHandler(path, handler[handler.length - 1]), ] : typeof handler === "function" ? instrumentation._wrapHandler(path, handler) : handler, ]); }; } } exports.HyperExpressInstrumentation = HyperExpressInstrumentation; //# sourceMappingURL=instrumentation.js.map