diff options
Diffstat (limited to 'node_modules/winston/lib')
-rw-r--r-- | node_modules/winston/lib/winston.js | 165 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/common.js | 483 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/config.js | 68 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/config/cli-config.js | 35 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/config/npm-config.js | 27 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/config/syslog-config.js | 31 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/container.js | 127 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/exception.js | 56 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/logger.js | 723 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/transports.js | 29 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/transports/console.js | 130 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/transports/file.js | 678 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/transports/http.js | 232 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/transports/memory.js | 89 | ||||
-rw-r--r-- | node_modules/winston/lib/winston/transports/transport.js | 135 |
15 files changed, 3008 insertions, 0 deletions
diff --git a/node_modules/winston/lib/winston.js b/node_modules/winston/lib/winston.js new file mode 100644 index 0000000..43aa6d1 --- /dev/null +++ b/node_modules/winston/lib/winston.js @@ -0,0 +1,165 @@ +/* + * winston.js: Top-level include defining Winston. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var winston = exports; + +// +// Expose version using `pkginfo` +// +require('pkginfo')(module, 'version'); + +// +// Include transports defined by default by winston +// +winston.transports = require('./winston/transports'); + +// +// Expose utility methods +// +var common = require('./winston/common'); +winston.hash = common.hash; +winston.clone = common.clone; +winston.longestElement = common.longestElement; +winston.exception = require('./winston/exception'); +winston.config = require('./winston/config'); +winston.addColors = winston.config.addColors; + +// +// Expose core Logging-related prototypes. +// +winston.Container = require('./winston/container').Container; +winston.Logger = require('./winston/logger').Logger; +winston.Transport = require('./winston/transports/transport').Transport; + +// +// We create and expose a default `Container` to `winston.loggers` so that the +// programmer may manage multiple `winston.Logger` instances without any additional overhead. +// +// ### some-file1.js +// +// var logger = require('winston').loggers.get('something'); +// +// ### some-file2.js +// +// var logger = require('winston').loggers.get('something'); +// +winston.loggers = new winston.Container(); + +// +// We create and expose a 'defaultLogger' so that the programmer may do the +// following without the need to create an instance of winston.Logger directly: +// +// var winston = require('winston'); +// winston.log('info', 'some message'); +// winston.error('some error'); +// +var defaultLogger = new winston.Logger({ + transports: [new winston.transports.Console()] +}); + +// +// Pass through the target methods onto `winston. +// +var methods = [ + 'log', + 'query', + 'stream', + 'add', + 'remove', + 'clear', + 'profile', + 'startTimer', + 'extend', + 'cli', + 'handleExceptions', + 'unhandleExceptions', + 'addRewriter', + 'addFilter' +]; +common.setLevels(winston, null, defaultLogger.levels); +methods.forEach(function (method) { + winston[method] = function () { + return defaultLogger[method].apply(defaultLogger, arguments); + }; +}); + +// +// ### function cli () +// Configures the default winston logger to have the +// settings for command-line interfaces: no timestamp, +// colors enabled, padded output, and additional levels. +// +winston.cli = function () { + winston.padLevels = true; + common.setLevels(winston, defaultLogger.levels, winston.config.cli.levels); + defaultLogger.setLevels(winston.config.cli.levels); + winston.config.addColors(winston.config.cli.colors); + + if (defaultLogger.transports.console) { + defaultLogger.transports.console.colorize = true; + defaultLogger.transports.console.timestamp = false; + } + + return winston; +}; + +// +// ### function setLevels (target) +// #### @target {Object} Target levels to use +// Sets the `target` levels specified on the default winston logger. +// +winston.setLevels = function (target) { + common.setLevels(winston, defaultLogger.levels, target); + defaultLogger.setLevels(target); +}; + +// +// Define getter / setter for the default logger level +// which need to be exposed by winston. +// +Object.defineProperty(winston, 'level', { + get: function () { + return defaultLogger.level; + }, + set: function (val) { + defaultLogger.level = val; + + Object.keys(defaultLogger.transports).forEach(function(key) { + defaultLogger.transports[key].level = val; + }); + } +}); + +// +// Define getters / setters for appropriate properties of the +// default logger which need to be exposed by winston. +// +['emitErrs', 'exitOnError', 'padLevels', 'levelLength', 'stripColors'].forEach(function (prop) { + Object.defineProperty(winston, prop, { + get: function () { + return defaultLogger[prop]; + }, + set: function (val) { + defaultLogger[prop] = val; + } + }); +}); + +// +// @default {Object} +// The default transports and exceptionHandlers for +// the default winston logger. +// +Object.defineProperty(winston, 'default', { + get: function () { + return { + transports: defaultLogger.transports, + exceptionHandlers: defaultLogger.exceptionHandlers + }; + } +}); diff --git a/node_modules/winston/lib/winston/common.js b/node_modules/winston/lib/winston/common.js new file mode 100644 index 0000000..29dfec0 --- /dev/null +++ b/node_modules/winston/lib/winston/common.js @@ -0,0 +1,483 @@ +/* + * common.js: Internal helper and utility functions for winston + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var util = require('util'), + crypto = require('crypto'), + cycle = require('cycle'), + fs = require('fs'), + StringDecoder = require('string_decoder').StringDecoder, + Stream = require('stream').Stream, + config = require('./config'); + +// +// ### function setLevels (target, past, current) +// #### @target {Object} Object on which to set levels. +// #### @past {Object} Previous levels set on target. +// #### @current {Object} Current levels to set on target. +// Create functions on the target objects for each level +// in current.levels. If past is defined, remove functions +// for each of those levels. +// +exports.setLevels = function (target, past, current, isDefault) { + var self = this; + if (past) { + Object.keys(past).forEach(function (level) { + delete target[level]; + }); + } + + target.levels = current || config.npm.levels; + if (target.padLevels) { + target.levelLength = exports.longestElement(Object.keys(target.levels)); + } + + // + // Define prototype methods for each log level + // e.g. target.log('info', msg) <=> target.info(msg) + // + Object.keys(target.levels).forEach(function (level) { + + // TODO Refactor logging methods into a different object to avoid name clashes + if (level === 'log') { + console.warn('Log level named "log" will clash with the method "log". Consider using a different name.'); + return; + } + + target[level] = function (msg) { + // build argument list (level, msg, ... [string interpolate], [{metadata}], [callback]) + var args = [level].concat(Array.prototype.slice.call(arguments)); + target.log.apply(target, args); + }; + }); + + return target; +}; + +// +// ### function longestElement +// #### @xs {Array} Array to calculate against +// Returns the longest element in the `xs` array. +// +exports.longestElement = function (xs) { + return Math.max.apply( + null, + xs.map(function (x) { return x.length; }) + ); +}; + +// +// ### function clone (obj) +// #### @obj {Object} Object to clone. +// Helper method for deep cloning pure JSON objects +// i.e. JSON objects that are either literals or objects (no Arrays, etc) +// +exports.clone = function (obj) { + // + // We only need to clone reference types (Object) + // + var copy = {}; + + if (obj instanceof Error) { + // With potential custom Error objects, this might not be exactly correct, + // but probably close-enough for purposes of this lib. + copy = new Error(obj.message); + Object.getOwnPropertyNames(obj).forEach(function (key) { + copy[key] = obj[key]; + }); + + return copy; + } + else if (!(obj instanceof Object)) { + return obj; + } + else if (obj instanceof Date) { + return new Date(obj.getTime()); + } + + for (var i in obj) { + if (Array.isArray(obj[i])) { + copy[i] = obj[i].slice(0); + } + else if (obj[i] instanceof Buffer) { + copy[i] = obj[i].slice(0); + } + else if (typeof obj[i] != 'function') { + copy[i] = obj[i] instanceof Object ? exports.clone(obj[i]) : obj[i]; + } + else if (typeof obj[i] === 'function') { + copy[i] = obj[i]; + } + } + + return copy; +}; + +// +// ### function log (options) +// #### @options {Object} All information about the log serialization. +// Generic logging function for returning timestamped strings +// with the following options: +// +// { +// level: 'level to add to serialized message', +// message: 'message to serialize', +// meta: 'additional logging metadata to serialize', +// colorize: false, // Colorizes output (only if `.json` is false) +// align: false // Align message level. +// timestamp: true // Adds a timestamp to the serialized message +// label: 'label to prepend the message' +// } +// +exports.log = function (options) { + var timestampFn = typeof options.timestamp === 'function' + ? options.timestamp + : exports.timestamp, + timestamp = options.timestamp ? timestampFn() : null, + showLevel = options.showLevel === undefined ? true : options.showLevel, + meta = options.meta !== null && options.meta !== undefined && !(options.meta instanceof Error) + ? exports.clone(cycle.decycle(options.meta)) + : options.meta || null, + output; + + // + // raw mode is intended for outputing winston as streaming JSON to STDOUT + // + if (options.raw) { + if (typeof meta !== 'object' && meta != null) { + meta = { meta: meta }; + } + output = exports.clone(meta) || {}; + output.level = options.level; + // + // Remark (jcrugzz): This used to be output.message = options.message.stripColors. + // I do not know why this is, it does not make sense but im handling that + // case here as well as handling the case that does make sense which is to + // make the `output.message = options.message` + // + output.message = options.message.stripColors + ? options.message.stripColors + : options.message; + + return JSON.stringify(output); + } + + // + // json mode is intended for pretty printing multi-line json to the terminal + // + if (options.json || true === options.logstash) { + if (typeof meta !== 'object' && meta != null) { + meta = { meta: meta }; + } + + output = exports.clone(meta) || {}; + output.level = options.level; + output.message = output.message || ''; + + if (options.label) { output.label = options.label; } + if (options.message) { output.message = options.message; } + if (timestamp) { output.timestamp = timestamp; } + + if (options.logstash === true) { + // use logstash format + var logstashOutput = {}; + if (output.message !== undefined) { + logstashOutput['@message'] = output.message; + delete output.message; + } + + if (output.timestamp !== undefined) { + logstashOutput['@timestamp'] = output.timestamp; + delete output.timestamp; + } + + logstashOutput['@fields'] = exports.clone(output); + output = logstashOutput; + } + + if (typeof options.stringify === 'function') { + return options.stringify(output); + } + + return JSON.stringify(output, function (key, value) { + return value instanceof Buffer + ? value.toString('base64') + : value; + }); + } + + // + // Remark: this should really be a call to `util.format`. + // + if (typeof options.formatter == 'function') { + return String(options.formatter(exports.clone(options))); + } + + output = timestamp ? timestamp + ' - ' : ''; + if (showLevel) { + output += options.colorize === 'all' || options.colorize === 'level' || options.colorize === true + ? config.colorize(options.level) + : options.level; + } + + output += (options.align) ? '\t' : ''; + output += (timestamp || showLevel) ? ': ' : ''; + output += options.label ? ('[' + options.label + '] ') : ''; + output += options.colorize === 'all' || options.colorize === 'message' + ? config.colorize(options.level, options.message) + : options.message; + + if (meta !== null && meta !== undefined) { + if (meta && meta instanceof Error && meta.stack) { + meta = meta.stack; + } + + if (typeof meta !== 'object') { + output += ' ' + meta; + } + else if (Object.keys(meta).length > 0) { + if (typeof options.prettyPrint === 'function') { + output += ' ' + options.prettyPrint(meta); + } else if (options.prettyPrint) { + output += ' ' + '\n' + util.inspect(meta, false, options.depth || null, options.colorize); + } else if ( + options.humanReadableUnhandledException + && Object.keys(meta).length === 5 + && meta.hasOwnProperty('date') + && meta.hasOwnProperty('process') + && meta.hasOwnProperty('os') + && meta.hasOwnProperty('trace') + && meta.hasOwnProperty('stack')) { + + // + // If meta carries unhandled exception data serialize the stack nicely + // + var stack = meta.stack; + delete meta.stack; + delete meta.trace; + output += ' ' + exports.serialize(meta); + output += '\n' + stack.join('\n'); + } else { + output += ' ' + exports.serialize(meta); + } + } + } + + return output; +}; + +exports.capitalize = function (str) { + return str && str[0].toUpperCase() + str.slice(1); +}; + +// +// ### function hash (str) +// #### @str {string} String to hash. +// Utility function for creating unique ids +// e.g. Profiling incoming HTTP requests on the same tick +// +exports.hash = function (str) { + return crypto.createHash('sha1').update(str).digest('hex'); +}; + +// +// ### function pad (n) +// Returns a padded string if `n < 10`. +// +exports.pad = function (n) { + return n < 10 ? '0' + n.toString(10) : n.toString(10); +}; + +// +// ### function timestamp () +// Returns a timestamp string for the current time. +// +exports.timestamp = function () { + return new Date().toISOString(); +}; + +// +// ### function serialize (obj, key) +// #### @obj {Object|literal} Object to serialize +// #### @key {string} **Optional** Optional key represented by obj in a larger object +// Performs simple comma-separated, `key=value` serialization for Loggly when +// logging to non-JSON inputs. +// +exports.serialize = function (obj, key) { + if (obj === null) { + obj = 'null'; + } + else if (obj === undefined) { + obj = 'undefined'; + } + else if (obj === false) { + obj = 'false'; + } + + if (typeof obj !== 'object') { + return key ? key + '=' + obj : obj; + } + + if (obj instanceof Buffer) { + return key ? key + '=' + obj.toString('base64') : obj.toString('base64'); + } + + var msg = '', + keys = Object.keys(obj), + length = keys.length; + + for (var i = 0; i < length; i++) { + if (Array.isArray(obj[keys[i]])) { + msg += keys[i] + '=['; + + for (var j = 0, l = obj[keys[i]].length; j < l; j++) { + msg += exports.serialize(obj[keys[i]][j]); + if (j < l - 1) { + msg += ', '; + } + } + + msg += ']'; + } + else if (obj[keys[i]] instanceof Date) { + msg += keys[i] + '=' + obj[keys[i]]; + } + else { + msg += exports.serialize(obj[keys[i]], keys[i]); + } + + if (i < length - 1) { + msg += ', '; + } + } + + return msg; +}; + +// +// ### function tailFile (options, callback) +// #### @options {Object} Options for tail. +// #### @callback {function} Callback to execute on every line. +// `tail -f` a file. Options must include file. +// +exports.tailFile = function(options, callback) { + var buffer = new Buffer(64 * 1024) + , decode = new StringDecoder('utf8') + , stream = new Stream + , buff = '' + , pos = 0 + , row = 0; + + if (options.start === -1) { + delete options.start; + } + + stream.readable = true; + stream.destroy = function() { + stream.destroyed = true; + stream.emit('end'); + stream.emit('close'); + }; + + fs.open(options.file, 'a+', '0644', function(err, fd) { + if (err) { + if (!callback) { + stream.emit('error', err); + } else { + callback(err); + } + stream.destroy(); + return; + } + + (function read() { + if (stream.destroyed) { + fs.close(fd); + return; + } + + return fs.read(fd, buffer, 0, buffer.length, pos, function(err, bytes) { + if (err) { + if (!callback) { + stream.emit('error', err); + } else { + callback(err); + } + stream.destroy(); + return; + } + + if (!bytes) { + if (buff) { + if (options.start == null || row > options.start) { + if (!callback) { + stream.emit('line', buff); + } else { + callback(null, buff); + } + } + row++; + buff = ''; + } + return setTimeout(read, 1000); + } + + var data = decode.write(buffer.slice(0, bytes)); + + if (!callback) { + stream.emit('data', data); + } + + var data = (buff + data).split(/\n+/) + , l = data.length - 1 + , i = 0; + + for (; i < l; i++) { + if (options.start == null || row > options.start) { + if (!callback) { + stream.emit('line', data[i]); + } else { + callback(null, data[i]); + } + } + row++; + } + + buff = data[l]; + + pos += bytes; + + return read(); + }); + })(); + }); + + if (!callback) { + return stream; + } + + return stream.destroy; +}; + +// +// ### function stringArrayToSet (array) +// #### @strArray {Array} Array of Set-elements as strings. +// #### @errMsg {string} **Optional** Custom error message thrown on invalid input. +// Returns a Set-like object with strArray's elements as keys (each with the value true). +// +exports.stringArrayToSet = function (strArray, errMsg) { + if (typeof errMsg === 'undefined') { + errMsg = 'Cannot make set from Array with non-string elements'; + } + return strArray.reduce(function (set, el) { + if (!(typeof el === 'string' || el instanceof String)) { + throw new Error(errMsg); + } + set[el] = true; + return set; + }, Object.create(null)); +}; diff --git a/node_modules/winston/lib/winston/config.js b/node_modules/winston/lib/winston/config.js new file mode 100644 index 0000000..1bb52fd --- /dev/null +++ b/node_modules/winston/lib/winston/config.js @@ -0,0 +1,68 @@ +/* + * config.js: Default settings for all levels that winston knows about + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var colors = require('colors/safe'); + +// Fix colors not appearing in non-tty environments +colors.enabled = true; + +var config = exports, + allColors = exports.allColors = {}; + +config.addColors = function (colors) { + mixin(allColors, colors); +}; + +config.colorize = function (level, message) { + if (typeof message === 'undefined') message = level; + + var colorized = message; + if (allColors[level] instanceof Array) { + for (var i = 0, l = allColors[level].length; i < l; ++i) { + colorized = colors[allColors[level][i]](colorized); + } + } + else if (allColors[level].match(/\s/)) { + var colorArr = allColors[level].split(/\s+/); + for (var i = 0; i < colorArr.length; ++i) { + colorized = colors[colorArr[i]](colorized); + } + allColors[level] = colorArr; + } + else { + colorized = colors[allColors[level]](colorized); + } + + return colorized; +}; + +// +// Export config sets +// +config.cli = require('./config/cli-config'); +config.npm = require('./config/npm-config'); +config.syslog = require('./config/syslog-config'); + +// +// Add colors for pre-defined config sets +// +config.addColors(config.cli.colors); +config.addColors(config.npm.colors); +config.addColors(config.syslog.colors); + +function mixin (target) { + var args = Array.prototype.slice.call(arguments, 1); + + args.forEach(function (a) { + var keys = Object.keys(a); + for (var i = 0; i < keys.length; i++) { + target[keys[i]] = a[keys[i]]; + } + }); + return target; +}; diff --git a/node_modules/winston/lib/winston/config/cli-config.js b/node_modules/winston/lib/winston/config/cli-config.js new file mode 100644 index 0000000..764d2a8 --- /dev/null +++ b/node_modules/winston/lib/winston/config/cli-config.js @@ -0,0 +1,35 @@ +/* + * cli-config.js: Config that conform to commonly used CLI logging levels. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var cliConfig = exports; + +cliConfig.levels = { + error: 0, + warn: 1, + help: 2, + data: 3, + info: 4, + debug: 5, + prompt: 6, + verbose: 7, + input: 8, + silly: 9, +}; + +cliConfig.colors = { + error: 'red', + warn: 'yellow', + help: 'cyan', + data: 'grey', + info: 'green', + debug: 'blue', + prompt: 'grey', + verbose: 'cyan', + input: 'grey', + silly: 'magenta' +}; diff --git a/node_modules/winston/lib/winston/config/npm-config.js b/node_modules/winston/lib/winston/config/npm-config.js new file mode 100644 index 0000000..6402ab3 --- /dev/null +++ b/node_modules/winston/lib/winston/config/npm-config.js @@ -0,0 +1,27 @@ +/* + * npm-config.js: Config that conform to npm logging levels. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var npmConfig = exports; + +npmConfig.levels = { + error: 0, + warn: 1, + info: 2, + verbose: 3, + debug: 4, + silly: 5 +}; + +npmConfig.colors = { + error: 'red', + warn: 'yellow', + info: 'green', + verbose: 'cyan', + debug: 'blue', + silly: 'magenta' +}; diff --git a/node_modules/winston/lib/winston/config/syslog-config.js b/node_modules/winston/lib/winston/config/syslog-config.js new file mode 100644 index 0000000..67c6a09 --- /dev/null +++ b/node_modules/winston/lib/winston/config/syslog-config.js @@ -0,0 +1,31 @@ +/* + * syslog-config.js: Config that conform to syslog logging levels. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var syslogConfig = exports; + +syslogConfig.levels = { + emerg: 0, + alert: 1, + crit: 2, + error: 3, + warning: 4, + notice: 5, + info: 6, + debug: 7 +}; + +syslogConfig.colors = { + emerg: 'red', + alert: 'yellow', + crit: 'red', + error: 'red', + warning: 'red', + notice: 'yellow', + info: 'green', + debug: 'blue' +}; diff --git a/node_modules/winston/lib/winston/container.js b/node_modules/winston/lib/winston/container.js new file mode 100644 index 0000000..f5faaa7 --- /dev/null +++ b/node_modules/winston/lib/winston/container.js @@ -0,0 +1,127 @@ +/* + * container.js: Inversion of control container for winston logger instances + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var common = require('./common'), + winston = require('../winston'), + extend = require('util')._extend; + +// +// ### function Container (options) +// #### @options {Object} Default pass-thru options for Loggers +// Constructor function for the Container object responsible for managing +// a set of `winston.Logger` instances based on string ids. +// +var Container = exports.Container = function (options) { + this.loggers = {}; + this.options = options || {}; + this.default = { + transports: [ + new winston.transports.Console({ + level: 'silly', + colorize: false + }) + ] + } +}; + +// +// ### function get / add (id, options) +// #### @id {string} Id of the Logger to get +// #### @options {Object} **Optional** Options for the Logger instance +// Retreives a `winston.Logger` instance for the specified `id`. If +// an instance does not exist, one is created. +// +Container.prototype.get = Container.prototype.add = function (id, options) { + var self = this, + existing; + + if (!this.loggers[id]) { + // + // Remark: Simple shallow clone for configuration options in case we pass in + // instantiated protoypal objects + // + options = extend({}, options || this.options || this.default); + existing = options.transports || this.options.transports; + // + // Remark: Make sure if we have an array of transports we slice it to make copies + // of those references. + // + options.transports = existing ? existing.slice() : []; + + if (options.transports.length === 0 && (!options || !options['console'])) { + options.transports.push(this.default.transports[0]); + } + + Object.keys(options).forEach(function (key) { + if (key === 'transports') { + return; + } + + var name = common.capitalize(key); + + if (!winston.transports[name]) { + throw new Error('Cannot add unknown transport: ' + name); + } + + var namedOptions = options[key]; + namedOptions.id = id; + options.transports.push(new (winston.transports[name])(namedOptions)); + }); + + this.loggers[id] = new winston.Logger(options); + + this.loggers[id].on('close', function () { + self._delete(id); + }); + } + + return this.loggers[id]; +}; + +// +// ### function close (id) +// #### @id {string} **Optional** Id of the Logger instance to find +// Returns a boolean value indicating if this instance +// has a logger with the specified `id`. +// +Container.prototype.has = function (id) { + return !!this.loggers[id]; +}; + +// +// ### function close (id) +// #### @id {string} **Optional** Id of the Logger instance to close +// Closes a `Logger` instance with the specified `id` if it exists. +// If no `id` is supplied then all Loggers are closed. +// +Container.prototype.close = function (id) { + var self = this; + + function _close (id) { + if (!self.loggers[id]) { + return; + } + + self.loggers[id].close(); + self._delete(id); + } + + return id ? _close(id) : Object.keys(this.loggers).forEach(function (id) { + _close(id); + }); +}; + +// +// ### @private function _delete (id) +// #### @id {string} Id of the Logger instance to delete from container +// Deletes a `Logger` instance with the specified `id`. +// +Container.prototype._delete = function (id) { + delete this.loggers[id]; +} + diff --git a/node_modules/winston/lib/winston/exception.js b/node_modules/winston/lib/winston/exception.js new file mode 100644 index 0000000..22717dd --- /dev/null +++ b/node_modules/winston/lib/winston/exception.js @@ -0,0 +1,56 @@ +/* + * exception.js: Utility methods for gathing information about uncaughtExceptions. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var os = require('os'), + stackTrace = require('stack-trace'); + +var exception = exports; + +exception.getAllInfo = function (err) { + return { + date: new Date().toString(), + process: exception.getProcessInfo(), + os: exception.getOsInfo(), + trace: exception.getTrace(err), + stack: err.stack && err.stack.split('\n') + }; +}; + +exception.getProcessInfo = function () { + return { + pid: process.pid, + uid: process.getuid ? process.getuid() : null, + gid: process.getgid ? process.getgid() : null, + cwd: process.cwd(), + execPath: process.execPath, + version: process.version, + argv: process.argv, + memoryUsage: process.memoryUsage() + }; +}; + +exception.getOsInfo = function () { + return { + loadavg: os.loadavg(), + uptime: os.uptime() + }; +}; + +exception.getTrace = function (err) { + var trace = err ? stackTrace.parse(err) : stackTrace.get(); + return trace.map(function (site) { + return { + column: site.getColumnNumber(), + file: site.getFileName(), + function: site.getFunctionName(), + line: site.getLineNumber(), + method: site.getMethodName(), + native: site.isNative(), + } + }); +}; diff --git a/node_modules/winston/lib/winston/logger.js b/node_modules/winston/lib/winston/logger.js new file mode 100644 index 0000000..97fba64 --- /dev/null +++ b/node_modules/winston/lib/winston/logger.js @@ -0,0 +1,723 @@ +/* + * 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'), + Stream = require('stream').Stream; + +const 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 = []; + + if (options.transports) { + options.transports.forEach(function (transport) { + self.add(transport, null, true); + }); + } + + // + // Set Levels and default logging level + // + this.padLevels = options.padLevels || false; + this.setLevels(options.levels); + if (options.colors) { + config.addColors(options.colors); + } + + // + // Hoist other options onto this instance. + // + 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 internal state as empty Objects even though it is + // defined lazily later to ensure a strong existential API contract. + // + this.exceptionHandlers = {}; + this.profilers = {}; + + ['rewriters', 'filters'].forEach(function (kind) { + self[kind] = Array.isArray(options[kind]) + ? options[kind] + : []; + }); + + if (options.exceptionHandlers) { + this.handleExceptions(options.exceptionHandlers); + } +}; + +// +// ### 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 args = Array.prototype.slice.call(arguments, 1), + self = this, + transports; + + 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; + + // + // Handle errors appropriately. + // + function onError(err) { + if (callback) { + callback(err); + } + else if (self.emitErrs) { + self.emit('error', err); + } + } + + 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 metaType = Object.prototype.toString.call(args[args.length - 1]), + fmtMatch = args[0] && args[0].match && args[0].match(formatRegExp), + isFormat = fmtMatch && fmtMatch.length, + validMeta = !isFormat + ? metaType === '[object Object]' || metaType === '[object Error]' || metaType === '[object Array]' + : metaType === '[object Object]', + 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(level, msg, meta, self); + if (typeof filtered === 'string') + msg = filtered; + else { + msg = filtered.msg; + meta = filtered.meta; + } + }); + + // + // 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/ANSI_escape_code + // + if (this.stripColors) { + var code = /\u001b\[(\d+(;\d+)*)?m/g; + msg = ('' + msg).replace(code, ''); + } + + // + // Log for each transport and emit 'logging' event + // + function transportLog(name, next) { + var transport = self.transports[name]; + transport.log(level, msg, meta, function (err) { + if (err) { + err.transport = transport; + finish(err); + return next(); + } + + self.emit('logging', transport, level, msg, meta); + next(); + }); + } + + async.forEach(targets, transportLog, finish); + return this; +}; + +// +// ### function query (options, callback) +// #### @options {Object} Query options for this instance. +// #### @callback {function} Continuation to respond to when complete. +// Queries the all transports for this instance with the specified `options`. +// This will aggregate each transport's results into one object containing +// a property per transport. +// +Logger.prototype.query = function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + var self = this, + options = options || {}, + results = {}, + query = common.clone(options.query) || {}, + transports; + + // + // Helper function to query a single transport + // + function queryTransport(transport, next) { + if (options.query) { + options.query = transport.formatQuery(query); + } + + transport.query(options, function (err, results) { + if (err) { + return next(err); + } + + next(null, transport.formatResults(results, options.format)); + }); + } + + // + // Helper function to accumulate the results from + // `queryTransport` into the `results`. + // + function addResults(transport, next) { + queryTransport(transport, function (err, result) { + // + // queryTransport could potentially invoke the callback + // multiple times since Transport code can be unpredictable. + // + if (next) { + result = err || result; + if (result) { + results[transport.name] = result; + } + + next(); + } + + next = null; + }); + } + + // + // If an explicit transport is being queried then + // respond with the results from only that transport + // + if (options.transport) { + options.transport = options.transport.toLowerCase(); + return queryTransport(this.transports[options.transport], callback); + } + + // + // Create a list of all transports for this instance. + // + transports = this._names.map(function (name) { + return self.transports[name]; + }).filter(function (transport) { + return !!transport.query; + }); + + // + // Iterate over the transports in parallel setting the + // appropriate key in the `results` + // + async.forEach(transports, addResults, function () { + callback(null, results); + }); +}; + +// +// ### function stream (options) +// #### @options {Object} Stream options for this instance. +// Returns a log stream for all transports. Options object is optional. +// +Logger.prototype.stream = function (options) { + var self = this, + options = options || {}, + out = new Stream, + streams = [], + transports; + + if (options.transport) { + var transport = this.transports[options.transport]; + delete options.transport; + if (transport && transport.stream) { + return transport.stream(options); + } + } + + out._streams = streams; + out.destroy = function () { + var i = streams.length; + while (i--) streams[i].destroy(); + }; + + // + // Create a list of all transports for this instance. + // + transports = this._names.map(function (name) { + return self.transports[name]; + }).filter(function (transport) { + return !!transport.stream; + }); + + transports.forEach(function (transport) { + var stream = transport.stream(options); + if (!stream) return; + + streams.push(stream); + + stream.on('log', function (log) { + log.transport = log.transport || []; + log.transport.push(transport.name); + out.emit('log', log); + }); + + stream.on('error', function (err) { + err.transport = err.transport || []; + err.transport.push(transport.name); + out.emit('error', err); + }); + }); + + return out; +}; + +// +// ### function close () +// Cleans up resources (streams, event listeners) for all +// transports associated with this instance (if necessary). +// +Logger.prototype.close = function () { + var self = this; + + this._names.forEach(function (name) { + var transport = self.transports[name]; + if (transport && transport.close) { + transport.close(); + } + }); + + this.emit('close'); +}; + +// +// ### 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; + + args.forEach(function (a) { + if (Array.isArray(a)) { + handlers = handlers.concat(a); + } + else { + handlers.push(a); + } + }); + + this.exceptionHandlers = this.exceptionHandlers || {}; + 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) { + var handler = self.exceptionHandlers[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) + if (!created) { + instance.on('error', instance._onError); + } + + // + // If this transport has `handleExceptions` set to `true` + // and we are not already handling exceptions, do so. + // + if (instance.handleExceptions && !this.catchExceptions) { + this.handleExceptions(); + } + + return this; +}; + +// +// ### function clear () +// Remove all transports from this instance +// +Logger.prototype.clear = function () { + Object.keys(this.transports).forEach(function (name) { + this.remove({ name: name }); + }, this); +}; + +// +// ### function remove (transport) +// #### @transport {Transport|String} Transport or Name to remove. +// Removes a transport of the specified type from this instance. +// +Logger.prototype.remove = function (transport) { + var name = typeof transport !== 'string' + ? transport.name || transport.prototype.name + : transport; + + 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(); + } + + if (instance._onError) { + instance.removeListener('error', instance._onError); + } + return this; +}; + +// +// ### 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 +// #### @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]) { + 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.durationMs = now - then; + 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 = this.transports.console.colorize || true; + this.transports.console.timestamp = 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, + doExit; + + // + // Calculate if we should exit on this error + // + doExit = typeof this.exitOnError === 'function' + ? this.exitOnError(err) + : this.exitOnError; + + function logAndWait(transport, next) { + transport.logException('uncaughtException: ' + (err.message || err), info, next, err); + } + + function gracefulExit() { + if (doExit && !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); + if (doExit) { + 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 (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); +}; diff --git a/node_modules/winston/lib/winston/transports.js b/node_modules/winston/lib/winston/transports.js new file mode 100644 index 0000000..34f800e --- /dev/null +++ b/node_modules/winston/lib/winston/transports.js @@ -0,0 +1,29 @@ +/* + * transports.js: Set of all transports Winston knows about + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var path = require('path'); + +// +// Setup all transports as lazy-loaded getters. +// +Object.defineProperties( + exports, + ['Console', 'File', 'Http', 'Memory'] + .reduce(function (acc, name) { + acc[name] = { + configurable: true, + enumerable: true, + get: function () { + var fullpath = path.join(__dirname, 'transports', name.toLowerCase()); + return exports[name] = require(fullpath)[name]; + } + }; + + return acc; + }, {}) +); diff --git a/node_modules/winston/lib/winston/transports/console.js b/node_modules/winston/lib/winston/transports/console.js new file mode 100644 index 0000000..f573e17 --- /dev/null +++ b/node_modules/winston/lib/winston/transports/console.js @@ -0,0 +1,130 @@ +/* + * console.js: Transport for outputting to the console + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var events = require('events'), + os = require('os'), + util = require('util'), + common = require('../common'), + Transport = require('./transport').Transport; + +// +// ### function Console (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Console transport object responsible +// for persisting log messages and metadata to a terminal or TTY. +// +var Console = exports.Console = function (options) { + Transport.call(this, options); + options = options || {}; + + this.json = options.json || false; + this.colorize = options.colorize || false; + this.prettyPrint = options.prettyPrint || false; + this.timestamp = typeof options.timestamp !== 'undefined' ? options.timestamp : false; + this.showLevel = options.showLevel === undefined ? true : options.showLevel; + this.label = options.label || null; + this.logstash = options.logstash || false; + this.depth = options.depth || null; + this.align = options.align || false; + this.stderrLevels = setStderrLevels(options.stderrLevels, options.debugStdout); + this.eol = options.eol || os.EOL; + + if (this.json) { + this.stringify = options.stringify || function (obj) { + return JSON.stringify(obj, null, 2); + }; + } + + // + // Convert stderrLevels into an Object for faster key-lookup times than an Array. + // + // For backwards compatibility, stderrLevels defaults to ['error', 'debug'] + // or ['error'] depending on whether options.debugStdout is true. + // + function setStderrLevels (levels, debugStdout) { + var defaultMsg = 'Cannot have non-string elements in stderrLevels Array'; + if (debugStdout) { + if (levels) { + // + // Don't allow setting both debugStdout and stderrLevels together, + // since this could cause behaviour a programmer might not expect. + // + throw new Error('Cannot set debugStdout and stderrLevels together'); + } + + return common.stringArrayToSet(['error'], defaultMsg); + } + + if (!levels) { + return common.stringArrayToSet(['error', 'debug'], defaultMsg); + } else if (!(Array.isArray(levels))) { + throw new Error('Cannot set stderrLevels to type other than Array'); + } + + return common.stringArrayToSet(levels, defaultMsg); + }; +}; + +// +// Inherit from `winston.Transport`. +// +util.inherits(Console, Transport); + +// +// Expose the name of this Transport on the prototype +// +Console.prototype.name = 'console'; + +// +// ### 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. +// +Console.prototype.log = function (level, msg, meta, callback) { + if (this.silent) { + return callback(null, true); + } + + var self = this, + output; + + output = common.log({ + colorize: this.colorize, + json: this.json, + level: level, + message: msg, + meta: meta, + stringify: this.stringify, + timestamp: this.timestamp, + showLevel: this.showLevel, + prettyPrint: this.prettyPrint, + raw: this.raw, + label: this.label, + logstash: this.logstash, + depth: this.depth, + formatter: this.formatter, + align: this.align, + humanReadableUnhandledException: this.humanReadableUnhandledException + }); + + if (this.stderrLevels[level]) { + process.stderr.write(output + '\n'); + } else { + process.stdout.write(output + this.eol); + } + + // + // Emit the `logged` event immediately because the event loop + // will not exit until `process.stdout` has drained anyway. + // + self.emit('logged'); + callback(null, true); +}; diff --git a/node_modules/winston/lib/winston/transports/file.js b/node_modules/winston/lib/winston/transports/file.js new file mode 100644 index 0000000..b3df4b5 --- /dev/null +++ b/node_modules/winston/lib/winston/transports/file.js @@ -0,0 +1,678 @@ +/* + * file.js: Transport for outputting to a local log file + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var events = require('events'), + fs = require('fs'), + path = require('path'), + util = require('util'), + async = require('async'), + zlib = require('zlib'), + common = require('../common'), + Transport = require('./transport').Transport, + isWritable = require('isstream').isWritable, + Stream = require('stream').Stream, + os = require('os'); + +// +// ### function File (options) +// #### @options {Object} Options for this instance. +// Constructor function for the File transport object responsible +// for persisting log messages and metadata to one or more files. +// +var File = exports.File = function (options) { + var self = this; + Transport.call(this, options); + + // + // Helper function which throws an `Error` in the event + // that any of the rest of the arguments is present in `options`. + // + function throwIf (target /*, illegal... */) { + Array.prototype.slice.call(arguments, 1).forEach(function (name) { + if (options[name]) { + throw new Error('Cannot set ' + name + ' and ' + target + 'together'); + } + }); + } + + if (options.filename || options.dirname) { + throwIf('filename or dirname', 'stream'); + this._basename = this.filename = options.filename + ? path.basename(options.filename) + : 'winston.log'; + + this.dirname = options.dirname || path.dirname(options.filename); + this.options = options.options || { flags: 'a' }; + + // + // "24 bytes" is maybe a good value for logging lines. + // + this.options.highWaterMark = this.options.highWaterMark || 24; + } + else if (options.stream) { + throwIf('stream', 'filename', 'maxsize'); + this._stream = options.stream; + this._isStreams2 = isWritable(this._stream); + this._stream.on('error', function(error){ + self.emit('error', error); + }); + // + // We need to listen for drain events when + // write() returns false. This can make node + // mad at times. + // + this._stream.setMaxListeners(Infinity); + } + else { + throw new Error('Cannot log to file without filename or stream.'); + } + + this.json = options.json !== false; + this.logstash = options.logstash || false; + this.colorize = options.colorize || false; + this.maxsize = options.maxsize || null; + this.rotationFormat = options.rotationFormat || false; + this.zippedArchive = options.zippedArchive || false; + this.maxFiles = options.maxFiles || null; + this.prettyPrint = options.prettyPrint || false; + this.label = options.label || null; + this.timestamp = options.timestamp != null ? options.timestamp : true; + this.eol = options.eol || os.EOL; + this.tailable = options.tailable || false; + this.depth = options.depth || null; + this.showLevel = options.showLevel === undefined ? true : options.showLevel; + this.maxRetries = options.maxRetries || 2; + + if (this.json) { + this.stringify = options.stringify; + } + + // + // Internal state variables representing the number + // of files this instance has created and the current + // size (in bytes) of the current logfile. + // + this._size = 0; + this._created = 0; + this._buffer = []; + this._draining = false; + this._opening = false; + this._failures = 0; + this._archive = null; +}; + +// +// Inherit from `winston.Transport`. +// +util.inherits(File, Transport); + +// +// Expose the name of this Transport on the prototype +// +File.prototype.name = 'file'; + +// +// ### 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. +// +File.prototype.log = function (level, msg, meta, callback) { + if (this.silent) { + return callback(null, true); + } + + // + // If failures exceeds maxRetries then we can't access the + // stream. In this case we need to perform a noop and return + // an error. + // + if (this._failures >= this.maxRetries) { + return callback(new Error('Transport is in a failed state.')); + } + + var self = this; + + if (typeof msg !== 'string') { + msg = '' + msg; + } + + var output = common.log({ + level: level, + message: msg, + meta: meta, + json: this.json, + logstash: this.logstash, + colorize: this.colorize, + prettyPrint: this.prettyPrint, + timestamp: this.timestamp, + showLevel: this.showLevel, + stringify: this.stringify, + label: this.label, + depth: this.depth, + formatter: this.formatter, + humanReadableUnhandledException: this.humanReadableUnhandledException + }); + + if (typeof output === 'string') { + output += this.eol; + } + + if (!this.filename) { + // + // If there is no `filename` on this instance then it was configured + // with a raw `WriteableStream` instance and we should not perform any + // size restrictions. + // + this._write(output, callback); + this._size += output.length; + this._lazyDrain(); + } + else { + this.open(function (err) { + if (err) { + // + // If there was an error enqueue the message + // + return self._buffer.push([output, callback]); + } + + self._write(output, callback); + self._size += output.length; + self._lazyDrain(); + }); + } +}; + +// +// ### function _write (data, cb) +// #### @data {String|Buffer} Data to write to the instance's stream. +// #### @cb {function} Continuation to respond to when complete. +// Write to the stream, ensure execution of a callback on completion. +// +File.prototype._write = function(data, callback) { + if (this._isStreams2) { + this._stream.write(data); + return callback && process.nextTick(function () { + callback(null, true); + }); + } + + // If this is a file write stream, we could use the builtin + // callback functionality, however, the stream is not guaranteed + // to be an fs.WriteStream. + var ret = this._stream.write(data); + if (!callback) return; + if (ret === false) { + return this._stream.once('drain', function() { + callback(null, true); + }); + } + process.nextTick(function () { + callback(null, true); + }); +}; + +// +// ### function query (options, callback) +// #### @options {Object} Loggly-like query options for this instance. +// #### @callback {function} Continuation to respond to when complete. +// Query the transport. Options object is optional. +// +File.prototype.query = function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + var file = path.join(this.dirname, this.filename), + options = this.normalizeQuery(options), + buff = '', + results = [], + row = 0; + + var stream = fs.createReadStream(file, { + encoding: 'utf8' + }); + + stream.on('error', function (err) { + if (stream.readable) { + stream.destroy(); + } + if (!callback) return; + return err.code !== 'ENOENT' + ? callback(err) + : callback(null, results); + }); + + stream.on('data', function (data) { + var data = (buff + data).split(/\n+/), + l = data.length - 1, + i = 0; + + for (; i < l; i++) { + if (!options.start || row >= options.start) { + add(data[i]); + } + row++; + } + + buff = data[l]; + }); + + stream.on('close', function () { + if (buff) add(buff, true); + if (options.order === 'desc') { + results = results.reverse(); + } + if (callback) callback(null, results); + }); + + function add(buff, attempt) { + try { + var log = JSON.parse(buff); + if (check(log)) push(log); + } catch (e) { + if (!attempt) { + stream.emit('error', e); + } + } + } + + function push(log) { + if (options.rows && results.length >= options.rows) { + if (stream.readable) { + stream.destroy(); + } + return; + } + + if (options.fields) { + var obj = {}; + options.fields.forEach(function (key) { + obj[key] = log[key]; + }); + log = obj; + } + + results.push(log); + } + + function check(log) { + if (!log) return; + + if (typeof log !== 'object') return; + + var time = new Date(log.timestamp); + if ((options.from && time < options.from) + || (options.until && time > options.until)) { + return; + } + + return true; + } +}; + +// +// ### function stream (options) +// #### @options {Object} Stream options for this instance. +// Returns a log stream for this transport. Options object is optional. +// +File.prototype.stream = function (options) { + var file = path.join(this.dirname, this.filename), + options = options || {}, + stream = new Stream; + + var tail = { + file: file, + start: options.start + }; + + stream.destroy = common.tailFile(tail, function (err, line) { + + if(err){ + return stream.emit('error',err); + } + + try { + stream.emit('data', line); + line = JSON.parse(line); + stream.emit('log', line); + } catch (e) { + stream.emit('error', e); + } + }); + + return stream; +}; + +// +// ### function open (callback) +// #### @callback {function} Continuation to respond to when complete +// Checks to see if a new file needs to be created based on the `maxsize` +// (if any) and the current size of the file used. +// +File.prototype.open = function (callback) { + if (this.opening) { + // + // If we are already attempting to open the next + // available file then respond with a value indicating + // that the message should be buffered. + // + return callback(true); + } + else if (!this._stream || (this.maxsize && this._size >= this.maxsize)) { + // + // If we dont have a stream or have exceeded our size, then create + // the next stream and respond with a value indicating that + // the message should be buffered. + // + callback(true); + return this._createStream(); + } + + this._archive = this.zippedArchive ? this._stream.path : null; + + // + // Otherwise we have a valid (and ready) stream. + // + callback(); +}; + +// +// ### function close () +// Closes the stream associated with this instance. +// +File.prototype.close = function () { + var self = this; + + if (this._stream) { + this._stream.end(); + this._stream.destroySoon(); + + this._stream.once('finish', function () { + self.emit('flush'); + self.emit('closed'); + }); + } +}; + +// +// ### function flush () +// Flushes any buffered messages to the current `stream` +// used by this instance. +// +File.prototype.flush = function () { + var self = this; + + // If nothing to flush, there will be no "flush" event from native stream + // Thus, the "open" event will never be fired (see _createStream.createAndFlush function) + // That means, self.opening will never set to false and no logs will be written to disk + if (!this._buffer.length) { + return self.emit('flush'); + } + + // + // Iterate over the `_buffer` of enqueued messaged + // and then write them to the newly created stream. + // + this._buffer.forEach(function (item) { + var str = item[0], + callback = item[1]; + + process.nextTick(function () { + self._write(str, callback); + self._size += str.length; + }); + }); + + // + // Quickly truncate the `_buffer` once the write operations + // have been started + // + self._buffer.length = 0; + + // + // When the stream has drained we have flushed + // our buffer. + // + self._stream.once('drain', function () { + self.emit('flush'); + self.emit('logged'); + }); +}; + +// +// ### @private function _createStream () +// Attempts to open the next appropriate file for this instance +// based on the common state (such as `maxsize` and `_basename`). +// +File.prototype._createStream = function () { + var self = this; + this.opening = true; + + (function checkFile (target) { + var fullname = path.join(self.dirname, target); + + // + // Creates the `WriteStream` and then flushes any + // buffered messages. + // + function createAndFlush (size) { + if (self._stream) { + self._stream.end(); + self._stream.destroySoon(); + } + + self._size = size; + self.filename = target; + self._stream = fs.createWriteStream(fullname, self.options); + self._isStreams2 = isWritable(self._stream); + self._stream.on('error', function(error){ + if (self._failures < self.maxRetries) { + self._createStream(); + self._failures++; + } + else { + self.emit('error', error); + } + }); + // + // We need to listen for drain events when + // write() returns false. This can make node + // mad at times. + // + self._stream.setMaxListeners(Infinity); + + // + // When the current stream has finished flushing + // then we can be sure we have finished opening + // and thus can emit the `open` event. + // + self.once('flush', function () { + // Because "flush" event is based on native stream "drain" event, + // logs could be written inbetween "self.flush()" and here + // Therefore, we need to flush again to make sure everything is flushed + self.flush(); + + self.opening = false; + self.emit('open', fullname); + }); + // + // Remark: It is possible that in the time it has taken to find the + // next logfile to be written more data than `maxsize` has been buffered, + // but for sensible limits (10s - 100s of MB) this seems unlikely in less + // than one second. + // + self.flush(); + compressFile(); + } + + function compressFile() { + if (self._archive) { + var gzip = zlib.createGzip(); + + var inp = fs.createReadStream(String(self._archive)); + var out = fs.createWriteStream(self._archive + '.gz'); + + inp.pipe(gzip).pipe(out); + + fs.unlink(String(self._archive)); + self._archive = ''; + } + } + + fs.stat(fullname, function (err, stats) { + if (err) { + if (err.code !== 'ENOENT') { + return self.emit('error', err); + } + return createAndFlush(0); + } + + if (!stats || (self.maxsize && stats.size >= self.maxsize)) { + // + // If `stats.size` is greater than the `maxsize` for + // this instance then try again + // + return self._incFile(function() { + checkFile(self._getFile()); + }); + } + + createAndFlush(stats.size); + }); + })(this._getFile()); +}; + + +File.prototype._incFile = function (callback) { + var ext = path.extname(this._basename), + basename = path.basename(this._basename, ext), + oldest, + target; + + if (!this.tailable) { + this._created += 1; + this._checkMaxFilesIncrementing(ext, basename, callback); + } + else { + this._checkMaxFilesTailable(ext, basename, callback); + } +}; + +// +// ### @private function _getFile () +// Gets the next filename to use for this instance +// in the case that log filesizes are being capped. +// +File.prototype._getFile = function () { + var ext = path.extname(this._basename), + basename = path.basename(this._basename, ext); + + // + // Caveat emptor (indexzero): rotationFormat() was broken by design + // when combined with max files because the set of files to unlink + // is never stored. + // + return !this.tailable && this._created + ? basename + (this.rotationFormat ? this.rotationFormat() : this._created) + ext + : basename + ext; +}; + +// +// ### @private function _checkMaxFilesIncrementing () +// Increment the number of files created or +// checked by this instance. +// +File.prototype._checkMaxFilesIncrementing = function (ext, basename, callback) { + var oldest, target, + self = this; + + if (self.zippedArchive) { + self._archive = path.join(self.dirname, basename + + ((self._created === 1) ? '' : self._created-1) + + ext); + } + + + // Check for maxFiles option and delete file + if (!self.maxFiles || self._created < self.maxFiles) { + return callback(); + } + + oldest = self._created - self.maxFiles; + target = path.join(self.dirname, basename + (oldest !== 0 ? oldest : '') + ext + + (self.zippedArchive ? '.gz' : '')); + fs.unlink(target, callback); +}; + +// +// ### @private function _checkMaxFilesTailable () +// +// Roll files forward based on integer, up to maxFiles. +// e.g. if base if file.log and it becomes oversized, roll +// to file1.log, and allow file.log to be re-used. If +// file is oversized again, roll file1.log to file2.log, +// roll file.log to file1.log, and so on. +File.prototype._checkMaxFilesTailable = function (ext, basename, callback) { + var tasks = [], + self = this; + + if (!this.maxFiles) + return; + + for (var x = this.maxFiles - 1; x > 0; x--) { + tasks.push(function (i) { + return function (cb) { + var tmppath = path.join(self.dirname, basename + (i - 1) + ext + + (self.zippedArchive ? '.gz' : '')); + fs.exists(tmppath, function (exists) { + if (!exists) { + return cb(null); + } + + fs.rename(tmppath, path.join(self.dirname, basename + i + ext + + (self.zippedArchive ? '.gz' : '')), cb); + }); + }; + }(x)); + } + + if (self.zippedArchive) { + self._archive = path.join(self.dirname, basename + 1 + ext); + } + async.series(tasks, function (err) { + fs.rename( + path.join(self.dirname, basename + ext), + path.join(self.dirname, basename + 1 + ext), + callback + ); + }); +}; + +// +// ### @private function _lazyDrain () +// Lazily attempts to emit the `logged` event when `this.stream` has +// drained. This is really just a simple mutex that only works because +// Node.js is single-threaded. +// +File.prototype._lazyDrain = function () { + var self = this; + + if (!this._draining && this._stream) { + this._draining = true; + + this._stream.once('drain', function () { + this._draining = false; + self.emit('logged'); + }); + } +}; diff --git a/node_modules/winston/lib/winston/transports/http.js b/node_modules/winston/lib/winston/transports/http.js new file mode 100644 index 0000000..f7e1af6 --- /dev/null +++ b/node_modules/winston/lib/winston/transports/http.js @@ -0,0 +1,232 @@ +var util = require('util'), + winston = require('../../winston'), + http = require('http'), + https = require('https'), + Stream = require('stream').Stream, + Transport = require('./transport').Transport; + +// +// ### function Http (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Http transport object responsible +// for persisting log messages and metadata to a terminal or TTY. +// +var Http = exports.Http = function (options) { + Transport.call(this, options); + options = options || {}; + + this.name = 'http'; + this.ssl = !!options.ssl; + this.host = options.host || 'localhost'; + this.port = options.port; + this.auth = options.auth; + this.path = options.path || ''; + + if (!this.port) { + this.port = this.ssl ? 443 : 80; + } +}; + +util.inherits(Http, winston.Transport); + +// +// Expose the name of this Transport on the prototype +// +Http.prototype.name = 'http'; + +// +// ### function _request (options, callback) +// #### @callback {function} Continuation to respond to when complete. +// Make a request to a winstond server or any http server which can +// handle json-rpc. +// +Http.prototype._request = function (options, callback) { + options = options || {}; + + var auth = options.auth || this.auth, + path = options.path || this.path || '', + req; + + delete options.auth; + delete options.path; + + // Prepare options for outgoing HTTP request + req = (this.ssl ? https : http).request({ + host: this.host, + port: this.port, + path: '/' + path.replace(/^\//, ''), + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + auth: (auth) ? auth.username + ':' + auth.password : '' + }); + + req.on('error', callback); + req.on('response', function (res) { + res.on('end', function () { + callback(null, res); + }); + + res.resume(); + }); + + req.end(new Buffer(JSON.stringify(options), 'utf8')); +}; + +// +// ### 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. +// +Http.prototype.log = function (level, msg, meta, callback) { + var self = this; + + if (typeof meta === 'function') { + callback = meta; + meta = {}; + } + + var options = { + method: 'collect', + params: { + level: level, + message: msg, + meta: meta + } + }; + + if (meta) { + if (meta.path) { + options.path = meta.path; + delete meta.path; + } + + if (meta.auth) { + options.auth = meta.auth; + delete meta.auth; + } + } + + this._request(options, function (err, res) { + if (res && res.statusCode !== 200) { + err = new Error('HTTP Status Code: ' + res.statusCode); + } + + if (err) return callback(err); + + // TODO: emit 'logged' correctly, + // keep track of pending logs. + self.emit('logged'); + + if (callback) callback(null, true); + }); +}; + +// +// ### function query (options, callback) +// #### @options {Object} Loggly-like query options for this instance. +// #### @callback {function} Continuation to respond to when complete. +// Query the transport. Options object is optional. +// +Http.prototype.query = function (options, callback) { + if (typeof options === 'function') { + callback = options; + options = {}; + } + + var self = this, + options = this.normalizeQuery(options); + + options = { + method: 'query', + params: options + }; + + if (options.params.path) { + options.path = options.params.path; + delete options.params.path; + } + + if (options.params.auth) { + options.auth = options.params.auth; + delete options.params.auth; + } + + this._request(options, function (err, res, body) { + if (res && res.statusCode !== 200) { + err = new Error('HTTP Status Code: ' + res.statusCode); + } + + if (err) return callback(err); + + if (typeof body === 'string') { + try { + body = JSON.parse(body); + } catch (e) { + return callback(e); + } + } + + callback(null, body); + }); +}; + +// +// ### function stream (options) +// #### @options {Object} Stream options for this instance. +// Returns a log stream for this transport. Options object is optional. +// +Http.prototype.stream = function (options) { + options = options || {}; + + var self = this, + stream = new Stream, + req, + buff; + + stream.destroy = function () { + req.destroy(); + }; + + options = { + method: 'stream', + params: options + }; + + if (options.params.path) { + options.path = options.params.path; + delete options.params.path; + } + + if (options.params.auth) { + options.auth = options.params.auth; + delete options.params.auth; + } + + req = this._request(options); + buff = ''; + + req.on('data', function (data) { + var data = (buff + data).split(/\n+/), + l = data.length - 1, + i = 0; + + for (; i < l; i++) { + try { + stream.emit('log', JSON.parse(data[i])); + } catch (e) { + stream.emit('error', e); + } + } + + buff = data[l]; + }); + + req.on('error', function (err) { + stream.emit('error', err); + }); + + return stream; +}; diff --git a/node_modules/winston/lib/winston/transports/memory.js b/node_modules/winston/lib/winston/transports/memory.js new file mode 100644 index 0000000..e4f562e --- /dev/null +++ b/node_modules/winston/lib/winston/transports/memory.js @@ -0,0 +1,89 @@ +var events = require('events'), + util = require('util'), + common = require('../common'), + Transport = require('./transport').Transport; + +// +// ### function Memory (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Memory transport object responsible +// for persisting log messages and metadata to a memory array of messages. +// +var Memory = exports.Memory = function (options) { + Transport.call(this, options); + options = options || {}; + + this.errorOutput = []; + this.writeOutput = []; + + this.json = options.json || false; + this.colorize = options.colorize || false; + this.prettyPrint = options.prettyPrint || false; + this.timestamp = typeof options.timestamp !== 'undefined' ? options.timestamp : false; + this.showLevel = options.showLevel === undefined ? true : options.showLevel; + this.label = options.label || null; + this.depth = options.depth || null; + + if (this.json) { + this.stringify = options.stringify || function (obj) { + return JSON.stringify(obj, null, 2); + }; + } +}; + +// +// Inherit from `winston.Transport`. +// +util.inherits(Memory, Transport); + +// +// Expose the name of this Transport on the prototype +// +Memory.prototype.name = 'memory'; + +// +// ### 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. +// +Memory.prototype.log = function (level, msg, meta, callback) { + if (this.silent) { + return callback(null, true); + } + + var self = this, + output; + + output = common.log({ + colorize: this.colorize, + json: this.json, + level: level, + message: msg, + meta: meta, + stringify: this.stringify, + timestamp: this.timestamp, + prettyPrint: this.prettyPrint, + raw: this.raw, + label: this.label, + depth: this.depth, + formatter: this.formatter, + humanReadableUnhandledException: this.humanReadableUnhandledException + }); + + if (level === 'error' || level === 'debug') { + this.errorOutput.push(output); + } else { + this.writeOutput.push(output); + } + + self.emit('logged'); + callback(null, true); +}; + +Memory.prototype.clearLogs = function () { + this.errorOutput = []; + this.writeOutput = []; +}; diff --git a/node_modules/winston/lib/winston/transports/transport.js b/node_modules/winston/lib/winston/transports/transport.js new file mode 100644 index 0000000..d279d00 --- /dev/null +++ b/node_modules/winston/lib/winston/transports/transport.js @@ -0,0 +1,135 @@ +/* + * transport.js: Base Transport object for all Winston transports. + * + * (C) 2010 Charlie Robbins + * MIT LICENCE + * + */ + +var events = require('events'), + util = require('util'); + +// +// ### function Transport (options) +// #### @options {Object} Options for this instance. +// Constructor function for the Tranport object responsible +// base functionality for all winston transports. +// +var Transport = exports.Transport = function (options) { + events.EventEmitter.call(this); + + options = options || {}; + this.silent = options.silent || false; + this.raw = options.raw || false; + this.name = options.name || this.name; + this.formatter = options.formatter; + + // + // Do not set a default level. When `level` is falsey on any + // `Transport` instance, any `Logger` instance uses the + // configured level (instead of the Transport level) + // + this.level = options.level; + + this.handleExceptions = options.handleExceptions || false; + this.exceptionsLevel = options.exceptionsLevel || 'error'; + this.humanReadableUnhandledException = options.humanReadableUnhandledException || false; +}; + +// +// Inherit from `events.EventEmitter`. +// +util.inherits(Transport, events.EventEmitter); + +// +// ### function formatQuery (query) +// #### @query {string|Object} Query to format +// Formats the specified `query` Object (or string) to conform +// with the underlying implementation of this transport. +// +Transport.prototype.formatQuery = function (query) { + return query; +}; + +// +// ### function normalizeQuery (query) +// #### @options {string|Object} Query to normalize +// Normalize options for query +// +Transport.prototype.normalizeQuery = function (options) { + // + // Use options similar to loggly. + // [See Loggly Search API](http://wiki.loggly.com/retrieve_events#optional) + // + + options = options || {}; + + // limit + options.rows = options.rows || options.limit || 10; + + // starting row offset + options.start = options.start || 0; + + // now + options.until = options.until || new Date; + if (typeof options.until !== 'object') { + options.until = new Date(options.until); + } + + // now - 24 + options.from = options.from || (options.until - (24 * 60 * 60 * 1000)); + if (typeof options.from !== 'object') { + options.from = new Date(options.from); + } + + + // 'asc' or 'desc' + options.order = options.order || 'desc'; + + // which fields to select + options.fields = options.fields; + + return options; +}; + +// +// ### function formatResults (results, options) +// #### @results {Object|Array} Results returned from `.query`. +// #### @options {Object} **Optional** Formatting options +// Formats the specified `results` with the given `options` accordinging +// to the implementation of this transport. +// +Transport.prototype.formatResults = function (results, options) { + return results; +}; + +// +// ### function logException (msg, meta, callback) +// #### @msg {string} Message to log +// #### @meta {Object} **Optional** Additional metadata to attach +// #### @callback {function} Continuation to respond to when complete. +// Logs the specified `msg`, `meta` and responds to the callback once the log +// operation is complete to ensure that the event loop will not exit before +// all logging has completed. +// +Transport.prototype.logException = function (msg, meta, callback) { + var self = this, + called; + + if (this.silent) { + return callback(); + } + + function onComplete () { + if (!called) { + called = true; + self.removeListener('logged', onComplete); + self.removeListener('error', onComplete); + callback(); + } + } + + this.once('logged', onComplete); + this.once('error', onComplete); + this.log(self.exceptionsLevel, msg, meta, function () { }); +}; |