Added functionality to reload configuration file periodically.

This commit is contained in:
Daniel Bell 2011-07-22 14:43:33 +10:00
parent ec21ec63f0
commit 69e64932b1
3 changed files with 347 additions and 217 deletions

View File

@ -52,7 +52,10 @@ Output:
You can either configure the appenders and log levels manually (as above), or provide a
configuration file (`log4js.configure('path/to/file.json')`) explicitly, or just let log4js look for a file called `log4js.json` (it looks in the current directory first, then the require paths, and finally looks for the default config included in the same directory as the `log4js.js` file).
An example file can be found in `test/log4js.json`. An example config file with log rolling is in `test/with-log-rolling.json`
An example file can be found in `test/log4js.json`. An example config file with log rolling is in `test/with-log-rolling.json`.
By default, the configuration file is checked for changes every 60 seconds, and if changed, reloaded. This allows changes to logging levels
to occur without restarting the application.
You can also pass an object to the configure function, which has the same properties as the json versions.
## connect/express logger

View File

@ -173,6 +173,10 @@ function configureLevels(levels) {
getLogger(category).setLevel(levels[category]);
}
}
} else {
for (l in loggers) {
loggers[l].setLevel();
}
}
}
@ -343,21 +347,6 @@ function fileExists (filename) {
}
}
function configure (configurationFileOrObject) {
var config = configurationFileOrObject;
if (typeof(config) === "string") {
config = JSON.parse(fs.readFileSync(config, "utf8"));
}
if (config) {
try {
configureAppenders(config.appenders, fileAppender, consoleAppender);
configureLevels(config.levels);
} catch (e) {
throw new Error("Problem reading log4js config " + sys.inspect(config) + ". Error was \"" + e.message + "\" ("+e.stack+")");
}
}
}
function findConfiguration() {
//add current directory onto the list of configPaths
var paths = ['.'].concat(require.paths);
@ -377,6 +366,79 @@ function findConfiguration() {
return undefined;
}
function loadConfigurationFile(filename) {
filename = filename || findConfiguration();
if (filename) {
return JSON.parse(fs.readFileSync(filename, "utf8"));
}
return undefined;
}
function configureOnceOff(config) {
if (config) {
try {
configureAppenders(config.appenders, fileAppender, consoleAppender);
configureLevels(config.levels);
} catch (e) {
throw new Error("Problem reading log4js config " + sys.inspect(config) + ". Error was \"" + e.message + "\" ("+e.stack+")");
}
}
}
var configState = {};
function reloadConfiguration() {
var filename = configState.filename || findConfiguration(),
mtime;
if (!filename) {
// can't find anything to reload
return;
}
try {
mtime = fs.statSync(filename).mtime;
} catch (e) {
getLogger('log4js').warn('Failed to load configuration file ' + filename);
return;
}
if (configState.lastFilename && configState.lastFilename === filename) {
if (mtime.getTime() > configState.lastMTime.getTime()) {
configState.lastMTime = mtime;
} else {
filename = null;
}
} else {
configState.lastFilename = filename;
configState.lastMTime = mtime;
}
configureOnceOff(loadConfigurationFile(filename));
}
function initReloadConfiguration(filename, options) {
if (configState.timerId) {
clearInterval(configState.timerId);
delete configState.timerId;
}
configState.filename = filename;
configState.timerId = setInterval(reloadConfiguration, options.reloadSecs*1000);
}
function configure (configurationFileOrObject, options) {
var config = configurationFileOrObject;
if (config === undefined || config === null || typeof(config) === 'string') {
options = options || { reloadSecs: 60 };
if (options.reloadSecs) {
initReloadConfiguration(config, options);
}
configureOnceOff(loadConfigurationFile(config));
} else {
options = options || {};
if (options.reloadSecs) {
getLogger('log4js').warn('Ignoring configuration reload parameter for "object" configuration.');
}
configureOnceOff(config);
}
}
function replaceConsole(logger) {
function replaceWith(fn) {
return function() {

View File

@ -545,6 +545,71 @@ vows.describe('log4js').addBatch({
assert.equal(logEvent.data[0], "This should go to the appender defined in firstLog4js");
}
},
'configuration reload' : {
topic: function() {
var pathsChecked = [],
messages = [],
logger,
modulePath = require('path').normalize(__dirname + '/../lib/log4js.json'),
fakeFS = {
config: { appenders: [ { type: 'console', layout: { type: 'messagePassThrough' } } ],
levels: { 'a-test' : 'INFO' } },
readFileSync: function (file, encoding) {
assert.equal(file, modulePath);
assert.equal(encoding, 'utf8');
return JSON.stringify(fakeFS.config);
},
statSync: function (path) {
pathsChecked.push(path);
if (path === modulePath) {
return { mtime: new Date() };
} else {
throw new Error("no such file");
}
}
},
fakeConsole = {
log : function (msg) { messages.push(msg); },
info: this.log,
warn: this.log,
debug: this.log,
error: this.log
},
setIntervalCallback,
fakeSetInterval = function(cb, timeout) {
setIntervalCallback = cb;
},
log4js = sandbox.require(
'../lib/log4js',
{
requires: {
'fs': fakeFS
},
globals: {
'console': fakeConsole,
'setInterval' : fakeSetInterval,
}
}
);
logger = log4js.getLogger('a-test');
logger.info("info1");
logger.debug("debug2 - should be ignored");
delete fakeFS.config.levels;
setIntervalCallback();
logger.info("info3");
logger.debug("debug4");
return [ pathsChecked, messages, modulePath ];
},
'should configure log4js from first log4js.json found': function(args) {
var messages = args[1];
assert.length(messages, 3);
assert.equal(messages[0], 'info1');
assert.equal(messages[1], 'info3');
assert.equal(messages[2], 'debug4');
}
}
}).export(module);