node.jsnestjspinojspino

How to Properly Format Multi-Line Logs with pino-pretty in a Node.js Application


I am currently working on a logging service in a Node.js application using pino and pino-pretty for better readability. However, I am facing an issue with multi-line log messages where escape characters (like \n) are not being properly formatted into new lines in the console output.

Current Configuration:

import pino from 'pino';

export class LoggerService {
  private readonly logger = pino({
    formatters: {
      bindings: (bindings) => ({
        pid: bindings.pid,
        hostname: bindings.hostname,
      }),
    },
    transport: {
      target: 'pino-pretty',
      options: {
        colorize: true,
      },
    },
    timestamp: pino.stdTimeFunctions.isoTime,
    redact: ['user.email', 'user.password', 'user.hash'],
  });

  async log(message: string, context?: string) {
    this.logger.info({message, context });
  }

  async error(message: string, context?: string) {
    this.logger.error({ message, context });
  }

  // Additional methods...
}

Example:

this.logger.error(error.message,'UserService.findAllByFilter')

i get:

[16:20:29.604] ERROR (20408):
    message: "\nInvalid `prisma.user.findMany()` invocation:\n\n{\n  where: {\n    banned: false\n  },\n  select: {\n?   id?: true,\n?   email?: true,\n?   profileUrl?: true,\n?   avatar?: true,\n?   firstName?: true,\n?   lastName?: true,\n?   hash?: true,\n?   banned?: true,\n?   confirmed?: true,\n?   redirectUrl?: true,\n?   sessions?: true,\n?   teacherProfile?: true,\n?   studentProfile?: true,\n?   adminProfile?: true,\n?   _count?: true\n  },\n  skip: 0,\n  take: 10\n}\n\nThe `select` statement for type User needs at least one truthy value."
    context: "UserService.findAllByFilter"

As you see, formatting just broke down.

P.S. for production I'm going to push all logs to BetterStack


Solution

  • After adding:

    messageKey: 'message'
    

    to options in transport, everything is now fine.

    Final code:

    private readonly logger = pino({
        formatters: {
          bindings: (bindings) => {
            return {
              pid: bindings.pid,
              hostname: bindings.hostname,
            };
          },
        },
        transport: {
          target: 'pino-pretty',
          options: {
            colorize: true,
            messageKey: 'message',
          },
        },
        timestamp: pino.stdTimeFunctions.isoTime,
        redact: ['user.email', 'user.password', 'user.hash'],
      });
    

    What i get now:

    [2024-04-11 16:34:44.563 +0200] ERROR (15036): 
    Invalid `prisma.user.findMany()` invocation:
    
    {
      where: {
        banned: false
      },
      select: {
    ?   id?: true,
    ?   email?: true,
    ?   profileUrl?: true,
    ?   avatar?: true,
    ?   firstName?: true,
    ?   lastName?: true,
    ?   hash?: true,
    ?   banned?: true,
    ?   confirmed?: true,
    ?   redirectUrl?: true,
    ?   sessions?: true,
    ?   teacherProfile?: true,
    ?   studentProfile?: true,
    ?   adminProfile?: true,
    ?   _count?: true
      },
      skip: 0,
      take: 10
    }
    
    The `select` statement for type User needs at least one truthy value.
        context: "UserService.findAllByFilter"