Node.js Logging with Winston

The functional requirements I have for logging are nothing elaborate or exotic. I’d like to be able to log events at standard severity levels, like debug, info, warning, and error. I’d like to be able to configure multiple log targets where log events can be written, for instance, to a database, to a file, or to the console. I’d like to be able to configure formatting for each log target separately. I’d like to be able to configure a log threshold for each log target, so that, for instance, full details are logged to the console when debugging, and only warnings and errors are logged to file.

At least as important to me as the functional requirements are the architectural requirements. I don’t intend to implement my own logging library, so I’ll be picking one that someone else has written. In case I don’t choose wisely, I’d like to be able to change out the logging library later with minimal impact to the rest of the code. This has implications in two major directions: the business code that writes to the logs and the application configuration code that sets up the logging system. Careful attention to these structural details reduces the pressure to select the perfect logging system.

There are a variety of logging components for Node.js available. One popular component is Winston, part of the Flatiron framework. Winston is actively maintained; the last commit as of this writing was about 10 days ago. I do have concerns about components that are part of frameworks. If I am not ready to commit to a logging framework, I’m even less ready to commit to a whole slew of interdependent components. Fortunately, the Flatiron project is committed to ensuring that their components can function on their own. I may eventually decide to adopt more of their components, but I will be careful to keep them separate from one another so that I can replace one component without replacing others. Winston can be installed using the following command line, run from the root of your node project:

npm install winston --save

I found a great example at http://tostring.it/2014/06/23/advanced-logging-with-nodejs. This example shows how to create a custom logger module that can be used from other modules without exposing unnecessary details of the underlying logging component. All that’s missing from this example is start-time configuration. I’ve added a mechanism for doing this, as shown below.

//logger.js
var winston = require(‘winston’);
winston.emitErrs = true;

var logger = new winston.Logger({exitOnError: false});

logger.addConsole = function(config) {
    logger.add (winston.transports.Console, config);
};

logger.addFile = function(config) {
    logger.add (winston.transports.File, config);
};

module.exports = logger;

module.exports.stream = {
    write: function(message, encoding){
        logger.info(message);
    }
};

Configuration is performed by passing in a JavaScript object corresponding to the underlying library’s expected configuration arguments, as follows:

/*
* app.js – main entry point for the application
* The purpose of app.js is to read configuration from configuration sources
* (defaults, command-line, options files, and so on)
* and start the server.
*
*/

. . .
var logger = require(“./logger”);

logger.addFile({
    level: ‘info’,
    filename: ‘./app.log’,
    handleExceptions: true,
    json: true,
    maxsize: 5242880, //5MB
    maxFiles: 5,
    colorize: false
});
. . .

server.start();

Using it in a separate module is clean and easy:


/* server.js
* The purpose of server.js is to receive, respond to, and route requests.
*/

var express = require(‘express’);
var logger = require(“./logger”);

var server = module.exports = express();
var port = 1337;

function start() {
    server.listen(port);
    logger.info(“app listening on port “ + port + “.”);

    server.get(‘/’, function(req, res) {
        logger.debug(“in app.get()”);
        logger.debug(req.headers);
        . . .
    });
}

module.exports.start = start;

I’m generally pretty satisfied with the result. I haven’t yet implemented the configuration components yet. I’m likely to alter the mechanism of feeding configuration of the logging module to do a better job of hiding implementation details from the application. This can be tackled later as it only affects the application startup code and the logging module, with no effect on the rest of the product.

Advertisements

One thought on “Node.js Logging with Winston

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s