How to enable pino-http logs with NestJS-Pino in a Nest.js middleware?

3k Views Asked by At

I'm currently using NestJS-Pino as a logging library in my Nest.js application. I have configured a middleware that is supposed to log requests, and I'd like it to show logs that are of the pino-http type.

Here is an example of the log format that I want (recieved from app controller):

{"level":30,"time":epochtime,"pid":pid,"hostname":"local","req":{"id":"someid","method":"GET","url":"/","query":{},"params":{"0":""},"headers":{"user-agent":"useragent","accept":"*/*","host":"localhost:3000","accept-encoding":"gzip, deflate, br","connection":"keep-alive"},"remoteAddress":"::1","remotePort":57377},"context":"AppController","msg":"Request..."}

However, I'm only getting a log with this format (non pino-http):

{"level":30,"time":epochtime,"pid":pid,"hostname":"local","msg":"Request..."}

My AppModule is defined as follows:

@Module({
  imports: [
    LoggerModule.forRoot({
      pinoHttp: {
        autoLogging: false,
      },
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerMiddleware).forRoutes('*');
  }
}

And my LoggerMiddleware is as follows:

@Injectable()
export class LoggerMiddleware implements NestMiddleware {
  private readonly logger = new Logger(LoggerMiddleware.name);
  use(req: Request, res: Response, next: NextFunction) {
    this.logger.log('Request...');
    next();
  }
}

What do I need to change in order to get logs in the Pino-HTTP format to work like they do in the app service and controller? Thanks in advance

2

There are 2 best solutions below

0
dexat0r On

Try this.logger.log({ req }, 'Request...');

Check out docs !

0
giang On

I suggest to try winston winston-transport to handle log easier with capture data and send it to your server log. PinoHttp can not accessible internally, it is for log out http req and res.

Here what you can do.

Create the logger-middleware use to capture the log output from pino-http and send to remote server. BUT this is catch on route only, not all internal app logging because it not same instance of nestjs-pino of LoggerModule.

Create LoggerModule implement nestjs-pino - this only use to see the log in console app.

Your implement logger-middleware:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { HttpService } from '@nestjs/axios';
import pino from 'pino';

@Injectable()
export class PinoLoggerMiddleware implements NestMiddleware {
  constructor(private readonly httpService: HttpService) { }

  use(req: Request, res: Response, next: NextFunction) {
    const logStream = this.createLogStream(req, res);

    // Create a custom logger and specify the custom log stream
    const logger = pino(
      {
        level: 'info', // Set the default log level
        prettifier: require('pino-pretty'),
        customLevels: {
          error: 50,
          warn: 40,
        },
      },
      logStream,
    );

    const pinoLogger = require('pino-http')({ logger });

    pinoLogger(req, res);

    next();
  }

  private createLogStream(req: Request, res: Response) {
    return {
      write: (chunk: any) => {
        // Extract log data from the chunk
        const logData = {
          method: req.method,
          url: req.url,
          headers: req.headers,
          message: chunk, // Assuming the log message is in the 'msg' field
        };

        // Log data using the pino logger
        //pino().info(logData);

        // Check log level and send log data to a remote server conditionally
        if (chunk.level === 40 || chunk.level === 50) {
          this.logRequestToRemoteServer(logData);
        }
      },
    };
  }

  private logRequestToRemoteServer(logData: any) {
    // Send log data to a remote server using HTTP service or any other method.
    // Customize this according to your requirements.
    this.httpService.post('your-remote-log-endpoint', logData).subscribe({
      next: (response: any) => {
        // Handle success
      },
      error: (error: any) => {
        // Handle error
      },
    });
  }
}