Winston logging object

Asked by At

I use Winston for my backend logging I cannot log the object without using JSON.stringify which is annoying

logger.debug(`Register ${JSON.stringify(req.body)}`)
const logger: Logger = createLogger({
    // change level if in dev environment versus production
    level: env === 'production' ? 'info' : 'debug',
    format: format.combine(
        format.label({label: path.basename(process.mainModule.filename)}),
        format.timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
        format.prettyPrint()
    ),
    transports: [
        new transports.Console({
            format: format.combine(format.colorize(), logFormat),
        }),
        new transports.File({
            filename,
            format: format.combine(format.json()),
        }),
    ],
    exitOnError: false,
})

Could you show me the way to log object with Winston. I am using version 3.2.1

2 Answers

1
SherloxFR On

You are trying to insert a JSON object directly into the string, so it will print [Object Object] without the JSON.stringify.

This is not fixable by configuring Winston, as this problem happens while the string is generated (before the logger.debug function actually reads it), so a console.log call would print the same thing.

The first parameter of the logger.* functions is the message (string), then you can pass a metadata object (JSON).

To use the metadata in your logFormat function, update your Logger instantiation as follow:

const winston = require('winston')
const { format, transports } = winston
const path = require('path')

const logFormat = format.printf(info => `${info.timestamp} ${info.level} [${info.label}]: ${info.message}`)

const logger = winston.createLogger({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
  format: format.combine(
    format.label({ label: path.basename(process.mainModule.filename) }),
    format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
    // Format the metadata object
    format.metadata({ fillExcept: ['message', 'level', 'timestamp', 'label'] })
  ),
  transports: [
    new transports.Console({
      format: format.combine(
        format.colorize(),
        logFormat
      )
    }),
    new transports.File({
      filename: 'logs/combined.log',
      format: format.combine(
        // Render in one line in your log file.
        // If you use prettyPrint() here it will be really
        // difficult to exploit your logs files afterwards.
        format.json()
      )
    })
  ],
  exitOnError: false
})

Usage:

const req = {
  body: {
    name: 'Daniel Duuch',
    email: '[email protected]',
    password: 'myGreatPassword'
  }
}

logger.debug(`Register ${req.body.name} with email ${req.body.email}`, { ...req.body, action: 'register' })

Console output:

2019-05-11 17:05:45 debug [index.js]: Register Daniel Duuch with email [email protected]

Logfile output (prettified by hand, see comment in the transport file format):

{
  message: 'Register Daniel Duuch with email [email protected]',
  level: 'debug',
  timestamp: '2019-05-11 17:05:45',
  label: 'index.js',
  metadata: {
    name: 'Daniel Duuch',
    email: '[email protected]',
    password: 'myGreatPassword'
  }
}

Hope this solves your issue.

Code for this answer

-1
Anton Pastukhov On

You can use format.splat() in your logger config:

const logger = createLogger({
    format: combine(
        ...
        format.splat(), // <--
        ...
    ),
    ...
});

...and log object using string interpolation:

let myObj = { /* ... */ };
logger.info('This message will include a complete object: %O', myObj);