node.jsloggingwinston

How to make a custom json formatter for winston@3 logger?


I'm trying to make a custom json formatter for winston v3.

Here is a single file with demo:

const { createLogger, format, transports } = require("winston")
const { combine, timestamp, prettyPrint } = format

const logger = createLogger({
  format: combine(timestamp(), prettyPrint())
})

logger.add(new transports.Console())

logger.info("test message", { data: { a: 1, b: [1, 2, 3, 4], d: new Date() } })

try {
  throw new Error("I'm an error!")
} catch (err) {
  logger.error(err)
}

It prints:

{ data: { a: 1, b: [ 1, 2, 3, 4 ], d: 2018-07-21T08:59:27.958Z },
  level: 'info',
  message: 'test message',
  timestamp: '2018-07-21T08:59:27.959Z',
  [Symbol(level)]: 'info',
  [Symbol(splat)]:
   [ { data: { a: 1, b: [ 1, 2, 3, 4 ], d: 2018-07-21T08:59:27.958Z } } ] }
{ Error: I'm an error!
    at Object.<anonymous> (/Users/max7z/projects/test/t32__test__winston__pretty-print_symbol/index.js:13:9)
    at Module._compile (internal/modules/cjs/loader.js:689:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
    at Module.load (internal/modules/cjs/loader.js:599:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
    at Function.Module._load (internal/modules/cjs/loader.js:530:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
    at startup (internal/bootstrap/node.js:266:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:596:3)
  level: 'error',
  timestamp: '2018-07-21T08:59:27.962Z',
  [Symbol(level)]: 'error' }

I'd like to have almost the same output, but without some fields: [Symbol(level)], [Symbol(splat)].

Is it possible to remove [Symbol] fields from prettyPrint formatter?

Or how can I make my own custom json formatter with error stacktrace like prettyPrint has?


Solution

  • I tested the sample with winston v3.3.3 and added a custom formatter to stringify the data object:

    const winston = require('winston');
    
    const myFormatter = winston.format((info) => {
      const {message} = info;
    
      if (info.data) {
        info.message = `${message} ${JSON.stringify(info.data)}`;
        delete info.data; // We added `data` to the message so we can delete it
      }
      
      return info;
    })();
    
    const logger = winston.createLogger({
      format: winston.format.combine(
        winston.format.timestamp({
          format: 'YYYY-MM-DD HH:mm:ss',
        }),
        myFormatter,
        winston.format.simple(),
      ),
      transports: [
        new winston.transports.Console(),
      ],
    });
    

    I ran the commands from the question and got the desired output.

    Execution:

    logger.info('test message', {data: {a: 1, b: [1, 2, 3, 4], d: new Date()}});
    
    try {
      throw new Error(`I'm an error!`);
    } catch (err) {
      logger.error(err);
    }
    

    Output:

    info: test message {"a":1,"b":[1,2,3,4],"d":"2020-09-09T09:41:22.525Z"} {"timestamp":"2020-09-09 11:41:22"}

    error: I'm an error! {"timestamp":"2020-09-09 11:41:22"}

    The JSON object can be also printed with tabs and line breaks when using winston.format.prettyPrint() instead of winston.format.simple().