Winston Logger

Hands-on example that shows how to capture logs with Winston in Node-based projects.

Project

Clone the sample project and run it to see the generated log files.

  1. Download the repository.

    git clone https://github.com/AlexQuispe/winston-logger-example.git
    
  2. Enter the project directory.

    cd winston-logger-example
    
  3. Install the dependencies.

    npm install
    
  4. Run the script.

    node index.js
    
    30/04/2020 22:02:27 [info] Mensaje informativo
    30/04/2020 22:02:27 [warn] Mensaje de advertencia
    30/04/2020 22:02:27 [error] Mensaje de error
    30/04/2020 22:02:27 [info] Mensaje con datos { user: 'John Smith' }
    

Result

Before running the script.

app
  ├─ logger.js
  └─ index.js

After running the script.

app
  ├─ logs
  │     ├─ info.log
  │     ├─ warn.log
  │     └─ error.log
  ├─ logger.js
  └─ index.js

The resulting log files contain entries like the following:

logs/info.log

{"timestamp":"30/04/2020 22:02:27","level":"info","message":"Mensaje informativo","data":null}
{"timestamp":"30/04/2020 22:02:27","level":"warn","message":"Mensaje de advertencia","data":null}
{"timestamp":"30/04/2020 22:02:27","level":"error","message":"Mensaje de error","data":null}
{"timestamp":"30/04/2020 22:02:27","level":"info","message":"Mensaje con datos","data":"{ user: 'John Smith' }"}

logs/warn.log

{"timestamp":"30/04/2020 22:02:27","level":"warn","message":"Mensaje de advertencia","data":null}
{"timestamp":"30/04/2020 22:02:27","level":"error","message":"Mensaje de error","data":null}

logs/error.log

{"timestamp":"30/04/2020 22:02:27","level":"error","message":"Mensaje de error","data":null}

How it works

The project defines a Logger class that bundles Winston's transports and formatting helpers.

index.js

const Logger = require('./logger')

const logger = new Logger()

logger.info('Mensaje informativo')
logger.warn('Mensaje de advertencia')
logger.error('Mensaje de error')
logger.info('Mensaje con datos', { user: 'John Smith' })

logger.js

const util    = require('util')
const moment  = require('moment')
const winston = require('winston')

class Logger {
  constructor() { this.winstonLogger = winston.createLogger({ ... }) }

  info(message, data) { this.winstonLogger.info({ message, data }) }
  warn(message, data) { this.winstonLogger.warn({ message, data }) }
  error(message, data) { this.winstonLogger.error({ message, data }) }

  _createTransports() { ... }
  _consoleFormat () { ... }
  _fileFormat() { ... }
}

module.exports = Logger

[Fn] constructor

Creates the underlying Winston logger instance.

constructor() {
  this.winstonLogger = winston.createLogger({
    levels      : { error: 1, warn: 2, info: 3 },
    transports  : this._createTransports(),
    exitOnError : false,
  })
}

[Fn] _createTransports

Configures the transports that will persist console and file logs.

_createTransports() {
  const TRANSPORTS = []
  TRANSPORTS.push(new winston.transports.Console({
    format           : winston.format.printf(this._consoleFormat()),
    level            : 'info', // Muestra logs de nivel 3 o menor
    handleExceptions : false,
    colorize         : false,
    json             : false,
  }))
  Array.from(['info', 'warn', 'error']).forEach(level => {
    TRANSPORTS.push(new winston.transports.File({
      format           : winston.format.printf(this._fileFormat()),
      level            : level,
      handleExceptions : false,
      colorize         : false,
      json             : true,
      filename         : `logs/${level}.log`,
      maxsize          : 5242880, // 5242880 Bytes = 5 MB
      maxFiles         : 5,
    }))
  })
  return TRANSPORTS
}

[Fn] _consoleFormat

Defines the console output format.

_consoleFormat () {
  const COLORS = {
    error : `\x1b[91m`, // LIGHT_RED
    warn  : `\x1b[93m`, // LIGHT_YELLOW
    info  : `\x1b[96m`, // LIGHT_CYAN
    reset : `\x1b[0m`,  // Restaura al color por defecto
  }
  return (info) => {
    const START     = COLORS[info.level]
    const END       = COLORS.reset
    const TIMESTAMP = moment().format('DD/MM/YYYY HH:mm:ss')
    const LEVEL     = info.level
    const MESSAGE   = info.message
    const DATA      = info.data ? util.inspect(info.data, false, null) : ''
    return `${START} ${TIMESTAMP} [${LEVEL}] ${MESSAGE} ${DATA} ${END}`
  }
}

[Fn] _fileFormat

Defines the JSON structure stored in log files.

_fileFormat() {
  return (info)  => {
    const TIMESTAMP = moment().format('DD/MM/YYYY HH:mm:ss')
    const LEVEL     = info.level
    const MESSAGE   = info.message
    const DATA      = info.data ? util.inspect(info.data, false, null) : null
    return JSON.stringify({
      timestamp : TIMESTAMP,
      level     : LEVEL,
      message   : MESSAGE,
      data      : DATA,
    })
  }
}

Log Levels

According to RFC5424—which Winston follows—log levels are identified numerically from most to least critical.

For example:

SeverityLevelDescriptionOptional color
fatal0Critical messages\x1b[31m RED
error1Error messages\x1b[91m LIGHT_RED
warn2Warning messages\x1b[93m LIGHT_YELLOW
notice3Notice-level information\x1b[92m LIGHT_GREEN
info4Informational messages\x1b[97m LIGHT_WHITE
verbose5Verbose messages\x1b[96m LIGHT_CYAN
debug6Debug output\x1b[94m LIGHT_BLUE
silly7Low-priority chatter\x1b[95m LIGHT_MAGENTA

See https://github.com/shiena/ansicolor for the full color reference.

References

Published: May 26, 2020