A Node Production Logger

What started out as a simple node logger to replace more heavy-weight loggers has become our standard logger for simple scripts as well as production servers. And as of version 0.91.90, with the ability to log domain, category, and levels we will begin using this logger even for clustered applications.

This multi-level logger can easily write to console, file, or rolling files. Some of the features include:

  • levels: trace, debug, info, warn, error and fatal levels (plus all and off)
  • flexible appender/formatters with default to HH:MM:ss.SSS LEVEL message
  • add appenders to send output to console, file, etc
  • dynamically change log levels for appenders and loggers on the fly
  • domain and category columns
  • overridable format methods in base appender

The open-source project is hosted on github and published to npm. Installation from npm is as easy as:

	npm install simple-node-logger --save

From there you are good to go for simple commandline scripts all the way through multi-service production installations.

Use

Here are a few examples. First, create a simple stdout console logger…

	var log = new require('simple-node-logger').createSimpleLogger();

or create a stdout and file logger…

	var log = require('simple-node-logger').createSimpleLogger('project.log');

or, crate a rolling file logger based on date/time…

    var opts = {
        logDirectory:'/mylogfiles',
        fileNamePattern:'roll-<DATE>.log',
        dateFormat:'YYYY.MM.DD'
    };

    var log = require('simple-node-logger').createRollingFileLogger( opts );

The first use will simply log to the console. The second will log to the console and to the project.log file. The third logs to the file only. The forth creates a rolling file log system in the target log folder that rolls daily. The date format controls when the file rolls, giving you the ability to roll hourly, daily, monthly, etc.

Log Levels

The standard log levels range from ‘trace’ to ‘fatal’. The default level is ‘info’ and is set by doing this:

	log.setLevel('warn');

This sets the log level to warn and suppresses trace, debug and info messages.

Log Statement Formats

Simple Logger

The default format is HH:mm:ss.SSS LEVEL message. For example, the log message:

log.info('subscription to ', channel, ' accepted at ', new Date().toJSON());

Yields:

14:14:21.363 INFO  subscription to /devchannel accepted at 2014-04-10T14:20:52.938Z

Category Logger

If you create a logger with a category name, all log statements will include this category. Typically a category is a class or module name. If you create a logger with the category name ‘MyCategory’, the log statement would format like this:

14:14:21.363 INFO  MyCategory subscription to /devchannel accepted at 2014-04-10T14:20:52.938Z

Domain Logger

For multi-service or clustered loggers, you will want to spearate log statements by domain. For this you would set up a configuration script (e.g., conf.js) to specify domain, category, and other options. You main also want your application to re-read the configuration file periodically during run-time to modify the appender or logger log level.

Here is what the configuration file would look like:

	'use strict';
	
	var port = 18344;
	
	module.exports.readLoggerConfig = function() {
		var config = {
			logDirectory: process.env.HOME + '/logs',
			filenamePattern: ['client-', port, '-<date>.log' ].join(''),
			domain: 'MyAwsomeApplication',
			refresh: 30 * 1000, // re-read each 30 seconds,
			loggerConfigFile: __dirname + '/logger-config.json',
			level: 'warn'
		};
		
		return config;
	};
	

The logger configuration file would look something like this:

{
    "appenders":[ 
        {
            "typeName":"RollingFileAppender",
            "level":"debug"
        },
        {
            "typeName":"ConsoleAppender",
            "level":"fatal"
        }
    ],
    "loggers":[
        {
            "category":"all",
            "level":"info"
        },
        {
            "category":"ApplicationFactory",
            "level":"warn"
        },
        {
            "category":"CalculatorDelegate",
            "level":"debug"
        }
    ]
}

Since the config file is read and parsed each 30 seconds, it’s easy to change the appender or logger levels to what ever is appropriate. With the above file, the rolling file appender would be set to debug, then all loggers set to info, then the ApplicationFactory logger set to warn and the CalculatorDelegate set to debug. Modifying the file once again, you could change the appender level to warn and only the warn, error and fatal messages would be written.

Appenders

Console

Writes to the console. This is the simplest appender typically used for command line applications or for development.

File

Writes to the specified file. This appender is typically used for services that periodically start and stop or that have a limited number of log statements. An example would be to log just error & fatal messages separate from other logs.

Rolling File Appender

The rolling file appender offers a full production logger where files roll based on date and time. The minimum roll time is a single hour. A typical application would be a production environment where log files are rolled throughout the day then archived to a separate location.

The rolling file appender requires a valid date format and file name pattern. The filename must contain the key word that will be replaced with the formatted date. The configuration must also include a target log directory where the files will be written.

Valid Filename Patterns

	mylog-<DATE>.log
	ApplicationName.log.<Date>
	<DATE>.log
	<date>

Valid Date Formats

Date formats must map to acceptable file names so have more restrictions than typical dates. If you use delimiters, you are restricted to a dash or dot delimiter to separate year, month, day and hour. Valid examples include:

	MMDD  // simple month day that rolls at midnight (no delimiters)
	YYYY.MM.DD-HH // year month day and hour that can roll up to once per hour
	YYYY-MM-DD.a // year month day and am/pm that rolls twice per day
	YYYY-MMM-DD // year month day where month is the short name (Mar, Apr, etc)

The default format YYYY.MM.DD is used if the format is not supplied.

Dynamic Configuration

Create a javascript configuration that implements ‘readConfig’ to return configuration details.

Examples

The examples folder includes a handful of simple to not so simple cases for console, file, multi-appender, category, etc.

Customizations

Appenders

Adding a new appender is as easy as implementing write( logEntry ). The easiest way to implement is by extending the base class AbstractAppender. You may also easily override the formatting, order, etc by overriding or providing your own abstract or concrete appender.

For example, you can extend the AbstractAppender to create a JSON appender by doing this:

    var AbstractAppender = require('simple-node-logger').AbstractAppender;

    var JSONAppender = function() {
    	'use strict';
    	var appender = this;
    	
        var opts = {
            typeName:'JSONAppender'
        };
        
        AbstractAppender.extend( this, opts );
        
        // format and write all entry/statements
        this.write = function(entry) {
        	var fields = appender.formatEntry( entry );
        	
        	process.stdout.write( JSON.stringify( entry ) + '\n' );
        };
    };

Conclusion

You might consider using this as your new go-to logger for simple command line scripts or production applications.

Written on June 30, 2014