diff --git a/README.md b/README.md index 412d950..f1926da 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ NOTE: v0.3.8 of log4js is the last that will work with node versions older than 0.4. To use v0.3.9 you will need node 0.4 or later. -This is a conversion of the [log4js](http://log4js.berlios.de/index.html) +This is a conversion of the [log4js](http://log4js.berlios.de/index.html) framework to work with [node](http://nodejs.org). I've mainly stripped out the browser-specific code -and tidied up some of the javascript. It includes a basic file logger, with log rolling based on file size, and also replaces node's console.log functions. +and tidied up some of the javascript. It includes a basic file logger, with log rolling based on file size, and also replaces node's console.log functions. NOTE: in v0.2.x require('log4js') returned a function, and you needed to call that function in your code before you could use it. This was to make testing easier. v0.3.x make use of [felixge's sandbox-module](https://github.com/felixge/node-sandboxed-module), so we don't need to return a function. @@ -14,7 +14,7 @@ npm install log4js ## tests -Tests now use [vows](http://vowsjs.org), run with `vows test/*.js`. +Tests now use [vows](http://vowsjs.org), run with `vows test/*.js`. ## usage @@ -33,27 +33,27 @@ See example.js: var log4js = require('log4js'); //note the need to call the function log4js.addAppender(log4js.consoleAppender()); log4js.addAppender(log4js.fileAppender('logs/cheese.log'), 'cheese'); - + var logger = log4js.getLogger('cheese'); logger.setLevel('ERROR'); - + logger.trace('Entering cheese testing'); logger.debug('Got cheese.'); - logger.info('Cheese is Gouda.'); + logger.info('Cheese is Gouda.'); logger.warn('Cheese is quite smelly.'); logger.error('Cheese is too ripe!'); logger.fatal('Cheese was breeding ground for listeria.'); - + Output: [2010-01-17 11:43:37.987] [ERROR] cheese - Cheese is too ripe! [2010-01-17 11:43:37.990] [FATAL] cheese - Cheese was breeding ground for listeria. - + ## configuration -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). +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`. 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. @@ -75,16 +75,16 @@ You can also pass an object to the configure function, which has the same proper ## connect/express logger -A connect/express logger has been added to log4js, by [danbell](https://github.com/danbell). This allows connect/express servers to log using log4js. See example-connect-logger.js. +A connect/express logger has been added to log4js, by [danbell](https://github.com/danbell). This allows connect/express servers to log using log4js. See example-connect-logger.js. var log4js = require('./lib/log4js'); log4js.addAppender(log4js.consoleAppender()); log4js.addAppender(log4js.fileAppender('cheese.log'), 'cheese'); - + var logger = log4js.getLogger('cheese'); - + logger.setLevel('INFO'); - + var app = require('express').createServer(); app.configure(function() { app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO })); @@ -100,6 +100,65 @@ The options object that is passed to log4js.connectLogger supports a format stri app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO, format: ':method :url' })); }); +## hook.io logger + +A [hook.io](https://github.com/hookio) logger has been added to log4js by [dbrain](https://github.com/dbrain). This allows multiple worker processes to log through a single master process, avoiding issues with rolling etc. in a clustered environment. +This was mainly created for [cluster](https://github.com/LearnBoost/cluster), but you can adapt the example to match your needs, you know, if it fits them. +
+ #### log4js-master.json ####
+ {
+ "appenders": [{
+ "type": "logLevelFilter",
+ "level": "DEBUG",
+ "appender": {
+ "type": "hookio",
+ "name": "hookio-logger",
+ "mode": "master",
+ "debug": false,
+ "appender": {
+ "type": "file",
+ "filename": "muffin.log",
+ "maxLogSize": 104857600,
+ "backups": 10,
+ "pollInterval": 15
+ }
+ }
+ }]
+ }
+
+ #### log4js-worker.json ####
+ {
+ "appenders": [{
+ "type": "logLevelFilter",
+ "level": "DEBUG",
+ "appender": {
+ "type": "hookio",
+ "name": "hookio-logger",
+ "mode": "worker",
+ "debug": false
+ }
+ }]
+ }
+
+ #### ilikebeans.js ####
+ var cluster = require('cluster');
+ var hookCluster = cluster('./doyoulikebeans');
+
+ // Perform the once off configuration depending on type of cluster
+ if (hookCluster.isMaster) {
+ require('log4js').configure('log4js-master.json');
+ } else {
+ require('log4js').configure('log4js-worker.json');
+ }
+
+ // Standard cluster startup
+ hookCluster
+ .use(cluster.logger('run/logs'))
+ .use(cluster.pidfiles('run/pids'))
+ .listen(3000);
+
+*NOTE* hook.io appender will currently (and probably indefinitely) explode if you enable hook.io debug because of the way log4js overrides console.log
+
## author (of this node version)
Gareth Jones (csausdev - gareth.jones@sensis.com.au)
@@ -107,5 +166,5 @@ Gareth Jones (csausdev - gareth.jones@sensis.com.au)
## License
The original log4js was distributed under the Apache 2.0 License, and so is this. I've tried to
-keep the original copyright and author credits in place, except in sections that I have rewritten
+keep the original copyright and author credits in place, except in sections that I have rewritten
extensively.
diff --git a/lib/appenders/hookio.js b/lib/appenders/hookio.js
new file mode 100644
index 0000000..3ce3301
--- /dev/null
+++ b/lib/appenders/hookio.js
@@ -0,0 +1,56 @@
+var log4js = require('../log4js');
+var layouts = require('../layouts');
+var Hook = require('hook.io').Hook;
+var util = require('util');
+
+var Logger = function createLogger(options) {
+ var self = this;
+ var actualAppender = options.actualAppender;
+ Hook.call(self, options);
+ self.on('hook::ready', function hookReady() {
+ self.on('*::' + options.name + '::log', function log(loggingEvent) {
+ deserializeLoggingEvent(loggingEvent);
+ actualAppender(loggingEvent);
+ });
+ });
+}
+util.inherits(Logger, Hook);
+
+function deserializeLoggingEvent(loggingEvent) {
+ loggingEvent.startTime = new Date(loggingEvent.startTime);
+ loggingEvent.level.toString = function levelToString() {
+ return loggingEvent.level.levelStr;
+ };
+}
+
+function createAppender(hookioOptions, actualAppender) {
+ var loggerHook;
+ if (hookioOptions.mode === 'master') {
+ // Start the master hook, handling the actual logging
+ loggerHook = new Logger({ name: hookioOptions.name, debug: hookioOptions.debug, actualAppender: actualAppender });
+ } else {
+ // Start a worker, just emitting events for a master
+ loggerHook = new Hook({ name: hookioOptions.name, debug: hookioOptions.debug });
+ }
+ loggerHook.start();
+
+ var loggerEvent = hookioOptions.name + '::log';
+ return function log(loggingEvent) {
+ loggerHook.emit(loggerEvent, loggingEvent);
+ };
+}
+
+function configure(config) {
+ var actualAppender;
+ if (config.appender && config.mode === 'master') {
+ log4js.loadAppender(config.appender.type);
+ actualAppender = log4js.appenderMakers[config.appender.type](config.appender);
+ }
+ delete config.appender;
+ delete config.type;
+ return createAppender(config, actualAppender);
+}
+
+exports.name = 'hookio';
+exports.appender = createAppender;
+exports.configure = configure;
diff --git a/lib/appenders/logLevelFilter.js b/lib/appenders/logLevelFilter.js
index 01375a4..2907aaa 100644
--- a/lib/appenders/logLevelFilter.js
+++ b/lib/appenders/logLevelFilter.js
@@ -1,4 +1,5 @@
var levels = require('../levels');
+var log4js = require('../log4js');
function logLevelFilter (levelString, appender) {
var level = levels.toLevel(levelString);
@@ -10,10 +11,11 @@ function logLevelFilter (levelString, appender) {
}
function configure(config) {
- var appender = config.makers[config.appender.type](config.appender);
+ log4js.loadAppender(config.appender.type);
+ var appender = log4js.appenderMakers[config.appender.type](config.appender);
return logLevelFilter(config.level, appender);
}
exports.name = "logLevelFilter";
exports.appender = logLevelFilter;
-exports.configure = configure;
\ No newline at end of file
+exports.configure = configure;
diff --git a/lib/log4js.js b/lib/log4js.js
index 11f4811..340f5c3 100644
--- a/lib/log4js.js
+++ b/lib/log4js.js
@@ -136,6 +136,7 @@ function configureAppenders(appenderList) {
clearAppenders();
if (appenderList) {
appenderList.forEach(function(appenderConfig) {
+ loadAppender(appenderConfig.type);
var appender;
appenderConfig.makers = appenderMakers;
appender = appenderMakers[appenderConfig.type](appenderConfig);
@@ -248,13 +249,13 @@ function getDefaultLogger () {
return getLogger(DEFAULT_CATEGORY);
}
-function findConfiguration() {
+function findConfiguration(filename) {
var path;
try {
- path = require.resolve('log4js.json');
+ path = require.resolve(filename || 'log4js.json');
} catch (e) {
//file not found. default to the one in the log4js module.
- path = __dirname + '/log4js.json';
+ path = filename || __dirname + '/log4js.json';
}
return path;
@@ -263,8 +264,8 @@ function findConfiguration() {
var configState = {};
function loadConfigurationFile(filename) {
- filename = filename || findConfiguration();
- if (filename && (!configState.lastFilename || filename !== configState.lastFilename ||
+ filename = findConfiguration(filename);
+ if (filename && (!configState.lastFilename || filename !== configState.lastFilename ||
!configState.lastMTime || fs.statSync(filename).mtime !== configState.lastMTime)) {
configState.lastFilename = filename;
configState.lastMTime = fs.statSync(filename).mtime;
@@ -285,7 +286,7 @@ function configureOnceOff(config) {
}
function reloadConfiguration() {
- var filename = configState.filename || findConfiguration(),
+ var filename = findConfiguration(configState.filename),
mtime;
if (!filename) {
// can't find anything to reload
@@ -347,15 +348,10 @@ function replaceConsole(logger) {
});
}
-function loadAppenders() {
- var appenderList = fs.readdirSync(__dirname + '/appenders');
- appenderList.forEach(function(file) {
- if (/\.js$/.test(file)) {
- var appenderModule = require('./appenders/' + file);
- module.exports.appenders[appenderModule.name] = appenderModule.appender;
- appenderMakers[appenderModule.name] = appenderModule.configure;
- }
- });
+function loadAppender(appender) {
+ var appenderModule = require('./appenders/' + appender);
+ module.exports.appenders[appenderModule.name] = appenderModule.appender;
+ appenderMakers[appenderModule.name] = appenderModule.configure;
}
module.exports = {
@@ -363,6 +359,7 @@ module.exports = {
getDefaultLogger: getDefaultLogger,
addAppender: addAppender,
+ loadAppender: loadAppender,
clearAppenders: clearAppenders,
configure: configure,
@@ -371,10 +368,15 @@ module.exports = {
layouts: layouts,
appenders: {},
+ appenderMakers: appenderMakers,
connectLogger: require('./connect-logger').connectLogger
};
-loadAppenders();
+//load the old-style appenders
+[ 'console', 'file', 'logLevelFilter' ].forEach(function(appender) {
+ loadAppender(appender);
+});
+
//set ourselves up if we can find a default log4js.json
configure(findConfiguration());
//replace console.log, etc with log4js versions
diff --git a/package.json b/package.json
index c4ce49d..fc006e4 100644
--- a/package.json
+++ b/package.json
@@ -6,23 +6,26 @@
"logging",
"log",
"log4j",
- "node"
+ "node"
],
"main": "./lib/log4js",
"author": "Gareth Jones