Search code examples
node.jsloggingwinston

winston format info objects differently


I am using the winston logging library. I have some objects that I want to log, and before logging them I want to do some transformations. I can manually do the transformations before putting them into the logger, but I want to avoid doing those transformations when the chosen log level is lower than the level that those transformations would appear on. If there is a way to format each individual info object, then I would not waste cpu on extra transformations.

Here is roughly my logger class to understand what I mean:

import * as winston from 'winston'
class Logger {
  constructor(logLevel) {
    this.logger = createLogger({ level: logLevel })
  }

  newValues = (valuesWithIds) => {
    this.logger.info(valuesWithIds.map(v => v.value))
  }
  selectedCachedValues = (valuesWithIds) => {
    this.logger.info(valuesWithIds.map(v => v.value))
  }
  downloadComplete = (url, queue) => {
    this.logger.info(`${url} complete, ${queue.length} items remaining`)
  }
}

const logger = new Logger('error')
// this performs an array map operation that doesnt need to be ran.
logger.newValues([{ id: 1, value: 'a' }, { id: 1, value: 'b' }])

libraries like scala-logging do this sort of thing with macros. I am hoping I can write something as efficient in javascript.


Solution

  • I actually ended up solving my problem with bunyan logger. Bunyan lets you define specific transformations on specific object keys (they refer to them as serializers). I can define certain serializers, and if I log at a level that is above the chosen log level, it will ignore that serializer.

    const bunyan = require('bunyan')
    const serializers = {
      valuesWithIds: vals => vals.map(v => v.value)
    }
    const logger = bunyan.createLogger({ name: 'root', level: 'error' })
    
    const valuesWithIds = [{ id: 1, value: 'a' }, { id: 1, value: 'b' }]
    // logs nothing, doesnt execute the serializer
    logger.info({ valuesWithIds }, 'loaded values')
    // logs to console, runs the serializer
    logger.error({ valuesWithIds }, loaded values')
    

    this produces one line of output:

    {"name":"root","hostname":"myhostname","pid":100,"level":30,"valuesWithIds":["a","b"],"msg":"loaded values","time":"2019-01-14T00:05:04.800Z","v":0}