Compare commits

..
23 changed files with 920 additions and 574 deletions
-1
View File
@@ -2,4 +2,3 @@ language: node_js
node_js: node_js:
- "0.10" - "0.10"
- "0.8" - "0.8"
+2
View File
@@ -17,6 +17,8 @@ Out of the box it supports the following features:
* configurable log message layout/patterns * configurable log message layout/patterns
* different log levels for different log categories (make some parts of your app log as DEBUG, others only ERRORS, etc.) * different log levels for different log categories (make some parts of your app log as DEBUG, others only ERRORS, etc.)
NOTE: version 0.6.0 onwards will only work with node v0.10.x upwards, since it makes use of the new streams API. If you're using node 0.8 or lower, use log4js@0.5.7.
NOTE: from log4js 0.5 onwards you'll need to explicitly enable replacement of node's console.log functions. Do this either by calling `log4js.replaceConsole()` or configuring with an object or json file like this: NOTE: from log4js 0.5 onwards you'll need to explicitly enable replacement of node's console.log functions. Do this either by calling `log4js.replaceConsole()` or configuring with an object or json file like this:
```javascript ```javascript
+8 -40
View File
@@ -1,46 +1,14 @@
//The connect/express logger was added to log4js by danbell. This allows connect/express servers to log using log4js. var log4js = require('./lib/log4js');
//https://github.com/nomiddlename/log4js-node/wiki/Connect-Logger log4js.addAppender(log4js.fileAppender('cheese.log'), 'cheese');
// load modules var logger = log4js.getLogger('cheese');
var log4js = require('log4js'); logger.setLevel('INFO');
var express = require("express");
var app = express();
//config var app = require('express').createServer();
log4js.configure({
appenders: [
{ type: 'console' },
{ type: 'file', filename: 'logs/log4jsconnect.log', category: 'log4jslog' }
]
});
//define logger
var logger = log4js.getLogger('log4jslog');
// set at which time msg is logged print like: only on error & above
// logger.setLevel('ERROR');
//express app
app.configure(function() { app.configure(function() {
app.use(express.favicon('')); app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO }));
// app.use(log4js.connectLogger(logger, { level: log4js.levels.INFO }));
// app.use(log4js.connectLogger(logger, { level: 'auto', format: ':method :url :status' }));
//### AUTO LEVEL DETECTION
//http responses 3xx, level = WARN
//http responses 4xx & 5xx, level = ERROR
//else.level = INFO
app.use(log4js.connectLogger(logger, { level: 'auto' }));
}); });
app.get('*', function(req,res) {
//route res.send('hello world\n <a href="/cheese">cheese</a>\n');
app.get('/', function(req,res) {
res.send('hello world');
}); });
//start app
app.listen(5000); app.listen(5000);
console.log('server runing at localhost:5000');
console.log('Simulation of normal response: goto localhost:5000');
console.log('Simulation of error response: goto localhost:5000/xxx');
-43
View File
@@ -1,43 +0,0 @@
//Note that smtp appender needs nodemailer to work.
//If you haven't got nodemailer installed, you'll get cryptic
//"cannot find module" errors when using the smtp appender
var log4js = require('../lib/log4js')
, log
, logmailer
, i = 0;
log4js.configure({
"appenders": [
{
type: "console",
category: "test"
},
{
"type": "smtp",
"recipients": "logfilerecipient@logging.com",
"sendInterval": 5,
"transport": "SMTP",
"SMTP": {
"host": "smtp.gmail.com",
"secureConnection": true,
"port": 465,
"auth": {
"user": "someone@gmail",
"pass": "********************"
},
"debug": true
},
"category": "mailer"
}
]
});
log = log4js.getLogger("test");
logmailer = log4js.getLogger("mailer");
function doTheLogging(x) {
log.info("Logging something %d", x);
logmailer.info("Logging something %d", x);
}
for ( ; i < 500; i++) {
doTheLogging(i);
}
+17 -13
View File
@@ -1,9 +1,10 @@
var streams = require('../streams'), var semver = require('semver')
layouts = require('../layouts'), , layouts = require('../layouts')
path = require('path'), , path = require('path')
os = require('os'), , os = require('os')
eol = os.EOL || '\n', , eol = os.EOL || '\n'
openFiles = []; , openFiles = []
, streams;
//close open files on process exit. //close open files on process exit.
process.on('exit', function() { process.on('exit', function() {
@@ -19,10 +20,17 @@ process.on('exit', function() {
* also used to check when to roll files - defaults to '.yyyy-MM-dd' * also used to check when to roll files - defaults to '.yyyy-MM-dd'
* @layout layout function for log messages - defaults to basicLayout * @layout layout function for log messages - defaults to basicLayout
*/ */
function appender(filename, pattern, alwaysIncludePattern, layout) { function appender(filename, pattern, layout) {
layout = layout || layouts.basicLayout; layout = layout || layouts.basicLayout;
var logFile;
var logFile = new streams.DateRollingFileStream(filename, pattern, { alwaysIncludePattern: alwaysIncludePattern }); if (semver.satisfies(process.version, '>=0.10.0')) {
streams = require('../streams');
logFile = new streams.DateRollingFileStream(filename, pattern);
} else {
streams = require('../old-streams');
logFile = new streams.BufferedWriteStream(new streams.DateRollingFileStream(filename, pattern));
}
openFiles.push(logFile); openFiles.push(logFile);
return function(logEvent) { return function(logEvent) {
@@ -38,15 +46,11 @@ function configure(config, options) {
layout = layouts.layout(config.layout.type, config.layout); layout = layouts.layout(config.layout.type, config.layout);
} }
if (!config.alwaysIncludePattern) {
config.alwaysIncludePattern = false;
}
if (options && options.cwd && !config.absolute) { if (options && options.cwd && !config.absolute) {
config.filename = path.join(options.cwd, config.filename); config.filename = path.join(options.cwd, config.filename);
} }
return appender(config.filename, config.pattern, config.alwaysIncludePattern, layout); return appender(config.filename, config.pattern, layout);
} }
exports.appender = appender; exports.appender = appender;
+35 -9
View File
@@ -1,10 +1,10 @@
var layouts = require('../layouts') var layouts = require('../layouts')
, path = require('path') , path = require('path')
, fs = require('fs') , fs = require('fs')
, streams = require('../streams') , semver = require('semver')
, os = require('os') , os = require('os')
, eol = os.EOL || '\n' , eol = os.EOL || '\n'
, openFiles = []; , openFiles = [];
//close open files on process exit. //close open files on process exit.
process.on('exit', function() { process.on('exit', function() {
@@ -22,7 +22,7 @@ process.on('exit', function() {
* @param numBackups - the number of log files to keep after logSize has been reached (default 5) * @param numBackups - the number of log files to keep after logSize has been reached (default 5)
*/ */
function fileAppender (file, layout, logSize, numBackups) { function fileAppender (file, layout, logSize, numBackups) {
var bytesWritten = 0; var logFile;
file = path.normalize(file); file = path.normalize(file);
layout = layout || layouts.basicLayout; layout = layout || layouts.basicLayout;
numBackups = numBackups === undefined ? 5 : numBackups; numBackups = numBackups === undefined ? 5 : numBackups;
@@ -30,7 +30,9 @@ function fileAppender (file, layout, logSize, numBackups) {
numBackups = numBackups === 0 ? 1 : numBackups; numBackups = numBackups === 0 ? 1 : numBackups;
function openTheStream(file, fileSize, numFiles) { function openTheStream(file, fileSize, numFiles) {
var stream; var stream
, streams = require('../streams');
if (fileSize) { if (fileSize) {
stream = new streams.RollingFileStream( stream = new streams.RollingFileStream(
file, file,
@@ -46,7 +48,31 @@ function fileAppender (file, layout, logSize, numBackups) {
return stream; return stream;
} }
var logFile = openTheStream(file, logSize, numBackups); function openTheOldStyleStream(file, fileSize, numFiles) {
var stream
, streams = require('../old-streams');
if (fileSize) {
stream = new streams.BufferedWriteStream(
new streams.RollingFileStream(
file,
fileSize,
numFiles
)
);
} else {
stream = new streams.BufferedWriteStream(fs.createWriteStream(file, { encoding: "utf8", mode: 0644, flags: 'a' }));
}
stream.on("error", function (err) {
console.error("log4js.fileAppender - Writing to file %s, error happened ", file, err);
});
return stream;
}
if (semver.satisfies(process.version, '>=0.10.0')) {
logFile = openTheStream(file, logSize, numBackups);
} else {
logFile = openTheOldStyleStream(file, logSize, numBackups);
}
// push file to the stack of open handlers // push file to the stack of open handlers
openFiles.push(logFile); openFiles.push(logFile);
+8 -13
View File
@@ -1,6 +1,6 @@
var layouts = require("../layouts"), var layouts = require("../layouts"),
mailer = require("nodemailer"), mailer = require("nodemailer"),
os = require('os'); os = require('os');
/** /**
* SMTP Appender. Sends logging events using SMTP protocol. * SMTP Appender. Sends logging events using SMTP protocol.
@@ -17,13 +17,12 @@ function smtpAppender(config, layout) {
var logEventBuffer = []; var logEventBuffer = [];
var sendTimer; var sendTimer;
var transport = mailer.createTransport(config.transport, config[config.transport]);
function sendBuffer() { function sendBuffer() {
if (logEventBuffer.length == 0) { if (logEventBuffer.length == 0)
return; return;
}
var transport = mailer.createTransport(config.transport, config[config.transport]);
var firstEvent = logEventBuffer[0]; var firstEvent = logEventBuffer[0];
var body = ""; var body = "";
while (logEventBuffer.length > 0) { while (logEventBuffer.length > 0) {
@@ -36,33 +35,29 @@ function smtpAppender(config, layout) {
text: body, text: body,
headers: {"Hostname": os.hostname()} headers: {"Hostname": os.hostname()}
}; };
if (config.sender) { if (config.sender)
msg.from = config.sender; msg.from = config.sender;
}
transport.sendMail(msg, function(error, success) { transport.sendMail(msg, function(error, success) {
if (error) { if (error) {
console.error("log4js.smtpAppender - Error happened ", error); console.error("log4js.smtpAppender - Error happened ", error);
} }
transport.close();
}); });
} }
function scheduleSend() { function scheduleSend() {
if (!sendTimer) { if (!sendTimer)
sendTimer = setTimeout(function() { sendTimer = setTimeout(function() {
sendTimer = null; sendTimer = null;
sendBuffer(); sendBuffer();
}, sendInterval); }, sendInterval);
}
} }
return function(loggingEvent) { return function(loggingEvent) {
logEventBuffer.push(loggingEvent); logEventBuffer.push(loggingEvent);
if (sendInterval > 0) { if (sendInterval > 0)
scheduleSend(); scheduleSend();
} else { else
sendBuffer(); sendBuffer();
}
}; };
} }
+11 -21
View File
@@ -5,7 +5,7 @@ var levels = require("./levels");
* Options: * Options:
* *
* - `format` Format string, see below for tokens * - `format` Format string, see below for tokens
* - `level` A log4js levels instance. Supports also 'auto' * - `level` A log4js levels instance.
* *
* Tokens: * Tokens:
* *
@@ -26,7 +26,7 @@ var levels = require("./levels");
* @api public * @api public
*/ */
function getLogger(logger4js, options) { function getLogger(logger4js, options) {
if ('object' == typeof options) { if ('object' == typeof options) {
options = options || {}; options = options || {};
} else if (options) { } else if (options) {
@@ -47,7 +47,8 @@ var levels = require("./levels");
// nologs // nologs
if (nolog && nolog.test(req.originalUrl)) return next(); if (nolog && nolog.test(req.originalUrl)) return next();
if (thislogger.isLevelEnabled(level) || options.level === 'auto') {
if (thislogger.isLevelEnabled(level)) {
var start = +new Date var start = +new Date
, statusCode , statusCode
@@ -64,15 +65,6 @@ var levels = require("./levels");
res.writeHead(code, headers); res.writeHead(code, headers);
res.__statusCode = statusCode = code; res.__statusCode = statusCode = code;
res.__headers = headers || {}; res.__headers = headers || {};
//status code response level handling
if(options.level === 'auto'){
level = levels.INFO;
if(code >= 300) level = levels.WARN;
if(code >= 400) level = levels.ERROR;
} else {
level = levels.toLevel(options.level, levels.INFO)
}
}; };
// proxy end to output a line to the provided logger. // proxy end to output a line to the provided logger.
@@ -80,20 +72,18 @@ var levels = require("./levels");
res.end = end; res.end = end;
res.end(chunk, encoding); res.end(chunk, encoding);
res.responseTime = +new Date - start; res.responseTime = +new Date - start;
if (thislogger.isLevelEnabled(level)) { if ('function' == typeof fmt) {
if (typeof fmt === 'function') {
var line = fmt(req, res, function(str){ return format(str, req, res); }); var line = fmt(req, res, function(str){ return format(str, req, res); });
if (line) thislogger.log(level, line); if (line) thislogger.log(level, line);
} else { } else {
thislogger.log(level, format(fmt, req, res)); thislogger.log(level, format(fmt, req, res));
} }
}
}; };
} }
//ensure next gets always called //ensure next gets always called
next(); next();
}; };
} }
/** /**
@@ -106,7 +96,7 @@ var levels = require("./levels");
* @api private * @api private
*/ */
function format(str, req, res) { function format(str, req, res) {
return str return str
.replace(':url', req.originalUrl) .replace(':url', req.originalUrl)
.replace(':method', req.method) .replace(':method', req.method)
@@ -124,7 +114,7 @@ var levels = require("./levels");
? (res._headers[field.toLowerCase()] || res.__headers[field]) ? (res._headers[field.toLowerCase()] || res.__headers[field])
: (res.__headers && res.__headers[field]); : (res.__headers && res.__headers[field]);
}); });
} }
/** /**
* Return RegExp Object about nolog * Return RegExp Object about nolog
@@ -153,7 +143,7 @@ var levels = require("./levels");
* 3.1 ["\\.jpg$", "\\.png", "\\.gif"] * 3.1 ["\\.jpg$", "\\.png", "\\.gif"]
* SAME AS "\\.jpg|\\.png|\\.gif" * SAME AS "\\.jpg|\\.png|\\.gif"
*/ */
function createNoLogCondition(nolog, type) { function createNoLogCondition(nolog, type) {
if(!nolog) return null; if(!nolog) return null;
type = type || ''; type = type || '';
@@ -173,6 +163,6 @@ var levels = require("./levels");
var regexps = nolog.map(function(o){ return createNoLogCondition(o, 'string')}); var regexps = nolog.map(function(o){ return createNoLogCondition(o, 'string')});
return new RegExp(regexps.join('|')); return new RegExp(regexps.join('|'));
} }
} }
exports.connectLogger = getLogger; exports.connectLogger = getLogger;
+99
View File
@@ -0,0 +1,99 @@
var fs = require('fs'),
util = require('util');
function debug(message) {
// console.log(message);
}
module.exports = BaseRollingFileStream;
function BaseRollingFileStream(filename, options) {
debug("In BaseRollingFileStream");
this.filename = filename;
this.options = options || { encoding: 'utf8', mode: 0644, flags: 'a' };
this.rolling = false;
this.writesWhileRolling = [];
this.currentSize = 0;
this.rollBeforeWrite = false;
function currentFileSize(file) {
var fileSize = 0;
try {
fileSize = fs.statSync(file).size;
} catch (e) {
// file does not exist
}
return fileSize;
}
function throwErrorIfArgumentsAreNotValid() {
if (!filename) {
throw new Error("You must specify a filename");
}
}
throwErrorIfArgumentsAreNotValid();
debug("Calling BaseRollingFileStream.super");
BaseRollingFileStream.super_.call(this, this.filename, this.options);
this.currentSize = currentFileSize(this.filename);
}
util.inherits(BaseRollingFileStream, fs.FileWriteStream);
BaseRollingFileStream.prototype.initRolling = function() {
var that = this;
function emptyRollingQueue() {
debug("emptying the rolling queue");
var toWrite;
while ((toWrite = that.writesWhileRolling.shift())) {
BaseRollingFileStream.super_.prototype.write.call(that, toWrite.data, toWrite.encoding);
that.currentSize += toWrite.data.length;
if (that.shouldRoll()) {
that.flush();
return true;
}
}
that.flush();
return false;
}
this.rolling = true;
this.roll(this.filename, function() {
that.currentSize = 0;
that.rolling = emptyRollingQueue();
if (that.rolling) {
process.nextTick(function() { that.initRolling(); });
}
});
};
BaseRollingFileStream.prototype.write = function(data, encoding) {
var canWrite = false;
if (this.rolling) {
this.writesWhileRolling.push({ data: data, encoding: encoding });
} else {
if (this.rollBeforeWrite && this.shouldRoll()) {
this.writesWhileRolling.push({ data: data, encoding: encoding });
this.initRolling();
} else {
canWrite = BaseRollingFileStream.super_.prototype.write.call(this, data, encoding);
this.currentSize += data.length;
debug('current size = ' + this.currentSize);
if (!this.rollBeforeWrite && this.shouldRoll()) {
this.initRolling();
}
}
}
return canWrite;
};
BaseRollingFileStream.prototype.shouldRoll = function() {
return false; // default behaviour is never to roll
};
BaseRollingFileStream.prototype.roll = function(filename, callback) {
callback(); // default behaviour is not to do anything
};
+78
View File
@@ -0,0 +1,78 @@
var events = require('events'),
Dequeue = require('dequeue'),
util = require('util');
module.exports = BufferedWriteStream;
function BufferedWriteStream(stream) {
var that = this;
this.stream = stream;
this.buffer = new Dequeue();
this.canWrite = false;
this.bytes = 0;
this.stream.on("open", function() {
that.canWrite = true;
that.flushBuffer();
});
this.stream.on("error", function (err) {
that.emit("error", err);
});
this.stream.on("drain", function() {
that.canWrite = true;
that.flushBuffer();
});
}
util.inherits(BufferedWriteStream, events.EventEmitter);
Object.defineProperty(
BufferedWriteStream.prototype,
"fd",
{
get: function() { return this.stream.fd; },
set: function(newFd) {
this.stream.fd = newFd;
this.bytes = 0;
}
}
);
Object.defineProperty(
BufferedWriteStream.prototype,
"bytesWritten",
{
get: function() { return this.bytes; }
}
);
BufferedWriteStream.prototype.write = function(data, encoding) {
this.buffer.push({ data: data, encoding: encoding });
this.flushBuffer();
};
BufferedWriteStream.prototype.end = function(data, encoding) {
if (data) {
this.buffer.push({ data: data, encoding: encoding });
}
this.flushBufferEvenIfCannotWrite();
};
BufferedWriteStream.prototype.writeToStream = function(toWrite) {
this.bytes += toWrite.data.length;
this.canWrite = this.stream.write(toWrite.data, toWrite.encoding);
};
BufferedWriteStream.prototype.flushBufferEvenIfCannotWrite = function() {
while (this.buffer.length > 0) {
this.writeToStream(this.buffer.shift());
}
};
BufferedWriteStream.prototype.flushBuffer = function() {
while (this.buffer.length > 0 && this.canWrite) {
this.writeToStream(this.buffer.shift());
}
};
+89
View File
@@ -0,0 +1,89 @@
var BaseRollingFileStream = require('./BaseRollingFileStream'),
format = require('../date_format'),
async = require('async'),
fs = require('fs'),
util = require('util');
module.exports = DateRollingFileStream;
function debug(message) {
// console.log(message);
}
function DateRollingFileStream(filename, pattern, options, now) {
debug("Now is " + now);
if (pattern && typeof(pattern) === 'object') {
now = options;
options = pattern;
pattern = null;
}
this.pattern = pattern || '.yyyy-MM-dd';
this.now = now || Date.now;
this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now()));
debug("this.now is " + this.now + ", now is " + now);
DateRollingFileStream.super_.call(this, filename, options);
this.rollBeforeWrite = true;
}
util.inherits(DateRollingFileStream, BaseRollingFileStream);
DateRollingFileStream.prototype.shouldRoll = function() {
var lastTime = this.lastTimeWeWroteSomething,
thisTime = format.asString(this.pattern, new Date(this.now()));
debug("DateRollingFileStream.shouldRoll with now = " + this.now() + ", thisTime = " + thisTime + ", lastTime = " + lastTime);
this.lastTimeWeWroteSomething = thisTime;
this.previousTime = lastTime;
return thisTime !== lastTime;
};
DateRollingFileStream.prototype.roll = function(filename, callback) {
var that = this,
newFilename = filename + this.previousTime;
debug("Starting roll");
debug("Queueing up data until we've finished rolling");
debug("Flushing underlying stream");
this.flush();
async.series([
deleteAnyExistingFile,
renameTheCurrentFile,
openANewFile
], callback);
function deleteAnyExistingFile(cb) {
//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
fs.unlink(newFilename, function (err) {
//ignore err: if we could not delete, it's most likely that it doesn't exist
cb();
});
}
function renameTheCurrentFile(cb) {
debug("Renaming the " + filename + " -> " + newFilename);
fs.rename(filename, newFilename, cb);
}
function openANewFile(cb) {
debug("Opening a new file");
fs.open(
filename,
that.options.flags,
that.options.mode,
function (err, fd) {
debug("opened new file");
var oldLogFileFD = that.fd;
that.fd = fd;
that.writable = true;
fs.close(oldLogFileFD, cb);
}
);
}
};
+1
View File
@@ -0,0 +1 @@
These are for pre-0.10.x versions of node and are here just for backwards compatibility. No bug fixes or enhancements will be made to these files.
+110
View File
@@ -0,0 +1,110 @@
var BaseRollingFileStream = require('./BaseRollingFileStream'),
util = require('util'),
path = require('path'),
fs = require('fs'),
async = require('async');
function debug(message) {
// util.debug(message);
// console.log(message);
}
module.exports = RollingFileStream;
function RollingFileStream (filename, size, backups, options) {
this.size = size;
this.backups = backups || 1;
function throwErrorIfArgumentsAreNotValid() {
if (!filename || !size || size <= 0) {
throw new Error("You must specify a filename and file size");
}
}
throwErrorIfArgumentsAreNotValid();
RollingFileStream.super_.call(this, filename, options);
}
util.inherits(RollingFileStream, BaseRollingFileStream);
RollingFileStream.prototype.shouldRoll = function() {
return this.currentSize >= this.size;
};
RollingFileStream.prototype.roll = function(filename, callback) {
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, cb) {
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
fs.unlink(filename + '.' + (idx+1), function (err) {
//ignore err: if we could not delete, it's most likely that it doesn't exist
debug('Renaming ' + fileToRename + ' -> ' + filename + '.' + (idx+1));
fs.rename(path.join(path.dirname(filename), fileToRename), filename + '.' + (idx + 1), cb);
});
} else {
cb();
}
}
function renameTheFiles(cb) {
//roll the backups (rename file.n to file.n+1, where n <= numBackups)
debug("Renaming the old files");
fs.readdir(path.dirname(filename), function (err, files) {
async.forEachSeries(
files.filter(justTheseFiles).sort(byIndex).reverse(),
increaseFileIndex,
cb
);
});
}
function openANewFile(cb) {
debug("Opening a new file");
fs.open(
filename,
that.options.flags,
that.options.mode,
function (err, fd) {
debug("opened new file");
var oldLogFileFD = that.fd;
that.fd = fd;
that.writable = true;
fs.close(oldLogFileFD, cb);
}
);
}
debug("Starting roll");
debug("Queueing up data until we've finished rolling");
debug("Flushing underlying stream");
this.flush();
async.series([
renameTheFiles,
openANewFile
], callback);
};
+3
View File
@@ -0,0 +1,3 @@
exports.BufferedWriteStream = require('./BufferedWriteStream');
exports.RollingFileStream = require('./RollingFileStream');
exports.DateRollingFileStream = require('./DateRollingFileStream');
+3 -10
View File
@@ -1,13 +1,6 @@
var fs = require('fs'), var fs = require('fs'),
stream, stream = require('stream'),
util = require('util'), util = require('util');
semver = require('semver');
if (semver.satisfies(process.version, '>=0.10.0')) {
stream = require('stream');
} else {
stream = require('readable-stream');
}
var debug; var debug;
if (process.env.NODE_DEBUG && /\blog4js\b/.test(process.env.NODE_DEBUG)) { if (process.env.NODE_DEBUG && /\blog4js\b/.test(process.env.NODE_DEBUG)) {
@@ -76,7 +69,7 @@ BaseRollingFileStream.prototype.openTheStream = function(cb) {
BaseRollingFileStream.prototype.closeTheStream = function(cb) { BaseRollingFileStream.prototype.closeTheStream = function(cb) {
debug("closing the underlying stream"); debug("closing the underlying stream");
this.theStream.end(cb); this.theStream.end(null, null, cb);
}; };
BaseRollingFileStream.prototype.shouldRoll = function() { BaseRollingFileStream.prototype.shouldRoll = function() {
+2 -23
View File
@@ -23,19 +23,6 @@ function DateRollingFileStream(filename, pattern, options, now) {
this.pattern = pattern || '.yyyy-MM-dd'; this.pattern = pattern || '.yyyy-MM-dd';
this.now = now || Date.now; this.now = now || Date.now;
this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now())); this.lastTimeWeWroteSomething = format.asString(this.pattern, new Date(this.now()));
this.baseFilename = filename;
this.alwaysIncludePattern = false;
if (options) {
if (options.alwaysIncludePattern) {
this.alwaysIncludePattern = true;
filename = this.baseFilename + this.lastTimeWeWroteSomething;
}
delete options.alwaysIncludePattern;
if (Object.keys(options).length === 0) {
options = null;
}
}
debug("this.now is " + this.now + ", now is " + now); debug("this.now is " + this.now + ", now is " + now);
DateRollingFileStream.super_.call(this, filename, options); DateRollingFileStream.super_.call(this, filename, options);
@@ -55,25 +42,17 @@ DateRollingFileStream.prototype.shouldRoll = function() {
}; };
DateRollingFileStream.prototype.roll = function(filename, callback) { DateRollingFileStream.prototype.roll = function(filename, callback) {
var that = this; var that = this,
newFilename = filename + this.previousTime;
debug("Starting roll"); debug("Starting roll");
if (this.alwaysIncludePattern) {
this.filename = this.baseFilename + this.lastTimeWeWroteSomething;
async.series([
this.closeTheStream.bind(this),
this.openTheStream.bind(this)
], callback);
} else {
var newFilename = this.baseFilename + this.previousTime;
async.series([ async.series([
this.closeTheStream.bind(this), this.closeTheStream.bind(this),
deleteAnyExistingFile, deleteAnyExistingFile,
renameTheCurrentFile, renameTheCurrentFile,
this.openTheStream.bind(this) this.openTheStream.bind(this)
], callback); ], callback);
}
function deleteAnyExistingFile(cb) { function deleteAnyExistingFile(cb) {
//on windows, you can get a EEXIST error if you rename a file to an existing file //on windows, you can get a EEXIST error if you rename a file to an existing file
+3 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "log4js", "name": "log4js",
"version": "0.6.6", "version": "0.6.2",
"description": "Port of Log4js to work with node.", "description": "Port of Log4js to work with node.",
"keywords": [ "keywords": [
"logging", "logging",
@@ -18,7 +18,7 @@
"url": "http://github.com/nomiddlename/log4js-node/issues" "url": "http://github.com/nomiddlename/log4js-node/issues"
}, },
"engines": { "engines": {
"node": ">=0.8" "node": ">=0.6.0"
}, },
"scripts": { "scripts": {
"test": "vows" "test": "vows"
@@ -30,8 +30,7 @@
"dependencies": { "dependencies": {
"async": "0.1.15", "async": "0.1.15",
"dequeue": "1.0.3", "dequeue": "1.0.3",
"semver": "~1.1.4", "semver": "~1.1.4"
"readable-stream": "~1.0.2"
}, },
"devDependencies": { "devDependencies": {
"vows": "0.7.0", "vows": "0.7.0",
+67 -40
View File
@@ -1,8 +1,9 @@
var vows = require('vows'), var vows = require('vows'),
assert = require('assert'), assert = require('assert'),
path = require('path'), path = require('path'),
fs = require('fs'), fs = require('fs'),
log4js = require('../lib/log4js'); sandbox = require('sandboxed-module'),
log4js = require('../lib/log4js');
function removeFile(filename) { function removeFile(filename) {
return function() { return function() {
@@ -89,45 +90,71 @@ vows.describe('../lib/appenders/dateFile').addBatch({
teardown: removeFile('date-file-test.log'), teardown: removeFile('date-file-test.log'),
'should load appender configuration from a json file': function(err, contents) { 'should load appender configuration from a json file': function(err, contents) {
assert.include(contents, 'this should be written to the file' + require('os').EOL); assert.include(contents, 'this should be written to the file\n');
assert.equal(contents.indexOf('this should not be written to the file'), -1); assert.equal(contents.indexOf('this should not be written to the file'), -1);
} }
},
'with options.alwaysIncludePattern': {
topic: function() {
var log4js = require('../lib/log4js')
, format = require('../lib/date_format')
, logger
, options = {
"appenders": [
{
"category": "tests",
"type": "dateFile",
"filename": "test/date-file-test",
"pattern": "-from-MM-dd.log",
"alwaysIncludePattern": true,
"layout": {
"type": "messagePassThrough"
}
}
]
}
, 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, 'utf8');
log4js.clearAppenders();
log4js.configure(options);
logger = log4js.getLogger('tests');
logger.warn('this should be written to the file with the appended date');
this.teardown = removeFile('date-file-test' + thisTime);
fs.readFile(path.join(__dirname, 'date-file-test' + thisTime), 'utf8', this.callback);
},
'should create file with the correct pattern': function(contents) {
assert.include(contents, 'this should be written to the file with the appended date');
},
'should not overwrite the file on open (bug found in issue #132)': function(contents) {
assert.include(contents, 'this is existing data');
}
} }
} }
}).addBatch({
'with node version less than 0.10': {
topic: function() {
var oldStyleStreamCreated = false
, appender = sandbox.require(
'../lib/appenders/dateFile',
{
globals: {
process: {
version: "v0.8.1",
on: function() {}
}
},
requires: {
'../old-streams': {
BufferedWriteStream: function() {
oldStyleStreamCreated = true;
this.on = function() {};
},
DateRollingFileStream: function() {
this.on = function() {};
}
}
}
}
).appender('cheese.log', null, 1000, 1);
return oldStyleStreamCreated;
},
'should load the old-style streams': function(loaded) {
assert.isTrue(loaded);
}
},
'with node version greater than or equal to 0.10': {
topic: function() {
var oldStyleStreamCreated = false
, appender = sandbox.require(
'../lib/appenders/dateFile',
{
globals: {
process: {
version: "v0.10.1",
on: function() {}
}
},
requires: {
'../streams': {
DateRollingFileStream: function() {
this.on = function() {};
}
}
}
}
).appender('cheese.log', null, 1000, 1);
return oldStyleStreamCreated;
},
'should load the new streams': function(loaded) {
assert.isFalse(loaded);
}
}
}).exportTo(module); }).exportTo(module);
+85 -1
View File
@@ -2,6 +2,8 @@ var vows = require('vows')
, fs = require('fs') , fs = require('fs')
, path = require('path') , path = require('path')
, log4js = require('../lib/log4js') , log4js = require('../lib/log4js')
, sandbox = require('sandboxed-module')
, semver = require('semver')
, assert = require('assert'); , assert = require('assert');
log4js.clearAppenders(); log4js.clearAppenders();
@@ -19,7 +21,8 @@ vows.describe('log4js fileAppender').addBatch({
topic: function () { topic: function () {
var listenersCount = process.listeners('exit').length var listenersCount = process.listeners('exit').length
, logger = log4js.getLogger('default-settings') , logger = log4js.getLogger('default-settings')
, count = 5, logfile; , count = 5
, logfile;
while (count--) { while (count--) {
logfile = path.join(__dirname, '/fa-default-test' + count + '.log'); logfile = path.join(__dirname, '/fa-default-test' + count + '.log');
@@ -133,7 +136,14 @@ vows.describe('log4js fileAppender').addBatch({
fs.readFile(path.join(__dirname, logFiles[0]), "utf8", this.callback); fs.readFile(path.join(__dirname, logFiles[0]), "utf8", this.callback);
}, },
'should be the last log message': function(contents) { 'should be the last log message': function(contents) {
//there's a difference in behaviour between
//old-style streams and new ones (the new ones are
//correct)
if (semver.satisfies(process.version, ">=0.10.0")) {
assert.include(contents, 'This is the fourth log message.'); assert.include(contents, 'This is the fourth log message.');
} else {
assert.isEmpty(contents);
}
} }
}, },
'and the contents of the second file': { 'and the contents of the second file': {
@@ -141,7 +151,14 @@ vows.describe('log4js fileAppender').addBatch({
fs.readFile(path.join(__dirname, logFiles[1]), "utf8", this.callback); fs.readFile(path.join(__dirname, logFiles[1]), "utf8", this.callback);
}, },
'should be the third log message': function(contents) { 'should be the third log message': function(contents) {
//there's a difference in behaviour between
//old-style streams and new ones (the new ones are
//correct)
if (semver.satisfies(process.version, ">=0.10.0")) {
assert.include(contents, 'This is the third log message.'); assert.include(contents, 'This is the third log message.');
} else {
assert.include(contents, 'This is the fourth log message.');
}
} }
}, },
'and the contents of the third file': { 'and the contents of the third file': {
@@ -149,7 +166,14 @@ vows.describe('log4js fileAppender').addBatch({
fs.readFile(path.join(__dirname, logFiles[2]), "utf8", this.callback); fs.readFile(path.join(__dirname, logFiles[2]), "utf8", this.callback);
}, },
'should be the second log message': function(contents) { 'should be the second log message': function(contents) {
//there's a difference in behaviour between
//old-style streams and new ones (the new ones are
//correct)
if (semver.satisfies(process.version, ">=0.10.0")) {
assert.include(contents, 'This is the second log message.'); assert.include(contents, 'This is the second log message.');
} else {
assert.include(contents, 'This is the third log message.');
}
} }
} }
} }
@@ -175,5 +199,65 @@ vows.describe('log4js fileAppender').addBatch({
} }
} }
} }
}).addBatch({
'with node version less than 0.10': {
topic: function() {
var oldStyleStreamCreated = false
, appender = sandbox.require(
'../lib/appenders/file',
{
globals: {
process: {
version: "v0.8.1",
on: function() {}
}
},
requires: {
'../old-streams': {
BufferedWriteStream: function() {
oldStyleStreamCreated = true;
this.on = function() {};
},
RollingFileStream: function() {
this.on = function() {};
}
}
}
}
).appender('cheese.log', null, 1000, 1);
return oldStyleStreamCreated;
},
'should load the old-style streams': function(loaded) {
assert.isTrue(loaded);
}
},
'with node version greater than or equal to 0.10': {
topic: function() {
var oldStyleStreamCreated = false
, appender = sandbox.require(
'../lib/appenders/file',
{
globals: {
process: {
version: "v0.10.1",
on: function() {}
}
},
requires: {
'../streams': {
RollingFileStream: function() {
this.on = function() {};
}
}
}
}
).appender('cheese.log', null, 1000, 1);
return oldStyleStreamCreated;
},
'should load the new streams': function(loaded) {
assert.isFalse(loaded);
}
}
}).export(module); }).export(module);
+6
View File
@@ -56,6 +56,12 @@ vows.describe('log4js-abspath').addBatch({
}; };
} }
} }
},
globals: {
process: {
version: "v0.10.1",
on: function() {}
}
} }
} }
); );
+1 -2
View File
@@ -13,8 +13,7 @@ function setupLogging(category, options) {
sendMail: function (msg, callback) { sendMail: function (msg, callback) {
msgs.push(msg); msgs.push(msg);
callback(null, true); callback(null, true);
}, }
close: function() {}
}; };
} }
}; };
+7 -67
View File
@@ -1,18 +1,11 @@
var vows = require('vows') var vows = require('vows')
, assert = require('assert') , assert = require('assert')
, streams = require('stream')
, fs = require('fs') , fs = require('fs')
, semver = require('semver') , semver = require('semver')
, streams
, DateRollingFileStream , DateRollingFileStream
, testTime = new Date(2012, 8, 12, 10, 37, 11); , testTime = new Date(2012, 8, 12, 10, 37, 11);
if (semver.satisfies(process.version, '>=0.10.0')) {
streams = require('stream');
} else {
streams = require('readable-stream');
}
DateRollingFileStream = require('../../lib/streams').DateRollingFileStream
function cleanUp(filename) { function cleanUp(filename) {
return function() { return function() {
fs.unlink(filename); fs.unlink(filename);
@@ -23,7 +16,10 @@ function now() {
return testTime.getTime(); return testTime.getTime();
} }
vows.describe('DateRollingFileStream').addBatch({ if (semver.satisfies(process.version, '>=0.10.0')) {
DateRollingFileStream = require('../../lib/streams').DateRollingFileStream;
vows.describe('DateRollingFileStream').addBatch({
'arguments': { 'arguments': {
topic: new DateRollingFileStream(__dirname + '/test-date-rolling-file-stream-1', 'yyyy-mm-dd.hh'), topic: new DateRollingFileStream(__dirname + '/test-date-rolling-file-stream-1', 'yyyy-mm-dd.hh'),
teardown: cleanUp(__dirname + '/test-date-rolling-file-stream-1'), teardown: cleanUp(__dirname + '/test-date-rolling-file-stream-1'),
@@ -125,63 +121,7 @@ vows.describe('DateRollingFileStream').addBatch({
} }
} }
} }
},
'with alwaysIncludePattern': {
topic: function() {
var that = this,
testTime = new Date(2012, 8, 12, 0, 10, 12),
stream = new DateRollingFileStream(__dirname + '/test-date-rolling-file-stream-pattern', '.yyyy-MM-dd', {alwaysIncludePattern: true}, now);
stream.write("First message\n", 'utf8', function() {
that.callback(null, stream);
});
},
teardown: cleanUp(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-12'),
'should create a file with the pattern set': {
topic: function(stream) {
fs.readFile(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-12', this.callback);
},
'file should contain first message': function(result) {
assert.equal(result.toString(), "First message\n");
}
},
'when the day changes': {
topic: function(stream) {
testTime = new Date(2012, 8, 13, 0, 10, 12);
stream.write("Second message\n", 'utf8', this.callback);
},
teardown: cleanUp(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-13'),
'the number of files': {
topic: function() {
fs.readdir(__dirname, this.callback);
},
'should be two': function(files) {
assert.equal(files.filter(function(file) { return file.indexOf('test-date-rolling-file-stream-pattern') > -1; }).length, 2);
}
},
'the file with the later date': {
topic: function() {
fs.readFile(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-13', this.callback);
},
'should contain the second message': function(contents) {
assert.equal(contents.toString(), "Second message\n");
}
},
'the file with the date': {
topic: function() {
fs.readFile(__dirname + '/test-date-rolling-file-stream-pattern.2012-09-12', this.callback);
},
'should contain the first message': function(contents) {
assert.equal(contents.toString(), "First message\n");
}
}
}
} }
}).exportTo(module); }).exportTo(module);
}
+7 -9
View File
@@ -3,16 +3,10 @@ var vows = require('vows')
, assert = require('assert') , assert = require('assert')
, events = require('events') , events = require('events')
, fs = require('fs') , fs = require('fs')
, streams = require('stream')
, semver = require('semver') , semver = require('semver')
, streams
, RollingFileStream; , RollingFileStream;
if (semver.satisfies(process.version, '>=0.10.0')) {
streams = require('stream');
} else {
streams = require('readable-stream');
}
RollingFileStream = require('../../lib/streams').RollingFileStream;
function remove(filename) { function remove(filename) {
try { try {
@@ -22,7 +16,10 @@ function remove(filename) {
} }
} }
vows.describe('RollingFileStream').addBatch({ if (semver.satisfies(process.version, '>=0.10.0')) {
RollingFileStream = require('../../lib/streams').RollingFileStream;
vows.describe('RollingFileStream').addBatch({
'arguments': { 'arguments': {
topic: function() { topic: function() {
remove(__dirname + "/test-rolling-file-stream"); remove(__dirname + "/test-rolling-file-stream");
@@ -131,4 +128,5 @@ vows.describe('RollingFileStream').addBatch({
} }
} }
} }
}).exportTo(module); }).exportTo(module);
}