I am new to NestJS and would like to customise the log messages to include the x-request-id/x-correlation-id and the name of the file the log message originated but am not sure if there is anything in NestJS to do that.
My application is using NestJS with the Fastify adapter and has the following configuration in the bootstrap() function
const app = await NestFactory.create<NestFastifyApplication>( AppModule, new FastifyAdapter(), { logger: WinstonModule.createLogger(winston.createLogger({ exitOnError: false, level: 'debug', handleExceptions: true, format: winston.format.combine( winston.format.timestamp(), winston.format.ms(), winston.format.colorize(), winston.format.align(), winston.format.splat(), winston.format.printf((info) => { return `${info.timestamp} [ ${info.level} ]: ${info.message}`; }), ), transports: [ new (winston.transports.Console)() ] }), ) } );
This seems to format the logs using winston as expected.
2022-03-09T11:21:22.131Z [ info ]: Starting Nest application...
However, I would also like to include the request/correlation id in the message and the name of the file the log message occurred eg
2022-03-09T11:21:22.131Z 2cfd4eee-ca2b-4869-b66b-2b7da291f567 [ info ] [ Main.ts ]: Starting Nest application...
Is there anything in NestJS itself to allow this or any external libraries that I could use to achieve the desired result?
I managed to get it working using the nest-pino library:
// main.ts import { Logger } from 'nestjs-pino'; async function bootstrap() { const app = await NestFactory.create<NestFastifyApplication>( AppModule, new FastifyAdapter(), { bufferLogs: true } ); app.useLogger(app.get(Logger)); } bootstrap();
// app.module.ts import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ LoggerModule.forRoot({ pinoHttp: { level: process.env.LOG_LEVEL || 'debug', redact: ['request.headers.authorization'], prettyPrint: { colorize: true, singleLine: true, levelFirst: false, translateTime: "yyyy-MM-dd'T'HH:mm:ss.l'Z'", messageFormat: "{req.headers.x-correlation-id} [{context}] {msg}", ignore: "pid,hostname,context,req,res,responseTime", errorLikeObjectKeys: ['err', 'error'] } } }), ], controllers: [MyController], }) export class AppModule {}
// my.controller.ts import { Controller, Get, Param, Logger } from '@nestjs/common'; @Controller() export class MyController { private readonly logger: Logger = new Logger(MyController.name); @Get('/:id') async getCustomerDetails(@Headers() headers, @Param('id') id: string): Promise<Customer> { this.logger.log(`Accepted incoming request with id: ${id}`); // Do some processing .... return customer; } }
[2022-11-14T11:03:07.100Z] INFO: 428f0df9-d12b-4fca-9b11-805a13ff41be [MyController] Accepted incoming request with id: 1
****** UPDATE **********
I also managed to update the redacted fields to be configurable from a .yaml file
// app.module.ts import { ConfigModule, ConfigService } from '@nestjs/config'; import { LoggerModule } from 'nestjs-pino'; @Module({ imports: [ LoggerModule.forRootAsync({ imports: [ConfigModule], useFactory: async (configService: ConfigService) => ({ pinoHttp: { level: process.env.LOG_LEVEL || 'info', redact: configService.get<string[]>('logger.redacted.fields'), prettyPrint: { colorize: false, singleLine: true, levelFirst: false, translateTime: "yyyy-mm-dd'T'HH:MM:ss.l'Z'", messageFormat: '{req.headers.x-correlation-id} [{context}] {msg}', ignore: 'pid,hostname,context,req,res,responseTime', errorLikeObjectKeys: ['err', 'error'], }, }, }), inject: [ConfigService], }), ], controllers: [MyController], }) export class AppModule {}
dev.yaml
logger:
redacted:
fields:
- 'headers.Authorization'
- 'headers["X-Api-Key"]'
middleware
.logger
. Make a class of Logger
, bind the request context
to logger. It will act basically as wrapper for WinstonLogger
. Override all the methods of winston logger to print the request ID in form an manner which you want (preferred way is using JSON as it will be easier for you to query in logs).If you are using winston,
Use logFormatter from winston to add more fields to each logs:-
One example below:
const { format} = require("winston");
var reqId = '123123' //function or const request id
const addRequestId = format((info, opts) => {
if(reqId)
info.reqId= reqId;
return info;
});
Then add below config while creating logger
var config = {
format: format.combine(
addRequestId(),
format.timestamp(new Date().toISOString()),
format.json(),
),
transports: [new transports.Console()],
level: 'debug'
}
const logger = createLogger(config);
To get file name, use (new Error()).trace property. This will give you full stack trace containing file names and line number in each file.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.