import { DynamicModule, FactoryProvider, Logger, ModuleMetadata } from '@nestjs/common'; import { TraceConfig } from '../trace/trace-config.js'; import { DecoratorInjector } from './injector/decorator-injector.js'; import { Constants } from './constants.js'; import { trace } from '@opentelemetry/api'; import { Tracer } from '@opentelemetry/sdk-trace-base'; import { TraceService } from './trace-service.js'; import { initTracing } from '../trace/trace.js'; import { ModuleRef, ModulesContainer } from '@nestjs/core'; import { EventEmitterModule } from '@nestjs/event-emitter'; import _ from 'lodash'; import { ControllerInjector } from './injector/controller-injector'; const logger = new Logger('') export class SfNestTraceModule { private static buildAsyncProvider(): FactoryProvider { return { provide: Constants.SDK, useFactory: async (config) => { if (!config.autoCreate) { return; } const configuration = { ...TraceConfig, ...config }; logger.log('TraceModule初始化sdk: %j', configuration); const sdk = initTracing( { ...configuration }) return sdk; }, inject: [Constants.SDK_CONFIG], }; } private static buildAsyncInjectors(): FactoryProvider { return { provide: Constants.SDK_INJECTORS, useFactory: async (config, moduleRef: ModuleRef) => { config = { ...TraceConfig, ...config }; if (_.toNumber(config.isTraceOn) !== 1) { return {} } const injectors = [ ControllerInjector ] logger.log('处理Span注入') const modulesContainer = moduleRef.get(ModulesContainer, { strict: false }); const decoratorInjector = await moduleRef.create(DecoratorInjector); decoratorInjector.setModulesContainer(modulesContainer) await decoratorInjector.inject(); for await (const injector of injectors) { const created = await moduleRef.create(injector); created.setModulesContainer(modulesContainer) if (created['inject']) await created.inject(); } logger.log('处理Span注入成功') return {}; }, inject: [Constants.SDK_CONFIG, ModuleRef], }; } private static buildTracer(serviceName?: string) { return { provide: Tracer, useFactory: (traceService: TraceService) => trace.getTracer(serviceName || 'default'), inject: [TraceService], }; } static async forRootAsync( options: ModuleMetadata & { useFactory: (...args: any) => TraceConfig, inject: any[], } ): Promise { return { global: true, module: SfNestTraceModule, imports: [ ...options?.imports, EventEmitterModule.forRoot() ], providers: [ TraceService, this.buildAsyncProvider(), this.buildAsyncInjectors(), this.buildTracer(), { provide: Constants.SDK_CONFIG, useFactory: options.useFactory, inject: options.inject, }, ], exports: [TraceService, Tracer], }; } }