15

The winston library is great for transports and flexibility. I'd like to use it to allow configuring levels and redirecting to files, but would like to reproduce console.log behavior for formatting, and having trouble.

Here's what I have so far:

const log = winston.createLogger({
  level: 'debug',
  format: format.combine(
    format.timestamp({format: 'YYYY-MM-DD HH:mm:ss.SSS'}),
    format.splat(),
    format.colorize(),
    format.printf(({level, message, label, timestamp}) => `${timestamp} ${label || '-'} ${level}: ${message}`),
  ),
  transports: [
    new winston.transports.Stream({
      stream: process.stderr,
      level: 'debug',
    })
  ],
});

log.info("Hello, %s", "Bob");   // Works: outputs "Hello, Bob"

But this don't work:

log.info("Hello", "Bob");
log.info("Hello", 123, {someObj: 1});

I'd like all extraneous objects after those taken up by splat() to get added on, space-separated, and converted to string preferably using util.inspect().

DS.
  • 22,632
  • 6
  • 47
  • 54
  • You need another transport for `console const console = new winston.transports.Console();` Check out the docs. The only issue I faced was `stack trace` was formatted to string, so not prettified on the terminal – 1565986223 Mar 28 '19 at 02:19
  • My question is about formatting of extra args, not about transports. – DS. Mar 29 '19 at 03:36
  • @DS.i'm in the same boat. did you ever find a solution? this library used to be good. edit, i literally just figured it out. so i'm going to leave my solution for everyone here. – r3wt Jun 03 '19 at 17:50
  • 1
    this works: https://github.com/winstonjs/winston/issues/1427#issuecomment-811184784 – Stan Wiechers Mar 31 '21 at 16:10

2 Answers2

24

Answering my own question. The issue is with format.splat -- pure util.format offers a simpler and more expected behavior. Replacing format.splat with this utilFormatter addresses the problem:

const util = require('util');

function transform(info, opts) {
  const args = info[Symbol.for('splat')];
  if (args) { info.message = util.format(info.message, ...args); }
  return info;
}

function utilFormatter() { return {transform}; }

The example from my question then looks like so:

const log = winston.createLogger({
  level: 'debug',
  format: format.combine(
    format.timestamp({format: 'YYYY-MM-DD HH:mm:ss.SSS'}),
    utilFormatter(),     // <-- this is what changed
    format.colorize(),
    format.printf(({level, message, label, timestamp}) => `${timestamp} ${label || '-'} ${level}: ${message}`),
  ),
  transports: [
    new winston.transports.Stream({
      stream: process.stderr,
      level: 'debug',
    })
  ],
});

log.info("Hello, %s", "Bob");          // Works: outputs "Hello, Bob"
log.info("Hello", "Bob");              // Works: outputs "Hello Bob"
log.info("Hello", 123, {someObj: 1});  // Works: outputs "Hello 123 { someObj: 1} "
DS.
  • 22,632
  • 6
  • 47
  • 54
4

I had a similar problem, and after much trial and error i think i have a solution that you might be interested in. As i mentioned in my last update, we ended up building our own logger. well, over the weekend i published this logger to npm, and you're welcome to check it out.

It should have more or less identical output to console.log. If you notice any inconsistencies, please let me know.

Also it also has multiple transports, and you can even pass custom ones, and you can even "wrap" the console functions to quickly integrate into your project.

example code:

const {createLogger,wrapConsole,unwrapConsole} = require('@r3wt/log');
const log = createLogger({log_level:'info',transports:['console','file']});
wrapConsole(log);//wraps the console globally with the log instance, making integration into large existing project less painful

// NOTE: only the following 4 functions are wrapped. 
console.log('hi!');
console.warn('warning');
console.error('error');
console.info('info');

unwrapConsole();//unwrap console globally

You can find the library here as well as more code examples and basic documentation if you're interested, and PR's with features and fixes are welcomed and encouraged. code is MIT so you are free to fork and create your own version :-)

Best of luck, and i hope this helps.

r3wt
  • 4,642
  • 2
  • 33
  • 55
  • 1
    When I try your exact code with the two lines from my example, the output I get is: `info: Hello {"0":"B","1":"o","2":"b"}` and `info: Hello {"someObj":1}` which is quite poor (not really printing the string, omitting the number entirely). This is winston 3.2.1. – DS. Jun 04 '19 at 21:02
  • @DS. how are you calling it, can you give me an example? also i updated the post with the final solution i am using in my project – r3wt Jun 05 '19 at 16:24
  • 1
    I think you may have misunderstood my question, which was about calls like `log.info('app running on port', 3000)` (notice absence of "%d"), which works with `console.log` and with older winston, but not with new winston formatters. – DS. Jul 01 '19 at 20:49
  • @DS. my bad. Honestly, we ended up moving away from winston and just writing our own logger. Sorry i couldn't help you. – r3wt Jul 08 '19 at 15:16
  • @DS. i made another go of it and published my work to NPM. please let me know if the output and functionality suits your needs. – r3wt Aug 12 '19 at 18:54