nestjs-otel
Version:
NestJS OpenTelemetry Library
85 lines (84 loc) • 3.6 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Span = Span;
const api_1 = require("@opentelemetry/api");
const opentelemetry_constants_1 = require("../../opentelemetry.constants");
const opentelemetry_utils_1 = require("../../opentelemetry.utils");
const recordException = (span, error) => {
span.recordException(error);
span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: error.message });
};
const handleOnResult = (span, onResult, result) => {
if (onResult) {
try {
const attrs = onResult(result);
if (attrs?.attributes) {
span.setAttributes(attrs.attributes);
}
}
catch (error) {
recordException(span, error);
}
}
};
function Span(nameOrOptions, maybeOptions) {
return (target, propertyKey, propertyDescriptor) => {
let name;
let options;
if (typeof nameOrOptions === "string") {
name = nameOrOptions;
options = maybeOptions ?? {};
}
else {
name = `${target.constructor.name}.${String(propertyKey)}`;
options = nameOrOptions ?? {};
}
const originalFunction = propertyDescriptor.value;
if (typeof originalFunction !== "function") {
throw new Error(`The @Span decorator can be only used on functions, but ${propertyKey.toString()} is not a function.`);
}
const wrappedFunction = function PropertyDescriptor(...args) {
const tracer = api_1.trace.getTracer(opentelemetry_constants_1.OTEL_TRACER_NAME);
const spanOptions = typeof options === "function" ? options(...args) : options;
const { onResult, ...otelOptions } = spanOptions || {};
return tracer.startActiveSpan(name, otelOptions, (span) => {
try {
const result = originalFunction.apply(this, args);
if (result &&
typeof result.then === "function" &&
typeof result.catch === "function") {
return result
.then((res) => {
handleOnResult(span, onResult, res);
return res;
})
.catch((error) => {
recordException(span, error);
// Throw error to propagate it further
throw error;
})
.finally(() => {
span.end();
});
}
handleOnResult(span, onResult, result);
span.end();
return result;
}
catch (error) {
recordException(span, error);
span.end();
// Throw error to propagate it further
throw error;
}
});
};
// Wrap the original function in a proxy to ensure that the function name is preserved.
// This should also preserve parameters for OpenAPI and other libraries
// that rely on the function name as metadata key.
propertyDescriptor.value = new Proxy(originalFunction, {
apply: (_, thisArg, args) => wrappedFunction.apply(thisArg, args),
});
(0, opentelemetry_utils_1.copyMetadataFromFunctionToFunction)(originalFunction, propertyDescriptor.value);
};
}