Compare commits

...

87 Commits

Author SHA1 Message Date
Gareth Jones
ae04cc9a4a 0.6.19 2014-08-21 21:21:16 +10:00
Gareth Jones
70a9444f4d Merge pull request #235 from j2ro/master
Pull request for #233
2014-08-21 21:20:27 +10:00
j2ro
3e78fcb630 #233: Adding ability to put pid in log layout pattern 2014-08-20 10:43:48 +01:00
j2ro
44687e1bd1 #233: Adding ability to put pid in log layout pattern 2014-08-20 10:23:31 +01:00
Gareth Jones
8e5754371a 0.6.18 2014-08-20 10:20:57 +01:00
Grégoire Charvet 黑瓜
feef9975c7 Remove test logging 2014-08-20 10:20:57 +01:00
Grégoire Charvet 黑瓜
93695fbfc4 Change spaces by tabs
Keeping constitency with the rest of the code.
2014-08-20 10:20:57 +01:00
Grégoire Charvet 黑瓜
0571089a8b Correctly send message, level and hostname
Fix issue #230 where the level would hold the message and the log level
information would be lost.
2014-08-20 10:20:57 +01:00
Gareth Jones
ab77895555 0.6.18 2014-08-20 09:12:13 +10:00
Gareth Jones
9637be8a41 Merge pull request #232 from GregoireDigbil/fix_loggly_formatting
Fix loggly formatting
2014-08-20 09:11:12 +10:00
Grégoire Charvet 黑瓜
0ecd729f49 Remove test logging 2014-08-18 07:32:23 +08:00
Grégoire Charvet 黑瓜
f9c2e78055 Change spaces by tabs
Keeping constitency with the rest of the code.
2014-08-18 07:31:33 +08:00
Grégoire Charvet 黑瓜
e7267ecf46 Correctly send message, level and hostname
Fix issue #230 where the level would hold the message and the log level
information would be lost.
2014-08-18 07:25:55 +08:00
Gareth Jones
c03185b563 Merge pull request #231 from eurekaa/buffered-logger
added getBufferedLogger function.
2014-08-17 11:00:10 +10:00
Stefano Graziato
c0aa8c5c86 gitignore 2014-08-16 14:52:00 +02:00
Stefano Graziato
59a6703549 getBufferedLogger tested. 2014-08-16 14:22:58 +02:00
Gareth Jones
ceffdf92e4 Removed hook.io appender, because hook.io does not build on systems without python 2014-08-16 12:23:50 +10:00
Stefano Graziato
c9e72d0f00 added getBufferedLogger function.
This function should be useful when you need to log during async
parallel operations, without having a mess in logs.
For example when you walk asynchronously a directory and you want logs
to be grouped by file.

It returns the same getLogger() object but messages are stored
internally and sent to appenders only when you call the flush() method
on it.
2014-08-15 13:42:08 +02:00
Gareth Jones
a27345461b altering the timings on some tests to make them slightly less flaky (maybe) 2014-08-15 20:06:35 +10:00
Gareth Jones
fb9948145c 0.6.17 2014-08-15 18:32:32 +10:00
Gareth Jones
0242bae78f Merge pull request #214 from GregoireDigbil/master
Format message before adding loggly metadata
2014-08-15 18:29:19 +10:00
Gareth Jones
b71f635267 Merge pull request #224 from idalv/express_fix
Update connect-logger.js to work correctly with express
2014-08-02 03:30:05 +10:00
mishless
66872d136d Update connect-logger.js to work correctly with express
When used with express levels are wrong since send() does not call writeHead, but sets responseCode on response.
2014-07-30 13:06:51 +03:00
Gareth Jones
49849a545a Merge pull request #219 from PSyton/fix_for_no_dir
Make some test windows friendly.
2014-07-18 08:43:27 +10:00
Pavel Sysolyatin
3c4c98bb0b Make some test windows friendly. 2014-07-17 14:29:03 +07:00
Gareth Jones
eaa77b0454 0.6.16 2014-07-12 17:46:03 +10:00
Gareth Jones
78c604b90d Merge branch 'mayconbordin-master' 2014-07-12 17:33:31 +10:00
Gareth Jones
4648e7a5e6 Merge branch 'master' of https://github.com/mayconbordin/log4js-node into mayconbordin-master 2014-07-12 17:18:07 +10:00
Gareth Jones
8f3e040786 Merge branch 'RolfKoenders-set-global-log-level' 2014-07-12 17:09:30 +10:00
Gareth Jones
60d2e093ae Merge branch 'set-global-log-level' of https://github.com/RolfKoenders/log4js-node into RolfKoenders-set-global-log-level 2014-07-12 17:08:39 +10:00
Gareth Jones
a06478632e Merge 2ion-gelf-spec-v1.1-compliance into master 2014-07-12 15:12:40 +10:00
Gareth Jones
a4d55b3339 fixed up test after merge 2014-07-12 15:11:21 +10:00
Gareth Jones
914ed5356b Merge pull request #216 from 2ion/gelf-no-full-message
GELF appender: don't send full_message field
2014-07-12 15:02:17 +10:00
Jens John
52b4aa1874 fix uninitialized field error 2014-07-11 19:19:19 +09:00
Jens John
2b0a58c2f2 reflect the above changes ; forgot a facility field 2014-07-11 13:02:06 +09:00
Jens John
fcafed9a7e fix gelfAppender-test.js to reflect the above changes 2014-07-11 12:52:23 +09:00
Jens John
7ff053f104 fix test/gelfAppender-test.js to reflect full_message being gone 2014-07-11 12:38:29 +09:00
Jens John
0f51ab1bb3 GELF appender: set version to 1.1 2014-07-10 14:26:27 +09:00
Jens John
5b2d840472 GELF appender: move facility key into a custom field if present
At the same time, don't assign a default value as the field is optional
according to the GELF spec v1.1.
2014-07-10 14:07:38 +09:00
Jens John
ca635fc4b9 GELF appender: don't send full_message field
According to the GELF spec v1.1 [1], the full_message field in GELF is
optional. The log4js implemention until now has sent identitical
short_message and full_message fields. Since this does not add any new
information to the log message, I suggest that full_message be dropped
from GELF.

--
[1] http://graylog2.org/gelf#specs
2014-07-10 12:17:35 +09:00
Grégoire Charvet 黑瓜
d65d053bc1 Format message before adding loggly metadata
Also removed a throw err inside an asynchronous callback.
2014-07-04 14:03:08 +08:00
Gareth Jones
f44af56f9e Added link to example app. 2014-07-03 08:44:24 +10:00
Gareth Jones
a703f2dc12 0.6.15 2014-07-03 08:16:07 +10:00
Gareth Jones
b2edbb1146 Merge pull request #175 from devotis/master
Loggly appender should not make use of any layout
2014-06-28 08:22:20 +10:00
Gareth Jones
99e7c0981d Merge pull request #189 from jengler/add-support-for-prerequired-appender-styles
Add support for prerequired appender styles
2014-06-28 08:20:12 +10:00
Gareth Jones
06bab894af Merge pull request #203 from idalv/master
Clusterred appender should consider the categories.
2014-06-28 08:17:16 +10:00
Gareth Jones
101739ebef Merge pull request #209 from Icehunter/patch-1
Update for "write after end" uncaught error.
2014-06-28 08:02:27 +10:00
Ryan Wilson
f8ffccffd5 epic day with a forgotten console 2014-06-20 13:23:57 -07:00
Ryan Wilson
7d50b4aeff removed check for writeable
caused broken unit tests
2014-06-20 13:23:09 -07:00
Ryan Wilson
b12200fabc Update for "write after end" uncaught error. 2014-06-20 13:16:23 -07:00
Vladimir Mitev
f8b6cc7c39 Revert "Update connect-logger.js to work correctly with express"
This reverts commit d18fb466fb.
2014-05-26 15:56:23 +03:00
idalv
6314e4a344 Merge pull request #1 from idalv/automation
Update connect-logger.js to work correctly with express
2014-05-26 15:46:07 +03:00
mishless
d18fb466fb Update connect-logger.js to work correctly with express
When used with express levels are wrong since send() does not call writeHead, but sets responseCode on response.
2014-05-23 13:58:52 +03:00
Vladimir Mitev
e638ff7271 Unit test.
Fixed the old unit tests.
Added validation for new functionality.

Signed-off-by: Vladimir Mitev <idalv@users.noreply.github.com>
2014-05-20 14:42:29 +03:00
Vladimir Mitev
2daf29b400 Clusterred appender should consider the categories.
It turns out that whenever the clusterred appender is used the log event is passed to all actual appenders.
The actual appender's category is ignored.

