Skip to content

Commit

Permalink
[api test] Finalized work on .handleExceptions()
Browse files Browse the repository at this point in the history
[dist] Update package.json with `async`
[dist] Move all log files generated by tests into test/fixtures/logs
[refactor] Update internal APIs for log message serialization
[breaking] File transport now logs to JSON by default
  • Loading branch information
indexzero committed Aug 22, 2011
1 parent 415b41a commit 54eb31f
Show file tree
Hide file tree
Showing 16 changed files with 292 additions and 91 deletions.
2 changes: 1 addition & 1 deletion .gitignore
@@ -1,6 +1,6 @@
test/*.log
test/fixtures/*.json
test/fixtures/*.log
test/fixtures/logs
node_modules/
node_modules/*
npm-debug.log
87 changes: 59 additions & 28 deletions lib/winston/common.js
Expand Up @@ -8,7 +8,7 @@

var util = require('util'),
crypto = require('crypto'),
qs = require('querystring'),
loggly = require('loggly'),
config = require('./config');

//
Expand All @@ -20,7 +20,7 @@ var util = require('util'),
// in current.levels. If past is defined, remove functions
// for each of those levels.
//
var setLevels = exports.setLevels = function (target, past, current, isDefault) {
exports.setLevels = function (target, past, current, isDefault) {
if (past) {
Object.keys(past).forEach(function (level) {
delete target[level];
Expand All @@ -29,7 +29,7 @@ var setLevels = exports.setLevels = function (target, past, current, isDefault)

target.levels = current || config.npm.levels;
if (target.padLevels) {
target.levelLength = longestElement(Object.keys(target.levels));
target.levelLength = exports.longestElement(Object.keys(target.levels));
}

//
Expand All @@ -54,7 +54,7 @@ var setLevels = exports.setLevels = function (target, past, current, isDefault)
// #### @xs {Array} Array to calculate against
// Returns the longest element in the `xs` array.
//
var longestElement = exports.longestElement = function (xs) {
exports.longestElement = function (xs) {
return Math.max.apply(
null,
xs.map(function (x) { return x.length })
Expand All @@ -67,41 +67,70 @@ var longestElement = exports.longestElement = function (xs) {
// Helper method for deep cloning pure JSON objects
// i.e. JSON objects that are either literals or objects (no Arrays, etc)
//
var clone = exports.clone = function (obj) {
var clone = {};
exports.clone = function (obj) {
var copy = {};
for (var i in obj) {
clone[i] = obj[i] instanceof Object ? exports.clone(obj[i]) : obj[i];
if (Array.isArray(obj[i])) {
copy[i] = obj[i].slice(0);
}
else {
copy[i] = obj[i] instanceof Object ? exports.clone(obj[i]) : obj[i];
}
}

return clone;
return copy;
};

//
// function log (level, msg, meta)
// Generic logging function for returning timestamped strings
//
var log = exports.log = function (level, msg, meta, options) {
var prefixfn = typeof options.timestamp === 'function' ? options.timestamp : timestamp,
output = options.timestamp ? prefixfn() + ' - ' : '',
targetLevel = options.colorize ? config.colorize(level) : level,
metac = exports.clone(meta);

output += targetLevel + ': ' + msg;

// ### 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)
// timestamp: true // Adds a timestamp to the serialized message
// }
//
exports.log = function (options) {
var timestampFn = typeof options.timestamp === 'function' ? options.timestamp : exports.timestamp,
timestamp = options.timestamp ? timestampFn() : null,
meta = options.meta ? exports.clone(options.meta) : null,
output;

if (options.json) {
output = meta || {};
output.level = options.level;
output.message = options.message;

if (timestamp) {
output.timestamp = timestamp;
}

return JSON.stringify(output);
}

output = timestamp ? timestamp + ' - ' : '';
output += options.colorize ? config.colorize(options.level) : options.level;
output += (': ' + options.message);

if (meta && typeof meta === 'object' && Object.keys(meta).length > 0) {
output += ' ' + qs.unescape(qs.stringify(meta, ','));
output += ' ' + loggly.serialize(meta);
}

return output;
}
};

//
// ### function hash (str)
// #### @str {string} String to hash.
// Utility function for creating unique ids
// e.g. Profiling incoming HTTP requests on the same tick
//
var hash = exports.hash = function (str) {
exports.hash = function (str) {
return crypto.createHash('sha1').update(str).digest('hex');
};

Expand All @@ -118,19 +147,21 @@ var months = ['Jan', 'Feb', 'Mar', 'Apr',
// ### function pad (n)
// Returns a padded string if `n < 10`.
//
var pad = exports.pad = function (n) {
exports.pad = function (n) {
return n < 10 ? '0' + n.toString(10) : n.toString(10);
};

//
// ### function timestamp ()
// Returns a timestamp string for the current time.
//
var timestamp = exports.timestamp = function () {
exports.timestamp = function () {
var d = new Date();
var time = [pad(d.getHours()),
pad(d.getMinutes()),
pad(d.getSeconds())].join(':');
var time = [
exports.pad(d.getHours()),
exports.pad(d.getMinutes()),
exports.pad(d.getSeconds())
].join(':');

return [d.getDate(), months[d.getMonth()], time].join(' ');
};
68 changes: 57 additions & 11 deletions lib/winston/logger.js
Expand Up @@ -8,6 +8,7 @@

var events = require('events'),
util = require('util'),
async = require('async'),
config = require('./config'),
common = require('./common'),
exception = require('./exception');
Expand Down Expand Up @@ -42,19 +43,23 @@ var Logger = exports.Logger = function (options) {
//
// Setup other intelligent default settings.
//
this.level = options.level || 'info';
this.emitErrs = options.emitErrs || false;
this.stripColors = options.stripColors || false;
this.transports = {};
this.profilers = {};
this._names = [];
this.level = options.level || 'info';
this.emitErrs = options.emitErrs || false;
this.stripColors = options.stripColors || false;
this.transports = {};
this.profilers = {};
this._names = [];

if (options.transports) {
options.transports.forEach(function (transport) {
self._names.push(transport.name);
self.transports[transport.name] = transport;
});
}
}

if (options.handleExceptions) {
this.handleExceptions();
}
};

//
Expand Down Expand Up @@ -144,7 +149,7 @@ Logger.prototype.log = function (level, msg) {
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('log', transport, level, msg, meta);
self.emit('logging', transport, level, msg, meta);
});
}
}
Expand All @@ -160,11 +165,52 @@ Logger.prototype.log = function (level, msg) {
};

Logger.prototype.handleExceptions = function () {
process.on('uncaughtException', function (err) {

});
if (!this.catchExceptions) {
this.catchExceptions = true;
process.on('uncaughtException', this._uncaughtException.bind(this));
}
};

Logger.prototype.unhandleExceptions = function () {
if (this.catchExceptions) {
this.catchExceptions = false;
process.removeListener('uncaughtException', this._uncaughtException);
}
};

Logger.prototype._uncaughtException = function (err) {
var self = this,
responded = false,
info = exception.getAllInfo(err),
timeout;

function logAndWait (name, next) {
var transport = self.transports['file'];
transport.logException('uncaughtException', info, next);
}

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

//
// Log to all transports and allow the operation to take
// only up to `3000ms`.
//
async.forEach(this._names, logAndWait, gracefulExit);
timeout = setTimeout(gracefulExit, 3000);
};


//
// ### function add (transport, [options])
// #### @transport {Transport} Prototype of the Transport object to add.
Expand Down
18 changes: 13 additions & 5 deletions lib/winston/transports/console.js
Expand Up @@ -9,7 +9,7 @@
var events = require('events'),
util = require('util'),
colors = require('colors'),
log = require('../common').log,
common = require('../common'),
Transport = require('./transport').Transport;

//
Expand All @@ -23,6 +23,7 @@ var Console = exports.Console = function (options) {
options = options || {};

this.name = 'console';
this.json = options.json || false;
this.colorize = options.colorize || false;
this.timestamp = typeof options.timestamp !== 'undefined' ? options.timestamp : false;
};
Expand Down Expand Up @@ -50,8 +51,11 @@ Console.prototype.log = function (level, msg, meta, callback) {
return callback(null, true);
}

var self = this, output = log(level, msg, meta, {
colorize: this.colorize,
var self = this, output = common.log({
level: level,
message: msg,
meta: meta,
colorize: this.colorize,
timestamp: this.timestamp
});

Expand All @@ -61,7 +65,11 @@ Console.prototype.log = function (level, msg, meta, callback) {
else {
util.puts(output);
}

process.stdout.once('drain', function () { self.emit('logged') });

//
// Emit the `logged` event immediately because the event loop
// will not exit until `process.stdout` has drained anyway.
//
self.emit('logged');
callback(null, true);
};
18 changes: 12 additions & 6 deletions lib/winston/transports/file.js
Expand Up @@ -11,7 +11,7 @@ var events = require('events'),
path = require('path'),
util = require('util'),
colors = require('colors'),
log = require('../common').log,
common = require('../common'),
Transport = require('./transport').Transport;

//
Expand Down Expand Up @@ -49,8 +49,9 @@ var File = exports.File = function (options) {
throw new Error('Cannot log to file without filename or stream.');
}

this.json = options.json !== false;
this.colorize = options.colorize || false;
this.maxsize = options.maxsize || null;
this.maxsize = options.maxsize || null;
this.timestamp = typeof options.timestamp !== 'undefined' ? options.timestamp : false;

//
Expand Down Expand Up @@ -86,8 +87,12 @@ File.prototype.log = function (level, msg, meta, callback) {
return callback(null, true);
}

var self = this, output = log(level, msg, meta, {
colorize: this.colorize,
var self = this, output = common.log({
level: level,
message: msg,
meta: meta,
json: this.json,
colorize: this.colorize,
timestamp: this.timestamp
}) + '\n';

Expand Down Expand Up @@ -144,8 +149,8 @@ File.prototype.open = function (callback) {
// the next stream and respond with a value indicating that
// the message should be buffered.
//
this._createStream();
return callback(true);
callback(true);
return this._createStream();
}

//
Expand Down Expand Up @@ -185,6 +190,7 @@ File.prototype.flush = function () {
//
self.stream.once('drain', function () {
self.emit('flush');
self.emit('logged');
});
};

Expand Down

0 comments on commit 54eb31f

Please sign in to comment.