diff --git a/lib/appenders/console.js b/lib/appenders/console.js index a5a14c7..aacdd73 100644 --- a/lib/appenders/console.js +++ b/lib/appenders/console.js @@ -16,6 +16,5 @@ function configure(config) { return consoleAppender(layout); } -exports.name = "console"; exports.appender = consoleAppender; exports.configure = configure; diff --git a/lib/appenders/file.js b/lib/appenders/file.js index 0a0267f..b586896 100644 --- a/lib/appenders/file.js +++ b/lib/appenders/file.js @@ -63,6 +63,5 @@ function configure(config, options) { return fileAppender(config.filename, layout, config.maxLogSize, config.backups); } -exports.name = "file"; exports.appender = fileAppender; exports.configure = configure; diff --git a/lib/appenders/gelf.js b/lib/appenders/gelf.js index 38a09b6..74089be 100644 --- a/lib/appenders/gelf.js +++ b/lib/appenders/gelf.js @@ -89,6 +89,5 @@ function configure(config) { return gelfAppender(layout, config.host, config.port, config.hostname, config.facility); } -exports.name = "gelf"; exports.appender = gelfAppender; exports.configure = configure; diff --git a/lib/appenders/hookio.js b/lib/appenders/hookio.js index 8574030..4f2dfe2 100644 --- a/lib/appenders/hookio.js +++ b/lib/appenders/hookio.js @@ -71,6 +71,5 @@ function configure(config) { return createAppender(config); } -exports.name = 'hookio'; exports.appender = createAppender; exports.configure = configure; diff --git a/lib/appenders/logLevelFilter.js b/lib/appenders/logLevelFilter.js index 2907aaa..8904094 100644 --- a/lib/appenders/logLevelFilter.js +++ b/lib/appenders/logLevelFilter.js @@ -16,6 +16,5 @@ function configure(config) { return logLevelFilter(config.level, appender); } -exports.name = "logLevelFilter"; exports.appender = logLevelFilter; exports.configure = configure; diff --git a/lib/appenders/multiprocess.js b/lib/appenders/multiprocess.js index 98d6163..668bc87 100644 --- a/lib/appenders/multiprocess.js +++ b/lib/appenders/multiprocess.js @@ -72,6 +72,5 @@ function configure(config) { return createAppender(config); } -exports.name = 'multiprocess'; exports.appender = createAppender; exports.configure = configure; diff --git a/lib/appenders/smtp.js b/lib/appenders/smtp.js index 82c9db0..b20fabc 100644 --- a/lib/appenders/smtp.js +++ b/lib/appenders/smtp.js @@ -2,15 +2,15 @@ var layouts = require("../layouts"), mailer = require("nodemailer"); /** -* SMTP Appender. Sends logging events using SMTP protocol. +* SMTP Appender. Sends logging events using SMTP protocol. * It can either send an email on each event or group several logging events gathered during specified interval. * * @param recipients comma separated list of email recipients * @param sender sender of all emails (defaults to SMTP user) -* @param subject subject of all email messages (defaults to first event's message) +* @param subject subject of all email messages (defaults to first event's message) * @param layout a function that takes a logevent and returns a string (defaults to basicLayout). * @param smtpConfig SMTP configuration for 'nodemailer' -* @param sendInterval the time in seconds between sending attempts (defaults to 0); +* @param sendInterval the time in seconds between sending attempts (defaults to 0); * all events are buffered and sent in one email during this time; if 0 than every event sends an email */ function smtpAppender(recipients, sender, subject, layout, smtpConfig, sendInterval) { @@ -19,20 +19,20 @@ function smtpAppender(recipients, sender, subject, layout, smtpConfig, sendInter subjectLayout = layouts.messagePassThroughLayout; mailer.SMTP = smtpConfig; sendInterval = sendInterval*1000 || 0; - + var logEventBuffer = []; var sendTimer; - + function sendBuffer() { if (logEventBuffer.length == 0) return; - + var firstEvent = logEventBuffer[0]; var body = ""; while (logEventBuffer.length > 0) { body += layout(logEventBuffer.shift()) + "\n"; } - + var msg = { sender: sender, to: recipients, @@ -45,15 +45,15 @@ function smtpAppender(recipients, sender, subject, layout, smtpConfig, sendInter } }); } - + function scheduleSend() { if (!sendTimer) sendTimer = setTimeout(function() { - sendTimer = null; + sendTimer = null; sendBuffer(); }, sendInterval); } - + return function(loggingEvent) { logEventBuffer.push(loggingEvent); if (sendInterval > 0) @@ -71,6 +71,5 @@ function configure(config) { return smtpAppender(config.recipients, config.sender, config.subject, layout, config.smtp, config.sendInterval); } -exports.name = "smtp"; exports.appender = smtpAppender; exports.configure = configure; diff --git a/lib/levels.js b/lib/levels.js index 0bea6b0..91199ba 100644 --- a/lib/levels.js +++ b/lib/levels.js @@ -47,6 +47,13 @@ Level.prototype.isGreaterThanOrEqualTo = function(otherLevel) { return this.level >= otherLevel.level; }; +Level.prototype.isEqualTo = function(otherLevel) { + if (typeof otherLevel == "string") { + otherLevel = toLevel(otherLevel); + } + return this.level === otherLevel.level; +} + module.exports = { ALL: new Level(Number.MIN_VALUE, "ALL") , TRACE: new Level(5000, "TRACE") diff --git a/lib/log4js.js b/lib/log4js.js index 4014105..ed23506 100644 --- a/lib/log4js.js +++ b/lib/log4js.js @@ -282,9 +282,14 @@ function restoreConsole() { } function loadAppender(appender) { - var appenderModule = require('./appenders/' + appender); - module.exports.appenders[appenderModule.name] = appenderModule.appender; - appenderMakers[appenderModule.name] = appenderModule.configure; + var appenderModule; + try { + appenderModule = require('./appenders/' + appender); + } catch (e) { + appenderModule = require(appender); + } + module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule); + appenderMakers[appender] = appenderModule.configure.bind(appenderModule); } module.exports = { diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..1492c0d --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,78 @@ +var levels = require('./levels'), + util = require('util'), + events = require('events'), + DEFAULT_CATEGORY = '[default]'; + +/** + * Models a logging event. + * @constructor + * @param {String} categoryName name of category + * @param {Log4js.Level} level level of message + * @param {Array} data objects to log + * @param {Log4js.Logger} logger the associated logger + * @author Seth Chisamore + */ +function LoggingEvent (categoryName, level, data, logger) { + this.startTime = new Date(); + this.categoryName = categoryName; + this.data = data; + this.level = level; + this.logger = logger; +} + +/** + * Logger to log messages. + * use {@see Log4js#getLogger(String)} to get an instance. + * @constructor + * @param name name of category to log to + * @author Stephan Strittmatter + */ +function Logger (name, level) { + this.category = name || DEFAULT_CATEGORY; + + if (! this.level) { + this.__proto__.level = levels.TRACE; + } +} +util.inherits(Logger, events.EventEmitter); +Logger.DEFAULT_CATEGORY = DEFAULT_CATEGORY; + +Logger.prototype.setLevel = function(level) { + this.level = levels.toLevel(level, this.level || levels.TRACE); +}; + +Logger.prototype.removeLevel = function() { + delete this.level; +}; + +Logger.prototype.log = function() { + var args = Array.prototype.slice.call(arguments) + , logLevel = args.shift() + , loggingEvent = new LoggingEvent(this.category, logLevel, args, this); + this.emit("log", loggingEvent); +}; + +Logger.prototype.isLevelEnabled = function(otherLevel) { + return this.level.isLessThanOrEqualTo(otherLevel); +}; + +['Trace','Debug','Info','Warn','Error','Fatal'].forEach( + function(levelString) { + var level = levels.toLevel(levelString); + Logger.prototype['is'+levelString+'Enabled'] = function() { + return this.isLevelEnabled(level); + }; + + Logger.prototype[levelString.toLowerCase()] = function () { + if (this.isLevelEnabled(level)) { + var args = Array.prototype.slice.call(arguments); + args.unshift(level); + Logger.prototype.log.apply(this, args); + } + }; + } +); + + +exports.LoggingEvent = LoggingEvent; +exports.Logger = Logger; diff --git a/test/configuration.js b/test/configuration.js new file mode 100644 index 0000000..3955b7c --- /dev/null +++ b/test/configuration.js @@ -0,0 +1,89 @@ +var assert = require('assert'), + vows = require('vows'), + sandbox = require('sandboxed-module'); + +function makeTestAppender() { + return { + configure: function(config, options) { + this.configureCalled = true; + this.config = config; + this.options = options; + return this.appender(); + }, + appender: function() { + var self = this; + return function(logEvt) { self.logEvt = logEvt; } + } + }; +} + +vows.describe('log4js configure').addBatch({ + 'appenders': { + 'when specified by type': { + topic: function() { + var testAppender = makeTestAppender(), + log4js = sandbox.require( + '../lib/log4js', + { + requires: { + './appenders/cheese': testAppender + } + } + ); + log4js.configure( + { + appenders: [ + { type: "cheese", flavour: "gouda" } + ] + }, + { pants: "yes" } + ); + return testAppender; + }, + 'should load appender': function(testAppender) { + assert.ok(testAppender.configureCalled); + }, + 'should pass config to appender': function(testAppender) { + assert.equal(testAppender.config.flavour, 'gouda'); + }, + 'should pass log4js options to appender': function(testAppender) { + assert.equal(testAppender.options.pants, 'yes'); + } + }, + 'when core appender loaded via loadAppender': { + topic: function() { + var testAppender = makeTestAppender(), + log4js = sandbox.require( + '../lib/log4js', + { requires: { './appenders/cheese': testAppender } } + ); + + log4js.loadAppender('cheese'); + return log4js; + }, + 'should load appender from ../lib/appenders': function(log4js) { + assert.ok(log4js.appenders.cheese); + }, + 'should add appender configure function to appenderMakers' : function(log4js) { + assert.isFunction(log4js.appenderMakers.cheese); + } + }, + 'when appender in node_modules loaded via loadAppender': { + topic: function() { + var testAppender = makeTestAppender(), + log4js = sandbox.require( + '../lib/log4js', + { requires: { 'some/other/external': testAppender } } + ); + log4js.loadAppender('some/other/external'); + return log4js; + }, + 'should load appender via require': function(log4js) { + assert.ok(log4js.appenders['some/other/external']); + }, + 'should add appender configure function to appenderMakers': function(log4js) { + assert.isFunction(log4js.appenderMakers['some/other/external']); + } + } + } +}).exportTo(module); diff --git a/test/levels.js b/test/levels.js index 88e6d23..567da0c 100644 --- a/test/levels.js +++ b/test/levels.js @@ -21,6 +21,12 @@ function assertThat(level) { }, isNotGreaterThanOrEqualTo: function(levels) { assertForEach(assert.isFalse, level.isGreaterThanOrEqualTo, levels); + }, + isEqualTo: function(levels) { + assertForEach(assert.isTrue, level.isEqualTo, levels); + }, + isNotEqualTo: function(levels) { + assertForEach(assert.isFalse, level.isEqualTo, levels); } }; } @@ -45,6 +51,10 @@ vows.describe('levels').addBatch({ }, 'should be greater than no levels': function(all) { assertThat(all).isNotGreaterThanOrEqualTo([levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); + }, + 'should only be equal to ALL': function(all) { + assertThat(all).isEqualTo([levels.toLevel("ALL")]); + assertThat(all).isNotEqualTo([levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); } }, 'TRACE': { @@ -56,6 +66,10 @@ vows.describe('levels').addBatch({ 'should be greater than ALL': function(trace) { assertThat(trace).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE]); assertThat(trace).isNotGreaterThanOrEqualTo([levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); + }, + 'should only be equal to TRACE': function(trace) { + assertThat(trace).isEqualTo([levels.toLevel("TRACE")]); + assertThat(trace).isNotEqualTo([levels.ALL, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); } }, 'DEBUG': { @@ -67,6 +81,10 @@ vows.describe('levels').addBatch({ 'should be greater than TRACE': function(debug) { assertThat(debug).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE]); assertThat(debug).isNotGreaterThanOrEqualTo([levels.INFO, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); + }, + 'should only be equal to DEBUG': function(trace) { + assertThat(trace).isEqualTo([levels.toLevel("DEBUG")]); + assertThat(trace).isNotEqualTo([levels.ALL, levels.TRACE, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); } }, 'INFO': { @@ -78,6 +96,10 @@ vows.describe('levels').addBatch({ 'should be greater than DEBUG': function(info) { assertThat(info).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE, levels.DEBUG]); assertThat(info).isNotGreaterThanOrEqualTo([levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); + }, + 'should only be equal to INFO': function(trace) { + assertThat(trace).isEqualTo([levels.toLevel("INFO")]); + assertThat(trace).isNotEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.WARN, levels.ERROR, levels.FATAL, levels.OFF]); } }, 'WARN': { @@ -89,6 +111,10 @@ vows.describe('levels').addBatch({ 'should be greater than INFO': function(warn) { assertThat(warn).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO]); assertThat(warn).isNotGreaterThanOrEqualTo([levels.ERROR, levels.FATAL, levels.OFF]); + }, + 'should only be equal to WARN': function(trace) { + assertThat(trace).isEqualTo([levels.toLevel("WARN")]); + assertThat(trace).isNotEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.ERROR, levels.FATAL, levels.OFF]); } }, 'ERROR': { @@ -100,6 +126,10 @@ vows.describe('levels').addBatch({ 'should be greater than WARN': function(error) { assertThat(error).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN]); assertThat(error).isNotGreaterThanOrEqualTo([levels.FATAL, levels.OFF]); + }, + 'should only be equal to ERROR': function(trace) { + assertThat(trace).isEqualTo([levels.toLevel("ERROR")]); + assertThat(trace).isNotEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.FATAL, levels.OFF]); } }, 'FATAL': { @@ -111,6 +141,10 @@ vows.describe('levels').addBatch({ 'should be greater than ERROR': function(fatal) { assertThat(fatal).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR]); assertThat(fatal).isNotGreaterThanOrEqualTo([levels.OFF]); + }, + 'should only be equal to FATAL': function(fatal) { + assertThat(fatal).isEqualTo([levels.toLevel("FATAL")]); + assertThat(fatal).isNotEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.OFF]); } }, 'OFF': { @@ -120,6 +154,10 @@ vows.describe('levels').addBatch({ }, 'should be greater than everything': function(off) { assertThat(off).isGreaterThanOrEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL]); + }, + 'should only be equal to OFF': function(off) { + assertThat(off).isEqualTo([levels.toLevel("OFF")]); + assertThat(off).isNotEqualTo([levels.ALL, levels.TRACE, levels.DEBUG, levels.INFO, levels.WARN, levels.ERROR, levels.FATAL]); } } }, diff --git a/test/test-connect-logger.js b/test/test-connect-logger.js index 15f3a78..9544192 100644 --- a/test/test-connect-logger.js +++ b/test/test-connect-logger.js @@ -77,7 +77,7 @@ vows.describe('log4js connect logger').addBatch({ 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); - assert.equal(messages[0].level, levels.INFO); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.include(messages[0].message, 'GET'); assert.include(messages[0].message, 'http://url'); assert.include(messages[0].message, 'my.remote.addr'); @@ -118,7 +118,7 @@ vows.describe('log4js connect logger').addBatch({ 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); - assert.equal(messages[0].level, levels.INFO); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.equal(messages[0].message, 'GET http://url'); } } diff --git a/test/test-nolog.js b/test/test-nolog.js index a2770a6..8cec1bc 100644 --- a/test/test-nolog.js +++ b/test/test-nolog.js @@ -67,7 +67,7 @@ vows.describe('log4js connect logger').addBatch({ , 'check message': function(messages){ assert.isArray(messages); assert.equal(messages.length, 1); - assert.equal(messages[0].level, levels.INFO); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.include(messages[0].message, 'GET'); assert.include(messages[0].message, 'http://url'); assert.include(messages[0].message, 'my.remote.addr'); @@ -109,7 +109,7 @@ vows.describe('log4js connect logger').addBatch({ , 'check message': function(messages){ assert.isArray(messages); assert.equal(messages.length, 1); - assert.equal(messages[0].level, levels.INFO); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.include(messages[0].message, 'GET'); assert.include(messages[0].message, 'http://url'); assert.include(messages[0].message, 'my.remote.addr'); @@ -163,7 +163,7 @@ vows.describe('log4js connect logger').addBatch({ , 'check message': function(messages){ assert.isArray(messages); assert.equal(messages.length, 1); - assert.equal(messages[0].level, levels.INFO); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.include(messages[0].message, 'GET'); assert.include(messages[0].message, 'http://url'); assert.include(messages[0].message, 'my.remote.addr'); @@ -218,7 +218,7 @@ vows.describe('log4js connect logger').addBatch({ , 'check message': function(messages){ assert.isArray(messages); assert.equal(messages.length, 1); - assert.equal(messages[0].level, levels.INFO); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); assert.include(messages[0].message, 'GET'); assert.include(messages[0].message, 'http://url'); assert.include(messages[0].message, 'my.remote.addr');