Merge branch 'flush-on-exit'
This commit is contained in:
commit
e4d5228f2b
1
.gitignore
vendored
1
.gitignore
vendored
@ -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
27
examples/flush-on-exit.js
Normal 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);
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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);
|
||||||
|
@ -75,13 +75,65 @@ vows.describe('log4js').addBatch({
|
|||||||
assert.equal(events[1].level.toString(), 'WARN');
|
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.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': {
|
||||||
|
Loading…
Reference in New Issue
Block a user