Signed-off-by: Vladimir Mitev <idalv@users.noreply.github.com>
2014-05-20 13:45:46 +03:00
=
8cf03507dd Add unit test to check if the level is set on all categories 2014-05-04 15:20:22 +02:00
Gareth Jones
ca5272aacc 0.6.14 2014-04-22 10:06:04 +10:00
Gareth Jones
614127bb10 added shutdown to datefile 2014-04-22 10:05:37 +10:00
Gareth Jones
a549df44b4 Merge pull request #199 from lulurun/fix_file_appender
fix shutdown method: make sure to callback when write completed immediat...
2014-04-22 09:58:03 +10:00
Gareth Jones
5e0982f0b1 Merge pull request #200 from lulurun/datefile_appender_fix
file won't get rolled if the process restarted daily
2014-04-22 09:29:15 +10:00
Xiaolu Liu
f5a76d9073 file won't get rolled if the process restarted daily 2014-04-24 00:07:11 +09:00
Xiaolu Liu
29d941f0a6 fix shutdown method: make sure to callback when write completed immediately 2014-04-21 16:27:51 +09:00
Gareth Jones
0c2baa9690 0.6.13 2014-04-09 07:44:23 +10:00
Gareth Jones
9b538ee8ed fixed timezone flakiness 2014-04-09 07:43:40 +10:00
Gareth Jones
e4d5228f2b Merge branch 'flush-on-exit' 2014-04-09 07:37:17 +10:00
Gareth Jones
6aacb0da0b Merge pull request #195 from jengler/flush-on-exit
Flush on exit
2014-04-09 07:35:25 +10:00
John Engler
6e3da6f44b Added error throwing when error loading test file.
This will hopefully give us better visibility into our Travis CI
build failures.
2014-04-08 12:40:27 -07:00
John Engler
3b5eb28115 Update dateFile EOL usage to be consistent with appender.
From the looks of the Travis CI failure, this could be the issue
causing failures. Not sure as I can't reproduce locally. However,
it is still an inconsistency and worth fixing.
2014-04-08 10:47:18 -07:00
John Engler
633ed3cddb Support for disabling log writes on shutdown.
Updated logger.js to support disabling all log writes.
Updated log4js.js shutdown function to disable log writes.
Added tests.
Update gitignore to ignore rolling date stream's test output.
2014-04-07 19:06:29 -07:00
John Engler
8ca092cdb9 Removed callback to write, as it is not needed. 2014-04-05 16:14:56 -07:00
John Engler
3ec9811b5e Update log4js module to expose a shutdown function.
loadAppender will check for a shutdown function exposed by
a loaded appender. If present, it will be cached so that the
shutdown function can execute it.

The intent here is that a Node application would not invoked
process.exit until after the log4js shutdown callback returns.
2014-04-05 15:12:45 -07:00
Gareth Jones
c852fceaf4 Update README.md 2014-04-01 11:09:01 +11:00
John Engler
c569919160 Simplied loadAppender logic. 2014-03-11 02:19:00 -07:00
John Engler
28f7c87a0e Allow adding of appenders as objects
Previously, appenders could only be added by specifying the filepath
to the appender. This required the appender's path to be specified
either relative to the log4js installation, relative to a NODE_PATH
token, or absolute. This creates a coupling between the log4js
configurer and the log4js installation location, or a coupling between
the log4js configurer and the global NODE_PATH. This commit removes
the coupling by allowing the loading of appenders to be done relative
to the log4js configurer.
2014-03-11 02:07:58 -07:00
Gareth Jones
492919b940 0.6.12 2014-03-05 13:17:15 +11:00
Gareth Jones
470baa6c09 Merge pull request #187 from jci-fox/addHasLogger
adding ability to check if a logger exists
2014-03-05 13:16:51 +11:00
jci-fox
fd05d90c2f adding ability to check if a logger exists
This allows for not accidentally adding a non-configured logger
2014-03-04 09:45:56 -06:00
Rolf Koenders
6ae01b2b84 Update log4js.js
Overlooked that there was already a var with '[all]'.
2014-01-23 10:34:56 +01:00
Rolf Koenders
e7c0b0da52 Changed to only the ‚levels’ option as discussed in #87 2014-01-22 20:13:36 +01:00
Rolf Koenders
25fa48ee8d Set the global level via the configuration object passed to
log4js.configure or target `all` in the `levels` object.
2014-01-21 23:39:54 +01:00
Christiaan Westerbeek
ae1a55fed9 Stop making use of any layout
Stop making use of any layout by default, because they are intended to
format a line for human reading. Loggly indexes the values (of all
properties of objects) and makes them available for querying.
2014-01-21 12:11:32 +01:00
Maycon Bordin
7fcdb2e651 fixed a issue with the encoding on node 0.8 2013-12-12 22:26:48 -02:00
Maycon Bordin
60a84f16cf added tests for the fileSync appender and changed the behavior of fileSync to create an empty log when called, just like the file appender does 2013-12-12 18:16:53 -02:00
Maycon Bordin
723dbec964 added a synchronous file appender 2013-12-06 12:48:49 -02:00
Gareth Jones
2a38f460dc tried adding process.nexttick - didn't help 2013-08-05 07:56:02 +10:00
Gareth Jones
9f77734f74 test case for flush on exit 2013-08-05 07:55:07 +10:00
Gareth Jones
ce8b6b06b9 trying out a shutdown function 2013-08-05 07:21:12 +10:00
32 changed files with 939 additions and 401 deletions

2
.gitignore vendored
View File

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

View File

