@forklaunch/opentelemetry-instrumentation-hyper-express
Version:
OpenTelemetry instrumentation for `hyper-express` http web application framework
228 lines • 11.3 kB
JavaScript
;
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