All files / src/utils trace.decorator.ts

100% Statements 15/15
100% Branches 0/0
100% Functions 4/4
100% Lines 15/15

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 4741x       41x             41x 827x         827x     827x 375x     375x 375x   375x     51x 51x       51x     375x         827x      
import { trace, SpanStatusCode } from '@opentelemetry/api';
 
// Get a tracer instance. In a real application, you might use a dependency
// injection system for this, but a singleton instance is fine for this use case.
const tracer = trace.getTracer('repoburg-backend');
 
/**
 * A method decorator that automatically wraps the decorated method in an
 * OpenTelemetry span. The span will be named using the pattern
 * "ClassName#methodName".
 */
export function Trace() {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor,
  ) {
    const originalMethod = descriptor.value;
 
    // Redefine the method with our tracing wrapper
    descriptor.value = function (...args: any[]) {
      const spanName = `${target.constructor.name}#${propertyKey}`;
 
      // tracer.startActiveSpan automatically handles timing, errors, and ending the span.
      return tracer.startActiveSpan(spanName, async (span) => {
        try {
          // Call the original method with its arguments, preserving the 'this' context
          return await originalMethod.apply(this, args);
        } catch (error) {
          // Record the exception on the span and re-throw it
          span.recordException(error);
          span.setStatus({
            code: SpanStatusCode.ERROR,
            message: error.message,
          });
          throw error;
        } finally {
          // End the span when the method is complete
          span.end();
        }
      });
    };
 
    return descriptor;
  };
}