Merge branch 'master' of https://github.com/nomiddlename/log4js-node
This commit is contained in:
commit
18e21ca473
@ -1,14 +1,46 @@
|
|||||||
var log4js = require('./lib/log4js');
|
//The connect/express logger was added to log4js by danbell. This allows connect/express servers to log using log4js.
|
||||||
log4js.addAppender(log4js.fileAppender('cheese.log'), 'cheese');
|
//https://github.com/nomiddlename/log4js-node/wiki/Connect-Logger
|
||||||
|
|
||||||
var logger = log4js.getLogger('cheese');
|
// load modules
|
||||||
logger.setLevel('INFO');
|
var log4js = require('log4js');
|
||||||
|
var express = require("express");
|
||||||
|
var app = express();
|
||||||
|
|
||||||
var app = require('express').createServer();
|
//config
|
||||||
|
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(log4js.connectLogger(logger, { level: log4js.levels.INFO }));
|
app.use(express.favicon(''));
|
||||||
|
// 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) {
|
|
||||||
res.send('hello world\n <a href="/cheese">cheese</a>\n');
|
//route
|
||||||
|
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');
|
||||||
|
@ -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.
|
* - `level` A log4js levels instance. Supports also 'auto'
|
||||||
*
|
*
|
||||||
* Tokens:
|
* Tokens:
|
||||||
*
|
*
|
||||||
@ -26,64 +26,74 @@ 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) {
|
||||||
options = { format: options };
|
options = { format: options };
|
||||||
} else {
|
} else {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var thislogger = logger4js
|
var thislogger = logger4js
|
||||||
, level = levels.toLevel(options.level, levels.INFO)
|
, level = levels.toLevel(options.level, levels.INFO)
|
||||||
, fmt = options.format || ':remote-addr - - ":method :url HTTP/:http-version" :status :content-length ":referrer" ":user-agent"'
|
, fmt = options.format || ':remote-addr - - ":method :url HTTP/:http-version" :status :content-length ":referrer" ":user-agent"'
|
||||||
, nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
|
, nolog = options.nolog ? createNoLogCondition(options.nolog) : null;
|
||||||
|
|
||||||
return function (req, res, next) {
|
return function (req, res, next) {
|
||||||
|
|
||||||
// mount safety
|
// mount safety
|
||||||
if (req._logging) return next();
|
if (req._logging) return next();
|
||||||
|
|
||||||
// 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
|
||||||
|
, statusCode
|
||||||
|
, writeHead = res.writeHead
|
||||||
|
, end = res.end
|
||||||
|
, url = req.originalUrl;
|
||||||
|
|
||||||
var start = +new Date
|
// flag as logging
|
||||||
, statusCode
|
req._logging = true;
|
||||||
, writeHead = res.writeHead
|
|
||||||
, end = res.end
|
|
||||||
, url = req.originalUrl;
|
|
||||||
|
|
||||||
// flag as logging
|
// proxy for statusCode.
|
||||||
req._logging = true;
|
res.writeHead = function(code, headers){
|
||||||
|
res.writeHead = writeHead;
|
||||||
|
res.writeHead(code, headers);
|
||||||
|
res.__statusCode = statusCode = code;
|
||||||
|
res.__headers = headers || {};
|
||||||
|
|
||||||
// proxy for statusCode.
|
//status code response level handling
|
||||||
res.writeHead = function(code, headers){
|
if(options.level === 'auto'){
|
||||||
res.writeHead = writeHead;
|
level = levels.INFO;
|
||||||
res.writeHead(code, headers);
|
if(code >= 300) level = levels.WARN;
|
||||||
res.__statusCode = statusCode = code;
|
if(code >= 400) level = levels.ERROR;
|
||||||
res.__headers = headers || {};
|
} 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.
|
||||||
res.end = function(chunk, encoding) {
|
res.end = function(chunk, encoding) {
|
||||||
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 ('function' == typeof fmt) {
|
if (thislogger.isLevelEnabled(level)) {
|
||||||
var line = fmt(req, res, function(str){ return format(str, req, res); });
|
if (typeof fmt === 'function') {
|
||||||
if (line) thislogger.log(level, line);
|
var line = fmt(req, res, function(str){ return format(str, req, res); });
|
||||||
} else {
|
if (line) thislogger.log(level, line);
|
||||||
thislogger.log(level, format(fmt, req, res));
|
} else {
|
||||||
|
thislogger.log(level, format(fmt, req, res));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//ensure next gets always called
|
//ensure next gets always called
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,25 +106,25 @@ function getLogger(logger4js, options) {
|
|||||||
* @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)
|
||||||
.replace(':status', res.__statusCode || res.statusCode)
|
.replace(':status', res.__statusCode || res.statusCode)
|
||||||
.replace(':response-time', res.responseTime)
|
.replace(':response-time', res.responseTime)
|
||||||
.replace(':date', new Date().toUTCString())
|
.replace(':date', new Date().toUTCString())
|
||||||
.replace(':referrer', req.headers['referer'] || req.headers['referrer'] || '')
|
.replace(':referrer', req.headers['referer'] || req.headers['referrer'] || '')
|
||||||
.replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor)
|
.replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor)
|
||||||
.replace(':remote-addr', req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)))
|
.replace(':remote-addr', req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)))
|
||||||
.replace(':user-agent', req.headers['user-agent'] || '')
|
.replace(':user-agent', req.headers['user-agent'] || '')
|
||||||
.replace(':content-length', (res._headers && res._headers['content-length']) || (res.__headers && res.__headers['Content-Length']) || '-')
|
.replace(':content-length', (res._headers && res._headers['content-length']) || (res.__headers && res.__headers['Content-Length']) || '-')
|
||||||
.replace(/:req\[([^\]]+)\]/g, function(_, field){ return req.headers[field.toLowerCase()]; })
|
.replace(/:req\[([^\]]+)\]/g, function(_, field){ return req.headers[field.toLowerCase()]; })
|
||||||
.replace(/:res\[([^\]]+)\]/g, function(_, field){
|
.replace(/:res\[([^\]]+)\]/g, function(_, field){
|
||||||
return res._headers
|
return res._headers
|
||||||
? (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
|
||||||
@ -143,26 +153,26 @@ function format(str, req, res) {
|
|||||||
* 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 || '';
|
||||||
|
|
||||||
if(nolog instanceof RegExp){
|
if(nolog instanceof RegExp){
|
||||||
if(type === 'string')
|
if(type === 'string')
|
||||||
return nolog.source;
|
return nolog.source;
|
||||||
return nolog;
|
return nolog;
|
||||||
} else if(typeof nolog === 'string'){
|
} else if(typeof nolog === 'string'){
|
||||||
if(type === 'string')
|
if(type === 'string')
|
||||||
return nolog;
|
return nolog;
|
||||||
try{
|
try{
|
||||||
return new RegExp(nolog);
|
return new RegExp(nolog);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else if(nolog instanceof Array){
|
} else if(nolog instanceof Array){
|
||||||
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;
|
||||||
|
@ -24,13 +24,15 @@ function DateRollingFileStream(filename, pattern, options, now) {
|
|||||||
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.baseFilename = filename;
|
||||||
|
this.alwaysIncludePattern = false;
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
if (options.alwaysIncludePattern) {
|
if (options.alwaysIncludePattern) {
|
||||||
filename = filename + this.lastTimeWeWroteSomething;
|
this.alwaysIncludePattern = true;
|
||||||
|
filename = this.baseFilename + this.lastTimeWeWroteSomething;
|
||||||
}
|
}
|
||||||
delete options.alwaysIncludePattern;
|
delete options.alwaysIncludePattern;
|
||||||
if (options === {}) {
|
if (Object.keys(options).length === 0) {
|
||||||
options = null;
|
options = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,18 +55,26 @@ DateRollingFileStream.prototype.shouldRoll = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
DateRollingFileStream.prototype.roll = function(filename, callback) {
|
DateRollingFileStream.prototype.roll = function(filename, callback) {
|
||||||
var that = this,
|
var that = this;
|
||||||
newFilename = this.baseFilename + this.previousTime;
|
|
||||||
|
|
||||||
debug("Starting roll");
|
debug("Starting roll");
|
||||||
|
|
||||||
async.series([
|
if (this.alwaysIncludePattern) {
|
||||||
this.closeTheStream.bind(this),
|
this.filename = this.baseFilename + this.lastTimeWeWroteSomething;
|
||||||
deleteAnyExistingFile,
|
async.series([
|
||||||
renameTheCurrentFile,
|
this.closeTheStream.bind(this),
|
||||||
this.openTheStream.bind(this)
|
this.openTheStream.bind(this)
|
||||||
], callback);
|
], callback);
|
||||||
|
} else {
|
||||||
|
var newFilename = this.baseFilename + this.previousTime;
|
||||||
|
async.series([
|
||||||
|
this.closeTheStream.bind(this),
|
||||||
|
deleteAnyExistingFile,
|
||||||
|
renameTheCurrentFile,
|
||||||
|
this.openTheStream.bind(this)
|
||||||
|
], 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
|
||||||
//so, we'll try to delete the file we're renaming to first
|
//so, we'll try to delete the file we're renaming to first
|
||||||
|
@ -113,6 +113,7 @@ vows.describe('../lib/appenders/dateFile').addBatch({
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
, thisTime = format.asString(options.appenders[0].pattern, new Date());
|
, 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.clearAppenders();
|
||||||
log4js.configure(options);
|
log4js.configure(options);
|
||||||
logger = log4js.getLogger('tests');
|
logger = log4js.getLogger('tests');
|
||||||
@ -122,6 +123,9 @@ vows.describe('../lib/appenders/dateFile').addBatch({
|
|||||||
},
|
},
|
||||||
'should create file with the correct pattern': function(contents) {
|
'should create file with the correct pattern': function(contents) {
|
||||||
assert.include(contents, 'this should be written to the file with the appended date');
|
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');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +125,63 @@ 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);
|
||||||
|
Loading…
Reference in New Issue
Block a user