Package Exports
- nestjs-pino
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (nestjs-pino) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
NestJS-Pino
✨✨✨ Platform agnostic logger for NestJS based on Pino with REQUEST CONTEXT IN EVERY LOG ✨✨✨
Example
Import module:
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [LoggerModule.forRoot()],
controllers: [AppController],
providers: [MyService]
})
class MyModule {}
In controller:
import { Logger } from 'nestjs-pino';
@Controller()
export class AppController {
constructor(
private readonly myService: MyService,
private readonly logger: Logger
) {}
@Get()
getHello(): string {
this.logger.log("getHello()", AppController.name);
return `Hello ${this.myService.getWorld()}`;
}
}
In service:
import { Logger } from 'nestjs-pino';
@Injectable()
export class MyService {
constructor(private readonly logger: Logger) {}
getWorld(...params: any[]) {
this.logger.log("getWorld(%o)", MyService.name, params);
return "World!";
}
}
Output:
// Logs by Nest itself, when set `app.useLogger(app.get(Logger))`
{"level":30,"time":1570470154387,"pid":17383,"hostname":"my-host","context":"RoutesResolver","msg":"AppController {/}: true","v":1}
{"level":30,"time":1570470154391,"pid":17383,"hostname":"my-host","context":"RouterExplorer","msg":"Mapped {/, GET} route true","v":1}
{"level":30,"time":1570470154405,"pid":17383,"hostname":"my-host","context":"NestApplication","msg":"Nest application successfully started true","v":1}
// Logs by injected Logger methods in Services/Controllers
// Every log has it's request data
{"level":30,"time":1570470161805,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"context":"AppController","msg":"getHello()","v":1}
{"level":30,"time":1570470161805,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"context":"MyService","msg":"getWorld([])","v":1}
// Automatic logs of every request/response
{"level":30,"time":1570470161819,"pid":17383,"hostname":"my-host","req":{"id":1,"method":"GET","url":"/","headers":{...},"remoteAddress":"::1","remotePort":53957},"res":{"statusCode":304,"headers":{...}},"responseTime":15,"msg":"request completed","v":1}
Comparison with others
There are other Nestjs loggers. The key purposes of this one are:
- to be compatible with built in
LoggerService
- to log with JSON (thanks to
pino
- super fast logger) (why JSON?) - to log every request/response automatically (thanks to
pino-http
) - to bind request data to the logs automatically from any service on any application layer without passing request context
Logger | Nest App logger | Logger service | Autobind request data to logs |
---|---|---|---|
nest-morgan | - | - | - |
nest-winston | + | + | - |
nestjs-pino-logger | + | + | - |
nestjs-pino | + | + | + |
Install
npm i nestjs-pino
Register module
Default params
Just import LoggerModule
to your module:
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [LoggerModule.forRoot()],
...
})
class MyModule {}
Synchronous configuration
LoggerModule.forRoot
has the same API as pino-http:
import { LoggerModule } from 'nestjs-pino';
@Module({
imports: [
LoggerModule.forRoot(
{
name: 'add some name to every JSON line',
level: process.env.NODE_ENV !== 'production' ? 'debug' : 'info',
prettyPrint: process.env.NODE_ENV !== 'production',
useLevelLabels: true,
// and all the others...
},
someWritableStream
)
],
...
})
class MyModule {}
Asynchronous configuration
With LoggerModule.forRootAsync
you can for example import your ConfigModule
and inject ConfigService
to use it in useFactory
method.
useFactory
should return result typeof arguments of pino-http or null
or Promise
of it, example:
import { LoggerModule } from 'nestjs-pino';
@Injectable()
class ConfigService {
public readonly level = "debug";
}
@Module({
providers: [ConfigService],
exports: [ConfigService]
})
class ConfigModule {}
@Module({
imports: [
LoggerModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (config: ConfigService) => {
await somePromise();
return { level: config.level };
}
})
],
...
})
class TestModule {}
Or without ConfigModule
you can just pass ConfigService
to providers
:
import { LoggerModule } from 'nestjs-pino';
@Injectable()
class ConfigService {
public readonly level = "debug";
public readonly stream = stream;
}
@Module({
imports: [
LoggerModule.forRootAsync({
providers: [ConfigService],
inject: [ConfigService],
useFactory: (config: ConfigService) => {
return [{ level: config.level }, config.stream];
}
})
],
controllers: [TestController]
})
class TestModule {}
Extreme mode
If you want to enable extreme
mode you should read pino extreme mode docs first.
If you are ok with that, so you can configure module like this:
import * as pino from 'pino';
import { LoggerModule } from 'nestjs-pino';
const dest = pino.extreme();
const logger = pino(dest);
@Module({
imports: [LoggerModule.forRoot({ logger })],
...
})
class MyModule {}
Also you can read more about Log loss prevention.
Usage as Logger service
Logger
implements standard NestJS LoggerService
interface. So if you are familiar with built in NestJS logger you are good to go.
// my.service.ts
import { Logger } from 'nestjs-pino';
@Injectable()
export class MyService {
constructor(private readonly logger: Logger) {}
getWorld(...params: any[]) {
this.logger.log("getWorld(%o)", MyService.name, params);
return "World!";
}
}
Usage as NestJS app logger
According to official docs, loggers with Dependency injection should be set via following construction:
import { Logger } from 'nestjs-pino';
const app = await NestFactory.create(MyModule, { logger: false });
app.useLogger(app.get(Logger));
FAQ
Q: How does it work?
A: It use pino-http under hood, so every request has it's own child-logger, and with help of async_hooks Logger
can get it while calling own methods. So your logs can be groupped by req.id
.
Q: Why use async_hooks instead of REQUEST scope?
A: REQUEST scope can have perfomance issues depending on your app. TL;DR: using it will cause to instantiating every class, that injects Logger
, as a result it will slow down your app.
Q: I'm using old nodejs version, will it work for me?
A: Please read this.
Q: What about pino built in methods/levels?
A: Pino built in methods are not compatible to NestJS built in LoggerService
methods, so decision is to map pino methods to LoggerService
methods to save Logger
API:
pino | LoggerService |
---|---|
trace |
verbose |
debug |
debug |
info |
log |
warn |
warn |
error |
error |