38

I was looking at top Node logging systems: npmlog, log4js, bunyan and winston and decided to use winston for having the most npm monthly downloads.

What I want to set up is custom logger which I will be able to use on development environment with logger.debug(...) which won't log anything on production environment. This will help me so when I'm on development environment, I won't need to write anything since I'll see all the outputs.

This is what I have now:

var level = 'debug';
if (process.env.NODE_ENV !== 'development'){
  level = 'production'; // this will never be logged!
}

var logger = new winston.Logger({
  transports: [
    // some other loggings
    new winston.transports.Console({
      name: 'debug-console',
      level: level,
      prettyPrint: true,
      handleExceptions: true,
      json: false,
      colorize: true
    })

  ],
  exitOnError: false // don't crush no error
});

Problem occurs when I'm trying to log JavaScript Object or Javascript Array. With Object, I need to do toJSON(), and for Array I need first JSON.stringify() and then JSON.parse().

It's not nice to write all the time this methods, just to log something that I want. Furthermore, it's not even resource-friendly, because those formatting methods need to be executed before logger.debug() realises that it's on production and that it shouldn't log it in the first place (basically, it's evaluating arguments before function call). I just like how old-fashined console.log() logs JavaScript objects and arrays.

Now, as I'm writing this question, I found that there is a way of describing custom format for every winston transports object. Is that the way of doing it, or is there some other way?

Tommz
  • 3,393
  • 7
  • 32
  • 44

8 Answers8

32
logger.log("info", "Starting up with config %j", config);

Winstons uses the built-in utils.format library. https://nodejs.org/dist/latest/docs/api/util.html#util_util_format_format_args

Leo
  • 1,102
  • 2
  • 11
  • 18
15

try changing prettyPrint parameter to

prettyPrint: function ( object ){
    return JSON.stringify(object);
}
Bharat Kul Ratan
  • 985
  • 2
  • 12
  • 24
8

In Winston > 3 you can use

logger.log('%o', { lol: 123 }')

Anyway... Couldn't accept that I have to use %o always and made this simple solution:

const prettyJson = format.printf(info => {
  if (info.message.constructor === Object) {
    info.message = JSON.stringify(info.message, null, 4)
  }
  return `${info.level}: ${info.message}`
})

const logger = createLogger({
  level: 'info',
  format: format.combine(
    format.colorize(),
    format.prettyPrint(),
    format.splat(),
    format.simple(),
    prettyJson,
  ),
  transports: [
    new transports.Console({})
  ],
})

So this logger....

  logger.info({ hi: 123 })

...transforms to this in the console

info: {
    "hi": 123
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
smith64fx
  • 329
  • 2
  • 10
4

Use the built-in Node.js function util.format to convert your objects to strings in the same way that console.log does.

Resigned June 2023
  • 4,638
  • 3
  • 38
  • 49
2

My recommendation is to write your own abstraction on top of winston that has a convenience method for printing your objects for debugging.

You may also look at this response for a hint of how the method could be developed.

https://stackoverflow.com/a/12620543/2211743

Community
  • 1
  • 1
ambrons
  • 136
  • 1
  • 7
1

As Leo already pointed out in his answer, Winston makes use of String Interpolation provided by util.format:

const winston = require("winston");                                                                                                                                                                                                    
const logger = new winston.Logger({                                                                                                                                                                                                    
  transports: [                                                                                                                                                                                                                        
    // some other loggings                                                                                                                                                                                                             
    new winston.transports.Console({                                                                                                                                                                                                   
      name: "debug-console",                                                                                                                                                                                                           
      level: process.env.LOGLEVEL || "info",                                                                                                                                                                                           
      prettyPrint: true,                                                                                                                                                                                                               
      handleExceptions: true,                                                                                                                                                                                                          
      json: false,                                                                                                                                                                                                                     
      colorize: true                                                                                                                                                                                                                   
    })                                                                                                                                                                                                                                 
  ],                                                                                                                                                                                                                                   
  exitOnError: false // don't crush no error                                                                                                                                                                                           
});                                                                                                                                                                                                                                    

const nestedObj = {                                                                                                                                                                                                                    
  foo: {                                                                                                                                                                                                                               
    bar: {                                                                                                                                                                                                                             
      baz: "example"                                                                                                                                                                                                                   
    }                                                                                                                                                                                                                                  
  }                                                                                                                                                                                                                                    
};                                                                                                                                                                                                                                     

const myString = "foo";                                                                                                                                                                                                                

logger.log("info", "my nested object: %j. My string: %s", nestedObj, myString);                                                                                                                                                                                                                                  

When calling logger.log, you can define placeholders which will be appropriately replaced. %j will be replaced by the equivalent of JSON.stringify(nestedObj)

K435
  • 53
  • 5
1

Try using util.inspect for objects. It handles circular references correctly as well. @radon-rosborough has already given this answer but I thought of adding an example. Please see below

const customTransports = [
    new winston.transports.Console({
        format: combine(
            timestamp({
                format: 'DD-MMM-YYYY HH:MM:SS'
            }),
            label({
                label: file
            }),
            prettyPrint(),
            format.splat(),
            simple(),
            printf( (msg)=> {
                let message = msg.message;

                return colorize().colorize(msg.level, `${ msg.level } :  ${msg.timestamp} :  ${ msg.label } : \n`) + `${ util.inspect(message,{
                    depth: 2,
                    compact:true,
                    colors: true,
                } )}`;
            })
        )
    })
] 
logeekal
  • 525
  • 4
  • 13
0

Instead of doing

prettyPrint: function ( object ){
    return JSON.stringify(object)
}

it's better go with utils-deep-clone package

// initialize package on the top
const { toJSON } = require('utils-deep-clone')


// and now in your `prettyPrint` parameter do this
prettyPrint: function ( object ){
    return toJSON(object)
}

if you'll go with JSON.stringify you won't be able to print error

console.log(JSON.stringify(new Error('some error')))
// output will '{}'
Atishay Jain
  • 1,425
  • 12
  • 22