diff --git a/.gitignore b/.gitignore index 279bc30..268c88d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build node_modules .bob/ test/streams/test-rolling-file-stream* +test/streams/test-rolling-stream-with-existing-files* diff --git a/lib/log4js.js b/lib/log4js.js index f2677ee..be5e2c4 100644 --- a/lib/log4js.js +++ b/lib/log4js.js @@ -50,8 +50,9 @@ var events = require('events') , util = require('util') , layouts = require('./layouts') , levels = require('./levels') -, LoggingEvent = require('./logger').LoggingEvent -, Logger = require('./logger').Logger +, loggerModule = require('./logger') +, LoggingEvent = loggerModule.LoggingEvent +, Logger = loggerModule.Logger , ALL_CATEGORIES = '[all]' , appenders = {} , loggers = {} @@ -306,11 +307,27 @@ function loadAppender(appender) { appenderMakers[appender] = appenderModule.configure.bind(appenderModule); } +/** + * Shutdown all log appenders. This will first disable all writing to appenders + * and then call the shutdown function each appender. + * + * @params {Function} cb - The callback to be invoked once all appenders have + * shutdown. If an error occurs, the callback will be given the error object + * as the first argument. + * @returns {void} + */ function shutdown(cb) { + // First, disable all writing to appenders. This prevents appenders from + // not being able to be drained because of run-away log writes. + loggerModule.disableAllLogWrites(); + + // Next, get all the shutdown functions for appenders as an array. var shutdownFunctions = Object.keys(appenderShutdowns).reduce( function(accum, category) { return accum.concat(appenderShutdowns[category]); }, []); + + // Call each of the shutdown functions. async.forEach( shutdownFunctions, function(shutdownFn, done) { diff --git a/lib/logger.js b/lib/logger.js index 4da0daf..de2b41f 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -4,6 +4,8 @@ var levels = require('./levels') , events = require('events') , DEFAULT_CATEGORY = '[default]'; +var logWritesEnabled = true; + /** * Models a logging event. * @constructor @@ -66,7 +68,7 @@ Logger.prototype.isLevelEnabled = function(otherLevel) { }; Logger.prototype[levelString.toLowerCase()] = function () { - if (this.isLevelEnabled(level)) { + if (logWritesEnabled && this.isLevelEnabled(level)) { var args = Array.prototype.slice.call(arguments); args.unshift(level); Logger.prototype.log.apply(this, args); @@ -75,6 +77,23 @@ Logger.prototype.isLevelEnabled = function(otherLevel) { } ); +/** + * Disable all log writes. + * @returns {void} + */ +function disableAllLogWrites() { + logWritesEnabled = false; +} + +/** + * Enable log writes. + * @returns {void} + */ +function enableAllLogWrites() { + logWritesEnabled = true; +} exports.LoggingEvent = LoggingEvent; exports.Logger = Logger; +exports.disableAllLogWrites = disableAllLogWrites; +exports.enableAllLogWrites = enableAllLogWrites; diff --git a/test/logger-test.js b/test/logger-test.js index 55899f2..0bd29e1 100644 --- a/test/logger-test.js +++ b/test/logger-test.js @@ -2,7 +2,8 @@ var vows = require('vows') , assert = require('assert') , levels = require('../lib/levels') -, Logger = require('../lib/logger').Logger; +, loggerModule = require('../lib/logger') +, Logger = loggerModule.Logger; vows.describe('../lib/logger').addBatch({ 'constructor with no parameters': { @@ -53,5 +54,28 @@ vows.describe('../lib/logger').addBatch({ assert.isTrue(logger.isErrorEnabled()); assert.isTrue(logger.isFatalEnabled()); } + }, + + 'should emit log events': { + topic: function() { + var events = [], + logger = new Logger(); + logger.addListener('log', function (logEvent) { events.push(logEvent); }); + logger.debug('Event 1'); + loggerModule.disableAllLogWrites(); + logger.debug('Event 2'); + loggerModule.enableAllLogWrites(); + logger.debug('Event 3'); + return events; + }, + + 'when log writes are enabled': function(events) { + assert.equal(events[0].data[0], 'Event 1'); + }, + + 'but not when log writes are disabled': function(events) { + assert.equal(events.length, 2); + assert.equal(events[1].data[0], 'Event 3'); + } } }).exportTo(module); diff --git a/test/logging-test.js b/test/logging-test.js index 32ff099..a62de31 100644 --- a/test/logging-test.js +++ b/test/logging-test.js @@ -75,13 +75,65 @@ vows.describe('log4js').addBatch({ assert.equal(events[1].level.toString(), 'WARN'); }, - 'should include the error if passed in': function (events) { + 'should include the error if passed in': function(events) { assert.instanceOf(events[2].data[1], Error); assert.equal(events[2].data[1].message, 'Pants are on fire!'); } - + } + }, + + 'when shutdown is called': { + topic: function() { + var events = { + appenderShutdownCalled: false, + shutdownCallbackCalled: false + }, + log4js = sandbox.require( + '../lib/log4js', + { + requires: { + './appenders/file': + { + name: "file", + appender: function() {}, + configure: function(configuration) { + return function() {}; + }, + shutdown: function(cb) { + events.appenderShutdownCalled = true; + cb(); + } + } + } + } + ), + shutdownCallback = function() { + events.shutdownCallbackCalled = true; + }, + config = { appenders: + [ { "type" : "file", + "filename" : "cheesy-wotsits.log", + "maxLogSize" : 1024, + "backups" : 3 + } + ] + }; + + log4js.configure(config); + log4js.shutdown(shutdownCallback); + // Re-enable log writing so other tests that use logger are not + // affected. + require('../lib/logger').enableAllLogWrites(); + return events; }, - + + 'should invoke appender shutdowns': function(events) { + assert.ok(events.appenderShutdownCalled); + }, + + 'should call callback': function(events) { + assert.ok(events.shutdownCallbackCalled); + } }, 'invalid configuration': {