for NestJS v8, v9 and v10
Why?
The Logger
class from @nestjs/common
is not a built-in provider. So we can't do this:
import { Module, Logger } from '@nestjs/common'
@Module({})
export class AppModule {
constructor(private readonly logger: Logger) {}
onModuleInit() {
this.logger.debug('hi')
}
}
So we should initialize that class by ourselves. This is good because we can define the context that the logger will have:
// ...
export class AppModule {
private readonly logger = new Logger(AppModule.name)
onModuleInit() {
this.logger.debug('hi')
}
}
but this is bad because we are not relying on DI (Dependency Injection) as we do for others native classes in our NestJS app, which looks a bit hacky. Also, if we want to ignore or change the behavior of that logger in some test suite, it would be harder than simply using the auto mocking feature from @nestjs/testing
or using any other idiomatic approach.
What?
Now we can inject the Logger
class as any other provider, like this:
import { Module, Logger, Scope, Inject } from '@nestjs/common'
import { INQUIRER } from '@nestjs/core'
@Module({
providers: [
{// Register this provider in the module that you want to inject the logger
provide: Logger,
scope: Scope.TRANSIENT,
inject: [INQUIRER],
useFactory: (parentClass: object) => new Logger(parentClass.constructor.name),
}
],
})
export class AppModule {
@Inject()
private readonly logger: Logger
// or via constructor-based injection
//constructor(private readonly logger: Logger) {}
onModuleInit() {
this.logger.debug('hi')
}
}
How?
In order to make that possible, we will have a new factory provider using the transient scope and the INQUIRER
native provider.
As per the docs:
Transient providers are not shared across consumers. Each consumer that injects a transient provider will receive a new, dedicated instance.
It means a new instance of the Logger
class will be created every time they are injected somewhere, instead of being a singleton as usual. This is good because we can then inject the INQUIRER
provider (from @nestjs/core
) into that factory provider.
The value of INQUIRER
provider will be the class where a provider was constructed. So we can use that value to define the logger context (ie., the first argument on new Logger
constructor).
And that's how we can move the initialization of the Logger
class to the module definition instead of having them polluting our providers.
Top comments (2)
Another strange feature of NestJS.
Logger
is so fundamental that it should be in@nestjs/core
.Muito bom! Cheguei a quebrar a cabeça com isso numa época.