import type { CallHandler, ExecutionContext, NestInterceptor, } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common'; import type { Observable } from 'rxjs'; import { InternalException } from '../exceptions'; import { ILogger, InjectLogger } from '../logging'; import { ProtocolDetector } from '../transport/protocol.detector'; import type { ITransport, PlatformAppPlugin, WithGenericHeaders, } from '../types'; import { APP_PLUGINS, APP_TRANSPORTS } from '../types'; import { HEADER_MAPPINGS } from './constants'; import { TracingHeaders } from './tracing.actx'; import type { TracingHeaderMappings } from './types'; /** * Extracts headers that should be passed to all logging messages * and external calls */ @Injectable() export class TracingInterceptor implements NestInterceptor { /** * Extractors can be either transports or additional strategies * supplied by plugins */ private readonly extractors = new Map(); constructor( @InjectLogger(TracingInterceptor) private logger: ILogger, @Inject(APP_TRANSPORTS) private transports: Set, @Inject(APP_PLUGINS) private plugins: Set, @Inject(HEADER_MAPPINGS) private mappings: TracingHeaderMappings[], private detector: ProtocolDetector, ) { for (const transport of this.transports) { this.extractors.set(transport.getProtocol(), transport); } for (const plugin of this.plugins) { for (const x of plugin.tracingHeadersExtractors?.() ?? []) { this.extractors.set(x.protocol, x.extractor); } } } intercept(ctx: ExecutionContext, next: CallHandler): Observable { const protocol = this.detector.getProtocol(ctx) ?? ctx.getType(); const extractor = this.extractors.get(protocol); if (!extractor) { throw new InternalException( `No headers extractor for protocol: ${protocol}`, ); } const headers: Record = {}; for (const aliases of this.mappings) { const key = aliases[0]; const value = aliases .map((x) => extractor.extractHeaders(ctx).get(x)) .find((x) => x); headers[key] = value || '[missing]'; } this.logger.trace('Extracted traceable headers', { headers }); TracingHeaders.set(headers); return next.handle(); } }