hw-logger
Efficient logger for node
Why another logger?
- Easy to use : no instance, no factory, common log levels out of the box, with error only by default for production systems
- Easy to configure : configure once with init options, use everywhere in your project
- Open : override the log output with any event emitter of your own, and add custom log levels
- Friendly : log format is customizable with EJS templates, w/ or w/o colors
- Expressjs compliant : a ready-to-use express middleware is provided (logger.express)
- Efficient : disabled log methods behave like noop function, allowing to consume fewer ressources
Install
npm install hw-logger
Getting started
samples/simple.js
Simply use log object to do the job :var log = log; log;log;log;
Output :
$ node samples/simple
INFO - simple:3 - 0ms - hey!
ERROR - simple:5 - 1ms - ouch
By default, hw-logger displays source filename and line number (simple:3). To get those informations hw-logger need to know the caller and this process is loud, so if you don't need to display the caller informations it's better to set caller property to false in init options.
Override log level with environment variable :
$ HW_LOG_LEVEL=debug node samples/simple
INFO - simple:3 - 4ms - hey!
DEBUG - simple:4 - 2ms - does nothing
ERROR - simple:5 - 0ms - ouch
Trace level also display log config at initialization :
$ HW_LOG_LEVEL=trace node samples/simple
TRACE - logger:97 - 4ms - log config : { levels: { NONE: 0, ERROR: 1, WARN: 2, INFO: 3, DEBUG: 4, TRACE: 5, ALL: 6 },
formatFile: '/home/openhoat/dev/nodejs/hw-logger/templates/default.tpl',
level: 'TRACE',
template: { filename: '/home/openhoat/dev/nodejs/hw-logger/templates/default.tpl' },
out: [Function],
caller: true,
format: '<%\nvar colorMethods, colorMethod, level;\ncolorMethods = {\n ERROR: \'red\',\n WARN: \'yellow\',\n INFO: \'blue\',\n DEBUG: \'bgBlack\',\n TRACE: \'inverse\'\n};\ncolorMethod = colorMethods[data.level] || \'white\';\nlevel = (data.level + new Array(config.levelsMaxLength + 1).join(\' \')).slice(0, config.levelsMaxLength);\n%><%- chalk.bold[colorMethod](level) %> - <%\nif (data.caller) {\n%><%- chalk.magenta(util.format(\'%s:%s\', path.basename(data.caller.file, \'.js\'), data.caller.line)) %> - <%\n} %><%- data.lastTime ? (function(duration) {\n return duration > 1000 ? Math.round(duration / 100) / 10 + \'s\' : duration + \'ms\';\n})(data.time.diff(data.lastTime)) : \'0ms\' %> - <%- util.format.apply(null, data.args) %>',
levelValue: 4,
levelsMaxLength: 5 }
INFO - simple:3 - 3ms - hey!
DEBUG - simple:4 - 0ms - does nothing
ERROR - simple:5 - 0ms - ouch
Usual NODE_ENV environment variable is detected to override default log level with error only (useful on production systems) :
$ NODE_ENV=production node samples/simple
ERROR - simple:5 - 4ms - ouch
Tips : if HW_LOG_LEVEL and NODE_ENV are both defined, HW_LOG_LEVEL has priority
samples/changeLevel.js
Use logger object to configure and change level :var logger = ; log = loggerlog; // log is immutable logger; // Initialize the log level log;log;log;logger; // Change the log levellog;
Output :
$ node samples/changeLevel
INFO - changeLevel:6 - 0ms - hey!
ERROR - changeLevel:7 - 1ms - ouch!
DEBUG - changeLevel:8 - 1ms - bug bug
TRACE - changeLevel:10 - 0ms - tssss
Log levels
Log level is defined by :
- a name used for log messages (uppercase)
- a matching log method to use (camelcase)
- a level value used to enable/disable logs (number)
Default log levels are :
- NONE: 0
- ERROR: 1
- WARN: 2
- INFO: 3
- DEBUG: 4
- TRACE: 5
- ALL: 6
Corresponding methods are : error, warn, info, debug, trace (NONE and ALL do not have methods, they're juste levels)
API doc
logger :
init(options)
Initialize logger with optionnal custom options.
Available options :
caller // (boolean) if true (default), insert caller data colors // (boolean) if true, enable colors if supported (enabled by default for tty) template // (object) EJS options (see https://lodash.com/docs#template) format // (string|function) log format template string or result of function formatFile // (string) log format template file, overrides format if defined, default base dir is templates and default extension is .tpl levels // (object) registered levels map (key : name, value : level value) out // (object) defines where to send log messages (call a function, use an [events.EventEmitter](http://nodejs.org/api/events.html#events_class_events_eventemitter), a writable stream or a file)
log
Log object that provides log methods for all levels.
If logger levels change then log methods are redefined.
getLogMethodName(level)
Return the log method name matching the specified level string
getLevelValue :
Return the log level value number for the specified level string
setLevel(level) :
Change the current log level (indeed the maximum log level value).
Throw an error if the specified log level is not supported (not in registered levels)
isEnabled(level) :
Return true if the specified log level is enabled, else false
registerLevels(levels) :
Register (add or override) the specified log levels map (key : level name, value : level value number).
Log levels are first sorted by value number, then value numbers are redefined to an index.
express :
Return an express middleware that logs request and response informations.
It adds HTTP level between INFO and DEBUG, and set HTTP to the current level.
An options object can be specified :
logger // (function) if set the function is called by logger as a custom express middleware logBefore // (boolean) if true logs are done before the end of request and do not include response infos
flush :
Flush data to the stream or file if specified as out, else do nothing.
Log format data
Each log event rendering is based on a data object with useful informations :
time // current timestamp (in moment format) lastTime // last log timestamp (in moment format) level // log level name levelValue // log level value number args // log method arguments, for example in log.info('hello', 'world') arguments are ['hello', 'world'] caller: // if enabled, provides caller informations file // source filename line // source file line number
Use cases
samples/customLevel.js
Custom level :var logger = log = loggerlog; logger; log;log;log;log; // methods are in camel caseconsole;/** * After having the new level inserted, the all level values are redefined to reflect indexes of an array */ logger; // lower or upper case, don't care, but the format has to be snake case for levelslog; // now it should display
Output :
$ node samples/customLevel
IMPORTANT - customLevel:15 - 4ms - hello world!
ERROR - customLevel:17 - 2ms - ouch
[ 'NONE', 'ERROR', 'WARN', 'IMPORTANT', 'INFO', 'DEBUG', 'TRACE', 'FED_UP', 'ALL' ]
FED_UP - customLevel:25 - 1ms - boring again
samples/customFormat.js
Custom format :var logger = log = loggerlog; logger; log;log;log;
Output :
$ node samples/customFormat
LOG EVENT @ Tue Feb 03 2015 14:19:35 GMT+0100 : hello world!
LOG EVENT @ Tue Feb 03 2015 14:19:35 GMT+0100 : ouch
To use a template file, use formatFile option instead of format option (template examples).
samples/customFormatFunc.js
Custom format function :var util = logger = log = loggerlog; { return util;} logger; log;log;log;
Output :
$ node samples/customFormatFunc
custom format - INFO : hello %s!, world
custom format - ERROR : ouch
samples/express.js
Replace express logger :Just register the express middleware before your routes (and after logger.init) :
var logger = log = loggerlog express = ; // logger.init({...}); // if needed, has to be before logger.express because init restore logger config to defaultsvar app = ;app; // Be sure to not have logger.init after this line to prevent http level removingapp;app;
Output :
$ node samples/express.js &
INFO - express:13 - 81ms - Http server ready
$ curl localhost:3000/hello
Hello World!
HTTP - logger:62 - 15s - 127.0.0.1 - GET /hello - 200 - 12
$ curl localhost:3000/world
HTTP - logger:62 - 51.7s - 127.0.0.1 - GET /world - 404 - 0
Cannot GET /world
Express log message format :
- (request method) (request path) - (response status code) - (response body length)
Handle log output
Specify any handler based on an event emitter, a function, a writable stream, or a file to handle all log messages.
samples/outEventEmitter.js
Event emitter :Each log message is a provided through 'data' event.
var logger = events = log = loggerlog eventEmitter = ; eventEmitter; logger; log; // Display nothing
Output :
$ node samples/outEventEmitter
data : INFO - out:14 - 5ms - handle this!
samples/outStream.js
Stream :The logger writes log messages asynchronously, so to be sure to be able to read data you can flush the logger.
var path = fs = logger = log = loggerlog tmpDir = path logFile = path; logger;log; // Display nothing { console;} logger;
Output :
$ node samples/outStream.js
INFO - outStream:13 - 6ms - stream data!
samples/outFile.js
File :The logger uses an interal stream, and writes log messages asynchronously into it, so use flush to be sure data are effectively written in file.
var path = fs = logger = log = loggerlog tmpDir = path logFile = path; logger;log; // Display nothing { console;} logger;
Output :
$ node samples/outFile.js
INFO - outFile:13 - 6ms - file content!
samples/outFunc.js
Function handler :Use any function to handle log messages.
var logger = log = loggerlog; { console;} logger; log; // Display nothing
Output :
$ node samples/outFunc.js
INFO - outFunc:14 - 5ms - file content!
Performances
$ node test/benchmark.js
Benchmarking 500000 iterations of random string logging using : console,hw-logger,winston,log4js,bunyan
processing console : .......
processing hw-logger : ..........
processing winston : .......................
processing log4js : ........................................
processing bunyan : ............................
###### Benchmark result : ######
console : 2668ms
hw-logger : 3198ms
winston : 5980ms
log4js : 6987ms
bunyan : 9210ms
################################
Build
Prerequisite :
$ npm install -g gulp-cli
Run tests
$ gulp test
Test coverage
$ gulp coverage
$ xdg-open dist/reports/coverage/html/coverage.html
Code quality
$ gulp lint
Enjoy !