15

I was trying to use winston for logging, but I am seeing that if I want to get the data with 2 arg, then I am getting the log, but if I want to get 3 arguments, then I am not getting the data,

Ex:

logger = new (winston.Logger)({
transports: [
   new (winston.transports.Console)({colorize : true, timestamp:true}),
 ]
});

Now trying to get the log as below:

 1. logger.info("We got the data %s, %d", data, port);
 O/P: We got the data %s, %d  => No data and port value
 2. logger.info("We got the data, port", data, port);
 O/P: We got the data, port   => No data and port value
 3. logger.info("We got the data", data);
 ===> Getting the data correctly.

Can you please let me know what I was missing in 1, 2 or does winston not log the data in case 1 & 2 really?

user1887432
  • 253
  • 1
  • 3
  • 10

5 Answers5

12

Since Winston API doesn't work like console.log, therefore a workaround would be to create a wrapper function which utilizes util.format in the following way:

const util = require('util')   

...

function log(...msg) {
  logger.info(util.format(...msg))
}

Then you can call the log like: log('my log entry', 'foo, 'bar')

Which will log like this: {"message":"my log entry foo bar","level":"info"}

anmatika
  • 1,581
  • 3
  • 21
  • 29
11

This is what worked for me and winston 3.2.x:

const { format, createLogger, transports } = require('winston');
const jsonStringify = require('fast-safe-stringify');

const logLikeFormat = {
  transform(info) {
    const { timestamp, label, message } = info;
    const level = info[Symbol.for('level')];
    const args = info[Symbol.for('splat')];
    const strArgs = args.map(jsonStringify).join(' ');
    info[Symbol.for('message')] = `${timestamp} [${label}] ${level}: ${message} ${strArgs}`;
    return info;
  }
};

const debugFormat = {
  transform(info) {
    console.log(info);
    return info;
  }
};

const logger = createLogger({
  format: format.combine(
    // debugFormat, // uncomment to see the internal log structure
    format.timestamp(),
    format.label({ label: 'myLabel' }),
    logLikeFormat,
    // debugFormat, // uncomment to see the internal log structure
  ),
  transports: [
    new transports.Console()
  ]
});


logger.info('foo', 'bar', 1, [2, 3], true, { name: 'John' });

which results in: 2019-07-04T21:30:08.455Z [myLabel] info: foo "bar" 1 [2,3] true {"name":"John"}

Basically format.combine sets a pipeline for an info object. For each format function, transform is called and the final log message should be written to info[Symbol.for('message')] hope this helps

pgorecki
  • 639
  • 6
  • 8
3

You are assuming there is a varargs style API, but there is not. The full API is level, msg, meta, callback, but when you use one of the methods that is the level, then it's just msg, meta, callback. You need to construct a single meta object and pass that.

https://github.com/flatiron/winston/blob/master/lib/winston/logger.js#L115

//
// ### function log (level, msg, [meta], callback)
// #### @level {string} Level at which to log the message.
// #### @msg {string} Message to log
// #### @meta {Object} **Optional** Additional metadata to attach
// #### @callback {function} Continuation to respond to when complete.
// Core logging method exposed to Winston. Metadata is optional.
//

https://github.com/flatiron/winston#logging-with-metadata

Peter Lyons
  • 142,938
  • 30
  • 279
  • 274
  • 1
    I have added this pull request to winston to make this more clear in the docs: https://github.com/vanthome/winston/compare/flatiron:master...patch-1?quick_pull=1 – vanthome Jul 22 '13 at 19:47
  • 1
    The bottom link now shows that winston does support varargs API, but treats the last argument as the "meta" object to print. Include {} as an additional argument at the end to make sure it uses the arguments before it for string interpolation: https://github.com/flatiron/winston#logging-with-metadata – DS. Mar 23 '15 at 03:27
2

I worked around this by wrapping the winston functions, and using the wrapper. I hope there's a better solution for this by now.

var logger = new (winston.Logger)({
        transports:[new (winston.transports.Console)({ json : false, timestamp : true, level : 0, colorize : true}),
                    new (winston.transports.File)({ filename: filepath, json : false, timestamp : true, level : 0, colorize: true })]
    });

// pass in function arguments object and returns string with whitespaces
function argumentsToString(v){
    // convert arguments object to real array
    var args = Array.prototype.slice.call(v);
    for(var k in args){
        if (typeof args[k] === "object"){
            // args[k] = JSON.stringify(args[k]);
            args[k] = util.inspect(args[k], false, null, true);
        }
    }
    var str = args.join(" ");
    return str;
}


    // wrapping the winston function to allow for multiple arguments
    var wrap = {};
    wrap.info = function () {
        logger.log.apply(logger, ["info", argumentsToString(arguments)]);
    };

    wrap.error = function () {
        logger.log.apply(logger, ["error", argumentsToString(arguments)]);
    };

    wrap.warn = function () {
        logger.log.apply(logger, ["warn", argumentsToString(arguments)]);
    };

    wrap.debug = function () {
        logger.log.apply(logger, ["debug", argumentsToString(arguments)]);
    };

/// then return wrap as is it was your logger. /// and use like log.info(1,2,3,4,"foo","bar");

Thijs Koerselman
  • 21,680
  • 22
  • 74
  • 108
0

This is my logging module and it works fine for my all projects.

const winston = require('winston');

module.exports = function() {
    const logger = winston.createLogger({
        format: winston.format.combine(
            winston.format.timestamp(),
            winston.format.colorize(),
            winston.format.json()           
        ),
        transports: [
          new winston.transports.File({ filename: 'error.log' }),
        ]
      });

      if (process.env.NODE_ENV !== 'production') {
        logger.add(new winston.transports.Console({
            format: winston.format.combine(
                winston.format.timestamp(),
                winston.format.colorize(),
                winston.format.json()               
            )
        }));
      }

    process.on('unhandledRejection', (error) =>{
        logger.error(error.stack);
    });

    process.on('uncaughtException', (error) =>{
        logger.error(error.stack);
    }); 
}
Hardik Raval
  • 3,406
  • 1
  • 26
  • 28