src/nodejs_supportlib/vendor-copy/winston/lib/winston/logger.js in passenger-6.0.12 vs src/nodejs_supportlib/vendor-copy/winston/lib/winston/logger.js in passenger-6.0.13

- old
+ new

@@ -12,22 +12,55 @@ config = require('./config'), common = require('./common'), exception = require('./exception'), Stream = require('stream').Stream; +var formatRegExp = /%[sdj%]/g; + // // ### function Logger (options) // #### @options {Object} Options for this instance. // Constructor function for the Logger object responsible // for persisting log messages and metadata to one or more transports. // var Logger = exports.Logger = function (options) { events.EventEmitter.call(this); + this.configure(options); +}; + +// +// Inherit from `events.EventEmitter`. +// +util.inherits(Logger, events.EventEmitter); + +// +// ### function configure (options) +// This will wholesale reconfigure this instance by: +// 1. Resetting all transports. Older transports will be removed implicitly. +// 2. Set all other options including levels, colors, rewriters, filters, +// exceptionHandlers, etc. +// +Logger.prototype.configure = function (options) { + var self = this; + + // + // If we have already been setup with transports + // then remove them before proceeding. + // + if (Array.isArray(this._names) && this._names.length) { + this.clear(); + } + options = options || {}; + this.transports = {}; + this._names = []; - var self = this, - handleExceptions = false; + if (options.transports) { + options.transports.forEach(function (transport) { + self.add(transport, null, true); + }); + } // // Set Levels and default logging level // this.padLevels = options.padLevels || false; @@ -37,127 +70,144 @@ } // // Hoist other options onto this instance. // + this.id = options.id || null; this.level = options.level || 'info'; this.emitErrs = options.emitErrs || false; this.stripColors = options.stripColors || false; this.exitOnError = typeof options.exitOnError !== 'undefined' ? options.exitOnError : true; // - // Setup other intelligent default settings. + // Setup internal state as empty Objects even though it is + // defined lazily later to ensure a strong existential API contract. // - this.transports = {}; - this.rewriters = []; - this.filters = []; this.exceptionHandlers = {}; this.profilers = {}; - this._names = []; - this._hnames = []; - if (options.transports) { - options.transports.forEach(function (transport) { - self.add(transport, null, true); + ['rewriters', 'filters'].forEach(function (kind) { + self[kind] = Array.isArray(options[kind]) + ? options[kind] + : []; + }); - if (transport.handleExceptions) { - handleExceptions = true; - } - }); - } - - if (options.rewriters) { - options.rewriters.forEach(function (rewriter) { - self.addRewriter(rewriter); - }); - } - if (options.exceptionHandlers) { - handleExceptions = true; - options.exceptionHandlers.forEach(function (handler) { - self._hnames.push(handler.name); - self.exceptionHandlers[handler.name] = handler; - }); + this.handleExceptions(options.exceptionHandlers); } - - if (options.handleExceptions || handleExceptions) { - this.handleExceptions(); - } }; // -// Inherit from `events.EventEmitter`. -// -util.inherits(Logger, events.EventEmitter); - -// -// ### function extend (target) -// #### @target {Object} Target to extend. -// Extends the target object with a 'log' method -// along with a method for each level in this instance. -// -Logger.prototype.extend = function (target) { - var self = this; - ['log', 'profile', 'startTimer'].concat(Object.keys(this.levels)).forEach(function (method) { - target[method] = function () { - return self[method].apply(self, arguments); - }; - }); - - return this; -}; - -// // ### function log (level, msg, [meta], callback) // #### @level {string} Level at which to log the message. // #### @msg {string} Message to log // #### @meta {Object} **Optional** Additional metadata to attach // #### @callback {function} Continuation to respond to when complete. // 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(); } - 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); + // + // 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; - // If we should pad for levels, do so - if (this.padLevels) { - msg = new Array(this.levelLength - level.length + 1).join(' ') + msg; - } - - function onError (err) { + // + // Handle errors appropriately. + // + function onError(err) { if (callback) { callback(err); } else if (self.emitErrs) { self.emit('error', err); } } - - 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; + } + + // + // 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 msg, meta = {}, validMeta = false; + var hasFormat = args && args[0] && args[0].match && args[0].match(formatRegExp) !== null; + var tokens = (hasFormat) ? args[0].match(formatRegExp) : []; + var ptokens = tokens.filter(function(t) { return t === '%%' }); + if (((args.length - 1) - (tokens.length - ptokens.length)) > 0 || args.length === 1) { + // last arg is meta + meta = args[args.length - 1] || args; + var metaType = Object.prototype.toString.call(meta); + validMeta = metaType === '[object Object]' || + metaType === '[object Error]' || metaType === '[object Array]'; + meta = validMeta ? args.pop() : {}; + } + msg = util.format.apply(null, args); + + // + // 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); }); this.filters.forEach(function(filter) { - var filtered = filter(msg, meta, level, self); + var filtered = filter(level, msg, meta, self); if (typeof filtered === 'string') msg = filtered; else { msg = filtered.msg; meta = filtered.meta; @@ -177,44 +227,25 @@ } // // 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 { + transport.log(level, msg, meta, function (err) { + if (err) { + err.transport = transport; + finish(err); + return next(); + } + + self.emit('logging', transport, level, msg, meta); 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); - } - } - - async.forEach(this._names, emit, cb); - + async.forEach(targets, transportLog, finish); return this; }; // // ### function query (options, callback) @@ -255,11 +286,11 @@ // // Helper function to accumulate the results from // `queryTransport` into the `results`. // - function addResults (transport, next) { + function addResults(transport, next) { queryTransport(transport, function (err, result) { // // queryTransport could potentially invoke the callback // multiple times since Transport code can be unpredictable. // @@ -377,12 +408,13 @@ this.emit('close'); }; // -// ### function handleExceptions () -// Handles `uncaughtException` events for the current process +// ### function handleExceptions ([tr0, tr1...] || tr0, tr1, ...) +// Handles `uncaughtException` events for the current process by +// ADDING any handlers passed in. // Logger.prototype.handleExceptions = function () { var args = Array.prototype.slice.call(arguments), handlers = [], self = this; @@ -394,10 +426,11 @@ else { handlers.push(a); } }); + this.exceptionHandlers = this.exceptionHandlers || {}; handlers.forEach(function (handler) { self.exceptionHandlers[handler.name] = handler; }); this._hnames = Object.keys(self.exceptionHandlers); @@ -449,11 +482,11 @@ if (!instance.name && !instance.log) { throw new Error('Unknown transport with no log() method'); } else if (this.transports[instance.name]) { - throw new Error('Transport already attached: ' + instance.name); + throw new Error('Transport already attached: ' + instance.name + ", assign a different name"); } this.transports[instance.name] = instance; this._names = Object.keys(this.transports); @@ -475,40 +508,17 @@ return this; }; // -// ### function addRewriter (transport, [options]) -// #### @transport {Transport} Prototype of the Transport object to add. -// #### @options {Object} **Optional** Options for the Transport to add. -// #### @instance {Boolean} **Optional** Value indicating if `transport` is already instantiated. -// Adds a transport of the specified type to this instance. -// -Logger.prototype.addRewriter = function (rewriter) { - this.rewriters.push(rewriter); -} - -// -// ### function addFilter (filter) -// #### @filter {function} Filter function, called with the message and -// optional metadata as the two arguments. -// Expected to return either the filtered message or an object with properties: -// - msg = the filtered message string -// - meta = the filtered metadata object -// -Logger.prototype.addFilter = function (filter) { - this.filters.push(filter); -} - -// // ### function clear () // Remove all transports from this instance // Logger.prototype.clear = function () { - for (var name in this.transports) { + Object.keys(this.transports).forEach(function (name) { this.remove({ name: name }); - } + }, this); }; // // ### function remove (transport) // #### @transport {Transport|String} Transport or Name to remove. @@ -535,30 +545,23 @@ instance.removeListener('error', instance._onError); } return this; }; -var ProfileHandler = function (logger) { - this.logger = logger; - - this.start = Date.now(); - - this.done = function (msg) { - var args, callback, meta; - args = Array.prototype.slice.call(arguments); - callback = typeof args[args.length - 1] === 'function' ? args.pop() : null; - meta = typeof args[args.length - 1] === 'object' ? args.pop() : {}; - - meta.durationMs = (Date.now()) - this.start; - - return this.logger.info(msg, meta, callback); - } -} - +// +// ### function startTimer () +// Returns an object corresponding to a specific timing. When done +// is called the timer will finish and log the duration. e.g.: +// +// timer = winston.startTimer() +// setTimeout(function(){ +// timer.done("Logging message"); +// }, 1000); +// Logger.prototype.startTimer = function () { return new ProfileHandler(this); -} +}; // // ### function profile (id, [msg, meta, callback]) // #### @id {string} Unique id of the profiler // #### @msg {string} **Optional** Message to log @@ -696,6 +699,31 @@ // Logger.prototype._onError = function (transport, err) { if (this.emitErrs) { this.emit('error', err, transport); } +}; + +// +// ### @private ProfileHandler +// Constructor function for the ProfileHandler instance used by +// `Logger.prototype.startTimer`. When done is called the timer +// will finish and log the duration. +// +function ProfileHandler(logger) { + this.logger = logger; + this.start = Date.now(); +} + +// +// ### function done (msg) +// Ends the current timer (i.e. ProfileHandler) instance and +// logs the `msg` along with the duration since creation. +// +ProfileHandler.prototype.done = function (msg) { + var args = Array.prototype.slice.call(arguments), + callback = typeof args[args.length - 1] === 'function' ? args.pop() : null, + meta = typeof args[args.length - 1] === 'object' ? args.pop() : {}; + + meta.duration = (Date.now()) - this.start + 'ms'; + return this.logger.info(msg, meta, callback); };