Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
288 lines (232 sloc) 10.4 KB

Upgrading to winston@3.0.0

This document represents a living guide on upgrading to winston@3. Much attention has gone into the details, but if you are having trouble upgrading to winston@3.0.0 PLEASE open an issue so we can improve this guide!

Breaking changes

Top-level winston.* API

  • winston.Logger has been replaced with winston.createLogger.
  • winston.setLevels has been removed. Levels are frozen at the time of Logger creation.
  • Setting the level on the default winston logger no longer sets the level on the transports associated with the default winston logger.


  • winston.transports.Memory was removed. Use any Node.js stream.Writeable with a large highWaterMark instance instead.
  • When writing transports use winston-transport instead of winston.Transport.
  • Many formatting options that were previously configurable on transports (e.g. json, raw, colorize, prettyPrint, timestamp, logstash, align) should now be set by adding the appropriate formatter instead. (See: "Removed winston.transports.{File,Console,Http} formatting options" below)
  • In winston.transports.Console, output for all log levels are now sent to stdout by default.
    • stderrLevels option now defaults to [].
    • debugStdout option has been removed.

winston.Container and winston.loggers

  • winston.Container instances no longer have default Console transports
  • winston.Container.prototype.add no longer does crazy options parsing. Implementation inspired by segmentio/winston-logger


  • Do not use new winston.Logger(opts) – it has been removed for improved performance. Use winston.createLogger(opts) instead.

  • winston.Logger.log and level-specific methods (.info, .error, etc) no longer accepts a callback. The vast majority of use cases for this feature was folks awaiting all logging to complete, not just a single logging message. To accomplish this:

logger.log('info', 'some message');
logger.on('finish', () => process.exit());
  • winston.Logger.add no longer accepts prototypes / classes. Pass an instance of our transport instead.
// DON'T DO THIS. It will no longer work

// Do this instead.
logger.add(new winston.transports.Console());
  • winston.Logger will no longer do automatic splat interpolation by default. Be sure to use format.splat() to enable this functionality.
  • winston.Logger will no longer respond with an error when logging with no transports
  • winston.Logger will no longer respond with an error if the same transports are added twice.
    • options.transport is removed. Use the transport instance on the logger directly.
  • Logger.prototype.query
    • options.transport is removed. Use the transport instance on the logger directly.
  • Logger.paddings was removed.

Exceptions & exception handling

  • winston.exception has been removed. Use:
const exception = winston.ExceptionHandler();
  • humanReadableUnhandledException is now the default exception format.
  • .unhandleExceptions() will no longer modify transports state, merely just add / remove the process.on('uncaughtException') handler.
    • Call close on any explicit ExceptionHandlers.
    • Set handleExceptions = false on all transports.

Other minor breaking changes

  • winston.hash was removed.
  • winston.common.pad was removed.
  • winston.common.serialized was removed (use winston-compat).
  • winston.common.log was removed (use winston-compat).
  • winston.paddings was removed.

Upgrading to winston.format

The biggest issue with winston@2 and previous major releases was that any new formatting options required changes to winston itself. All formatting is now handled by formats.

Custom formats can now be created with no changes to winston core. We encourage you to consider a custom format before opening an issue.

Removed winston.Logger formatting options

Removed winston.transports.{File,Console,Http} formatting options

  • stringify: Use a custom format.
  • formatter: Use a custom format.
  • json: Use format.json().
  • raw: Use format.json().
  • label: Use format.label().
  • logstash: Use format.logstash().
  • prettyPrint: Use format.prettyPrint() or a custom format.
    • depth is an option provided to format.prettyPrint().
  • colorize: Use format.colorize().
  • timestamp: Use format.timestamp().
  • logstash: Use format.logstash().
  • align: Use format.align().
  • showLevel: Use a custom format.

Migrating filters and rewriters to formats in winston@3

In winston@3.x.x info objects are considered mutable. The API combined formatters and rewriters into a single, new concept: formats.


If you are looking to upgrade your filter behavior please read on. In winston@2.x this filter behavior:

const isSecret = /super secret/;
const logger = new winston.Logger(options);
logger.filters.push(function(level, msg, meta) {
  return msg.replace(isSecret, 'su*** se****');

// Outputs: {"level":"error","message":"Public error to share"}
logger.error('Public error to share');

// Outputs: {"level":"error","message":"This is su*** se**** - hide it."}
logger.error('This is super secret - hide it.');

Can be modeled as a custom format that you combine with other formats:

const { createLogger, format, transports } = require('winston');

// Ignore log messages if the have { private: true }
const isSecret = /super secret/;
const filterSecret = format((info, opts) => {
  info.message = info.message.replace(isSecret, 'su*** se****');
  return info;

const logger = createLogger({
  format: format.combine(
  transports: [new transports.Console()]

// Outputs: {"level":"error","message":"Public error to share"}
  level: 'error',
  message: 'Public error to share'

// Outputs: {"level":"error","message":"This is su*** se**** - hide it."}
  level: 'error',
  message: 'This is super secret - hide it.'


If you are looking to upgrade your rewriter behavior please read on. In winston@2.x this rewriter behavior:

const logger = new winston.Logger(options);
logger.rewriters.push(function(level, msg, meta) {
  if (meta.creditCard) {
    meta.creditCard = maskCardNumbers(meta.creditCard)

  return meta;
});'transaction ok', { creditCard: 123456789012345 });

Can be modeled as a custom format that you combine with other formats:

const maskFormat = winston.format(info => {
  // You can CHANGE existing property values
  if (info.creditCard) {
    info.creditCard = maskCardNumbers(info.creditCard);

  // You can also ADD NEW properties if you wish
  info.hasCreditCard = !!info.creditCard;

  return info;

const logger = winston.createLogger({
  format: winston.format.combine(
});'transaction ok', { creditCard: 123456789012345 });

See examples/format-mutate.js for a complete end-to-end example that covers both filtering and rewriting behavior in winston@2.x.

Modularity: winston-transport, logform and more

As of winston@3.0.0 the project has been broken out into a few modules:

Let's dig in deeper. The example below has been annotated to demonstrate the different packages that compose the example itself:

const { createLogger, transports, format } = require('winston');
const Transport = require('winston-transport');
const logform = require('logform');
const { combine, timestamp, label, printf } = logform.format;

// winston.format is require('logform')
console.log(logform.format === format) // true

const logger = createLogger({
  format: combine(
    label({ label: 'right meow!' }),
    printf(nfo => {
      return `${nfo.timestamp} [${nfo.label}] ${nfo.level}: ${nfo.message}`;
  transports: [new transports.Console()]