diff --git a/lib/connect-logger.js b/lib/connect-logger.js deleted file mode 100644 index a441be5..0000000 --- a/lib/connect-logger.js +++ /dev/null @@ -1,194 +0,0 @@ -"use strict"; -var levels = require("./levels"); -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. - * - * Options: - * - * - `format` Format string, see below for tokens - * - `level` A log4js levels instance. Supports also 'auto' - * - * Tokens: - * - * - `:req[header]` ex: `:req[Accept]` - * - `:res[header]` ex: `:res[Content-Length]` - * - `:http-version` - * - `:response-time` - * - `:remote-addr` - * - `:date` - * - `:method` - * - `:url` - * - `:referrer` - * - `:user-agent` - * - `:status` - * - * @param {String|Function|Object} format or options - * @return {Function} - * @api public - */ - -function getLogger(logger4js, options) { - if ('object' == typeof options) { - options = options || {}; - } else if (options) { - options = { format: options }; - } else { - options = {}; - } - - var thislogger = logger4js - , level = levels.toLevel(options.level, levels.INFO) - , fmt = options.format || DEFAULT_FORMAT - , nolog = options.nolog ? createNoLogCondition(options.nolog) : null; - - return function (req, res, next) { - // mount safety - if (req._logging) return next(); - - // nologs - if (nolog && nolog.test(req.originalUrl)) return next(); - if (thislogger.isLevelEnabled(level) || options.level === 'auto') { - - var start = new Date() - , statusCode - , writeHead = res.writeHead - , end = res.end - , url = req.originalUrl; - - // flag as logging - req._logging = true; - - // proxy for statusCode. - res.writeHead = function(code, headers){ - res.writeHead = writeHead; - res.writeHead(code, headers); - res.__statusCode = statusCode = code; - 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. - res.end = function(chunk, encoding) { - res.end = end; - res.end(chunk, encoding); - res.responseTime = new Date() - start; - if (thislogger.isLevelEnabled(level)) { - if (typeof fmt === 'function') { - var line = fmt(req, res, function(str){ return format(str, req, res); }); - if (line) thislogger.log(level, line); - } else { - thislogger.log(level, format(fmt, req, res)); - } - } - }; - } - - //ensure next gets always called - next(); - }; -} - -/** - * Return formatted log line. - * - * @param {String} str - * @param {IncomingMessage} req - * @param {ServerResponse} res - * @return {String} - * @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.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]); - }); -} - -/** - * Return RegExp Object about nolog - * - * @param {String} nolog - * @return {RegExp} - * @api private - * - * syntax - * 1. String - * 1.1 "\\.gif" - * 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 - * http://example.com/hoge.gif?fuga and http://example.com/hoge.jpg?fuga - * 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 - * LOGGING http://example.com/hoge.gif?uid=2 and http://example.com/hoge.jpg?pid=3 - * 2. RegExp - * 2.1 in /\.(gif|jpe?g|png)$/ - * SAME AS 1.3 - * 3. Array - * 3.1 ["\\.jpg$", "\\.png", "\\.gif"] - * SAME AS "\\.jpg|\\.png|\\.gif" - */ -function createNoLogCondition(nolog) { - var regexp = null; - - 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) { - return o.source ? o.source : o; - } - ); - regexp = new RegExp(regexpsAsStrings.join('|')); - } - } - - return regexp; -} - -exports.connectLogger = getLogger; diff --git a/test/connect-logger-test.js b/test/connect-logger-test.js deleted file mode 100644 index 5a0caa7..0000000 --- a/test/connect-logger-test.js +++ /dev/null @@ -1,226 +0,0 @@ -/* jshint maxparams:7 */ -"use strict"; -var vows = require('vows') -, assert = require('assert') -, levels = require('../lib/levels'); - -function MockLogger() { - - var that = this; - this.messages = []; - - this.log = function(level, message, exception) { - that.messages.push({ level: level, message: message }); - }; - - this.isLevelEnabled = function(level) { - return level.isGreaterThanOrEqualTo(that.level); - }; - - this.level = levels.TRACE; - -} - -function MockRequest(remoteAddr, method, originalUrl, headers) { - - this.socket = { remoteAddress: remoteAddr }; - this.originalUrl = originalUrl; - this.method = method; - this.httpVersionMajor = '5'; - this.httpVersionMinor = '0'; - this.headers = headers || {}; - - var self = this; - Object.keys(this.headers).forEach(function(key) { - self.headers[key.toLowerCase()] = self.headers[key]; - }); -} - -function MockResponse() { - - this.end = function(chunk, encoding) { - }; - - this.writeHead = function(code, headers) { - }; - -} - -function request(cl, method, url, code, reqHeaders, resHeaders) { - var req = new MockRequest('my.remote.addr', method, url, reqHeaders); - var res = new MockResponse(); - cl(req, res, function() {}); - res.writeHead(code, resHeaders); - res.end('chunk','encoding'); -} - -vows.describe('log4js connect logger').addBatch({ - 'getConnectLoggerModule': { - topic: function() { - var clm = require('../lib/connect-logger'); - return clm; - }, - - 'should return a "connect logger" factory' : function(clm) { - assert.isObject(clm); - }, - - 'take a log4js logger and return a "connect logger"' : { - topic: function(clm) { - var ml = new MockLogger(); - 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(); - var cl = clm.connectLogger(ml); - request(cl, 'GET', 'http://url', 200); - return ml.messages; - }, - - 'check message': function(messages) { - assert.isArray(messages); - assert.equal(messages.length, 1); - assert.ok(levels.INFO.isEqualTo(messages[0].level)); - assert.include(messages[0].message, 'GET'); - assert.include(messages[0].message, 'http://url'); - assert.include(messages[0].message, 'my.remote.addr'); - assert.include(messages[0].message, '200'); - } - }, - - 'log events with level below logging level' : { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.FATAL; - var cl = clm.connectLogger(ml); - request(cl, 'GET', 'http://url', 200); - return ml.messages; - }, - - 'check message': function(messages) { - assert.isArray(messages); - assert.isEmpty(messages); - } - }, - - 'log events with non-default level and custom format' : { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.INFO; - var cl = clm.connectLogger(ml, { level: levels.INFO, format: ':method :url' } ); - request(cl, 'GET', 'http://url', 200); - return ml.messages; - }, - - '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'); - } - }, - - 'logger with options as string': { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.INFO; - var cl = clm.connectLogger(ml, ':method :url'); - request(cl, 'POST', 'http://meh', 200); - return ml.messages; - }, - 'should use the passed in format': function(messages) { - assert.equal(messages[0].message, 'POST http://meh'); - } - }, - - 'auto log levels': { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.INFO; - var cl = clm.connectLogger(ml, { level: 'auto', format: ':method :url' }); - request(cl, 'GET', 'http://meh', 200); - request(cl, 'GET', 'http://meh', 201); - request(cl, 'GET', 'http://meh', 302); - request(cl, 'GET', 'http://meh', 404); - request(cl, 'GET', 'http://meh', 500); - return ml.messages; - }, - - 'should use INFO for 2xx': function(messages) { - assert.ok(levels.INFO.isEqualTo(messages[0].level)); - assert.ok(levels.INFO.isEqualTo(messages[1].level)); - }, - - 'should use WARN for 3xx': function(messages) { - assert.ok(levels.WARN.isEqualTo(messages[2].level)); - }, - - 'should use ERROR for 4xx': function(messages) { - assert.ok(levels.ERROR.isEqualTo(messages[3].level)); - }, - - 'should use ERROR for 5xx': function(messages) { - assert.ok(levels.ERROR.isEqualTo(messages[4].level)); - } - }, - - 'format using a function': { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.INFO; - var cl = clm.connectLogger(ml, function(req, res, formatFn) { return "I was called"; }); - request(cl, 'GET', 'http://blah', 200); - return ml.messages; - }, - - 'should call the format function': function(messages) { - assert.equal(messages[0].message, 'I was called'); - } - }, - - 'format that includes request headers': { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.INFO; - var cl = clm.connectLogger(ml, ':req[Content-Type]'); - request( - cl, - 'GET', 'http://blah', 200, - { 'Content-Type': 'application/json' } - ); - return ml.messages; - }, - 'should output the request header': function(messages) { - assert.equal(messages[0].message, 'application/json'); - } - }, - - 'format that includes response headers': { - topic: function(clm) { - var ml = new MockLogger(); - ml.level = levels.INFO; - var cl = clm.connectLogger(ml, ':res[Content-Type]'); - request( - cl, - 'GET', 'http://blah', 200, - null, - { 'Content-Type': 'application/cheese' } - ); - return ml.messages; - }, - - 'should output the response header': function(messages) { - assert.equal(messages[0].message, 'application/cheese'); - } - } - - } -}).export(module);