Jump To …
winston/common.html winston/config/cli-config.html winston/config/npm-config.html winston/config/syslog-config.html winston/config.html winston/exception.html winston/logger.html winston/transports/console.html winston/transports/couchdb.html winston/transports/file.html winston/transports/loggly.html winston/transports/transport.html winston/transports/webhook.html winston/transports.html winston.html

# logger.js |
--- | ---
¶ |
    /*
     * logger.js: Core logger object used by winston.
     *
     * (C) 2010 Charlie Robbins
     * MIT LICENCE
     *
     */

    var events = require('events'),
        util = require('util'),
        async = require('async'),
        config = require('./config'),
        common = require('./common'),
        exception = require('./exception');

    function capitalize(str) {
      return str && str[0].toUpperCase() + str.slice(1);
    }
¶ Time constants |
    var ticksPerMillisecond = 10000;
¶ ### 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);
      options = options || {};

      var self = this,
          handleExceptions = false;
¶ Set Levels and default logging level |
      this.padLevels = options.padLevels || false;
      this.setLevels(options.levels);
¶ Setup other intelligent default settings. |
      this.level             = options.level || 'info';
      this.emitErrs          = options.emitErrs || false;
      this.stripColors       = options.stripColors || false;
      this.transports        = {};
      this.exceptionHandlers = {};
      this.profilers         = {};
      this._names            = [];
      this._hnames           = [];

      if (options.transports) {
        options.transports.forEach(function (transport) {
          self.add(transport, null, true);

          if (transport.handleExceptions) {
            handleExceptions = true;
          }
        });
      }

      if (options.exceptionHandlers) {
        options.exceptionHandlers.forEach(function (handler) {
          self._hnames.push(handler.name);
          self.exceptionHandlers[handler.name] = handler;
        });
      }

      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'].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, msg) {
      var self = this,
          callback,
          meta;

      if (arguments.length === 3) {
        if (typeof arguments[2] === 'function') {
          meta = {};
          callback = arguments[2];
        }
        else if (typeof arguments[2] === 'object') {
          meta = arguments[2];
        }
      }
      else if (arguments.length === 4) {
        meta = arguments[2];
        callback = arguments[3];
      }
¶ If we should pad for levels, do so |
      if (this.padLevels) {
        msg = new Array(this.levelLength - level.length).join(' ') + msg;
      }

      function onError (err) {
        if (callback) {
          callback(err);
        }
        else if (self.emitErrs) {
          self.emit('error', err);
        };
      }

      if (this.transports.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));
      }
¶ For consideration of terminal 'color" programs like colors.js, which can add ANSI escape color codes to strings, we destyle the ANSI color escape codes when `this.stripColors` is set. see: http://en.wikipedia.org/wiki/ANSIescapecode |
      if (this.stripColors) {
        var code = /\u001b\[\d+m/g;
        msg = ('' + msg).replace(code, '');
      }

      for (var i = 0, l = this._names.length; i < l; i++) {
        var transport = this.transports[this._names[i]];
        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) {
            self.emit('logging', transport, level, msg, meta);
          });
        }
      }
¶ Immediately respond to the callback |
      if (callback) {
        callback(null, level, msg, meta);
      }

      return this;
    };
¶ ### function handleExceptions () Handles `uncaughtException` events for the current process |
    Logger.prototype.handleExceptions = function () {
      var args = Array.prototype.slice.call(arguments),
          handlers = [],
          self = this;

      args.forEach(function (a) {
        if (Array.isArray(a)) {
          handlers = handlers.concat(a);
        }
        else {
          handlers.push(a);
        }
      });

      handlers.forEach(function (handler) {
        self.exceptionHandlers[handler.name] = handler;
      });

      this._hnames = Object.keys(self.exceptionHandlers);

      if (!this.catchExceptions) {
        this.catchExceptions = this._uncaughtException.bind(this);
        process.on('uncaughtException', this.catchExceptions);
      }
    };
¶ ### function unhandleExceptions () Removes any handlers to `uncaughtException` events for the current process |
    Logger.prototype.unhandleExceptions = function () {
      var self = this;

      if (this.catchExceptions) {
        Object.keys(this.exceptionHandlers).forEach(function (name) {
          if (handler.close) {
            handler.close();
          }
        });

        this.exceptionHandlers = {};
        Object.keys(this.transports).forEach(function (name) {
          var transport = self.transports[name];
          if (transport.handleExceptions) {
            transport.handleExceptions = false;
          }
        })

        process.removeListener('uncaughtException', this.catchExceptions);
        this.catchExceptions = false;
      }
    };
