8754. Logging with Winston
Express and Winston


Introduce how to use Winston for logging in express server.

1. Winston

Winston a versatile async logging library for Node.js.

1.1 Logging Levels

Winston provides the following default log levels. They are prioritized from 0 to 5 (highest to lowest)

const levels = { error: 0, warn: 1, info: 2, verbose: 3, debug: 4, silly: 5 }

You can pass a string representing the logging level to the log() method or use the level specified methods defined on every winston logger.

logger.log('info', "hello world!");
logger.log('error', "Ops, you got an error!");
// equivalent to
logger.info("hello world!");
logger.error("Ops, you got an error!");

// same output
// info: hello world!
// error: Ops, you got an error!

1.2 Transports

A transport is essentially a storage device for your logs. Each Winston logger can have multiple transports configured at different levels. Winston provides two default transports, Console and File.

transports: [
  new winston.transports.Console(),
  new winston.transports.File({ filename: 'combined.log' })
]

1.3 Multiple Transports of Same Type

It is possible to use multiple transports of the same type e.g. winston.transports.File when you construct the transport.

const logger = winston.createLogger({
  transports: [
    new winston.transports.File({
      filename: 'combined.log',
      level: 'info'
    }),
    new winston.transports.File({
      filename: 'errors.log',
      level: 'error'
    })
  ]
});

2. Examples

2.1 Print Logs to Console

Create winston logger.

//winston-config-console.js
var path = require("path");
var winston = require("winston");

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      colorize: true
    })
  ]
});

module.exports = logger;

Express server.

// server.js
var express = require("express");
var logger = require("./config/winston-config-console");
var app = express();

app.get("/", function(req, res) {
  logger.info("Hello world");
  res.send("hello, world!");
});
app.use(function(req, res) {
  logger.error("File not found");
  res.status(404).send("File not found!");
});

app.listen(3000, function() {
  console.log("Web Server started on port 3000");
});

Start the server and access http://localhost:3000/ and http://localhost:3000/random in browser. You should see the output in console.

Web Server started on port 3000
{"message":"File not found","level":"error"}
{"message":"Hello world","level":"info"}

Add format to transport.

const logger = winston.createLogger({
  transports: [
    new winston.transports.Console({
      format: winston.format.simple(),
      colorize: true
    })
  ]
});

Try to access those two URLs again. This time you would see the following logs.

Web Server started on port 3000
info: Hello world
error: File not found

2.2 Write Logs to File

Create another winston logger.

//winston-config-file.js
var path = require("path");
var fs = require("fs");
var appRoot = require("app-root-path");
var winston = require("winston");

// ensure log directory exists
var logDirectory = path.resolve(`${appRoot}`, "logs");
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory);

var options = {
  infofile: {
    level: "info",
    filename: path.resolve(logDirectory, "info.log"),
    handleExceptions: true,
    json: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5
  },
  errorfile: {
    level: "error",
    filename: path.resolve(logDirectory, "error.log"),
    handleExceptions: true,
    json: true,
    maxsize: 5242880, // 5MB
    maxFiles: 5
  }
};

const logger = winston.createLogger({
  transports: [
    new winston.transports.File(options.infofile),
    new winston.transports.File(options.errorfile)
  ]
});

module.exports = logger;

Update server.js with the new winston logger.

// server.js
var express = require("express");
//var logger = require("./config/winston-config-console");
var logger = require("./config/winston-config-file");
...

Start the server and access http://localhost:3000/ and http://localhost:3000/random in browser. This time you won’t see any log in console. Instead, you should see the following logs in ./logs/error.log.

{"message":"File not found","level":"error"}

And the following logs in ./logs/info.log

{"message":"Hello world","level":"info"}
{"message":"File not found","level":"error"}

Notice that, the error log is also recorded. This is because, for level ‘info’, all level value less or equals 2 will be logged. Logs for level ‘error: 0’, ‘warn: 1’ and ‘info: 2’ are all saved to this file.

2.3 Log File Rotation

Winston supports to log rotation by winston-daily-rotate-file. The DailyRotateFile transport can rotate files by minute, hour, day, month, year or weekday.

//winston-config-rotate.js
var path = require("path");
var fs = require("fs");
var appRoot = require("app-root-path");
var winston = require("winston");
require("winston-daily-rotate-file");

// ensure log directory exists
var logDirectory = path.resolve(`${appRoot}`, "logs");
fs.existsSync(logDirectory) || fs.mkdirSync(logDirectory);

var transport = new winston.transports.DailyRotateFile({
  filename: path.resolve(logDirectory, "application-%DATE%.log"),
  datePattern: "YYYY-MM-DD-HH",
  zippedArchive: true,
  maxSize: "20m",
  maxFiles: "14d" // keep logs for 14 days
});

transport.on("rotate", function(oldFilename, newFilename) {
  // do something fun
});

const logger = winston.createLogger({
  transports: [transport]
});

module.exports = logger;

Update server.js with the new winston logger.

// server.js
var express = require("express");
//var logger = require("./config/winston-config-console");
//var logger = require("./config/winston-config-file");
var logger = require("./config/winston-config-rotate");
...

Start the server then access http://localhost:3000/ and http://localhost:3000/random in browser. You would see multiple log files in directory ./logs. Each file name has the format ‘application-YYYY-MM-DD-HH.log’. From the following screenshot, we see multiple log files are created from 11:00 to 16:00 on Jan 08, 2018. image

3. Source Files

4. Reference