import type { TraceAPI } from '@opentelemetry/api'; import { Assert, text } from '@rsdk/common'; import { redecorate } from '@rsdk/decorators'; import type { ILogger } from '@rsdk/logging'; import { LoggerFactory } from '@rsdk/logging'; import { Observable } from 'rxjs'; import { endWithError, processObservableResult, processPromiseResult, processSyncResult, toAttribute, } from './opentelemetry.helpers'; import { isNoSpan, isWrapped, setWrapped } from './opentelemetry.metadata'; export class OpentelemetryWrapper { private readonly logger: ILogger = LoggerFactory.create(OpentelemetryWrapper); private readonly trace: TraceAPI | null; constructor() { try { // eslint-disable-next-line @typescript-eslint/no-var-requires const { trace } = require('@opentelemetry/api'); this.trace = trace; } catch { this.trace = null; this.logger.debug(text` @opentelemetry/api is not installed. @Span() decorator will not make any effect.`); } } wrap( cls: any, // eslint-disable-next-line @typescript-eslint/ban-types original: Function, descriptor?: TypedPropertyDescriptor, ): void { if (!this.trace || isWrapped(original) || isNoSpan(original)) { return; } const spanName = `${cls.constructor.name} -> ${original.name}`; this.logger.debug(`Wrapping method: ${spanName}`); const tracer = this.trace.getTracer(`rsdk:${cls.constructor.name}`); const wrapped = { [original.name](...args: unknown[]): unknown { return tracer.startActiveSpan(spanName, (span) => { span.setAttribute('args', toAttribute(args)); try { const result = original.apply(this, args); if (result instanceof Promise) { return processPromiseResult(result, span); } if (result instanceof Observable) { return processObservableResult(result, span); } return processSyncResult(result, span); } catch (error) { Assert.isError(error); endWithError(span, error); throw error; } }); }, }[original.name]; redecorate(original, wrapped); if (descriptor) { descriptor.value = wrapped; } else { cls[original.name] = wrapped; } /** * Отмечает метод, как обёрнутый (чтобы избежать повторного оборачивания) */ setWrapped(wrapped); } }