import EventEmitter from 'events'; import { Tracer } from '@opentelemetry/api'; import { addTracingToMethods, traceFunction } from '../tracing/methodTracing.js'; type DefaultEventMap = [never]; // eslint-disable-next-line @typescript-eslint/no-explicit-any type EventMap = Record | DefaultEventMap; type Key = T extends DefaultEventMap ? string | symbol : K | keyof T; type Listener = T extends DefaultEventMap ? F : K extends keyof T ? T[K] extends unknown[] ? (...args: T[K]) => void : never : never; // eslint-disable-next-line @typescript-eslint/no-explicit-any type Listener1 = Listener void>; export class TracedEventEmitter> extends EventEmitter { private readonly tracer: Tracer; constructor(tracer: Tracer, options?: { captureRejections?: boolean }) { super(options); this.tracer = tracer; const methods = ['emit']; addTracingToMethods(this, methods, this.tracer); } public override on(eventName: Key, listener1: Listener1): this { const tracer = this.tracer; const klassName = this.constructor.name; // eslint-disable-next-line @typescript-eslint/no-explicit-any const name = (listener1 as any).name; const listenerStr = name ? name : 'anonymous'; const outputStr = `${klassName}.emit listener on ${String(eventName)}: ${listenerStr}`; // eslint-disable-next-line @typescript-eslint/no-explicit-any const wrappedListener = ((...args: any[]) => { traceFunction({ name: outputStr, tracer: tracer, fn: async () => { // await the listener, but since we're not awaiting the wrapper, we've decoupled .emit() // returning from the amount of time that the listener takes, but this will let us get a // more accurate idea of how long callers take // eslint-disable-next-line @typescript-eslint/await-thenable await listener1(...args); } }).catch((err) => { throw err; }); }) as typeof listener1; super.on(eventName, wrappedListener); return this; } }