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