如何在NestJS的AppModule中访问自定义Logger实例?
解决NestJS AppModule中无法获取自定义Logger实例的问题
问题原因
你在bootstrap中传入的自定义Logger是给Nest框架内部使用的实例,但这个实例并没有自动注册到DI容器中。其他模块能正常注入是因为CustomLoggerModule正确提供了Logger实例,而AppModule要么没正确导入该模块,要么尝试通过ModuleRef获取时,DI容器中还没有注册自定义Logger的token,导致拿到了Nest默认的Logger实例。
解决方案
方案1:确保CustomLoggerModule全局化并直接注入
- 修改CustomLoggerModule,将其设为全局模块并导出自定义Logger:
import { Module, Global, LoggerService } from '@nestjs/common'; import { CustomLogger } from './custom.logger'; @Global() @Module({ providers: [ CustomLogger, // 同时注册为LoggerService抽象,兼容Nest的Logger注入 { provide: LoggerService, useClass: CustomLogger, }, ], exports: [CustomLogger, LoggerService], }) export class CustomLoggerModule { static createLogger() { return new CustomLogger(); } }
- 在AppModule中直接注入CustomLogger,无需使用
ModuleRef:
import { Module, OnModuleInit } from '@nestjs/common'; import { CustomLogger } from './custom.logger'; @Module({ // 因为是全局模块,这里可以不用导入,但显式导入更清晰 imports: [CustomLoggerModule], }) export class AppModule implements OnModuleInit { constructor(private readonly customLogger: CustomLogger) {} onModuleInit() { // 这里可以正常调用initMiddleware方法 this.customLogger.initMiddleware(); } }
方案2:让bootstrap和DI使用同一个Logger实例
如果需要保证启动阶段和DI中的Logger是同一个实例,可以修改CustomLoggerModule复用实例:
import { Module, Global, LoggerService, DynamicModule } from '@nestjs/common'; import { CustomLogger } from './custom.logger'; @Global() @Module({}) export class CustomLoggerModule { private static loggerInstance: CustomLogger; static createLogger(): CustomLogger { if (!this.loggerInstance) { this.loggerInstance = new CustomLogger(); } return this.loggerInstance; } static forRoot(): DynamicModule { return { module: CustomLoggerModule, providers: [ { provide: CustomLogger, useValue: this.loggerInstance, }, { provide: LoggerService, useValue: this.loggerInstance, }, ], exports: [CustomLogger, LoggerService], }; } }
然后在bootstrap和AppModule中配合使用:
// bootstrap.ts async function bootstrap() { // 创建全局唯一的Logger实例 const logger = CustomLoggerModule.createLogger(); // 传入NestFactory const app = await NestFactory.create(AppModule, { logger }); await app.listen(3000); } // AppModule.ts @Module({ imports: [CustomLoggerModule.forRoot()], }) export class AppModule implements OnModuleInit { constructor(private readonly customLogger: CustomLogger) {} onModuleInit() { this.customLogger.initMiddleware(); } }
方案3:检查ModuleRef的使用方式
如果一定要用ModuleRef,需要确保在模块初始化完成后获取,比如在OnModuleInit生命周期钩子中,并且指定正确的token:
import { Module, OnModuleInit, ModuleRef } from '@nestjs/common'; import { CustomLogger } from './custom.logger'; import { CustomLoggerModule } from './custom-logger.module'; @Module({ imports: [CustomLoggerModule], }) export class AppModule implements OnModuleInit { constructor(private readonly moduleRef: ModuleRef) {} async onModuleInit() { // 明确指定获取CustomLogger类型的实例 const customLogger = this.moduleRef.get(CustomLogger); customLogger.initMiddleware(); } }
关键注意点
- 不要混淆Nest框架内部使用的Logger和DI容器中的Logger:前者是通过
NestFactory.create传入的,后者需要在模块中显式注册。 - 确保
CustomLoggerModule的导出和导入逻辑正确,DI容器才能找到自定义Logger的实例。
内容的提问来源于stack exchange,提问作者Tiziano
相关产品推荐
相关产品推荐