¶ ### function add (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.add = function (transport, options, created) {
      var instance = created ? transport : (new (transport)(options));

      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);
      }

      this.transports[instance.name] = instance;
      this._names = Object.keys(this.transports);
¶ Listen for the `error` event on the new Transport |
      instance._onError = this._onError.bind(this, instance)
      instance.on('error', instance._onError);

      return this;
    };
¶ ### function remove (transport) #### @transport {Transport} Transport to remove. Removes a transport of the specified type from this instance. |
    Logger.prototype.remove = function (transport) {
      var name = transport.name || transport.prototype.name;

      if (!this.transports[name]) {
        throw new Error('Transport ' + name + ' not attached to this instance');
      }

      var instance = this.transports[name];
      delete this.transports[name];
      this._names = Object.keys(this.transports);

      if (instance.close) {
        instance.close();
      }

      instance.removeListener('error', instance._onError);
      return this;
    };
¶ ### function profile (id, [msg, meta, callback]) #### @id {string} Unique id of the profiler #### @msg {string} Optional Message to log #### @meta {Object} Optional Additional metadata to attach #### @callback {function} Optional Continuation to respond to when complete. Tracks the time inbetween subsequent calls to this method with the same `id` parameter. The second call to this method will log the difference in milliseconds along with the message. |
    Logger.prototype.profile = function (id) {
      var now = Date.now(), then, args,
          msg, meta, callback;

      if (this.profilers[id] && arguments.length !== 1) {
        then = this.profilers[id];
        delete this.profilers[id];
¶ Support variable arguments: msg, meta, callback |
        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() : {};
        msg      = args.length === 2 ? args[1] : id;
¶ Set the duration property of the metadata |
        meta.duration = now - then + 'ms';
        return this.info(msg, meta, callback);
      }
      else {
        this.profilers[id] = now;
      }

      return this;
    };
¶ ### function setLevels (target) #### @target {Object} Target levels to use on this instance Sets the `target` levels specified on this instance. |
    Logger.prototype.setLevels = function (target) {
      return common.setLevels(this, this.levels, target);
    };
¶ ### function cli () Configures this instance to have the default settings for command-line interfaces: no timestamp, colors enabled, padded output, and additional levels. |
    Logger.prototype.cli = function () {
      this.padLevels = true;
      this.setLevels(config.cli.levels);
      config.addColors(config.cli.colors);

      if (this.transports.console) {
        this.transports.console.colorize = true;
        this.transports.console.timestamp = false;
      }

      return this;
    };
¶ ### @private function _uncaughtException (err) #### @err {Error} Error to handle Logs all relevant information around the `err` and exits the current process. |
    Logger.prototype._uncaughtException = function (err) {
      var self = this,
          responded = false,
          info = exception.getAllInfo(err),
          handlers = this._getExceptionHandlers(),
          timeout;

      function logAndWait (transport, next) {
        transport.logException('uncaughtException', info, next);
      }

      function gracefulExit () {
        if (!responded) {
¶ Remark: Currently ignoring any exceptions from transports when catching uncaught exceptions. |
          clearTimeout(timeout);
          responded = true;
          process.exit(1);
        }
      }

      if (!handlers || handlers.length === 0) {
        return gracefulExit();
      }
¶ Log to all transports and allow the operation to take only up to `3000ms`. |
      async.forEach(handlers, logAndWait, gracefulExit);
      timeout = setTimeout(gracefulExit, 3000);
    };
¶ ### @private function _getExceptionHandlers () Returns the list of transports and exceptionHandlers for this instance. |
    Logger.prototype._getExceptionHandlers = function () {
      var self = this;

      return this._hnames.map(function (name) {
        return self.exceptionHandlers[name];
      }).concat(this._names.map(function (name) {
        return self.transports[name].handleExceptions && self.transports[name];
      })).filter(Boolean);
    };
¶ ### @private function _onError (transport, err) #### @transport {Object} Transport on which the error occured #### @err {Error} Error that occurred on the transport Bubbles the error, `err`, that occured on the specified `transport` up from this instance if `emitErrs` has been set. |
    Logger.prototype._onError = function (transport, err) {
      if (self.emitErrs) {
        self.emit('error', err, transport);
      }
    };