From ba80dc1588e08015b058e423ccc74e54dba253d3 Mon Sep 17 00:00:00 2001 From: Christo Fogelberg Date: Sun, 8 Mar 2015 19:35:54 +0000 Subject: [PATCH 1/5] Trailing whitespace Sublime removed --- lib/connect-logger.js | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/connect-logger.js b/lib/connect-logger.js index 1f287c0..16eef50 100644 --- a/lib/connect-logger.js +++ b/lib/connect-logger.js @@ -1,8 +1,8 @@ "use strict"; var levels = require("./levels"); -var DEFAULT_FORMAT = ':remote-addr - -' + - ' ":method :url HTTP/:http-version"' + - ' :status :content-length ":referrer"' + +var DEFAULT_FORMAT = ':remote-addr - -' + + ' ":method :url HTTP/:http-version"' + + ' :status :content-length ":referrer"' + ' ":user-agent"'; /** * Log requests with the given `options` or a `format` string. @@ -52,7 +52,7 @@ function getLogger(logger4js, options) { // nologs if (nolog && nolog.test(req.originalUrl)) return next(); if (thislogger.isLevelEnabled(level) || options.level === 'auto') { - + var start = new Date() , statusCode , writeHead = res.writeHead @@ -60,7 +60,7 @@ function getLogger(logger4js, options) { // flag as logging req._logging = true; - + // proxy for statusCode. res.writeHead = function(code, headers){ res.writeHead = writeHead; @@ -77,7 +77,7 @@ function getLogger(logger4js, options) { level = levels.toLevel(options.level, levels.INFO); } }; - + //hook on end request to emit the log entry of the HTTP request. res.on('finish', function() { res.responseTime = new Date() - start; @@ -97,7 +97,7 @@ function getLogger(logger4js, options) { } }); } - + //ensure next gets always called next(); }; @@ -123,20 +123,20 @@ function format(str, req, res) { .replace(':referrer', req.headers.referer || req.headers.referrer || '') .replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor) .replace( - ':remote-addr', req.ip || req._remoteAddress || ( - req.socket && + ':remote-addr', req.ip || req._remoteAddress || ( + req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)) )) .replace(':user-agent', req.headers['user-agent'] || '') .replace( - ':content-length', - (res._headers && res._headers['content-length']) || - (res.__headers && res.__headers['Content-Length']) || + ':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(/:res\[([^\]]+)\]/g, function(_, field){ - return res._headers ? + return res._headers ? (res._headers[field.toLowerCase()] || res.__headers[field]) : (res.__headers && res.__headers[field]); }); @@ -155,9 +155,9 @@ function format(str, req, res) { * NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.gif?fuga * LOGGING http://example.com/hoge.agif * 1.2 in "\\.gif|\\.jpg$" - * NOT LOGGING http://example.com/hoge.gif and + * NOT LOGGING http://example.com/hoge.gif and * http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga - * LOGGING http://example.com/hoge.agif, + * LOGGING http://example.com/hoge.agif, * http://example.com/hoge.ajpg and http://example.com/hoge.jpg?hoge * 1.3 in "\\.(gif|jpe?g|png)$" * NOT LOGGING http://example.com/hoge.gif and http://example.com/hoge.jpeg @@ -175,15 +175,15 @@ function createNoLogCondition(nolog) { if (nolog) { if (nolog instanceof RegExp) { regexp = nolog; - } - + } + if (typeof nolog === 'string') { regexp = new RegExp(nolog); } - + if (Array.isArray(nolog)) { var regexpsAsStrings = nolog.map( - function convertToStrings(o) { + function convertToStrings(o) { return o.source ? o.source : o; } ); From 24268422cfa58bcfe6550f9e1837b1a8e034b5b3 Mon Sep 17 00:00:00 2001 From: Christo Fogelberg Date: Tue, 10 Mar 2015 06:13:49 +0000 Subject: [PATCH 2/5] lib/connect-logger.js - format takes tokens array instead of req, res --- lib/connect-logger.js | 71 +++++++++++++++++++++++++------------------ package.json | 3 +- 2 files changed, 44 insertions(+), 30 deletions(-) diff --git a/lib/connect-logger.js b/lib/connect-logger.js index 16eef50..8162b50 100644 --- a/lib/connect-logger.js +++ b/lib/connect-logger.js @@ -1,5 +1,6 @@ "use strict"; var levels = require("./levels"); +var _ = require('underscore'); var DEFAULT_FORMAT = ':remote-addr - -' + ' ":method :url HTTP/:http-version"' + ' :status :content-length ":referrer"' + @@ -88,11 +89,12 @@ function getLogger(logger4js, options) { if(res.statusCode >= 400) level = levels.ERROR; } if (thislogger.isLevelEnabled(level)) { + var default_tokens = assemble_tokens(req, res); 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, default_tokens); }); if (line) thislogger.log(level, line); } else { - thislogger.log(level, format(fmt, req, res)); + thislogger.log(level, format(fmt, default_tokens)); } } }); @@ -103,6 +105,40 @@ function getLogger(logger4js, options) { }; } +/** + * Adds custom {token, replacement} objects to defaults, overwriting the defaults if any tokens clash + * + * @param {IncomingMessage} req + * @param {ServerResponse} res + * @param {Array} custom_tokens [{ token: string-or-regexp, replacement: string-or-replace-function }] + * @return {Array} + */ +function assemble_tokens(req, res) { + var default_tokens = []; + default_tokens.push({ token: ':url', replacement: req.originalUrl }); + default_tokens.push({ token: ':method', replacement: req.method }); + default_tokens.push({ token: ':status', replacement: res.__statusCode || res.statusCode }); + default_tokens.push({ token: ':response-time', replacement: res.responseTime }); + default_tokens.push({ token: ':date', replacement: new Date().toUTCString() }); + default_tokens.push({ token: ':referrer', replacement: req.headers.referer || req.headers.referrer || '' }); + default_tokens.push({ token: ':http-version', replacement: req.httpVersionMajor + '.' + req.httpVersionMinor }); + default_tokens.push({ token: ':remote-addr', replacement: req.ip || req._remoteAddress || + (req.socket && (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress))) }); + default_tokens.push({ token: ':user-agent', replacement: req.headers['user-agent'] }); + default_tokens.push({ token: ':content-length', replacement: (res._headers && res._headers['content-length']) || + (res.__headers && res.__headers['Content-Length']) || '-' }); + default_tokens.push({ token: /:req\[([^\]]+)\]/g, replacement: function(_, field) { + return req.headers[field.toLowerCase()]; + } }); + default_tokens.push({ token: /:res\[([^\]]+)\]/g, replacement: function(_, field) { + return res._headers ? + (res._headers[field.toLowerCase()] || res.__headers[field]) + : (res.__headers && res.__headers[field]); + } }); + + return default_tokens; +}; + /** * Return formatted log line. * @@ -113,33 +149,10 @@ function getLogger(logger4js, options) { * @api private */ -function format(str, req, res) { - return str - .replace(':url', req.originalUrl) - .replace(':method', req.method) - .replace(':status', res.__statusCode || res.statusCode) - .replace(':response-time', res.responseTime) - .replace(':date', new Date().toUTCString()) - .replace(':referrer', req.headers.referer || req.headers.referrer || '') - .replace(':http-version', req.httpVersionMajor + '.' + req.httpVersionMinor) - .replace( - ':remote-addr', req.ip || req._remoteAddress || ( - req.socket && - (req.socket.remoteAddress || (req.socket.socket && req.socket.socket.remoteAddress)) - )) - .replace(':user-agent', req.headers['user-agent'] || '') - .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(/:res\[([^\]]+)\]/g, function(_, field){ - return res._headers ? - (res._headers[field.toLowerCase()] || res.__headers[field]) - : (res.__headers && res.__headers[field]); - }); +function format(str, tokens) { + return _.reduce(tokens, function(current_string, token) { + return current_string.replace(token.token, token.replacement); + }, str); } /** diff --git a/package.json b/package.json index 0a2231e..bf7ba8d 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,8 @@ "dependencies": { "async": "~0.2.0", "readable-stream": "~1.0.2", - "semver": "~1.1.4" + "semver": "~1.1.4", + "underscore": "1.8.2" }, "devDependencies": { "vows": "0.7.0", From 25c543f8ae94e43e471874b573d1d4fd762c7187 Mon Sep 17 00:00:00 2001 From: Christo Fogelberg Date: Tue, 10 Mar 2015 06:18:02 +0000 Subject: [PATCH 3/5] lib/connect-logger.js - allow options.tokens --- lib/connect-logger.js | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/connect-logger.js b/lib/connect-logger.js index 8162b50..a53f7e0 100644 --- a/lib/connect-logger.js +++ b/lib/connect-logger.js @@ -89,12 +89,12 @@ function getLogger(logger4js, options) { if(res.statusCode >= 400) level = levels.ERROR; } if (thislogger.isLevelEnabled(level)) { - var default_tokens = assemble_tokens(req, res); + var combined_tokens = assemble_tokens(req, res, options.tokens || []); if (typeof fmt === 'function') { - var line = fmt(req, res, function(str){ return format(str, default_tokens); }); + var line = fmt(req, res, function(str){ return format(str, combined_tokens); }); if (line) thislogger.log(level, line); } else { - thislogger.log(level, format(fmt, default_tokens)); + thislogger.log(level, format(fmt, combined_tokens)); } } }); @@ -113,7 +113,19 @@ function getLogger(logger4js, options) { * @param {Array} custom_tokens [{ token: string-or-regexp, replacement: string-or-replace-function }] * @return {Array} */ -function assemble_tokens(req, res) { +function assemble_tokens(req, res, custom_tokens) { + var array_unique_tokens = function(array) { + var a = array.concat(); + for(var i=0; i Date: Tue, 10 Mar 2015 06:24:57 +0000 Subject: [PATCH 4/5] test/connect-logger-test.js - trailing whitespace Sublime removed --- test/connect-logger-test.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/test/connect-logger-test.js b/test/connect-logger-test.js index 96d0409..1f50416 100644 --- a/test/connect-logger-test.js +++ b/test/connect-logger-test.js @@ -10,7 +10,7 @@ function MockLogger() { var that = this; this.messages = []; - + this.log = function(level, message, exception) { that.messages.push({ level: level, message: message }); }; @@ -18,7 +18,7 @@ function MockLogger() { this.isLevelEnabled = function(level) { return level.isGreaterThanOrEqualTo(that.level); }; - + this.level = levels.TRACE; } @@ -40,7 +40,7 @@ function MockRequest(remoteAddr, method, originalUrl, headers) { function MockResponse() { var r = this; - this.end = function(chunk, encoding) { + this.end = function(chunk, encoding) { r.emit('finish'); }; @@ -66,7 +66,7 @@ vows.describe('log4js connect logger').addBatch({ var clm = require('../lib/connect-logger'); return clm; }, - + 'should return a "connect logger" factory' : function(clm) { assert.isObject(clm); }, @@ -77,12 +77,12 @@ vows.describe('log4js connect logger').addBatch({ var cl = clm.connectLogger(ml); return cl; }, - + 'should return a "connect logger"': function(cl) { assert.isFunction(cl); } }, - + 'log events' : { topic: function(clm) { var ml = new MockLogger(); @@ -113,7 +113,7 @@ vows.describe('log4js connect logger').addBatch({ request(cl, 'GET', 'http://url', 200); return ml.messages; }, - + 'check message': function(messages) { assert.isArray(messages); assert.isEmpty(messages); @@ -130,7 +130,7 @@ vows.describe('log4js connect logger').addBatch({ setTimeout(function() { cb(null, ml.messages); },10); }, - + 'check message': function(messages) { assert.isArray(messages); assert.equal(messages.length, 1); @@ -168,7 +168,7 @@ vows.describe('log4js connect logger').addBatch({ request(cl, 'GET', 'http://meh', 500); setTimeout(function() { cb(null, ml.messages); - },10); + },10); }, 'should use INFO for 2xx': function(messages) { @@ -198,7 +198,7 @@ vows.describe('log4js connect logger').addBatch({ request(cl, 'GET', 'http://blah', 200); setTimeout(function() { cb(null, ml.messages); - },10); + },10); }, 'should call the format function': function(messages) { @@ -213,8 +213,8 @@ vows.describe('log4js connect logger').addBatch({ ml.level = levels.INFO; var cl = clm.connectLogger(ml, ':req[Content-Type]'); request( - cl, - 'GET', 'http://blah', 200, + cl, + 'GET', 'http://blah', 200, { 'Content-Type': 'application/json' } ); setTimeout(function() { @@ -246,7 +246,7 @@ vows.describe('log4js connect logger').addBatch({ 'should output the response header': function(messages) { assert.equal(messages[0].message, 'application/cheese'); } - } - + } + } }).export(module); From 41504a755d0b6cd0627ae9e80871b4c6242e4cf3 Mon Sep 17 00:00:00 2001 From: Christo Fogelberg Date: Tue, 10 Mar 2015 06:37:53 +0000 Subject: [PATCH 5/5] test/connect-logger-test.js - tests for custom tokens --- test/connect-logger-test.js | 45 ++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/test/connect-logger-test.js b/test/connect-logger-test.js index 1f50416..dd1e717 100644 --- a/test/connect-logger-test.js +++ b/test/connect-logger-test.js @@ -246,7 +246,50 @@ vows.describe('log4js connect logger').addBatch({ 'should output the response header': function(messages) { assert.equal(messages[0].message, 'application/cheese'); } - } + }, + + 'log events with custom token' : { + topic: function(clm) { + var ml = new MockLogger(); + var cb = this.callback; + ml.level = levels.INFO; + var cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url :custom_string', tokens: [{ + token: ':custom_string', replacement: 'fooBAR' + }] } ); + request(cl, 'GET', 'http://url', 200); + setTimeout(function() { + cb(null, ml.messages); + },10); + }, + 'check message': function(messages) { + assert.isArray(messages); + assert.equal(messages.length, 1); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); + assert.equal(messages[0].message, 'GET http://url fooBAR'); + } + }, + + 'log events with custom override token' : { + topic: function(clm) { + var ml = new MockLogger(); + var cb = this.callback; + ml.level = levels.INFO; + var cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url :date', tokens: [{ + token: ':date', replacement: "20150310" + }] } ); + request(cl, 'GET', 'http://url', 200); + setTimeout(function() { + cb(null, ml.messages); + },10); + }, + + 'check message': function(messages) { + assert.isArray(messages); + assert.equal(messages.length, 1); + assert.ok(levels.INFO.isEqualTo(messages[0].level)); + assert.equal(messages[0].message, 'GET http://url 20150310'); + } + } } }).export(module);