简体   繁体   中英

NestJS: How to customise log messages to include request id and name of the file the log message occurred

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"]'
  1. Assign a request ID. Make this your first middleware .
  2. Bind the request context with 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM