Merge branch 'flush-on-exit'

This commit is contained in:
Gareth Jones 2014-04-09 07:37:17 +10:00
commit e4d5228f2b
8 changed files with 186 additions and 10 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@ build
node_modules node_modules
.bob/ .bob/
test/streams/test-rolling-file-stream* test/streams/test-rolling-file-stream*
test/streams/test-rolling-stream-with-existing-files*

27
examples/flush-on-exit.js Normal file
View File

@ -0,0 +1,27 @@
/**
* run this, then "ab -c 10 -n 100 localhost:4444/" to test (in
* another shell)
*/
var log4js = require('../lib/log4js');
log4js.configure({
appenders: [
{ type: 'file', filename: 'cheese.log', category: 'cheese' },
{ type: 'console'}
]
});
var logger = log4js.getLogger('cheese');
logger.setLevel('INFO');
var http=require('http');
var server = http.createServer(function(request, response){
response.writeHead(200, {'Content-Type': 'text/plain'});
var rd = Math.random() * 50;
logger.info("hello " + rd);
response.write('hello ');
if (Math.floor(rd) == 30){
log4js.shutdown(function() { process.exit(1); });
}
response.end();
}).listen(4444);

View File

@ -1,5 +1,6 @@
"use strict"; "use strict";
var layouts = require('../layouts') var layouts = require('../layouts')
, async = require('async')
, path = require('path') , path = require('path')
, fs = require('fs') , fs = require('fs')
, streams = require('../streams') , streams = require('../streams')
@ -78,5 +79,16 @@ function configure(config, options) {
return fileAppender(config.filename, layout, config.maxLogSize, config.backups); return fileAppender(config.filename, layout, config.maxLogSize, config.backups);
} }
function shutdown(cb) {
async.forEach(openFiles, function(file, done) {
if (!file.write(eol, "utf-8")) {
file.once('drain', function() {
file.end(done);
});
}
}, cb);
}
exports.appender = fileAppender; exports.appender = fileAppender;
exports.configure = configure; exports.configure = configure;
exports.shutdown = shutdown;

View File

@ -44,17 +44,20 @@
* Website: http://log4js.berlios.de * Website: http://log4js.berlios.de
*/ */
var events = require('events') var events = require('events')
, async = require('async')
, fs = require('fs') , fs = require('fs')
, path = require('path') , path = require('path')
, util = require('util') , util = require('util')
, layouts = require('./layouts') , layouts = require('./layouts')
, levels = require('./levels') , levels = require('./levels')
, LoggingEvent = require('./logger').LoggingEvent , loggerModule = require('./logger')
, Logger = require('./logger').Logger , LoggingEvent = loggerModule.LoggingEvent
, Logger = loggerModule.Logger
, ALL_CATEGORIES = '[all]' , ALL_CATEGORIES = '[all]'
, appenders = {} , appenders = {}
, loggers = {} , loggers = {}
, appenderMakers = {} , appenderMakers = {}
, appenderShutdowns = {}
, defaultConfig = { , defaultConfig = {
appenders: [ appenders: [
{ type: "console" } { type: "console" }
@ -303,9 +306,42 @@ function loadAppender(appender) {
appenderModule = require(appender); appenderModule = require(appender);
} }
module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule); module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule);
if (appenderModule.shutdown) {
appenderShutdowns[appender] = appenderModule.shutdown.bind(appenderModule);
}
appenderMakers[appender] = appenderModule.configure.bind(appenderModule); 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) {
shutdownFn(done);
},
cb
);
}
module.exports = { module.exports = {
getLogger: getLogger, getLogger: getLogger,
getDefaultLogger: getDefaultLogger, getDefaultLogger: getDefaultLogger,
@ -315,6 +351,7 @@ module.exports = {
loadAppender: loadAppender, loadAppender: loadAppender,
clearAppenders: clearAppenders, clearAppenders: clearAppenders,
configure: configure, configure: configure,
shutdown: shutdown,
replaceConsole: replaceConsole, replaceConsole: replaceConsole,
restoreConsole: restoreConsole, restoreConsole: restoreConsole,

View File

@ -4,6 +4,8 @@ var levels = require('./levels')
, events = require('events') , events = require('events')
, DEFAULT_CATEGORY = '[default]'; , DEFAULT_CATEGORY = '[default]';
var logWritesEnabled = true;
/** /**
* Models a logging event. * Models a logging event.
* @constructor * @constructor
@ -69,7 +71,7 @@ Logger.prototype.isLevelEnabled = function(otherLevel) {
}; };
Logger.prototype[levelString.toLowerCase()] = function () { Logger.prototype[levelString.toLowerCase()] = function () {
if (this.isLevelEnabled(level)) { if (logWritesEnabled && this.isLevelEnabled(level)) {
var args = Array.prototype.slice.call(arguments); var args = Array.prototype.slice.call(arguments);
args.unshift(level); args.unshift(level);
Logger.prototype.log.apply(this, args); Logger.prototype.log.apply(this, args);
@ -78,6 +80,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.LoggingEvent = LoggingEvent;
exports.Logger = Logger; exports.Logger = Logger;
exports.disableAllLogWrites = disableAllLogWrites;
exports.enableAllLogWrites = enableAllLogWrites;

View File

@ -4,7 +4,8 @@ var vows = require('vows')
, path = require('path') , path = require('path')
, fs = require('fs') , fs = require('fs')
, sandbox = require('sandboxed-module') , sandbox = require('sandboxed-module')
, log4js = require('../lib/log4js'); , log4js = require('../lib/log4js')
, EOL = require('os').EOL || '\n';
function removeFile(filename) { function removeFile(filename) {
return function() { return function() {
@ -134,7 +135,10 @@ vows.describe('../lib/appenders/dateFile').addBatch({
teardown: removeFile('date-file-test.log'), teardown: removeFile('date-file-test.log'),
'should load appender configuration from a json file': function(err, contents) { 'should load appender configuration from a json file': function(err, contents) {
assert.include(contents, 'this should be written to the file' + require('os').EOL); if (err) {
throw err;
}
assert.include(contents, 'this should be written to the file' + EOL);
assert.equal(contents.indexOf('this should not be written to the file'), -1); assert.equal(contents.indexOf('this should not be written to the file'), -1);
} }
}, },
@ -161,7 +165,7 @@ vows.describe('../lib/appenders/dateFile').addBatch({
, thisTime = format.asString(options.appenders[0].pattern, new Date()); , thisTime = format.asString(options.appenders[0].pattern, new Date());
fs.writeFileSync( fs.writeFileSync(
path.join(__dirname, 'date-file-test' + thisTime), path.join(__dirname, 'date-file-test' + thisTime),
"this is existing data" + require('os').EOL, "this is existing data" + EOL,
'utf8' 'utf8'
); );
log4js.clearAppenders(); log4js.clearAppenders();

View File

@ -2,7 +2,8 @@
var vows = require('vows') var vows = require('vows')
, assert = require('assert') , assert = require('assert')
, levels = require('../lib/levels') , levels = require('../lib/levels')
, Logger = require('../lib/logger').Logger; , loggerModule = require('../lib/logger')
, Logger = loggerModule.Logger;
vows.describe('../lib/logger').addBatch({ vows.describe('../lib/logger').addBatch({
'constructor with no parameters': { 'constructor with no parameters': {
@ -53,5 +54,28 @@ vows.describe('../lib/logger').addBatch({
assert.isTrue(logger.isErrorEnabled()); assert.isTrue(logger.isErrorEnabled());
assert.isTrue(logger.isFatalEnabled()); 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); }).exportTo(module);

View File

@ -79,9 +79,61 @@ vows.describe('log4js').addBatch({
assert.instanceOf(events[2].data[1], Error); assert.instanceOf(events[2].data[1], Error);
assert.equal(events[2].data[1].message, 'Pants are on fire!'); 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': { 'invalid configuration': {