@@ -1,7 +1,7 @@
# log4js-node [![Build Status](https://secure.travis-ci.org/nomiddlename/log4js-node.png?branch=master)](http://travis-ci.org/nomiddlename/log4js-node)
This is a conversion of the [log4js](http://log4js.berlios.de/index.html)
This is a conversion of the [log4js](https://github.com/stritti/log4js)
framework to work with [node](http://nodejs.org). I've mainly stripped out the browser-specific code and tidied up some of the javascript.
Out of the box it supports the following features:
@@ -134,6 +134,8 @@ Documentation for most of the core appenders can be found on the [wiki](https://
## Documentation
See the [wiki](https://github.com/nomiddlename/log4js-node/wiki). Improve the [wiki](https://github.com/nomiddlename/log4js-node/wiki), please.
There's also [an example application](https://github.com/nomiddlename/log4js-example).
## Contributing
Contributions welcome, but take a look at the [rules](https://github.com/nomiddlename/log4js-node/wiki/Contributing) first.

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

@@ -71,7 +71,10 @@ function createAppender(config) {
if (config.actualAppenders) {
var size = config.actualAppenders.length;
for(var i = 0; i < size; i++) {
config.actualAppenders[i](loggingEvent);
if (!config.appenders[i].category || config.appenders[i].category === loggingEvent.categoryName) {
// Relying on the index is not a good practice but otherwise the change would have been bigger.
config.actualAppenders[i](loggingEvent);
}
}
}
}

View File

@@ -1,6 +1,7 @@
"use strict";
var streams = require('../streams')
, layouts = require('../layouts')
, async = require('async')
, path = require('path')
, os = require('os')
, eol = os.EOL || '\n'
@@ -24,12 +25,12 @@ function appender(filename, pattern, alwaysIncludePattern, layout) {
layout = layout || layouts.basicLayout;
var logFile = new streams.DateRollingFileStream(
filename,
pattern,
filename,
pattern,
{ alwaysIncludePattern: alwaysIncludePattern }
);
openFiles.push(logFile);
return function(logEvent) {
logFile.write(layout(logEvent) + eol, "utf8");
};
@@ -38,15 +39,15 @@ function appender(filename, pattern, alwaysIncludePattern, layout) {
function configure(config, options) {
var layout;
if (config.layout) {
layout = layouts.layout(config.layout.type, config.layout);
}
if (!config.alwaysIncludePattern) {
config.alwaysIncludePattern = false;
}
if (options && options.cwd && !config.absolute) {
config.filename = path.join(options.cwd, config.filename);
}
@@ -54,5 +55,18 @@ function configure(config, options) {
return appender(config.filename, config.pattern, config.alwaysIncludePattern, layout);
}
function shutdown(cb) {
async.forEach(openFiles, function(file, done) {
if (!file.write(eol, "utf-8")) {
file.once('drain', function() {
file.end(done);
});
} else {
file.end(done);
}
}, cb);
}
exports.appender = appender;
exports.configure = configure;
exports.shutdown = shutdown;

View File

@@ -1,5 +1,6 @@
"use strict";
var layouts = require('../layouts')
, async = require('async')
, path = require('path')
, fs = require('fs')
, streams = require('../streams')
@@ -78,5 +79,18 @@ function configure(config, options) {
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);
});
} else {
file.end(done);
}
}, cb);
}
exports.appender = fileAppender;
exports.configure = configure;
exports.shutdown = shutdown;

187
lib/appenders/fileSync.js Executable file
View File

@@ -0,0 +1,187 @@
"use strict";
var debug = require('../debug')('fileSync')
, layouts = require('../layouts')
, path = require('path')
, fs = require('fs')
, streams = require('../streams')
, os = require('os')
, eol = os.EOL || '\n'
;
function RollingFileSync (filename, size, backups, options) {
debug("In RollingFileStream");
function throwErrorIfArgumentsAreNotValid() {
if (!filename || !size || size <= 0) {
throw new Error("You must specify a filename and file size");
}
}
throwErrorIfArgumentsAreNotValid();
this.filename = filename;
this.size = size;
this.backups = backups || 1;
this.options = options || { encoding: 'utf8', mode: parseInt('0644', 8), flags: 'a' };
this.currentSize = 0;
function currentFileSize(file) {
var fileSize = 0;
try {
fileSize = fs.statSync(file).size;
} catch (e) {
// file does not exist
fs.appendFileSync(filename, '');
}
return fileSize;
}
this.currentSize = currentFileSize(this.filename);
}
RollingFileSync.prototype.shouldRoll = function() {
debug("should roll with current size %d, and max size %d", this.currentSize, this.size);
return this.currentSize >= this.size;
};
RollingFileSync.prototype.roll = function(filename) {
var that = this,
nameMatcher = new RegExp('^' + path.basename(filename));
function justTheseFiles (item) {
return nameMatcher.test(item);
}
function index(filename_) {
return parseInt(filename_.substring((path.basename(filename) + '.').length), 10) || 0;
}
function byIndex(a, b) {
if (index(a) > index(b)) {
return 1;
} else if (index(a) < index(b) ) {
return -1;
} else {
return 0;
}
}
function increaseFileIndex (fileToRename) {
var idx = index(fileToRename);
debug('Index of ' + fileToRename + ' is ' + idx);
if (idx < that.backups) {
//on windows, you can get a EEXIST error if you rename a file to an existing file
//so, we'll try to delete the file we're renaming to first
try {
fs.unlinkSync(filename + '.' + (idx+1));
} catch(e) {
//ignore err: if we could not delete, it's most likely that it doesn't exist
}
debug('Renaming ' + fileToRename + ' -> ' + filename + '.' + (idx+1));
fs.renameSync(path.join(path.dirname(filename), fileToRename), filename + '.' + (idx + 1));
}
}
function renameTheFiles() {
//roll the backups (rename file.n to file.n+1, where n <= numBackups)
debug("Renaming the old files");
var files = fs.readdirSync(path.dirname(filename));
files.filter(justTheseFiles).sort(byIndex).reverse().forEach(increaseFileIndex);
}
debug("Rolling, rolling, rolling");
renameTheFiles();
};
RollingFileSync.prototype.write = function(chunk, encoding) {
var that = this;
function writeTheChunk() {
debug("writing the chunk to the file");
that.currentSize += chunk.length;
fs.appendFileSync(that.filename, chunk);
}
debug("in write");
if (this.shouldRoll()) {
this.currentSize = 0;
this.roll(this.filename);
}
writeTheChunk();
};
/**
* File Appender writing the logs to a text file. Supports rolling of logs by size.
*
* @param file file log messages will be written to
* @param layout a function that takes a logevent and returns a string
* (defaults to basicLayout).
* @param logSize - the maximum size (in bytes) for a log file,
* if not provided then logs won't be rotated.
* @param numBackups - the number of log files to keep after logSize
* has been reached (default 5)
*/
function fileAppender (file, layout, logSize, numBackups) {
debug("fileSync appender created");
var bytesWritten = 0;
file = path.normalize(file);
layout = layout || layouts.basicLayout;
numBackups = numBackups === undefined ? 5 : numBackups;
//there has to be at least one backup if logSize has been specified
numBackups = numBackups === 0 ? 1 : numBackups;
function openTheStream(file, fileSize, numFiles) {
var stream;
if (fileSize) {
stream = new RollingFileSync(
file,
fileSize,
numFiles
);
} else {
stream = (function(f) {
// create file if it doesn't exist
if (!fs.existsSync(f))
fs.appendFileSync(f, '');
return {
write: function(data) {
fs.appendFileSync(f, data);
}
};
})(file);
}
return stream;
}
var logFile = openTheStream(file, logSize, numBackups);
return function(loggingEvent) {
logFile.write(layout(loggingEvent) + eol);
};
}
function configure(config, options) {
var layout;
if (config.layout) {
layout = layouts.layout(config.layout.type, config.layout);
}
if (options && options.cwd && !config.absolute) {
config.filename = path.join(options.cwd, config.filename);
}
return fileAppender(config.filename, layout, config.maxLogSize, config.backups);
}
exports.appender = fileAppender;
exports.configure = configure;

View File

@@ -48,11 +48,14 @@ function gelfAppender (layout, host, port, hostname, facility) {
host = host || 'localhost';
port = port || 12201;
hostname = hostname || require('os').hostname();
facility = facility || 'nodejs-server';
layout = layout || layouts.messagePassThroughLayout;
var defaultCustomFields = customFields || {};
if(facility) {
defaultCustomFields['_facility'] = facility;
}
var client = dgram.createSocket("udp4");
process.on('exit', function() {
@@ -96,14 +99,12 @@ function gelfAppender (layout, host, port, hostname, facility) {
function preparePacket(loggingEvent) {
var msg = {};
addCustomFields(loggingEvent, msg);
msg.full_message = layout(loggingEvent);
msg.short_message = msg.full_message;
msg.short_message = layout(loggingEvent);
msg.version="1.0";
msg.version="1.1";
msg.timestamp = msg.timestamp || new Date().getTime() / 1000; // log should use millisecond
msg.host = hostname;
msg.level = levelMapping[loggingEvent.level || levels.DEBUG];
msg.facility = facility;
return msg;
}

View File

@@ -1,76 +0,0 @@
"use strict";
var log4js = require('../log4js')
, layouts = require('../layouts')
, Hook = require('hook.io').Hook
, 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 initHook(hookioOptions) {
var loggerHook;
if (hookioOptions.mode === 'master') {
// Start the master hook, handling the actual logging
loggerHook = new Logger(hookioOptions);
} else {
// Start a worker, just emitting events for a master
loggerHook = new Hook(hookioOptions);
}
loggerHook.start();
return loggerHook;
}
function getBufferedHook(hook, eventName) {
var hookBuffer = [];
var hookReady = false;
hook.on('hook::ready', function emptyBuffer() {
hookBuffer.forEach(function logBufferItem(loggingEvent) {
hook.emit(eventName, loggingEvent);
});
hookReady = true;
});
return function log(loggingEvent) {
if (hookReady) {
hook.emit(eventName, loggingEvent);
} else {
hookBuffer.push(loggingEvent);
}
};
}
function createAppender(hookioOptions) {
var loggerHook = initHook(hookioOptions);
var loggerEvent = hookioOptions.name + '::log';
return getBufferedHook(loggerHook, loggerEvent);
}
function configure(config) {
var actualAppender;
if (config.appender && config.mode === 'master') {
log4js.loadAppender(config.appender.type);
actualAppender = log4js.appenderMakers[config.appender.type](config.appender);
config.actualAppender = actualAppender;
}
return createAppender(config);
}
exports.appender = createAppender;
exports.configure = configure;

View File

@@ -1,7 +1,9 @@
"use strict";
var layouts = require("../layouts")
, loggly = require("loggly")
, os = require('os');
'use strict';
var layouts = require('../layouts')
, loggly = require('loggly')
, os = require('os')
, passThrough = layouts.messagePassThroughLayout;
/**
* Loggly Appender. Sends logging events to Loggly using node-loggly
@@ -12,16 +14,21 @@ var layouts = require("../layouts")
* subdomain: 'your-subdomain',
* tags: ['loggly-tag1', 'loggly-tag2', .., 'loggly-tagn']
* }
* @param layout a function that takes a logevent and returns a string (defaults to basicLayout).
* @param layout a function that takes a logevent and returns a string (defaults to objectLayout).
*/
function logglyAppender(config, layout) {
layout = layout || layouts.basicLayout;
var client = loggly.createClient(config);
return function(loggingEvent) {
client.log(layout(loggingEvent), config.tags);
};
if(!layout) layout = passThrough;
return function(loggingEvent) {
var msg = layout(loggingEvent);
client.log({
msg: msg,
level: loggingEvent.level.levelStr,
category: loggingEvent.categoryName,
hostname: os.hostname().toString(),
});
}
}
function configure(config) {
@@ -32,6 +39,6 @@ function configure(config) {
return logglyAppender(config, layout);
}
exports.name = "loggly";
exports.name = 'loggly';
exports.appender = logglyAppender;
exports.configure = configure;
exports.configure = configure;

View File

@@ -84,6 +84,12 @@ function getLogger(logger4js, options) {
res.end = end;
res.end(chunk, encoding);
res.responseTime = new Date() - start;
//status code response level handling
if(res.statusCode && options.level === 'auto'){
level = levels.INFO;
if(res.statusCode >= 300) level = levels.WARN;
if(res.statusCode >= 400) level = levels.ERROR;
}
if (thislogger.isLevelEnabled(level)) {
if (typeof fmt === 'function') {
var line = fmt(req, res, function(str){ return format(str, req, res); });

View File

@@ -125,6 +125,7 @@ function messagePassThroughLayout (loggingEvent) {
* - %d date in various formats
* - %% %
* - %n newline
* - %z pid
* - %x{<tokenname>} add dynamic tokens to your log. Tokens are specified in the tokens parameter
* You can use %[ and %] to define a colored block.
*
@@ -144,7 +145,7 @@ function messagePassThroughLayout (loggingEvent) {
*/
function patternLayout (pattern, tokens) {
var TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";
var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprx%])(\{([^\}]+)\})?|([^%]+)/;
var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([\[\]cdhmnprzx%])(\{([^\}]+)\})?|([^%]+)/;
pattern = pattern || TTCC_CONVERSION_PATTERN;
@@ -211,6 +212,10 @@ function patternLayout (pattern, tokens) {
return '%';
}
function pid() {
return process.pid;
}
function userDefined(loggingEvent, specifier) {
if (typeof(tokens[specifier]) !== 'undefined') {
if (typeof(tokens[specifier]) === 'function') {
@@ -232,6 +237,7 @@ function patternLayout (pattern, tokens) {
'r': startTime,
'[': startColour,
']': endColour,
'z': pid,
'%': percent,
'x': userDefined
};

View File

@@ -44,17 +44,20 @@
* Website: http://log4js.berlios.de
*/
var events = require('events')
, async = require('async')
, fs = require('fs')
, path = require('path')
, util = require('util')
, layouts = require('./layouts')
, levels = require('./levels')
, LoggingEvent = require('./logger').LoggingEvent
, Logger = require('./logger').Logger
, loggerModule = require('./logger')
, LoggingEvent = loggerModule.LoggingEvent
, Logger = loggerModule.Logger
, ALL_CATEGORIES = '[all]'
, appenders = {}
, loggers = {}
, appenderMakers = {}
, appenderShutdowns = {}
, defaultConfig = {
appenders: [
{ type: "console" }
@@ -62,6 +65,34 @@ var events = require('events')
replaceConsole: false
};
function hasLogger(logger) {
return loggers.hasOwnProperty(logger);
}
function getBufferedLogger(categoryName) {
var base_logger = getLogger(categoryName);
var logger = {};
logger.temp = [];
logger.target = base_logger;
logger.flush = function () {
for (var i = 0; i < logger.temp.length; i++) {
var log = logger.temp[i];
logger.target[log.level](log.message);
delete logger.temp[i];
}
};
logger.trace = function (message) { logger.temp.push({level: 'trace', message: message}); };
logger.debug = function (message) { logger.temp.push({level: 'debug', message: message}); };
logger.info = function (message) { logger.temp.push({level: 'info', message: message}); };
logger.warn = function (message) { logger.temp.push({level: 'warn', message: message}); };
logger.error = function (message) { logger.temp.push({level: 'error', message: message}); };
logger.fatal = function (message) { logger.temp.push({level: 'fatal', message: message}); };
return logger;
}
/**
* Get a logger instance. Instance is cached on categoryName level.
* @param {String} categoryName name of category to log to.
@@ -76,7 +107,7 @@ function getLogger (categoryName) {
}
var appenderList;
if (!loggers[categoryName]) {
if (!hasLogger(categoryName)) {
// Create the logger for this name if it doesn't already exist
loggers[categoryName] = new Logger(categoryName);
if (appenders[categoryName]) {
@@ -115,7 +146,7 @@ function addAppender () {
if (category === ALL_CATEGORIES) {
addAppenderToAllLoggers(appender);
} else if (loggers[category]) {
} else if (hasLogger(category)) {
loggers[category].addListener("log", appender);
}
});
@@ -123,7 +154,7 @@ function addAppender () {
function addAppenderToAllLoggers(appender) {
for (var logger in loggers) {
if (loggers.hasOwnProperty(logger)) {
if (hasLogger(logger)) {
loggers[logger].addListener("log", appender);
}
}
@@ -139,7 +170,7 @@ function addAppenderToCategory(appender, category) {
function clearAppenders () {
appenders = {};
for (var logger in loggers) {
if (loggers.hasOwnProperty(logger)) {
if (hasLogger(logger)) {
loggers[logger].removeAllListeners("log");
}
}
@@ -166,6 +197,9 @@ function configureLevels(levels) {
if (levels) {
for (var category in levels) {
if (levels.hasOwnProperty(category)) {
if(category === ALL_CATEGORIES) {
setGlobalLogLevel(levels[category]);
}
getLogger(category).setLevel(levels[category]);
}
}
@@ -290,25 +324,90 @@ function restoreConsole() {
});
}
function loadAppender(appender) {
/**
* Load an appenderModule based on the provided appender filepath. Will first
* check if the appender path is a subpath of the log4js "lib/appenders" directory.
* If not, it will attempt to load the the appender as complete path.
*
* @param {string} appender The filepath for the appender.
* @returns {Object|null} The required appender or null if appender could not be loaded.
* @private
*/
function requireAppender(appender) {
var appenderModule;
try {
appenderModule = require('./appenders/' + appender);
} catch (e) {
appenderModule = require(appender);
}
return appenderModule;
}
/**
* Load an appender. Provided the appender path to be loaded. If appenderModule is defined,
* it will be used in place of requiring the appender module.
*
* @param {string} appender The path to the appender module.
* @param {Object|void} [appenderModule] The pre-required appender module. When provided,
* instead of requiring the appender by its path, this object will be used.
* @returns {void}
* @private
*/
function loadAppender(appender, appenderModule) {
appenderModule = appenderModule || requireAppender(appender);
if (!appenderModule) {
throw new Error("Invalid log4js appender: " + util.inspect(appender));
}
module.exports.appenders[appender] = appenderModule.appender.bind(appenderModule);
if (appenderModule.shutdown) {
appenderShutdowns[appender] = appenderModule.shutdown.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 = {
getBufferedLogger: getBufferedLogger,
getLogger: getLogger,
getDefaultLogger: getDefaultLogger,
hasLogger: hasLogger,
addAppender: addAppender,
loadAppender: loadAppender,
clearAppenders: clearAppenders,
configure: configure,
shutdown: shutdown,
replaceConsole: replaceConsole,
restoreConsole: restoreConsole,

View File

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

View File

@@ -48,7 +48,13 @@ BaseRollingFileStream.prototype._write = function(chunk, encoding, callback) {
function writeTheChunk() {
debug("writing the chunk to the underlying stream");
that.currentSize += chunk.length;
that.theStream.write(chunk, encoding, callback);
try {
that.theStream.write(chunk, encoding, callback);
}
catch (err){
debug(err);
callback();
}
}
debug("in _write");

View File

@@ -17,7 +17,14 @@ function DateRollingFileStream(filename, pattern, options, now) {
}
this.pattern = pattern || '.yyyy-MM-dd';
this.now = now || Date.now;
this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now()));
if (fs.existsSync(filename)) {
var stat = fs.statSync(filename);
this.lastTimeWeWroteSomething = format.asString(this.pattern, stat.mtime);
} else {
this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now()));
}
this.baseFilename = filename;
this.alwaysIncludePattern = false;

View File

@@ -1,6 +1,6 @@
{
"name": "log4js",
"version": "0.6.11",
"version": "0.6.19",
"description": "Port of Log4js to work with node.",
"keywords": [
"logging",
@@ -35,7 +35,6 @@
"devDependencies": {
"vows": "0.7.0",
"sandboxed-module": "0.1.3",
"hook.io": "0.8.10",
"underscore": "1.2.1"
},
"browser": {

View File

@@ -2,7 +2,8 @@
var vows = require('vows')
, fs = require('fs')
, assert = require('assert');
, assert = require('assert')
, EOL = require('os').EOL || '\n';
function remove(filename) {
try {
@@ -15,24 +16,24 @@ function remove(filename) {
vows.describe('log4js categoryFilter').addBatch({
'appender': {
topic: function() {
var log4js = require('../lib/log4js'), logEvents = [], webLogger, appLogger;
log4js.clearAppenders();
var appender = require('../lib/appenders/categoryFilter')
.appender(
['app'],
['app'],
function(evt) { logEvents.push(evt); }
);
log4js.addAppender(appender, ["app","web"]);
webLogger = log4js.getLogger("web");
appLogger = log4js.getLogger("app");
webLogger.debug('This should get logged');
appLogger.debug('This should not');
webLogger.debug('Hello again');
log4js.getLogger('db').debug('This shouldn\'t be included by the appender anyway');
return logEvents;
},
'should only pass matching category' : function(logEvents) {
@@ -41,32 +42,32 @@ vows.describe('log4js categoryFilter').addBatch({
assert.equal(logEvents[1].data[0], 'Hello again');
}
},
'configure': {
topic: function() {
var log4js = require('../lib/log4js')
, logger, weblogger;
remove(__dirname + '/categoryFilter-web.log');
remove(__dirname + '/categoryFilter-noweb.log');
log4js.configure('test/with-categoryFilter.json');
logger = log4js.getLogger("app");
weblogger = log4js.getLogger("web");
logger.info('Loading app');
logger.debug('Initialising indexes');
weblogger.info('00:00:00 GET / 200');
weblogger.warn('00:00:00 GET / 500');
//wait for the file system to catch up
setTimeout(this.callback, 100);
setTimeout(this.callback, 500);
},
'tmp-tests.log': {
topic: function() {
fs.readFile(__dirname + '/categoryFilter-noweb.log', 'utf8', this.callback);
},
'should contain all log messages': function(contents) {
var messages = contents.trim().split('\n');
var messages = contents.trim().split(EOL);
assert.deepEqual(messages, ['Loading app','Initialising indexes']);
}
},
@@ -75,7 +76,7 @@ vows.describe('log4js categoryFilter').addBatch({
fs.readFile(__dirname + '/categoryFilter-web.log','utf8',this.callback);
},
'should contain only error and warning log messages': function(contents) {
var messages = contents.trim().split('\n');
var messages = contents.trim().split(EOL);
assert.deepEqual(messages, ['00:00:00 GET / 200','00:00:00 GET / 500']);
}
}

View File

@@ -37,7 +37,8 @@ vows.describe('log4js cluster appender').addBatch({
});
var masterAppender = appenderModule.appender({
actualAppenders: [ fakeActualAppender ]
actualAppenders: [fakeActualAppender, fakeActualAppender, fakeActualAppender],
appenders: [{}, {category: "test"}, {category: "wovs"}]
});
// Actual test - log message using masterAppender
@@ -56,7 +57,9 @@ vows.describe('log4js cluster appender').addBatch({
},
"should log using actual appender": function(topic) {
assert.equal(topic.loggingEvents.length, 2)
assert.equal(topic.loggingEvents[0].data[0], 'masterAppender test');
assert.equal(topic.loggingEvents[1].data[0], 'masterAppender test');
},
},

View File

@@ -86,6 +86,21 @@ vows.describe('log4js configure').addBatch({
assert.isFunction(log4js.appenderMakers['some/other/external']);
}
},
'when appender object loaded via loadAppender': {
topic: function() {
var testAppender = makeTestAppender(),
log4js = sandbox.require('../lib/log4js');
log4js.loadAppender('some/other/external', testAppender);
return log4js;
},
'should load appender with provided object': 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']);
}
},
'when configuration file loaded via LOG4JS_CONFIG environment variable': {
topic: function() {
process.env.LOG4JS_CONFIG = 'some/path/to/mylog4js.json';

View File

@@ -4,7 +4,8 @@ var vows = require('vows')
, path = require('path')
, fs = require('fs')
, sandbox = require('sandboxed-module')
, log4js = require('../lib/log4js');
, log4js = require('../lib/log4js')
, EOL = require('os').EOL || '\n';
function removeFile(filename) {
return function() {
@@ -134,7 +135,10 @@ vows.describe('../lib/appenders/dateFile').addBatch({
teardown: removeFile('date-file-test.log'),
'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);
}
},
@@ -161,7 +165,7 @@ vows.describe('../lib/appenders/dateFile').addBatch({
, thisTime = format.asString(options.appenders[0].pattern, new Date());
fs.writeFileSync(
path.join(__dirname, 'date-file-test' + thisTime),
"this is existing data" + require('os').EOL,
"this is existing data" + EOL,
'utf8'
);
log4js.clearAppenders();
@@ -182,7 +186,7 @@ vows.describe('../lib/appenders/dateFile').addBatch({
}
},
'with cwd option': {
topic: function() {
topic: function () {
var fileOpened,
appender = sandbox.require(
'../lib/appenders/dateFile',
@@ -209,8 +213,9 @@ vows.describe('../lib/appenders/dateFile').addBatch({
);
return fileOpened;
},
'should prepend options.cwd to config.filename': function(fileOpened) {
assert.equal(fileOpened, "/absolute/path/to/whatever.log");
'should prepend options.cwd to config.filename': function (fileOpened) {
var expected = path.sep + path.join("absolute", "path", "to", "whatever.log");
assert.equal(fileOpened, expected);
}
}

View File

@@ -4,7 +4,8 @@ var vows = require('vows')
, path = require('path')
, sandbox = require('sandboxed-module')
, log4js = require('../lib/log4js')
, assert = require('assert');
, assert = require('assert')
, EOL = require('os').EOL || '\n';
log4js.clearAppenders();
@@ -93,8 +94,8 @@ vows.describe('log4js fileAppender').addBatch({
fs.readFile(testFile, "utf8", that.callback);
}, 100);
},
'should write log messages to the file': function(err, fileContents) {
assert.include(fileContents, "This should be in the file.\n");
'should write log messages to the file': function (err, fileContents) {
assert.include(fileContents, "This should be in the file." + EOL);
},
'log messages should be in the basic layout format': function(err, fileContents) {
assert.match(
@@ -125,7 +126,7 @@ vows.describe('log4js fileAppender').addBatch({
}, 100);
},
'log file should only contain the second message': function(err, fileContents) {
assert.include(fileContents, "This is the second log message.\n");
assert.include(fileContents, "This is the second log message.");
assert.equal(fileContents.indexOf("This is the first log message."), -1);
},
'the number of files': {
@@ -229,8 +230,8 @@ vows.describe('log4js fileAppender').addBatch({
fs.readFile('tmp-tests.log', 'utf8', this.callback);
},
'should load appender configuration from a json file': function(err, contents) {
assert.include(contents, 'this should be written to the file\n');
'should load appender configuration from a json file': function (err, contents) {
assert.include(contents, 'this should be written to the file' + EOL);
assert.equal(contents.indexOf('this should not be written to the file'), -1);
}
}

182
test/fileSyncAppender-test.js Executable file
View File

@@ -0,0 +1,182 @@
"use strict";
var vows = require('vows')
, fs = require('fs')
, path = require('path')
, sandbox = require('sandboxed-module')
, log4js = require('../lib/log4js')
, assert = require('assert')
, EOL = require('os').EOL || '\n';
log4js.clearAppenders();
function remove(filename) {
try {
fs.unlinkSync(filename);
} catch (e) {
//doesn't really matter if it failed
}
}
vows.describe('log4js fileSyncAppender').addBatch({
'with default fileSyncAppender settings': {
topic: function() {
var that = this
, testFile = path.join(__dirname, '/fa-default-sync-test.log')
, logger = log4js.getLogger('default-settings');
remove(testFile);
log4js.clearAppenders();
log4js.addAppender(require('../lib/appenders/fileSync').appender(testFile), 'default-settings');
logger.info("This should be in the file.");
fs.readFile(testFile, "utf8", that.callback);
},
'should write log messages to the file': function (err, fileContents) {
assert.include(fileContents, "This should be in the file." + EOL);
},
'log messages should be in the basic layout format': function(err, fileContents) {
assert.match(
fileContents,
/\[\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3}\] \[INFO\] default-settings - /
);
}
},
'with a max file size and no backups': {
topic: function() {
var testFile = path.join(__dirname, '/fa-maxFileSize-sync-test.log')
, logger = log4js.getLogger('max-file-size')
, that = this;
remove(testFile);
remove(testFile + '.1');
//log file of 100 bytes maximum, no backups
log4js.clearAppenders();
log4js.addAppender(
require('../lib/appenders/fileSync').appender(testFile, log4js.layouts.basicLayout, 100, 0),
'max-file-size'
);
logger.info("This is the first log message.");
logger.info("This is an intermediate log message.");
logger.info("This is the second log message.");
fs.readFile(testFile, "utf8", that.callback);
},
'log file should only contain the second message': function (err, fileContents) {
assert.include(fileContents, "This is the second log message." + EOL);
assert.equal(fileContents.indexOf("This is the first log message."), -1);
},
'the number of files': {
topic: function() {
fs.readdir(__dirname, this.callback);
},
'starting with the test file name should be two': function(err, files) {
//there will always be one backup if you've specified a max log size
var logFiles = files.filter(
function(file) { return file.indexOf('fa-maxFileSize-sync-test.log') > -1; }
);
assert.equal(logFiles.length, 2);
}
}
},
'with a max file size and 2 backups': {
topic: function() {
var testFile = path.join(__dirname, '/fa-maxFileSize-with-backups-sync-test.log')
, logger = log4js.getLogger('max-file-size-backups');
remove(testFile);
remove(testFile+'.1');
remove(testFile+'.2');
//log file of 50 bytes maximum, 2 backups
log4js.clearAppenders();
log4js.addAppender(
require('../lib/appenders/fileSync').appender(testFile, log4js.layouts.basicLayout, 50, 2),
'max-file-size-backups'
);
logger.info("This is the first log message.");
logger.info("This is the second log message.");
logger.info("This is the third log message.");
logger.info("This is the fourth log message.");
var that = this;
fs.readdir(__dirname, function(err, files) {
if (files) {
that.callback(null, files.sort());
} else {
that.callback(err, files);
}
});
},
'the log files': {
topic: function(files) {
var logFiles = files.filter(
function(file) { return file.indexOf('fa-maxFileSize-with-backups-sync-test.log') > -1; }
);
return logFiles;
},
'should be 3': function (files) {
assert.equal(files.length, 3);
},
'should be named in sequence': function (files) {
assert.deepEqual(files, [
'fa-maxFileSize-with-backups-sync-test.log',
'fa-maxFileSize-with-backups-sync-test.log.1',
'fa-maxFileSize-with-backups-sync-test.log.2'
]);
},
'and the contents of the first file': {
topic: function(logFiles) {
fs.readFile(path.join(__dirname, logFiles[0]), "utf8", this.callback);
},
'should be the last log message': function(contents) {
assert.include(contents, 'This is the fourth log message.');
}
},
'and the contents of the second file': {
topic: function(logFiles) {
fs.readFile(path.join(__dirname, logFiles[1]), "utf8", this.callback);
},
'should be the third log message': function(contents) {
assert.include(contents, 'This is the third log message.');
}
},
'and the contents of the third file': {
topic: function(logFiles) {
fs.readFile(path.join(__dirname, logFiles[2]), "utf8", this.callback);
},
'should be the second log message': function(contents) {
assert.include(contents, 'This is the second log message.');
}
}
}
}
}).addBatch({
'configure' : {
'with fileSyncAppender': {
topic: function() {
var log4js = require('../lib/log4js')
, logger;
//this config defines one file appender (to ./tmp-sync-tests.log)
//and sets the log level for "tests" to WARN
log4js.configure({
appenders: [{
category: "tests",
type: "file",
filename: "tmp-sync-tests.log",
layout: { type: "messagePassThrough" }
}],
levels: { tests: "WARN" }
});
logger = log4js.getLogger('tests');
logger.info('this should not be written to the file');
logger.warn('this should be written to the file');
fs.readFile('tmp-sync-tests.log', 'utf8', this.callback);
},
'should load appender configuration from a json file': function(err, contents) {
assert.include(contents, 'this should be written to the file' + EOL);
assert.equal(contents.indexOf('this should not be written to the file'), -1);
}
}
}
}).export(module);

View File

@@ -73,7 +73,7 @@ var vows = require('vows')
console: fakeConsole
}
});
log4js.clearAppenders();
log4js.addAppender(appender.configure(options || {}), category || "gelf-test");
return {
@@ -87,7 +87,7 @@ var vows = require('vows')
};
vows.describe('log4js gelfAppender').addBatch({
'with default gelfAppender settings': {
topic: function() {
var setup = setupLogging();
@@ -115,12 +115,10 @@ vows.describe('log4js gelfAppender').addBatch({
return message;
},
'should be in the gelf format': function(message) {
assert.equal(message.version, '1.0');
assert.equal(message.version, '1.1');
assert.equal(message.host, require('os').hostname());
assert.equal(message.level, 6); //INFO
assert.equal(message.facility, 'nodejs-server');
assert.equal(message.full_message, message.short_message);
assert.equal(message.full_message, 'This is a test');
assert.equal(message.short_message, 'This is a test');
}
}
},
@@ -166,7 +164,7 @@ vows.describe('log4js gelfAppender').addBatch({
},
'should pick up the options': function(message) {
assert.equal(message.host, 'cheese');
assert.equal(message.facility, 'nonsense');
assert.equal(message._facility, 'nonsense');
}
}
},
@@ -246,12 +244,11 @@ vows.describe('log4js gelfAppender').addBatch({
},
'should pick up the options': function(message) {
assert.equal(message.host, 'cheese');
assert.equal(message.facility, 'nonsense');
assert.equal(message._facility, 'nonsense');
assert.equal(message._every1, 'Hello every one'); // the default value
assert.equal(message._every2, 'Overwritten!'); // the overwritten value
assert.equal(message._myField, 'This is my field!'); // the value for this message only
assert.equal(message.short_message, 'Just testing.'); // skip the field object
assert.equal(message.full_message, 'Just testing.'); // should be as same as short_message
assert.equal(message.short_message, 'Just testing.'); // skip the field object
}
}
}

View File

@@ -8,7 +8,7 @@ vows.describe('log4js global loglevel').addBatch({
var log4js = require('../lib/log4js');
return log4js;
},
'set global loglevel on creation': function(log4js) {
var log1 = log4js.getLogger('log1');
var level = 'OFF';
@@ -16,14 +16,14 @@ vows.describe('log4js global loglevel').addBatch({
level = 'TRACE';
}
assert.notEqual(log1.level.toString(), level);
log4js.setGlobalLogLevel(level);
assert.equal(log1.level.toString(), level);
var log2 = log4js.getLogger('log2');
assert.equal(log2.level.toString(), level);
},
'global change loglevel': function(log4js) {
var log1 = log4js.getLogger('log1');
var log2 = log4js.getLogger('log2');
@@ -32,7 +32,7 @@ vows.describe('log4js global loglevel').addBatch({
level = 'TRACE';
}
assert.notEqual(log1.level.toString(), level);
log4js.setGlobalLogLevel(level);
assert.equal(log1.level.toString(), level);
assert.equal(log2.level.toString(), level);
@@ -46,20 +46,20 @@ vows.describe('log4js global loglevel').addBatch({
level = 'TRACE';
}
assert.notEqual(log1.level.toString(), level);
var oldLevel = log1.level.toString();
assert.equal(log2.level.toString(), oldLevel);
log2.setLevel(level);
assert.equal(log1.level.toString(), oldLevel);
assert.equal(log2.level.toString(), level);
assert.notEqual(oldLevel, level);
log2.removeLevel();
assert.equal(log1.level.toString(), oldLevel);
assert.equal(log2.level.toString(), oldLevel);
},
'preload loglevel': function(log4js) {
var log1 = log4js.getLogger('log1');
var level = 'OFF';
@@ -67,20 +67,55 @@ vows.describe('log4js global loglevel').addBatch({
level = 'TRACE';
}
assert.notEqual(log1.level.toString(), level);
var oldLevel = log1.level.toString();
log4js.getLogger('log2').setLevel(level);
assert.equal(log1.level.toString(), oldLevel);
// get again same logger but as different variable
var log2 = log4js.getLogger('log2');
assert.equal(log2.level.toString(), level);
assert.notEqual(oldLevel, level);
log2.removeLevel();
assert.equal(log1.level.toString(), oldLevel);
assert.equal(log2.level.toString(), oldLevel);
},
'set level on all categories': function(log4js) {
// Get 2 loggers
var log1 = log4js.getLogger('log1');
var log2 = log4js.getLogger('log2');
// First a test with 2 categories with different levels
var config = {
'levels': {
'log1': 'ERROR',
'log2': 'WARN'
}
};
log4js.configure(config);
// Check if the levels are set correctly
assert.equal('ERROR', log1.level.toString());
assert.equal('WARN', log2.level.toString());
log1.removeLevel();
log2.removeLevel();
// Almost identical test, but now we set
// level on all categories
var config2 = {
'levels': {
'[all]': 'DEBUG'
}
};
log4js.configure(config2);
// Check if the loggers got the DEBUG level
assert.equal('DEBUG', log1.level.toString());
assert.equal('DEBUG', log2.level.toString());
}
}
}).export(module);

View File

@@ -1,176 +0,0 @@
"use strict";
var vows = require('vows')
, assert = require('assert')
, sandbox = require('sandboxed-module');
function fancyResultingHookioAppender(hookNotReady) {
var emitHook = !hookNotReady
, result = { ons: {}, emissions: {}, logged: [], configs: [] };
var fakeLog4Js = {
appenderMakers: {}
};
fakeLog4Js.loadAppender = function (appender) {
fakeLog4Js.appenderMakers[appender] = function (config) {
result.actualLoggerConfig = config;
return function log(logEvent) {
result.logged.push(logEvent);
};
};
};
var fakeHookIo = { Hook: function(config) { result.configs.push(config); } };
fakeHookIo.Hook.prototype.start = function () {
result.startCalled = true;
};
fakeHookIo.Hook.prototype.on = function (eventName, functionToExec) {
result.ons[eventName] = { functionToExec: functionToExec };
if (emitHook && eventName === 'hook::ready') {
functionToExec();
}
};
fakeHookIo.Hook.prototype.emit = function (eventName, data) {
result.emissions[eventName] = result.emissions[eventName] || [];
result.emissions[eventName].push({data: data});
var on = '*::' + eventName;
if (eventName !== 'hook::ready' && result.ons[on]) {
result.ons[on].callingCount =
result.ons[on].callingCount ? result.ons[on].callingCount += 1 : 1;
result.ons[on].functionToExec(data);
}
};
return { theResult: result,
theModule: sandbox.require('../lib/appenders/hookio', {
requires: {
'../log4js': fakeLog4Js,
'hook.io': fakeHookIo
}
})
};
}
vows.describe('log4js hookioAppender').addBatch({
'master': {
topic: function() {
var fancy = fancyResultingHookioAppender();
var logger = fancy.theModule.configure(
{
name: 'ohno',
mode: 'master',
'hook-port': 5001,
appender: { type: 'file' }
}
);
logger(
{
level: { levelStr: 'INFO' },
data: "ALRIGHTY THEN",
startTime: '2011-10-27T03:53:16.031Z'
}
);
logger(
{
level: { levelStr: 'DEBUG' },
data: "OH WOW",
startTime: '2011-10-27T04:53:16.031Z'
}
);
return fancy.theResult;
},
'should write to the actual appender': function (result) {
assert.isTrue(result.startCalled);
assert.equal(result.configs.length, 1);
assert.equal(result.configs[0]['hook-port'], 5001);
assert.equal(result.logged.length, 2);
assert.equal(result.emissions['ohno::log'].length, 2);
assert.equal(result.ons['*::ohno::log'].callingCount, 2);
},
'data written should be formatted correctly': function (result) {
assert.equal(result.logged[0].level.toString(), 'INFO');
assert.equal(result.logged[0].data, 'ALRIGHTY THEN');
assert.isTrue(typeof(result.logged[0].startTime) === 'object');
assert.equal(result.logged[1].level.toString(), 'DEBUG');
assert.equal(result.logged[1].data, 'OH WOW');
assert.isTrue(typeof(result.logged[1].startTime) === 'object');
},
'the actual logger should get the right config': function (result) {
assert.equal(result.actualLoggerConfig.type, 'file');
}
},
'worker': {
'should emit logging events to the master': {
topic: function() {
var fancy = fancyResultingHookioAppender();
var logger = fancy.theModule.configure({
name: 'ohno',
mode: 'worker',
appender: { type: 'file' }
});
logger({
level: { levelStr: 'INFO' },
data: "ALRIGHTY THEN",
startTime: '2011-10-27T03:53:16.031Z'
});
logger({
level: { levelStr: 'DEBUG' },
data: "OH WOW",
startTime: '2011-10-27T04:53:16.031Z'
});
return fancy.theResult;
},
'should not write to the actual appender': function (result) {
assert.isTrue(result.startCalled);
assert.equal(result.logged.length, 0);
assert.equal(result.emissions['ohno::log'].length, 2);
assert.isUndefined(result.ons['*::ohno::log']);
}
}
},
'when hook not ready': {
topic: function() {
var fancy = fancyResultingHookioAppender(true)
, logger = fancy.theModule.configure({
name: 'ohno',
mode: 'worker'
});
logger({
level: { levelStr: 'INFO' },
data: "something",
startTime: '2011-10-27T03:45:12.031Z'
});
return fancy;
},
'should buffer the log events': function(fancy) {
assert.isUndefined(fancy.theResult.emissions['ohno::log']);
},
},
'when hook ready': {
topic: function() {
var fancy = fancyResultingHookioAppender(true)
, logger = fancy.theModule.configure({
name: 'ohno',
mode: 'worker'
});
logger({
level: { levelStr: 'INFO' },
data: "something",
startTime: '2011-10-27T03:45:12.031Z'
});
fancy.theResult.ons['hook::ready'].functionToExec();
return fancy;
},
'should emit the buffered events': function(fancy) {
assert.equal(fancy.theResult.emissions['ohno::log'].length, 1);
}
}
}).exportTo(module);

View File

@@ -1,13 +1,15 @@
"use strict";
var vows = require('vows')
, assert = require('assert');
, assert = require('assert')
, os = require('os')
, EOL = os.EOL || '\n';
//used for patternLayout tests.
function test(args, pattern, value) {
var layout = args[0]
, event = args[1]
, tokens = args[2];
assert.equal(layout(pattern, tokens)(event), value);
}
@@ -16,7 +18,7 @@ vows.describe('log4js layouts').addBatch({
topic: function() {
return require('../lib/layouts').colouredLayout;
},
'should apply level colour codes to output': function(layout) {
var output = layout({
data: ["nonsense"],
@@ -40,7 +42,7 @@ vows.describe('log4js layouts').addBatch({
assert.equal(output, '\x1B[31m[2010-12-05 14:18:30.045] [ERROR] cheese - \x1B[39mthing 2');
}
},
'messagePassThroughLayout': {
topic: function() {
return require('../lib/layouts').messagePassThroughLayout;
@@ -58,49 +60,49 @@ vows.describe('log4js layouts').addBatch({
},
'should support the console.log format for the message' : function(layout) {
assert.equal(layout({
data: ["thing %d", 1, "cheese"],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
data: ["thing %d", 1, "cheese"],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
level : {
colour: "green",
colour: "green",
toString: function() { return "ERROR"; }
}
}), "thing 1 cheese");
},
'should output the first item even if it is not a string': function(layout) {
assert.equal(layout({
data: [ { thing: 1} ],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
data: [ { thing: 1} ],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
level: {
colour: "green",
colour: "green",
toString: function() { return "ERROR"; }
}
}), "{ thing: 1 }");
},
'should print the stacks of a passed error objects': function(layout) {
assert.isArray(layout({
data: [ new Error() ],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
data: [ new Error() ],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
level: {
colour: "green",
colour: "green",
toString: function() { return "ERROR"; }
}
}).match(/Error\s+at Object\..*\s+\((.*)test[\\\/]layouts-test\.js\:\d+\:\d+\)\s+at runTest/)
, 'regexp did not return a match');
},
'with passed augmented errors': {
'with passed augmented errors': {
topic: function(layout){
var e = new Error("My Unique Error Message");
e.augmented = "My Unique attribute value";
e.augObj = { at1: "at2" };
return layout({
data: [ e ],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
data: [ e ],
startTime: new Date(2010, 11, 5, 14, 18, 30, 45),
categoryName: "cheese",
level: {
colour: "green",
colour: "green",
toString: function() { return "ERROR"; }
}
});
@@ -118,10 +120,10 @@ vows.describe('log4js layouts').addBatch({
assert.isArray(m);
}
}
},
'basicLayout': {
topic: function() {
var layout = require('../lib/layouts').basicLayout,
@@ -143,17 +145,17 @@ vows.describe('log4js layouts').addBatch({
var layout = args[0], event = args[1], output, lines,
error = new Error("Some made-up error"),
stack = error.stack.split(/\n/);
event.data = ['this is a test', error];
output = layout(event);
lines = output.split(/\n/);
assert.equal(lines.length - 1, stack.length);
assert.equal(
lines[0],
lines[0],
"[2010-12-05 14:18:30.045] [DEBUG] tests - this is a test [Error: Some made-up error]"
);
for (var i = 1; i < stack.length; i++) {
assert.equal(lines[i+2], stack[i+1]);
}
@@ -166,13 +168,13 @@ vows.describe('log4js layouts').addBatch({
}];
output = layout(event);
assert.equal(
output,
"[2010-12-05 14:18:30.045] [DEBUG] tests - this is a test " +
output,
"[2010-12-05 14:18:30.045] [DEBUG] tests - this is a test " +
"{ name: 'Cheese', message: 'Gorgonzola smells.' }"
);
}
},
'patternLayout': {
topic: function() {
var event = {
@@ -188,11 +190,14 @@ vows.describe('log4js layouts').addBatch({
testFunction: function() { return 'testFunctionToken'; },
fnThatUsesLogEvent: function(logEvent) { return logEvent.level.toString(); }
};
//override getTimezoneOffset
event.startTime.getTimezoneOffset = function() { return 0; };
return [layout, event, tokens];
},
'should default to "time logLevel loggerName - message"': function(args) {
test(args, null, "14:18:30 DEBUG multiple.levels.of.tests - this is a test\n");
test(args, null, "14:18:30 DEBUG multiple.levels.of.tests - this is a test" + EOL);
},
'%r should output time only': function(args) {
test(args, '%r', '14:18:30');
@@ -207,10 +212,13 @@ vows.describe('log4js layouts').addBatch({
test(args, '%m', 'this is a test');
},
'%n should output a new line': function(args) {
test(args, '%n', '\n');
test(args, '%n', EOL);
},
'%h should output hostname' : function(args) {
test(args, '%h', require('os').hostname().toString());
test(args, '%h', os.hostname().toString());
},
'%z should output pid' : function(args) {
test(args, '%z', process.pid);
},
'%c should handle category names like java-style package names': function(args) {
test(args, '%c{1}', 'tests');
@@ -244,7 +252,7 @@ vows.describe('log4js layouts').addBatch({
'should handle complicated patterns': function(args) {
test(args,
'%m%n %c{2} at %d{ABSOLUTE} cheese %p%n',
'this is a test\n of.tests at 14:18:30.045 cheese DEBUG\n'
'this is a test'+ EOL +' of.tests at 14:18:30.045 cheese DEBUG' + EOL
);
},
'should truncate fields if specified': function(args) {

View File

@@ -1,6 +1,7 @@
"use strict";
var vows = require('vows')
, assert = require('assert')
, path = require('path')
, sandbox = require('sandboxed-module');
vows.describe('log4js-abspath').addBatch({
@@ -69,7 +70,8 @@ vows.describe('log4js-abspath').addBatch({
return fileOpened;
},
'should prepend options.cwd to config.filename': function(fileOpened) {
assert.equal(fileOpened, "/absolute/path/to/whatever.log");
var expected = path.sep + path.join("absolute", "path", "to", "whatever.log");
assert.equal(fileOpened, expected);
}
},
}).export(module);

View File

@@ -1,7 +1,9 @@
"use strict";
var vows = require('vows')
, fs = require('fs')
, assert = require('assert');
, assert = require('assert')
, os = require('os')
, EOL = require('os').EOL || '\n';
function remove(filename) {
try {
@@ -19,12 +21,12 @@ vows.describe('log4js logLevelFilter').addBatch({
log4js.addAppender(
require('../lib/appenders/logLevelFilter')
.appender(
'ERROR',
'ERROR',
function(evt) { logEvents.push(evt); }
),
),
"logLevelTest"
);
logger = log4js.getLogger("logLevelTest");
logger.debug('this should not trigger an event');
logger.warn('neither should this');
@@ -43,10 +45,10 @@ vows.describe('log4js logLevelFilter').addBatch({
topic: function() {
var log4js = require('../lib/log4js')
, logger;
remove(__dirname + '/logLevelFilter.log');
remove(__dirname + '/logLevelFilter-warnings.log');
log4js.configure('test/with-logLevelFilter.json');
logger = log4js.getLogger("tests");
logger.info('main');
@@ -54,14 +56,14 @@ vows.describe('log4js logLevelFilter').addBatch({
logger.warn('both');
logger.debug('main');
//wait for the file system to catch up
setTimeout(this.callback, 100);
setTimeout(this.callback, 500);
},
'tmp-tests.log': {
topic: function() {
fs.readFile(__dirname + '/logLevelFilter.log', 'utf8', this.callback);
},
'should contain all log messages': function(contents) {
var messages = contents.trim().split('\n');
'should contain all log messages': function (contents) {
var messages = contents.trim().split(EOL);
assert.deepEqual(messages, ['main','both','both','main']);
}
},
@@ -70,7 +72,7 @@ vows.describe('log4js logLevelFilter').addBatch({
fs.readFile(__dirname + '/logLevelFilter-warnings.log','utf8',this.callback);
},
'should contain only error and warning log messages': function(contents) {
var messages = contents.trim().split('\n');
var messages = contents.trim().split(EOL);
assert.deepEqual(messages, ['both','both']);
}
}

View File

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

View File

@@ -32,6 +32,72 @@ function setupConsoleTest() {
}
vows.describe('log4js').addBatch({
'getBufferedLogger': {
topic: function () {
var log4js = require('../lib/log4js');
log4js.clearAppenders();
var logger = log4js.getBufferedLogger('tests');
return logger;
},
'should take a category and return a logger': function (logger) {
assert.equal(logger.target.category, 'tests');
assert.isFunction(logger.flush);
assert.isFunction(logger.trace);
assert.isFunction(logger.debug);
assert.isFunction(logger.info);
assert.isFunction(logger.warn);
assert.isFunction(logger.error);
assert.isFunction(logger.fatal);
},
'cache events': {
topic: function () {
var log4js = require('../lib/log4js');
log4js.clearAppenders();
var logger = log4js.getBufferedLogger('tests1');
var events = [];
logger.target.addListener("log", function (logEvent) { events.push(logEvent); });
logger.debug("Debug event");
logger.trace("Trace event 1");
logger.trace("Trace event 2");
logger.warn("Warning event");
logger.error("Aargh!", new Error("Pants are on fire!"));
logger.error("Simulated CouchDB problem", { err: 127, cause: "incendiary underwear" });
return events;
},
'should not emit log events if .flush() is not called.': function (events) {
assert.equal(events.length, 0);
}
},
'log events after flush() is called': {
topic: function () {
var log4js = require('../lib/log4js');
log4js.clearAppenders();
var logger = log4js.getBufferedLogger('tests2');
logger.target.setLevel("TRACE");
var events = [];
logger.target.addListener("log", function (logEvent) { events.push(logEvent); });
logger.debug("Debug event");
logger.trace("Trace event 1");
logger.trace("Trace event 2");
logger.warn("Warning event");
logger.error("Aargh!", new Error("Pants are on fire!"));
logger.error("Simulated CouchDB problem", { err: 127, cause: "incendiary underwear" });
logger.flush();
return events;
},
'should emit log events when .flush() is called.': function (events) {
assert.equal(events.length, 6);
}
}
},
'getLogger': {
topic: function() {
var log4js = require('../lib/log4js');
@@ -75,13 +141,65 @@ vows.describe('log4js').addBatch({
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.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': {

View File

@@ -7,7 +7,7 @@ var vows = require('vows')
function setupLogging(category, options) {
var msgs = [];
var fakeMailer = {
createTransport: function (name, options) {
return {
@@ -49,7 +49,7 @@ function setupLogging(category, options) {
});
log4js.addAppender(smtpModule.configure(options), category);
return {
logger: log4js.getLogger(category),
mailer: fakeMailer,
@@ -150,10 +150,10 @@ vows.describe('log4js smtpAppender').addBatch({
}, 500);
setTimeout(function () {
setup.logger.info('Log event #3');
}, 1050);
}, 1100);
setTimeout(function () {
self.callback(null, setup);
}, 2100);
}, 3000);
},
'there should be three messages': function (result) {
assert.equal(result.results.length, 3);
@@ -181,13 +181,13 @@ vows.describe('log4js smtpAppender').addBatch({
}, 0);
setTimeout(function () {
setup.logger.info('Log event #2');
}, 500);
}, 100);
setTimeout(function () {
setup.logger.info('Log event #3');
}, 1050);
}, 1500);
setTimeout(function () {
self.callback(null, setup);
}, 2100);
}, 3000);
},
'there should be two messages': function (result) {
assert.equal(result.results.length, 2);
@@ -218,7 +218,7 @@ vows.describe('log4js smtpAppender').addBatch({
close: function() { }
};
};
setup.logger.info("This will break");
return setup.console;
},
@@ -228,6 +228,4 @@ vows.describe('log4js smtpAppender').addBatch({
assert.equal(cons.errors[0].value.message, 'oh noes');
}
}
}).export(module);