From 79767c70cd70c28344d90ca518c2f52d6083b244 Mon Sep 17 00:00:00 2001 From: indexzero Date: Thu, 29 Oct 2015 02:50:46 -0700 Subject: [PATCH] [refactor] Make some performance optimizations for `Logger.prototype.log`. --- lib/winston/logger.js | 99 ++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/lib/winston/logger.js b/lib/winston/logger.js index 8fc71bed1..1bf03240a 100755 --- a/lib/winston/logger.js +++ b/lib/winston/logger.js @@ -104,22 +104,27 @@ Logger.prototype.configure = function (options) { // Core logging method exposed to Winston. Metadata is optional. // Logger.prototype.log = function (level) { - var self = this, - args = Array.prototype.slice.call(arguments, 1); + var args = Array.prototype.slice.call(arguments, 1), + self = this, + transports; - while(args[args.length - 1] === null) { + while (args[args.length - 1] === null) { args.pop(); } + // + // Determining what is `meta` and what are arguments for string interpolation + // turns out to be VERY tricky. e.g. in the cases like this: + // + // logger.info('No interpolation symbols', 'ok', 'why', { meta: 'is-this' }); + // var callback = typeof args[args.length - 1] === 'function' ? args.pop() : null, meta = typeof args[args.length - 1] === 'object' && Object.prototype.toString.call(args[args.length - 1]) !== '[object RegExp]' ? args.pop() : {}, msg = util.format.apply(null, args); - // If we should pad for levels, do so - if (this.padLevels) { - msg = new Array(this.levelLength - level.length + 1).join(' ') + msg; - } - + // + // Handle errors appropriately. + // function onError(err) { if (callback) { callback(err); @@ -129,13 +134,49 @@ Logger.prototype.log = function (level) { } } - if (Object.keys(this.transports).length === 0) { + if (this._names.length === 0) { return onError(new Error('Cannot log with no transports.')); } else if (typeof self.levels[level] === 'undefined') { return onError(new Error('Unknown log level: ' + level)); } + // + // If there are no transports that match the level + // then be eager and return. This could potentially be calculated + // during `setLevels` for more performance gains. + // + var targets = this._names.filter(function (name) { + var transport = self.transports[name]; + return (transport.level && self.levels[transport.level] >= self.levels[level]) + || (!transport.level && self.levels[self.level] >= self.levels[level]); + }); + + if (!targets.length) { + if (callback) { callback(); } + return; + } + + // + // Respond to the callback. + // + function finish(err) { + if (callback) { + if (err) return callback(err); + callback(null, level, msg, meta); + } + + callback = null; + if (!err) { + self.emit('logged', level, msg, meta); + } + } + + // If we should pad for levels, do so + if (this.padLevels) { + msg = new Array(this.levelLength - level.length + 1).join(' ') + msg; + } + this.rewriters.forEach(function (rewriter) { meta = rewriter(level, msg, meta, self); }); @@ -165,39 +206,21 @@ Logger.prototype.log = function (level) { // // Log for each transport and emit 'logging' event // - function emit(name, next) { + function transportLog(name, next) { var transport = self.transports[name]; - if ((transport.level && self.levels[transport.level] >= self.levels[level]) - || (!transport.level && self.levels[self.level] >= self.levels[level])) { - transport.log(level, msg, meta, function (err) { - if (err) { - err.transport = transport; - cb(err); - return next(); - } - self.emit('logging', transport, level, msg, meta); - next(); - }); - } else { - next(); - } - } + transport.log(level, msg, meta, function (err) { + if (err) { + err.transport = transport; + finish(err); + return next(); + } - // - // Respond to the callback - // - function cb(err) { - if (callback) { - if (err) return callback(err); - callback(null, level, msg, meta); - } - callback = null; - if (!err) { - self.emit('logged', level, msg, meta); - } + self.emit('logging', transport, level, msg, meta); + next(); + }); } - async.forEach(this._names, emit, cb); + async.forEach(targets, transportLog, finish); return this; };