From 3289692f4f579f402541d6fcfe1a6adc0e73365c Mon Sep 17 00:00:00 2001 From: brendancwood Date: Tue, 27 Feb 2018 06:07:24 -0800 Subject: [PATCH 01/59] allow msg to accept a function to customize output string (#160) * allow msg to accept a function to customize output string * use if/else, make errorLogger accept msg function * data is a better parameter name for the template object * update README --- Readme.md | 4 ++-- index.js | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 94bc2cb..25c8e6d 100644 --- a/Readme.md +++ b/Readme.md @@ -65,7 +65,7 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req transports: [], // list of all winston transports instances to use. winstonInstance: , // a winston logger instance. If this is provided the transports option is ignored. level: String or function(req, res) { return String; }, // log level to use, the default is "info". Assign a function to dynamically set the level based on request and response, or a string to statically set it always at that level. statusLevels must be false for this setting to be used. - msg: String // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}". + msg: String or function // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } expressFormat: Boolean, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors when colorize set to true colorize: Boolean, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red). meta: Boolean, // control whether you want to log the meta data about the request (default to true). @@ -110,7 +110,7 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE ``` js transports: [], // list of all winston transports instances to use. winstonInstance: , // a winston logger instance. If this is provided the transports option is ignored - msg: String // customize the default logging message. E.g. "{{err.message}} {{res.statusCode}} {{req.method}}". + msg: String or function // customize the default logging message. E.g. "{{err.message}} {{res.statusCode}} {{req.method}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } baseMeta: Object, // default meta data to be added to log, this will be merged with the error data. metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. requestFilter: function (req, propName) { return req[propName]; } // A function to filter/return request values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. diff --git a/index.js b/index.js index 1a1bab7..900444e 100644 --- a/index.js +++ b/index.js @@ -125,9 +125,9 @@ exports.errorLogger = function errorLogger(options) { options.dynamicMeta = options.dynamicMeta || function(req, res, err) { return null; }; // Using mustache style templating - var template = _.template(options.msg, { - interpolate: /\{\{([\s\S]+?)\}\}/g - }); + var getTemplate = function(msg, data) { + return _.template(msg, {interpolate: /\{\{([\s\S]+?)\}\}/g})(data) + }; return function (err, req, res, next) { @@ -150,8 +150,10 @@ exports.errorLogger = function errorLogger(options) { var level = _.isFunction(options.level) ? options.level(req, res, err) : options.level; + options.msg = typeof options.msg === 'function' ? options.msg(req, res) : options.msg; + // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback - options.winstonInstance.log(level, template({err: err, req: req, res: res}), exceptionMeta); + options.winstonInstance.log(level, getTemplate(options.msg, {err: err, req: req, res: res}), exceptionMeta); next(err); }; @@ -315,6 +317,18 @@ exports.logger = function logger(options) { coloredRes.statusCode = chalk[statusColor](res.statusCode); } + var msgFormat + if (!options.expressFormat) { + msgFormat = typeof options.msg === 'function' ? options.msg(req, res) : options.msg + } else { + msgFormat = expressMsgFormat + } + + // Using mustache style templating + var template = _.template(msgFormat, { + interpolate: /\{\{(.+?)\}\}/g + }); + var msg = template({req: req, res: _.assign({}, res, coloredRes)}); // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback From 2c4ea6785e7a28df750c441e0de79f3b6b22a1d0 Mon Sep 17 00:00:00 2001 From: Lorenzo Fundaro Date: Wed, 28 Feb 2018 12:04:49 +0100 Subject: [PATCH 02/59] move order of middleware definitions --- Readme.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index 25c8e6d..2010a05 100644 --- a/Readme.md +++ b/Readme.md @@ -141,12 +141,7 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre return next(new Error("This is an error and it should be logged to the console")); }); - app.get('/', function(req, res, next) { - res.write('This is a normal request, it should be logged to the console too'); - res.end(); - }); - - // express-winston logger makes sense BEFORE the router. + // express-winston logger makes sense BEFORE the router and and app-defined methods. app.use(expressWinston.logger({ transports: [ new winston.transports.Console({ @@ -156,6 +151,11 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre ] })); + app.get('/', function(req, res, next) { + res.write('This is a normal request, it should be logged to the console too'); + res.end(); + }); + // Now we can tell the app to use our routing code: app.use(router); From afa93325e05b00b7dd8de77d558766b5aadb8c1d Mon Sep 17 00:00:00 2001 From: Lorenzo Fundaro Date: Wed, 28 Feb 2018 18:45:09 +0100 Subject: [PATCH 03/59] register / on router and not on app --- Readme.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Readme.md b/Readme.md index 2010a05..3b45af6 100644 --- a/Readme.md +++ b/Readme.md @@ -141,6 +141,11 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre return next(new Error("This is an error and it should be logged to the console")); }); + router.get('/', function(req, res, next) { + res.write('This is a normal request, it should be logged to the console too'); + res.end(); + }); + // express-winston logger makes sense BEFORE the router and and app-defined methods. app.use(expressWinston.logger({ transports: [ @@ -151,11 +156,6 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre ] })); - app.get('/', function(req, res, next) { - res.write('This is a normal request, it should be logged to the console too'); - res.end(); - }); - // Now we can tell the app to use our routing code: app.use(router); From c7dbc20f3b43dc49f785481523f7caf3a432e57b Mon Sep 17 00:00:00 2001 From: Lorenzo Fundaro Date: Wed, 28 Feb 2018 18:46:13 +0100 Subject: [PATCH 04/59] changed comment --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 3b45af6..6aff27b 100644 --- a/Readme.md +++ b/Readme.md @@ -146,7 +146,7 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre res.end(); }); - // express-winston logger makes sense BEFORE the router and and app-defined methods. + // express-winston logger makes sense BEFORE the router app.use(expressWinston.logger({ transports: [ new winston.transports.Console({ From acb5de2a15574067a5e5320dbf3991570977adeb Mon Sep 17 00:00:00 2001 From: mehmet Date: Fri, 9 Mar 2018 16:16:21 +0300 Subject: [PATCH 05/59] added exceptionToMeta method for filtering returned meta object --- Readme.md | 1 + index.js | 5 +++-- test/test.js | 27 +++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 6aff27b..f4681e7 100644 --- a/Readme.md +++ b/Readme.md @@ -117,6 +117,7 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE requestWhitelist: [String] // Array of request properties to log. Overrides global requestWhitelist for this instance level: String or function(req, res, err) { return String; }// custom log level for errors (default is 'error'). Assign a function to dynamically set the log level based on request, response, and the exact error. dynamicMeta: function(req, res, err) { return [Object]; } // Extract additional meta data from request or response (typically req.user data if using passport). meta must be true for this function to be activated + exceptionToMeta: function(error){return Object; } // Function to format the returned meta information on error log. If not given `winston.exception.getAllInfo` will be used by default ``` To use winston's existing transports, set `transports` to the values (as in key-value) of the `winston.default.transports` object. This may be done, for example, by using underscorejs: `transports: _.values(winston.default.transports)`. diff --git a/index.js b/index.js index 900444e..893e8a2 100644 --- a/index.js +++ b/index.js @@ -123,6 +123,7 @@ exports.errorLogger = function errorLogger(options) { options.metaField = options.metaField || null; options.level = options.level || 'error'; options.dynamicMeta = options.dynamicMeta || function(req, res, err) { return null; }; + options.exceptionToMeta = options.exceptionToMeta || winston.exception.getAllInfo; // Using mustache style templating var getTemplate = function(msg, data) { @@ -131,8 +132,8 @@ exports.errorLogger = function errorLogger(options) { return function (err, req, res, next) { - // Let winston gather all the error data. - var exceptionMeta = winston.exception.getAllInfo(err); + // Let winston gather all the error data + var exceptionMeta = options.exceptionToMeta(err); exceptionMeta.req = filterObject(req, options.requestWhitelist, options.requestFilter); if(options.dynamicMeta) { diff --git a/test/test.js b/test/test.js index a611dd2..da9948c 100644 --- a/test/test.js +++ b/test/test.js @@ -229,6 +229,33 @@ describe('express-winston', function () { }); }); + describe.only('exceptionToMeta option', function () { + it('should, use exceptionToMeta function when given', function () { + function exceptionToMeta(error) { + return { + stack: error.stack && error.stack.split('\n') + }; + } + + var testHelperOptions = { loggerOptions: { exceptionToMeta: exceptionToMeta } }; + return errorLoggerTestHelper(testHelperOptions).then(function (result) { + result.log.meta.stack.should.be.ok(); + result.log.meta.should.not.have.property('trace'); + }); + }); + + it('should, use getAllInfo function when not given', function () { + var testHelperOptions = { loggerOptions: { } }; + return errorLoggerTestHelper(testHelperOptions).then(function (result) { + result.log.meta.should.have.property('date'); + result.log.meta.should.have.property('process'); + result.log.meta.should.have.property('os'); + result.log.meta.should.have.property('trace'); + result.log.meta.should.have.property('stack'); + }); + }); + }); + describe('metaField option', function () { it('should, when using a custom metaField, log the custom metaField', function () { var testHelperOptions = {loggerOptions: {metaField: 'metaField'}}; From 134adf14a7aaed7eba2b33c937a9e905d059b061 Mon Sep 17 00:00:00 2001 From: mehmet Date: Fri, 9 Mar 2018 16:35:25 +0300 Subject: [PATCH 06/59] blacklistedMetaFields option is added --- Readme.md | 1 + index.js | 3 ++- test/test.js | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index f4681e7..4faba51 100644 --- a/Readme.md +++ b/Readme.md @@ -118,6 +118,7 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE level: String or function(req, res, err) { return String; }// custom log level for errors (default is 'error'). Assign a function to dynamically set the log level based on request, response, and the exact error. dynamicMeta: function(req, res, err) { return [Object]; } // Extract additional meta data from request or response (typically req.user data if using passport). meta must be true for this function to be activated exceptionToMeta: function(error){return Object; } // Function to format the returned meta information on error log. If not given `winston.exception.getAllInfo` will be used by default + blacklistedMetaFields: [String] // fields to blacklist from meta data ``` To use winston's existing transports, set `transports` to the values (as in key-value) of the `winston.default.transports` object. This may be done, for example, by using underscorejs: `transports: _.values(winston.default.transports)`. diff --git a/index.js b/index.js index 893e8a2..777dba3 100644 --- a/index.js +++ b/index.js @@ -124,6 +124,7 @@ exports.errorLogger = function errorLogger(options) { options.level = options.level || 'error'; options.dynamicMeta = options.dynamicMeta || function(req, res, err) { return null; }; options.exceptionToMeta = options.exceptionToMeta || winston.exception.getAllInfo; + options.blacklistedMetaFields = options.blacklistedMetaFields || []; // Using mustache style templating var getTemplate = function(msg, data) { @@ -133,7 +134,7 @@ exports.errorLogger = function errorLogger(options) { return function (err, req, res, next) { // Let winston gather all the error data - var exceptionMeta = options.exceptionToMeta(err); + var exceptionMeta = _.omit(options.exceptionToMeta(err), options.blacklistedMetaFields); exceptionMeta.req = filterObject(req, options.requestWhitelist, options.requestFilter); if(options.dynamicMeta) { diff --git a/test/test.js b/test/test.js index da9948c..1946e68 100644 --- a/test/test.js +++ b/test/test.js @@ -256,6 +256,20 @@ describe('express-winston', function () { }); }); + describe.only('blacklistedMetaFields option', function () { + it('should, remove given fields from the meta result', function () { + var testHelperOptionsWithBlacklist = { loggerOptions: { blacklistedMetaFields: ['trace'] } }; + return errorLoggerTestHelper(testHelperOptionsWithBlacklist).then(function (result) { + result.log.meta.should.not.have.property('trace'); + }); + + var testHelperOptionsWithoutBlacklist = { loggerOptions: {} }; + return errorLoggerTestHelper(testHelperOptionsWithoutBlacklist).then(function (result) { + result.log.meta.should.have.property('trace'); + }); + }); + }); + describe('metaField option', function () { it('should, when using a custom metaField, log the custom metaField', function () { var testHelperOptions = {loggerOptions: {metaField: 'metaField'}}; From caa0feca57bfec217eb71aa77c4ca259d6338588 Mon Sep 17 00:00:00 2001 From: mehmet Date: Fri, 9 Mar 2018 16:36:21 +0300 Subject: [PATCH 07/59] removed only options from tests --- test/test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test.js b/test/test.js index 1946e68..d72b92a 100644 --- a/test/test.js +++ b/test/test.js @@ -229,7 +229,7 @@ describe('express-winston', function () { }); }); - describe.only('exceptionToMeta option', function () { + describe('exceptionToMeta option', function () { it('should, use exceptionToMeta function when given', function () { function exceptionToMeta(error) { return { @@ -256,7 +256,7 @@ describe('express-winston', function () { }); }); - describe.only('blacklistedMetaFields option', function () { + describe('blacklistedMetaFields option', function () { it('should, remove given fields from the meta result', function () { var testHelperOptionsWithBlacklist = { loggerOptions: { blacklistedMetaFields: ['trace'] } }; return errorLoggerTestHelper(testHelperOptionsWithBlacklist).then(function (result) { From 878fe2407c0ecfbffb700daccf11f59304abd60f Mon Sep 17 00:00:00 2001 From: bithavoc Date: Thu, 5 Apr 2018 18:18:18 -0500 Subject: [PATCH 08/59] Version bump v2.5.1 #174 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 76a9c77..3f23c09 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "2.5.0", + "version": "2.5.1", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 2492d5f0b268a7940173289d6661b6331cf524ad Mon Sep 17 00:00:00 2001 From: Liran Date: Wed, 13 Jun 2018 14:11:20 +0300 Subject: [PATCH 09/59] mistake in readme - wrong variable of req _bodyBlacklist -> _routeBlacklist _bodyWhitelist -> _routeWhitelists --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 6aff27b..037912f 100644 --- a/Readme.md +++ b/Readme.md @@ -368,7 +368,7 @@ Blacklisting supports only the `body` property. }); ``` -If both `req._bodyWhitelist.body` and `req._bodyBlacklist.body` are set the result will be the white listed properties +If both `req._routeWhitelists.body` and `req._routeBlacklists.body` are set the result will be the white listed properties excluding any black listed ones. In the above example, only 'email' and 'age' would be included. From a3ebbb33163b233c368c80a3446b54231c6642ba Mon Sep 17 00:00:00 2001 From: Florian Traber Date: Mon, 18 Jun 2018 16:40:56 +0200 Subject: [PATCH 10/59] fix: limit winston to smaller 3, as winston 3 breaks express-winston. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f23c09..061c723 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "winston": ">=1.x" }, "peerDependencies": { - "winston": ">=1.x" + "winston": ">=1.x <3" }, "engines": { "node": ">=0.10.0" From 6b0d8b64c36d9962ded9ed4501d8b6e2f271a829 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Mon, 16 Jul 2018 10:29:27 -0500 Subject: [PATCH 11/59] Version bump 2.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3f23c09..4e305a9 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "2.5.1", + "version": "2.6.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 0bfbcbb7070ca97270b1e373fbbd9b96d778ce88 Mon Sep 17 00:00:00 2001 From: Johan Hernandez Date: Wed, 8 Aug 2018 22:06:02 -0500 Subject: [PATCH 12/59] Reactivate CI (#185) --- .travis.yml | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a87f582..53b4f4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,9 @@ node_js: - "4" - "6" - "7" + - "8" + - "9" + - "10" script: - "test $TRAVIS_NODE_VERSION = '0.6' || npm test" diff --git a/package.json b/package.json index c82df9a..0bc8726 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "promise": "^7.1.1", "should": "^8.2.2", "travis-cov": "^0.2.5", - "winston": ">=1.x" + "winston": ">=1.x <3" }, "peerDependencies": { "winston": ">=1.x <3" From 93df82ef9feff467bdedf25478ca62dbd05effa5 Mon Sep 17 00:00:00 2001 From: rosston Date: Wed, 8 Aug 2018 21:46:20 -0400 Subject: [PATCH 13/59] Upgrade to winston@3 express-winston _shouldn't_ have any breaking changes as a result. --- index.js | 15 ++++++++++----- package.json | 5 +++-- test/test.js | 25 ++++++++++++++----------- 3 files changed, 27 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index 777dba3..0c201e6 100644 --- a/index.js +++ b/index.js @@ -117,13 +117,14 @@ exports.errorLogger = function errorLogger(options) { options.requestWhitelist = options.requestWhitelist || exports.requestWhitelist; options.requestFilter = options.requestFilter || exports.defaultRequestFilter; - options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports })); + options.winstonInstance = options.winstonInstance || (winston.createLogger ({ transports: options.transports })); options.msg = options.msg || 'middlewareError'; options.baseMeta = options.baseMeta || {}; options.metaField = options.metaField || null; options.level = options.level || 'error'; options.dynamicMeta = options.dynamicMeta || function(req, res, err) { return null; }; - options.exceptionToMeta = options.exceptionToMeta || winston.exception.getAllInfo; + const exceptionHandler = new winston.ExceptionHandler(options.winstonInstance); + options.exceptionToMeta = options.exceptionToMeta || exceptionHandler.getAllInfo.bind(exceptionHandler); options.blacklistedMetaFields = options.blacklistedMetaFields || []; // Using mustache style templating @@ -155,7 +156,11 @@ exports.errorLogger = function errorLogger(options) { options.msg = typeof options.msg === 'function' ? options.msg(req, res) : options.msg; // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback - options.winstonInstance.log(level, getTemplate(options.msg, {err: err, req: req, res: res}), exceptionMeta); + options.winstonInstance.log({ + level, + message: getTemplate(options.msg, {err: err, req: req, res: res}), + meta: exceptionMeta + }); next(err); }; @@ -187,7 +192,7 @@ exports.logger = function logger(options) { options.requestFilter = options.requestFilter || exports.defaultRequestFilter; options.responseFilter = options.responseFilter || exports.defaultResponseFilter; options.ignoredRoutes = options.ignoredRoutes || exports.ignoredRoutes; - options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports })); + options.winstonInstance = options.winstonInstance || (winston.createLogger ({ transports: options.transports })); options.statusLevels = options.statusLevels || false; options.level = options.statusLevels ? levelFromStatus(options) : (options.level || "info"); options.msg = options.msg || "HTTP {{req.method}} {{req.url}}"; @@ -336,7 +341,7 @@ exports.logger = function logger(options) { // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback if (!options.skip(req, res)) { var level = _.isFunction(options.level) ? options.level(req, res) : options.level; - options.winstonInstance.log(level, msg, meta); + options.winstonInstance.log({level, message: msg, meta}); } }; diff --git a/package.json b/package.json index 0bc8726..82ffc88 100644 --- a/package.json +++ b/package.json @@ -57,10 +57,11 @@ "promise": "^7.1.1", "should": "^8.2.2", "travis-cov": "^0.2.5", - "winston": ">=1.x <3" + "winston": ">=3.x <4", + "winston-transport": "^4.2.0" }, "peerDependencies": { - "winston": ">=1.x <3" + "winston": ">=3.x <4" }, "engines": { "node": ">=0.10.0" diff --git a/test/test.js b/test/test.js index d72b92a..bf9ee5b 100644 --- a/test/test.js +++ b/test/test.js @@ -5,6 +5,7 @@ var Promise = require('promise/lib/es6-extensions'); var should = require('should'); var _ = require('lodash'); var winston = require('winston'); +var Transport = require('winston-transport'); var expressWinston = require('../index.js'); @@ -12,21 +13,23 @@ expressWinston.ignoredRoutes.push('/ignored'); expressWinston.responseWhitelist.push('body'); expressWinston.bodyBlacklist.push('potato'); -var MockTransport = function (test, options) { - test.transportInvoked = false; +class MockTransport extends Transport { + constructor(test, options) { + super(options || {}); - winston.Transport.call(this, options || {}); + this._test = test; + this._test.transportInvoked = false; + } - this.log = function (level, msg, meta, cb) { - test.transportInvoked = true; - test.log.level = level; - test.log.msg = msg; - test.log.meta = meta; + log(info, cb) { + this._test.transportInvoked = true; + this._test.log.level = info.level; + this._test.log.msg = info.message; + this._test.log.meta = info.meta; this.emit('logged'); return cb(); - }; -}; -util.inherits(MockTransport, winston.Transport); + } +} function mockReq(reqMock) { var reqSpec = _.extend({ From f37dcc543d3fdadaab8586597dc4be8c978d826e Mon Sep 17 00:00:00 2001 From: rosston Date: Thu, 9 Aug 2018 09:36:15 -0400 Subject: [PATCH 14/59] Drop support for EOL'd versions of node Currently-supported versions of node (https://github.com/nodejs/Release) are 6, 8, and 10. --- .travis.yml | 10 ---------- package.json | 2 +- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 53b4f4a..c60f507 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,6 @@ sudo: false language: node_js node_js: - - "0.10" - - "0.11" - - "0.12" - - "4" - "6" - - "7" - "8" - - "9" - "10" - -script: - - "test $TRAVIS_NODE_VERSION = '0.6' || npm test" - - "test $TRAVIS_NODE_VERSION != '0.6' || npm run-script test-coverage" diff --git a/package.json b/package.json index 82ffc88..a64f05d 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "winston": ">=3.x <4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 6" }, "license": "MIT", "contributors": [ From 10ea2f7cb075888f001c71cd82626552a77a73c3 Mon Sep 17 00:00:00 2001 From: rosston Date: Thu, 9 Aug 2018 09:45:31 -0400 Subject: [PATCH 15/59] Remove unnecessary promise polyfill --- package.json | 1 - test/test.js | 1 - 2 files changed, 2 deletions(-) diff --git a/package.json b/package.json index a64f05d..8e262b5 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "blanket": "^1.2.2", "mocha": "^2.4.5", "node-mocks-http": "^1.5.1", - "promise": "^7.1.1", "should": "^8.2.2", "travis-cov": "^0.2.5", "winston": ">=3.x <4", diff --git a/test/test.js b/test/test.js index bf9ee5b..8cbc3ec 100644 --- a/test/test.js +++ b/test/test.js @@ -1,7 +1,6 @@ var util = require('util'); var mocks = require('node-mocks-http'); -var Promise = require('promise/lib/es6-extensions'); var should = require('should'); var _ = require('lodash'); var winston = require('winston'); From 733d7b2ab689dfe395ea274b9fe7263019739f53 Mon Sep 17 00:00:00 2001 From: rosston Date: Thu, 9 Aug 2018 09:55:22 -0400 Subject: [PATCH 16/59] Upgrade old dependencies --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 8e262b5..1672855 100644 --- a/package.json +++ b/package.json @@ -47,14 +47,14 @@ } }, "dependencies": { - "chalk": "~0.4.0", - "lodash": "~4.17.5" + "chalk": "^2.4.1", + "lodash": "^4.17.10" }, "devDependencies": { "blanket": "^1.2.2", - "mocha": "^2.4.5", + "mocha": "^5.2.0", "node-mocks-http": "^1.5.1", - "should": "^8.2.2", + "should": "^13.2.3", "travis-cov": "^0.2.5", "winston": ">=3.x <4", "winston-transport": "^4.2.0" From 2db0b1939b928fb584419155745076b5560ba6c2 Mon Sep 17 00:00:00 2001 From: rosston Date: Fri, 10 Aug 2018 10:41:28 -0400 Subject: [PATCH 17/59] Ignore package-lock.json Since we're a library (not an app), there seems to be mostly harm from locking down our package versions. The package-lock.json would only apply on a developer's machine, not on CI or for end users, so it seems like it would just mess up users. --- .gitignore | 1 + .npmrc | 1 + 2 files changed, 2 insertions(+) create mode 100644 .npmrc diff --git a/.gitignore b/.gitignore index a4f66c1..21a12ce 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ ################# /node_modules npm-debug.log +package-lock.json ################# ## Jetbrains diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false From 8f01006ec012884274ff2502ddbab0e999fa652a Mon Sep 17 00:00:00 2001 From: rosston Date: Mon, 20 Aug 2018 20:43:32 -0400 Subject: [PATCH 18/59] Update readme for 3.x --- Readme.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Readme.md b/Readme.md index 8625999..d1d8e61 100644 --- a/Readme.md +++ b/Readme.md @@ -3,11 +3,13 @@ > [winston](https://github.com/winstonjs/winston) middleware for express.js +[Changelog](CHANGELOG.md) + ## Installation npm install winston express-winston -(supports node >= 0.10) +(supports node >= 6) ## Usage @@ -21,8 +23,8 @@ In `package.json`: { "dependencies": { "...": "...", - "winston": "^2.0.0", - "express-winston": "^2.0.0", + "winston": "^3.0.0", + "express-winston": "^3.0.0", "...": "..." } } From ce9142fae2e95c318aa8cb3f81d4e8202de41fac Mon Sep 17 00:00:00 2001 From: rosston Date: Mon, 20 Aug 2018 21:04:16 -0400 Subject: [PATCH 19/59] Update changelog for 2.5.0 - 3.0.0 --- CHANGELOG.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 87727bc..24d26af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## 3.0.0 +express-winston@3 shouldn't have any breaking changes _of its own_, but there are breaking changes as a result of upgrading winston and Node.js. + +express-winston@2.6.0 will be the last version to support winston@2. + +#### Breaking changes +* Drop support for winston < 3. winston@3 includes quite a few breaking changes. Check their [changelog](https://github.com/winstonjs/winston/blob/master/CHANGELOG.md) and [upgrade guide](https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md) to get an idea of winston's breaking changes. +* Drop support for Node.js < 6. v6 is the oldest version of Node.js [currently supported by the Node.js team](https://github.com/nodejs/Release). + +## 2.6.0 +* Add `exceptionToMeta` and `blacklistedMetaFields` for filtering returned meta + object ([#173](https://github.com/bithavoc/express-winston/pull/173), @cubbuk) + +## 2.5.1 +* Allow `msg` to be a function ([#160](https://github.com/bithavoc/express-winston/pull/160), @brendancwood) + +## 2.5.0 +* Reduce memory usage ([#164](https://github.com/bithavoc/express-winston/pull/164), @Kmaschta) + ## 2.4.0 * Allow `options.level` to be a function for dynamic level setting ([#148](https://github.com/bithavoc/express-winston/pull/148), @CryptArchy) From 90b1b5020eefd843a8d548f284b4d07b798f7728 Mon Sep 17 00:00:00 2001 From: rosston Date: Mon, 20 Aug 2018 21:10:22 -0400 Subject: [PATCH 20/59] 3.0.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1672855..371e096 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "2.6.0", + "version": "3.0.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 079e19ac69959d9b4136359b8784a15e88dee9ca Mon Sep 17 00:00:00 2001 From: Cole Ellison Date: Wed, 5 Sep 2018 17:23:37 -0400 Subject: [PATCH 21/59] Support winston 3.x transports and formats In their 3.0.0 release, winston deprecated formatting inside transports. This PR adds formats to the options passed into the express-winston logger and errorLogger. --- Readme.md | 48 +++++++++++++++++++++++++++--------------------- index.js | 11 ++++++++--- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Readme.md b/Readme.md index d1d8e61..df9dd7e 100644 --- a/Readme.md +++ b/Readme.md @@ -46,11 +46,12 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req app.use(expressWinston.logger({ transports: [ - new winston.transports.Console({ - json: true, - colorize: true - }) + new winston.transports.Console() ], + format: winston.format.combine( + winston.format.colorize() + winston.format.json() + ) meta: true, // optional: control whether you want to log the meta data about the request (default to true) msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}" expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true @@ -65,7 +66,8 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req ``` js transports: [], // list of all winston transports instances to use. - winstonInstance: , // a winston logger instance. If this is provided the transports option is ignored. + format: [], // formatting desired for log output. + winstonInstance: , // a winston logger instance. If this is provided the transports and formats options are ignored. level: String or function(req, res) { return String; }, // log level to use, the default is "info". Assign a function to dynamically set the level based on request and response, or a string to statically set it always at that level. statusLevels must be false for this setting to be used. msg: String or function // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } expressFormat: Boolean, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors when colorize set to true @@ -97,11 +99,12 @@ Use `expressWinston.errorLogger(options)` to create a middleware that log the er app.use(router); // notice how the router goes first. app.use(expressWinston.errorLogger({ transports: [ - new winston.transports.Console({ - json: true, - colorize: true - }) - ] + new winston.transports.Console() + ], + format: winston.format.combine( + winston.format.colorize() + winston.format.json() + ) })); ``` @@ -111,7 +114,8 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE ``` js transports: [], // list of all winston transports instances to use. - winstonInstance: , // a winston logger instance. If this is provided the transports option is ignored + format: [], // formatting desired for log output + winstonInstance: , // a winston logger instance. If this is provided the transports and formats options are ignored. msg: String or function // customize the default logging message. E.g. "{{err.message}} {{res.statusCode}} {{req.method}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } baseMeta: Object, // default meta data to be added to log, this will be merged with the error data. metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. @@ -153,11 +157,12 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre // express-winston logger makes sense BEFORE the router app.use(expressWinston.logger({ transports: [ - new winston.transports.Console({ - json: true, - colorize: true - }) - ] + new winston.transports.Console() + ], + format: winston.format.combine( + winston.format.colorize() + winston.format.json() + ) })); // Now we can tell the app to use our routing code: @@ -166,11 +171,12 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre // express-winston errorLogger makes sense AFTER the router. app.use(expressWinston.errorLogger({ transports: [ - new winston.transports.Console({ - json: true, - colorize: true - }) - ] + new winston.transports.Console() + ], + format: winston.format.combine( + winston.format.colorize() + winston.format.json() + ) })); // Optionally you can include your custom error handler after the logging. diff --git a/index.js b/index.js index 0c201e6..9df1e84 100644 --- a/index.js +++ b/index.js @@ -19,7 +19,6 @@ // THE SOFTWARE. // var winston = require('winston'); -var util = require('util'); var chalk = require('chalk'); var _ = require('lodash'); @@ -117,7 +116,10 @@ exports.errorLogger = function errorLogger(options) { options.requestWhitelist = options.requestWhitelist || exports.requestWhitelist; options.requestFilter = options.requestFilter || exports.defaultRequestFilter; - options.winstonInstance = options.winstonInstance || (winston.createLogger ({ transports: options.transports })); + options.winstonInstance = options.winstonInstance || (winston.createLogger({ + transports: options.transports, + format: options.format + })); options.msg = options.msg || 'middlewareError'; options.baseMeta = options.baseMeta || {}; options.metaField = options.metaField || null; @@ -192,7 +194,10 @@ exports.logger = function logger(options) { options.requestFilter = options.requestFilter || exports.defaultRequestFilter; options.responseFilter = options.responseFilter || exports.defaultResponseFilter; options.ignoredRoutes = options.ignoredRoutes || exports.ignoredRoutes; - options.winstonInstance = options.winstonInstance || (winston.createLogger ({ transports: options.transports })); + options.winstonInstance = options.winstonInstance || (winston.createLogger({ + transports: options.transports, + format: options.format + })); options.statusLevels = options.statusLevels || false; options.level = options.statusLevels ? levelFromStatus(options) : (options.level || "info"); options.msg = options.msg || "HTTP {{req.method}} {{req.url}}"; From 37535143dd0b07fddedb959980beab63f72e8cc8 Mon Sep 17 00:00:00 2001 From: rosston Date: Wed, 19 Sep 2018 22:44:49 -0400 Subject: [PATCH 22/59] Add link to call for maintainers in Readme --- Readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Readme.md b/Readme.md index d1d8e61..9f0e488 100644 --- a/Readme.md +++ b/Readme.md @@ -5,6 +5,8 @@ [Changelog](CHANGELOG.md) +[CALL FOR MAINTAINERS](https://github.com/bithavoc/express-winston/issues/192) + ## Installation npm install winston express-winston From 31d2916036ca5d2f7639f67607c9074c9d1601c3 Mon Sep 17 00:00:00 2001 From: Cole Ellison Date: Mon, 8 Oct 2018 19:51:13 -0400 Subject: [PATCH 23/59] Update changelog for 3.0.0 - 3.0.1 --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 24d26af..0906db6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.0.1 +* Add `format` to `options` to allow user-specified formatting following winston@3 conventions ([#190](https://github.com/bithavoc/express-winston/pull/190), @crellison) + ## 3.0.0 express-winston@3 shouldn't have any breaking changes _of its own_, but there are breaking changes as a result of upgrading winston and Node.js. From f03170c8f36c56d0ec7b80e76b4f7d382076be51 Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Thu, 11 Oct 2018 17:01:50 -0400 Subject: [PATCH 24/59] Fix missing commas --- Readme.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Readme.md b/Readme.md index e6d31d2..6170d8e 100644 --- a/Readme.md +++ b/Readme.md @@ -51,7 +51,7 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req new winston.transports.Console() ], format: winston.format.combine( - winston.format.colorize() + winston.format.colorize(), winston.format.json() ) meta: true, // optional: control whether you want to log the meta data about the request (default to true) @@ -104,7 +104,7 @@ Use `expressWinston.errorLogger(options)` to create a middleware that log the er new winston.transports.Console() ], format: winston.format.combine( - winston.format.colorize() + winston.format.colorize(), winston.format.json() ) })); @@ -162,7 +162,7 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre new winston.transports.Console() ], format: winston.format.combine( - winston.format.colorize() + winston.format.colorize(), winston.format.json() ) })); @@ -176,7 +176,7 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre new winston.transports.Console() ], format: winston.format.combine( - winston.format.colorize() + winston.format.colorize(), winston.format.json() ) })); From 32571a416e472be07a149a97675d655491589875 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Tue, 16 Oct 2018 21:47:56 -0500 Subject: [PATCH 25/59] Bump version v3.0.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 371e096..2e78484 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.0.0", + "version": "3.0.1", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From bed553c004b3583730c87b09805982136b8bf76a Mon Sep 17 00:00:00 2001 From: Ryan Lee Date: Wed, 17 Oct 2018 18:08:09 -0400 Subject: [PATCH 26/59] Add Commas to Options --- Readme.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Readme.md b/Readme.md index 6170d8e..ffc0794 100644 --- a/Readme.md +++ b/Readme.md @@ -71,22 +71,22 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req format: [], // formatting desired for log output. winstonInstance: , // a winston logger instance. If this is provided the transports and formats options are ignored. level: String or function(req, res) { return String; }, // log level to use, the default is "info". Assign a function to dynamically set the level based on request and response, or a string to statically set it always at that level. statusLevels must be false for this setting to be used. - msg: String or function // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } + msg: String or function, // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } expressFormat: Boolean, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors when colorize set to true colorize: Boolean, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red). meta: Boolean, // control whether you want to log the meta data about the request (default to true). baseMeta: Object, // default meta data to be added to log, this will be merged with the meta data. metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. - statusLevels: Boolean or Object // different HTTP status codes caused log messages to be logged at different levels (info/warn/error), the default is false. Use an object to control the levels various status codes are logged at. Using an object for statusLevels overrides any setting of options.level. - ignoreRoute: function (req, res) { return false; } // A function to determine if logging is skipped, defaults to returning false. Called _before_ any later middleware. - skip: function(req, res) { return false; } // A function to determine if logging is skipped, defaults to returning false. Called _after_ response has already been sent. - requestFilter: function (req, propName) { return req[propName]; } // A function to filter/return request values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. - responseFilter: function (res, propName) { return res[propName]; } // A function to filter/return response values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. - requestWhitelist: [String] // Array of request properties to log. Overrides global requestWhitelist for this instance - responseWhitelist: [String] // Array of response properties to log. Overrides global responseWhitelist for this instance - bodyWhitelist: [String] // Array of body properties to log. Overrides global bodyWhitelist for this instance - bodyBlacklist: [String] // Array of body properties to omit from logs. Overrides global bodyBlacklist for this instance - ignoredRoutes: [String] // Array of paths to ignore/skip logging. Overrides global ignoredRoutes for this instance + statusLevels: Boolean or Object, // different HTTP status codes caused log messages to be logged at different levels (info/warn/error), the default is false. Use an object to control the levels various status codes are logged at. Using an object for statusLevels overrides any setting of options.level. + ignoreRoute: function (req, res) { return false; }, // A function to determine if logging is skipped, defaults to returning false. Called _before_ any later middleware. + skip: function(req, res) { return false; }, // A function to determine if logging is skipped, defaults to returning false. Called _after_ response has already been sent. + requestFilter: function (req, propName) { return req[propName]; }, // A function to filter/return request values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. + responseFilter: function (res, propName) { return res[propName]; }, // A function to filter/return response values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. + requestWhitelist: [String], // Array of request properties to log. Overrides global requestWhitelist for this instance + responseWhitelist: [String], // Array of response properties to log. Overrides global responseWhitelist for this instance + bodyWhitelist: [String], // Array of body properties to log. Overrides global bodyWhitelist for this instance + bodyBlacklist: [String], // Array of body properties to omit from logs. Overrides global bodyBlacklist for this instance + ignoredRoutes: [String], // Array of paths to ignore/skip logging. Overrides global ignoredRoutes for this instance dynamicMeta: function(req, res) { return [Object]; } // Extract additional meta data from request or response (typically req.user data if using passport). meta must be true for this function to be activated ``` From 76e7cc7e36f286098db1cd4da5984890e2f76053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C5=A9=20Quang=20Th=E1=BB=8Bnh?= Date: Mon, 12 Nov 2018 10:28:37 +0700 Subject: [PATCH 27/59] Update Readme.md --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index ffc0794..994970a 100644 --- a/Readme.md +++ b/Readme.md @@ -53,7 +53,7 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req format: winston.format.combine( winston.format.colorize(), winston.format.json() - ) + ), meta: true, // optional: control whether you want to log the meta data about the request (default to true) msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}" expressFormat: true, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors with colorize set to true From 60af475a2b438ba6b79203f448e312fd151019ce Mon Sep 17 00:00:00 2001 From: Matthew Blasius Date: Mon, 4 Feb 2019 22:30:11 -0600 Subject: [PATCH 28/59] Fix large _.template memory consumption **Problem:** In order to support accepting a function for options.msg, express-winston is calling `_.template` for every request. This compiles a brand new lodash template every request. Under heavy load, this has significant performance and memory usage implications. **Solution:** During initialization, make the decision on whether to use a single cached template or a dynamic function that compiles a template for each request. This way, we get the efficiency of a precompiled template for the more common use cases, while remaining backwards compatible with published features. In a future version, it might be worth considering dropping support for mustache formatting and just have express-winston consumers always provide a function if they want a custom options.msg. The api consumer can use JS template literals if they need templating in their custom message. --- AUTHORS | 1 + Readme.md | 2 +- index.js | 67 ++++++++++++++++++++++++++++++---------------------- test/test.js | 28 ++++++++++++++++++++++ 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/AUTHORS b/AUTHORS index ecbeb9e..4a08bb3 100644 --- a/AUTHORS +++ b/AUTHORS @@ -7,3 +7,4 @@ Damian Kaczmarek Robbie Trencheny (http://robbie.io) Ross Brandes Kévin Maschtaler (https://www.kmaschta.me) +Matthew Blasius (https://expel.io) diff --git a/Readme.md b/Readme.md index 994970a..9b976dc 100644 --- a/Readme.md +++ b/Readme.md @@ -71,7 +71,7 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req format: [], // formatting desired for log output. winstonInstance: , // a winston logger instance. If this is provided the transports and formats options are ignored. level: String or function(req, res) { return String; }, // log level to use, the default is "info". Assign a function to dynamically set the level based on request and response, or a string to statically set it always at that level. statusLevels must be false for this setting to be used. - msg: String or function, // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } + msg: String or function, // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}" or function(req, res) { return `${res.statusCode} - ${req.method}`. Warning: while supported, returning mustache style interpolation from an options.msg function has performance and memory implications under load. expressFormat: Boolean, // Use the default Express/morgan request formatting. Enabling this will override any msg if true. Will only output colors when colorize set to true colorize: Boolean, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red). meta: Boolean, // control whether you want to log the meta data about the request (default to true). diff --git a/index.js b/index.js index 9df1e84..076335f 100644 --- a/index.js +++ b/index.js @@ -104,6 +104,38 @@ function filterObject(originalObj, whiteList, initialFilter) { return fieldsSet?obj:undefined; } +function getTemplate(loggerOptions, templateOptions) { + if (loggerOptions.expressFormat) { + var expressMsgFormat = "{{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms"; + if (loggerOptions.colorize) { + expressMsgFormat = chalk.grey("{{req.method}} {{req.url}}") + + " {{res.statusCode}} " + + chalk.grey("{{res.responseTime}}ms"); + } + + return _.template(expressMsgFormat, templateOptions); + } + + if (!_.isFunction(loggerOptions.msg)) { + return _.template(loggerOptions.msg, templateOptions); + } + + return function (data) { + data = data || {}; + var m = loggerOptions.msg(data.req, data.res); + + // if there is no interpolation, don't waste resources creating a template. + // this quick regex is still way faster than just blindly compiling a new template. + if (!/\{\{/.test(m)) { + return m; + } + // since options.msg was a function, and the results seem to contain moustache + // interpolation, we'll compile a new template for each request. + // Warning: this eats a ton of memory under heavy load. + return _.template(m, templateOptions)(data); + } +} + // // ### function errorLogger(options) // #### @options {Object} options to initialize the middleware. @@ -129,10 +161,12 @@ exports.errorLogger = function errorLogger(options) { options.exceptionToMeta = options.exceptionToMeta || exceptionHandler.getAllInfo.bind(exceptionHandler); options.blacklistedMetaFields = options.blacklistedMetaFields || []; + // backwards comparability. + // just in case they're using the same options object as exports.logger. + options = _.omit(options, 'expressFormat'); + // Using mustache style templating - var getTemplate = function(msg, data) { - return _.template(msg, {interpolate: /\{\{([\s\S]+?)\}\}/g})(data) - }; + var template = getTemplate(options, { interpolate: /\{\{([\s\S]+?)\}\}/g }); return function (err, req, res, next) { @@ -155,12 +189,10 @@ exports.errorLogger = function errorLogger(options) { var level = _.isFunction(options.level) ? options.level(req, res, err) : options.level; - options.msg = typeof options.msg === 'function' ? options.msg(req, res) : options.msg; - // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback options.winstonInstance.log({ level, - message: getTemplate(options.msg, {err: err, req: req, res: res}), + message: template({err: err, req: req, res: res}), meta: exceptionMeta }); @@ -209,17 +241,8 @@ exports.logger = function logger(options) { options.skip = options.skip || exports.defaultSkip; options.dynamicMeta = options.dynamicMeta || function(req, res) { return null; }; - var expressMsgFormat = "{{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms"; - if (options.colorize) { - expressMsgFormat = chalk.grey("{{req.method}} {{req.url}}") + - " {{res.statusCode}} " + - chalk.grey("{{res.responseTime}}ms"); - } - - var msgFormat = !options.expressFormat ? options.msg : expressMsgFormat; - // Using mustache style templating - var template = _.template(msgFormat, { + var template = getTemplate(options, { interpolate: /\{\{(.+?)\}\}/g }); @@ -329,18 +352,6 @@ exports.logger = function logger(options) { coloredRes.statusCode = chalk[statusColor](res.statusCode); } - var msgFormat - if (!options.expressFormat) { - msgFormat = typeof options.msg === 'function' ? options.msg(req, res) : options.msg - } else { - msgFormat = expressMsgFormat - } - - // Using mustache style templating - var template = _.template(msgFormat, { - interpolate: /\{\{(.+?)\}\}/g - }); - var msg = template({req: req, res: _.assign({}, res, coloredRes)}); // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback diff --git a/test/test.js b/test/test.js index 8cbc3ec..cec2e9e 100644 --- a/test/test.js +++ b/test/test.js @@ -874,6 +874,34 @@ describe('express-winston', function () { result.log.msg.should.eql('Foo GET /all-the-things'); }); }); + + it('can be a function', function () { + var testHelperOptions = { + loggerOptions: { + msg: function (req) { return 'fn ' + req.url; } + }, + req: { + url: '/all-the-things' + } + }; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.msg.should.eql('fn /all-the-things'); + }); + }); + + it('can be interpolated when it is a function', function () { + var testHelperOptions = { + loggerOptions: { + msg: function () { return 'fn {{req.url}}'; } + }, + req: { + url: '/all-the-things' + } + }; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.msg.should.eql('fn /all-the-things'); + }); + }); }); describe('ignoreRoute option', function () { From 1d14bc63c0440e8178efa21fe48b258de635d9c5 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Tue, 5 Feb 2019 11:03:00 -0500 Subject: [PATCH 29/59] Version bump v3.1.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0906db6..06e8acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.1.0 +* Fix large _.template memory consumption ([#203](https://github.com/bithavoc/express-winston/pull/203), @slickmb) + ## 3.0.1 * Add `format` to `options` to allow user-specified formatting following winston@3 conventions ([#190](https://github.com/bithavoc/express-winston/pull/190), @crellison) diff --git a/package.json b/package.json index 2e78484..7de0b24 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.0.1", + "version": "3.1.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From eb76ad957ce6fd122f235f233f9deedd364debfa Mon Sep 17 00:00:00 2001 From: gregoirevandera Date: Wed, 5 Jun 2019 16:03:02 +0200 Subject: [PATCH 30/59] _header -> getHeader --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 076335f..a5981de 100644 --- a/index.js +++ b/index.js @@ -287,8 +287,8 @@ exports.logger = function logger(options) { if (_.includes(responseWhitelist, 'body')) { if (chunk) { - var isJson = (res._headers && res._headers['content-type'] - && res._headers['content-type'].indexOf('json') >= 0); + var isJson = (res.getHeader('content-type') + && res.getHeader('content-type').indexOf('json') >= 0); logData.res.body = bodyToString(chunk, isJson); } From 6841b150a97011abdad86684ed8dd9097edbd2e2 Mon Sep 17 00:00:00 2001 From: Chiawen Chen Date: Wed, 12 Jun 2019 16:53:42 +0800 Subject: [PATCH 31/59] Build: replace coverage tool blanket with nyc --- .gitignore | 3 ++- Readme.md | 8 ++------ package.json | 21 ++------------------- 3 files changed, 6 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 21a12ce..3e509d2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,6 @@ package-lock.json ################# .DS_Store lib/.DS_Store -coverage.html +coverage +.nyc_output .vimrc diff --git a/Readme.md b/Readme.md index 9b976dc..1ed9eb2 100644 --- a/Readme.md +++ b/Readme.md @@ -439,13 +439,9 @@ Run the basic Mocha tests: npm test -Run the Travis-CI tests (which will fail with < 100% coverage): +View the coverage report: - npm run test-travis - -Generate the `coverage.html` coverage report: - - npm run test-coverage + npx http-server coverage/lcov-report ## Issues and Collaboration diff --git a/package.json b/package.json index 7de0b24..1d7426b 100644 --- a/package.json +++ b/package.json @@ -28,34 +28,17 @@ }, "main": "index.js", "scripts": { - "test": "node_modules/.bin/mocha --reporter spec", - "test-travis": "node_modules/.bin/mocha --require blanket --reporter travis-cov", - "test-coverage": "node_modules/.bin/mocha --require blanket --reporter html-cov > coverage.html || true" - }, - "config": { - "travis-cov": { - "threshold": 100 - }, - "blanket": { - "pattern": [ - "index.js" - ], - "data-cover-never": [ - "node_modules", - "test" - ] - } + "test": "nyc mocha && nyc report --reporter lcov" }, "dependencies": { "chalk": "^2.4.1", "lodash": "^4.17.10" }, "devDependencies": { - "blanket": "^1.2.2", "mocha": "^5.2.0", "node-mocks-http": "^1.5.1", + "nyc": "^14.1.1", "should": "^13.2.3", - "travis-cov": "^0.2.5", "winston": ">=3.x <4", "winston-transport": "^4.2.0" }, From a3345227349af7e7e3705b5a90693d7a6633f1b1 Mon Sep 17 00:00:00 2001 From: Chiawen Chen Date: Wed, 12 Jun 2019 19:14:45 +0800 Subject: [PATCH 32/59] Add eslint and fix lint errors --- .eslintrc | 21 +++++++++++++++++++++ index.js | 6 +++--- package.json | 4 +++- test/test.js | 10 +--------- 4 files changed, 28 insertions(+), 13 deletions(-) create mode 100644 .eslintrc diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..b1a3d6b --- /dev/null +++ b/.eslintrc @@ -0,0 +1,21 @@ +{ + "extends": ["eslint:recommended"], + "env": { + "es6": true, + "node": true + }, + "parserOptions": { + "ecmaVersion": 6 + }, + "rules": { + "no-unused-vars": [2, { "args": "none" }] + }, + "overrides": [ + { + "files": "test/**", + "env": { + "mocha": true + } + } + ] +} diff --git a/index.js b/index.js index a5981de..f47f027 100644 --- a/index.js +++ b/index.js @@ -98,7 +98,7 @@ function filterObject(originalObj, whiteList, initialFilter) { if(typeof (value) !== 'undefined') { obj[propName] = value; fieldsSet = true; - }; + } }); return fieldsSet?obj:undefined; @@ -249,7 +249,7 @@ exports.logger = function logger(options) { return function (req, res, next) { var coloredRes = {}; - var currentUrl = req.originalUrl || req.url; + var currentUrl = req.originalUrl || req.url; if (currentUrl && _.includes(options.ignoredRoutes, currentUrl)) return next(); if (options.ignoreRoute(req, res)) return next(); @@ -273,7 +273,7 @@ exports.logger = function logger(options) { res.end = end; res.end(chunk, encoding); - req.url = req.originalUrl || req.url; + req.url = req.originalUrl || req.url; var meta = {}; diff --git a/package.json b/package.json index 1d7426b..d3a1a42 100644 --- a/package.json +++ b/package.json @@ -28,13 +28,15 @@ }, "main": "index.js", "scripts": { - "test": "nyc mocha && nyc report --reporter lcov" + "pretest": "eslint .", + "test": "node_modules/.bin/mocha --reporter spec" }, "dependencies": { "chalk": "^2.4.1", "lodash": "^4.17.10" }, "devDependencies": { + "eslint": "^5.16.0", "mocha": "^5.2.0", "node-mocks-http": "^1.5.1", "nyc": "^14.1.1", diff --git a/test/test.js b/test/test.js index cec2e9e..50e3ba6 100644 --- a/test/test.js +++ b/test/test.js @@ -1,9 +1,6 @@ -var util = require('util'); - var mocks = require('node-mocks-http'); var should = require('should'); var _ = require('lodash'); -var winston = require('winston'); var Transport = require('winston-transport'); var expressWinston = require('../index.js'); @@ -264,11 +261,6 @@ describe('express-winston', function () { return errorLoggerTestHelper(testHelperOptionsWithBlacklist).then(function (result) { result.log.meta.should.not.have.property('trace'); }); - - var testHelperOptionsWithoutBlacklist = { loggerOptions: {} }; - return errorLoggerTestHelper(testHelperOptionsWithoutBlacklist).then(function (result) { - result.log.meta.should.have.property('trace'); - }); }); }); @@ -676,7 +668,7 @@ describe('express-winston', function () { describe('when middleware function is invoked on a route that returns JSON', function() { it('should parse JSON in response body', function() { - var bodyObject = { "message": "Hi! I\'m a chunk!" }; + var bodyObject = { "message": "Hi! I'm a chunk!" }; function next(req, res, next) { // Set Content-Type in a couple different case types, just in case. // Seems like the mock response doesn't quite handle the case From 08f9d4c9cee8b60205feff3ef0736d69ea09022f Mon Sep 17 00:00:00 2001 From: bithavoc Date: Wed, 12 Jun 2019 13:08:04 -0500 Subject: [PATCH 33/59] Version bump 3.2.0 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 06e8acc..9c5c407 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.2.0 +* Replaced: _header -> getHeader ([#210](https://github.com/bithavoc/express-winston/pull/210), @Gregoirevda) +* Replaced coverage tool blanket with nyc ([#211](https://github.com/bithavoc/express-winston/pull/211), @golopot) +* Add eslint and fix lint errors ([#212](https://github.com/bithavoc/express-winston/pull/212), @golopot) + ## 3.1.0 * Fix large _.template memory consumption ([#203](https://github.com/bithavoc/express-winston/pull/203), @slickmb) diff --git a/package.json b/package.json index d3a1a42..58ff115 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.1.0", + "version": "3.2.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From a07a60985dd5e7a74fb91944dcdbcb5c30012ec4 Mon Sep 17 00:00:00 2001 From: Andrew Lee Date: Tue, 2 Jul 2019 20:49:58 -0700 Subject: [PATCH 34/59] add skip option to error logger --- Readme.md | 1 + index.js | 12 +++++++----- test/test.js | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/Readme.md b/Readme.md index 1ed9eb2..38d17e9 100644 --- a/Readme.md +++ b/Readme.md @@ -127,6 +127,7 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE dynamicMeta: function(req, res, err) { return [Object]; } // Extract additional meta data from request or response (typically req.user data if using passport). meta must be true for this function to be activated exceptionToMeta: function(error){return Object; } // Function to format the returned meta information on error log. If not given `winston.exception.getAllInfo` will be used by default blacklistedMetaFields: [String] // fields to blacklist from meta data + skip: function(req, res, err) { return false; } // A function to determine if logging is skipped, defaults to returning false. ``` To use winston's existing transports, set `transports` to the values (as in key-value) of the `winston.default.transports` object. This may be done, for example, by using underscorejs: `transports: _.values(winston.default.transports)`. diff --git a/index.js b/index.js index f47f027..d30d01d 100644 --- a/index.js +++ b/index.js @@ -160,6 +160,7 @@ exports.errorLogger = function errorLogger(options) { const exceptionHandler = new winston.ExceptionHandler(options.winstonInstance); options.exceptionToMeta = options.exceptionToMeta || exceptionHandler.getAllInfo.bind(exceptionHandler); options.blacklistedMetaFields = options.blacklistedMetaFields || []; + options.skip = options.skip || exports.defaultSkip; // backwards comparability. // just in case they're using the same options object as exports.logger. @@ -169,8 +170,7 @@ exports.errorLogger = function errorLogger(options) { var template = getTemplate(options, { interpolate: /\{\{([\s\S]+?)\}\}/g }); return function (err, req, res, next) { - - // Let winston gather all the error data + // Let winston gather all the error data var exceptionMeta = _.omit(options.exceptionToMeta(err), options.blacklistedMetaFields); exceptionMeta.req = filterObject(req, options.requestWhitelist, options.requestFilter); @@ -189,12 +189,14 @@ exports.errorLogger = function errorLogger(options) { var level = _.isFunction(options.level) ? options.level(req, res, err) : options.level; - // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback - options.winstonInstance.log({ + if (!options.skip(req, res, err)) { + // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback + options.winstonInstance.log({ level, message: template({err: err, req: req, res: res}), meta: exceptionMeta - }); + }); + } next(err); }; diff --git a/test/test.js b/test/test.js index 50e3ba6..c2588da 100644 --- a/test/test.js +++ b/test/test.js @@ -363,6 +363,31 @@ describe('express-winston', function () { loggerFn.should.throw(); }); }); + + describe('skip option', function() { + it('should log error by default', function() { + var options = { + req: {foo: "bar"} + }; + + return errorLoggerTestHelper(options).then(function (result) { + result.transportInvoked.should.eql(true); + }); + }); + + it('should not log error when function returns true', function() { + var options = { + req: {foo: "bar"}, + loggerOptions: { + skip: function() {return true;} + } + }; + + return errorLoggerTestHelper(options).then(function (result) { + result.transportInvoked.should.eql(false); + }); + }); + }); }); describe('.logger()', function () { From 52e3b79990187bf5057652ad95fb34348a0a193e Mon Sep 17 00:00:00 2001 From: bithavoc Date: Tue, 2 Jul 2019 23:01:41 -0500 Subject: [PATCH 35/59] Version bump 3.2.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c5c407..8c44941 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.2.1 +* Added: options.skip ([#214](https://github.com/bithavoc/express-winston/pull/214), [#147](https://github.com/bithavoc/express-winston/pull/147), @ahnkee) + ## 3.2.0 * Replaced: _header -> getHeader ([#210](https://github.com/bithavoc/express-winston/pull/210), @Gregoirevda) * Replaced coverage tool blanket with nyc ([#211](https://github.com/bithavoc/express-winston/pull/211), @golopot) diff --git a/package.json b/package.json index 58ff115..a9dffef 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.2.0", + "version": "3.2.1", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From dc119ac8c65fea06f3ca9c91a5af10131dffb9a0 Mon Sep 17 00:00:00 2001 From: Maxime David Date: Tue, 6 Aug 2019 17:19:24 +0200 Subject: [PATCH 36/59] Add an headerBlacklist option (#217) --- AUTHORS | 1 + Readme.md | 2 ++ index.js | 31 ++++++++++++++++----- test/test.js | 77 ++++++++++++++++++++++++++++++++++++++++------------ 4 files changed, 86 insertions(+), 25 deletions(-) diff --git a/AUTHORS b/AUTHORS index 4a08bb3..d852516 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,3 +8,4 @@ Robbie Trencheny (http://robbie.io) Ross Brandes Kévin Maschtaler (https://www.kmaschta.me) Matthew Blasius (https://expel.io) +Maxime David diff --git a/Readme.md b/Readme.md index 38d17e9..21bb0af 100644 --- a/Readme.md +++ b/Readme.md @@ -88,6 +88,7 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req bodyBlacklist: [String], // Array of body properties to omit from logs. Overrides global bodyBlacklist for this instance ignoredRoutes: [String], // Array of paths to ignore/skip logging. Overrides global ignoredRoutes for this instance dynamicMeta: function(req, res) { return [Object]; } // Extract additional meta data from request or response (typically req.user data if using passport). meta must be true for this function to be activated + headerBlacklist: [String], // Array of headers to omit from logs. Applied after any previous filters. ``` @@ -123,6 +124,7 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. requestFilter: function (req, propName) { return req[propName]; } // A function to filter/return request values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. requestWhitelist: [String] // Array of request properties to log. Overrides global requestWhitelist for this instance + headerBlacklist: [String], // Array of headers to omit from logs. Applied after any previous filters. level: String or function(req, res, err) { return String; }// custom log level for errors (default is 'error'). Assign a function to dynamically set the log level based on request, response, and the exact error. dynamicMeta: function(req, res, err) { return [Object]; } // Extract additional meta data from request or response (typically req.user data if using passport). meta must be true for this function to be activated exceptionToMeta: function(error){return Object; } // Function to format the returned meta information on error log. If not given `winston.exception.getAllInfo` will be used by default diff --git a/index.js b/index.js index d30d01d..eb7990e 100644 --- a/index.js +++ b/index.js @@ -69,6 +69,12 @@ exports.defaultRequestFilter = function (req, propName) { return req[propName]; }; +/** + * A default list of headers in the request object that are not allowed to be logged. + * @type {Array} + */ +exports.defaultHeaderBlacklist = []; + /** * A default function to filter the properties of the res object. * @param res @@ -87,7 +93,7 @@ exports.defaultSkip = function() { return false; }; -function filterObject(originalObj, whiteList, initialFilter) { +function filterObject(originalObj, whiteList, headerBlacklist, initialFilter) { var obj = {}; var fieldsSet = false; @@ -98,7 +104,16 @@ function filterObject(originalObj, whiteList, initialFilter) { if(typeof (value) !== 'undefined') { obj[propName] = value; fieldsSet = true; + if(propName === 'headers') { + [].concat(headerBlacklist).forEach(function (headerName) { + var lowerCaseHeaderName = headerName ? headerName.toLowerCase() : null; + if(obj[propName].hasOwnProperty(lowerCaseHeaderName)) { + delete obj[propName][lowerCaseHeaderName]; + } + }) + } } + }); return fieldsSet?obj:undefined; @@ -148,6 +163,7 @@ exports.errorLogger = function errorLogger(options) { options.requestWhitelist = options.requestWhitelist || exports.requestWhitelist; options.requestFilter = options.requestFilter || exports.defaultRequestFilter; + options.headerBlacklist = options.headerBlacklist || exports.defaultHeaderBlacklist; options.winstonInstance = options.winstonInstance || (winston.createLogger({ transports: options.transports, format: options.format @@ -172,7 +188,7 @@ exports.errorLogger = function errorLogger(options) { return function (err, req, res, next) { // Let winston gather all the error data var exceptionMeta = _.omit(options.exceptionToMeta(err), options.blacklistedMetaFields); - exceptionMeta.req = filterObject(req, options.requestWhitelist, options.requestFilter); + exceptionMeta.req = filterObject(req, options.requestWhitelist, options.headerBlacklist, options.requestFilter); if(options.dynamicMeta) { var dynamicMeta = options.dynamicMeta(req, res, err); @@ -224,6 +240,7 @@ exports.logger = function logger(options) { options.requestWhitelist = options.requestWhitelist || exports.requestWhitelist; options.bodyWhitelist = options.bodyWhitelist || exports.bodyWhitelist; options.bodyBlacklist = options.bodyBlacklist || exports.bodyBlacklist; + options.headerBlacklist = options.headerBlacklist || exports.defaultHeaderBlacklist; options.responseWhitelist = options.responseWhitelist || exports.responseWhitelist; options.requestFilter = options.requestFilter || exports.defaultRequestFilter; options.responseFilter = options.responseFilter || exports.defaultResponseFilter; @@ -296,8 +313,8 @@ exports.logger = function logger(options) { } } - logData.req = filterObject(req, requestWhitelist, options.requestFilter); - logData.res = filterObject(res, responseWhitelist, options.responseFilter); + logData.req = filterObject(req, requestWhitelist, options.headerBlacklist, options.requestFilter); + logData.res = filterObject(res, responseWhitelist, options.headerBlacklist, options.responseFilter); var bodyWhitelist = _.union(options.bodyWhitelist, (req._routeWhitelists.body || [])); var blacklist = _.union(options.bodyBlacklist, (req._routeBlacklists.body || [])); @@ -307,15 +324,15 @@ exports.logger = function logger(options) { if ( req.body !== undefined ) { if (blacklist.length > 0 && bodyWhitelist.length === 0) { var whitelist = _.difference(Object.keys(req.body), blacklist); - filteredBody = filterObject(req.body, whitelist, options.requestFilter); + filteredBody = filterObject(req.body, whitelist, options.headerBlacklist, options.requestFilter); } else if ( requestWhitelist.indexOf('body') !== -1 && bodyWhitelist.length === 0 && blacklist.length === 0 ) { - filteredBody = filterObject(req.body, Object.keys(req.body), options.requestFilter); + filteredBody = filterObject(req.body, Object.keys(req.body), options.headerBlacklist, options.requestFilter); } else { - filteredBody = filterObject(req.body, bodyWhitelist, options.requestFilter); + filteredBody = filterObject(req.body, bodyWhitelist, options.headerBlacklist, options.requestFilter); } } diff --git a/test/test.js b/test/test.js index c2588da..03600d5 100644 --- a/test/test.js +++ b/test/test.js @@ -32,7 +32,9 @@ function mockReq(reqMock) { method: 'GET', url: '/hello', headers: { - 'header-1': 'value 1' + 'header-1': 'value 1', + 'header-2': 'value 2', + 'header-3': 'value 3' }, query: { val: '1' @@ -165,23 +167,6 @@ describe('express-winston', function () { }); }); - it('should use the exported defaultRequestFilter', function() { - var originalRequestFilter = expressWinston.defaultRequestFilter; - expressWinston.defaultRequestFilter = function() { - return 'foo'; - }; - - var options = { - req: {foo: "bar"} - }; - return errorLoggerTestHelper(options).then(function (result) { - // Return to the original value for later tests - expressWinston.defaultRequestFilter = originalRequestFilter; - - result.log.meta.req.url.should.equal('foo'); - }); - }); - describe('when middleware function encounters an error in the pipeline', function () { it('should invoke the transport', function () { return errorLoggerTestHelper().then(function (result) { @@ -1174,6 +1159,56 @@ describe('express-winston', function () { }); }); + describe('headerBlacklist option', function () { + it('should default to global defaultHeaderBlackList', function () { + return loggerTestHelper().then(function (result) { + result.log.meta.req.headers.should.have.property('header-1'); + result.log.meta.req.headers.should.have.property('header-2'); + result.log.meta.req.headers.should.have.property('header-3'); + }); + }); + + it('should use specified headerBlackList', function () { + var options = { + loggerOptions: { + headerBlacklist: ['header-1', 'Header-3'] + } + }; + return loggerTestHelper(options).then(function (result) { + result.log.meta.req.headers.should.not.have.property('header-1'); + result.log.meta.req.headers.should.have.property('header-2'); + result.log.meta.req.headers.should.not.have.property('header-3'); + }); + }); + + it('should not use specified headerBlackList since the requestWhiteList is empty', function () { + var options = { + loggerOptions: { + requestWhitelist: ['url'], + headerBlacklist: ['header-1'] + } + }; + return loggerTestHelper(options).then(function (result) { + result.log.meta.req.should.not.have.property('headers'); + }); + }); + + it('should not headerBlackList but since a requestFilter is set', function () { + const customRequestFilter = (req, propName) => { + return (propName !== 'headers') ? req[propName] : undefined; + } + var options = { + loggerOptions: { + requestFilter: customRequestFilter, + headerBlacklist: ['header-1'] + } + }; + return loggerTestHelper(options).then(function (result) { + result.log.meta.req.should.not.have.property('headers'); + }); + }); + }); + describe('requestWhitelist option', function () { it('should default to global requestWhitelist', function () { var options = { @@ -1358,6 +1393,12 @@ describe('express-winston', function () { }); }); + describe('.defaultHeaderBlacklist', function () { + it('should be an array with all the header which are prevented to be logged', function () { + expressWinston.defaultHeaderBlacklist.should.be.an.Array(); + }); + }); + describe('.defaultRequestFilter', function () { it('should be a function', function () { expressWinston.defaultRequestFilter.should.be.a.Function(); From 2bd4fbdfbb033c5c98df83d6571f394a61258908 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Tue, 6 Aug 2019 10:23:44 -0500 Subject: [PATCH 37/59] Version bump 3.3.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c44941..343f0a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.3.0 +* Added: options.headerBlacklist ([#217](https://github.com/bithavoc/express-winston/pull/217), @maxday) + ## 3.2.1 * Added: options.skip ([#214](https://github.com/bithavoc/express-winston/pull/214), [#147](https://github.com/bithavoc/express-winston/pull/147), @ahnkee) diff --git a/package.json b/package.json index a9dffef..ec4009e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.2.1", + "version": "3.3.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 598da58c62decb469e69fe7fd5e0d86cbe074c87 Mon Sep 17 00:00:00 2001 From: "GIL B. Chan" Date: Tue, 17 Sep 2019 18:13:41 +0900 Subject: [PATCH 38/59] Fix license year to 2012 --- LICENSE | 2 +- Readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 1eee682..91599e0 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2012-2014 Bithavoc.io - http://bithavoc.io +Copyright (c) 2012 Bithavoc.io - http://bithavoc.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Readme.md b/Readme.md index 21bb0af..3cd80ed 100644 --- a/Readme.md +++ b/Readme.md @@ -461,7 +461,7 @@ Also see AUTHORS file, add yourself if you are missing. ## MIT License -Copyright (c) 2012-2014 Bithavoc.io and Contributors - http://bithavoc.io +Copyright (c) 2012 Bithavoc.io and Contributors - http://bithavoc.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 30d1ef7f7440c808ca940f630a94013c3961b6da Mon Sep 17 00:00:00 2001 From: "alexkaplan.mail@gmail.com" Date: Sun, 22 Sep 2019 09:23:48 +0300 Subject: [PATCH 39/59] #66 add response and request whitelist nesting --- index.js | 8 ++++---- test/test.js | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index eb7990e..89f7290 100644 --- a/index.js +++ b/index.js @@ -66,7 +66,7 @@ exports.ignoredRoutes = []; * @return {*} */ exports.defaultRequestFilter = function (req, propName) { - return req[propName]; + return _.get(req, propName); }; /** @@ -82,7 +82,7 @@ exports.defaultHeaderBlacklist = []; * @return {*} */ exports.defaultResponseFilter = function (res, propName) { - return res[propName]; + return _.get(res, propName); }; /** @@ -102,7 +102,7 @@ function filterObject(originalObj, whiteList, headerBlacklist, initialFilter) { var value = initialFilter(originalObj, propName); if(typeof (value) !== 'undefined') { - obj[propName] = value; + _.set(obj, propName, value); fieldsSet = true; if(propName === 'headers') { [].concat(headerBlacklist).forEach(function (headerName) { @@ -304,7 +304,7 @@ exports.logger = function logger(options) { logData.res = res; - if (_.includes(responseWhitelist, 'body')) { + if (_.includes(responseWhitelist.map(term => term.split('.')[0]), 'body')) { if (chunk) { var isJson = (res.getHeader('content-type') && res.getHeader('content-type').indexOf('json') >= 0); diff --git a/test/test.js b/test/test.js index 03600d5..928dd56 100644 --- a/test/test.js +++ b/test/test.js @@ -280,6 +280,19 @@ describe('express-winston', function () { result.log.meta.req.should.not.have.property('method'); }); }); + + it('should work with nested requestWhitelist', function () { + var options = { + req: {foo: {test: "bar"}}, + loggerOptions: { + requestWhitelist: ['foo.test'] + } + }; + return errorLoggerTestHelper(options).then(function (result) { + result.log.meta.req.should.have.property('foo'); + result.log.meta.req.foo.should.have.property('test'); + }); + }); }); describe('dynamicMeta option', function () { @@ -1283,6 +1296,19 @@ describe('express-winston', function () { result.log.meta.res.should.not.have.property('method'); }); }); + + it('should work with nested responseWhitelist', function () { + var options = { + res: {foo: {test: "bar"}}, + loggerOptions: { + responseWhitelist: ['foo.test'] + } + }; + return loggerTestHelper(options).then(function (result) { + result.log.meta.res.should.have.property('foo'); + result.log.meta.res.foo.should.have.property('test'); + }); + }); }); describe('ignoredRoutes option', function () { From aebd898cd31b5a68877a17a30d8aa71ba22e11ea Mon Sep 17 00:00:00 2001 From: Alex Kaplan Date: Thu, 26 Sep 2019 10:09:58 +0300 Subject: [PATCH 40/59] Update Readme.md Add example for nested whitelists --- Readme.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Readme.md b/Readme.md index 3cd80ed..6680652 100644 --- a/Readme.md +++ b/Readme.md @@ -327,6 +327,40 @@ Note that you can log the whole request and/or response body: expressWinston.requestWhitelist.push('body'); expressWinston.responseWhitelist.push('body'); + +### Nested Whitelists + +`requestWhitelist` and `responseWhitelist` also support nested whitelist values, allowing access to parts of an object. + +For example, using the following during logger setup: + + expressWinston.responseWhitelist.push('body.import.value'); + +A response that looks like this : + + { + body: { + important: { + value: 5 + }, + notImportant: { + value: 7 + } + }, + other: { + value: 3 + } + } + +Would only log the following value : + + { + body: { + important: { + value: 5 + } + } + } ## Route-Specific Whitelists and Blacklists @@ -456,6 +490,7 @@ If you ran into any problems, please use the project [Issues section](https://gi * [Lars Jacob](https://github.com/jaclar) (https://github.com/jaclar) * [Jonathan Lomas](https://github.com/floatingLomas) (https://github.com/floatingLomas) * [Ross Brandes](https://github.com/rosston) (https://github.com/rosston) +* [Alex Kaplan](https://github.com/kapalex) (https://github.com/kapalex) Also see AUTHORS file, add yourself if you are missing. From 611661e41c87f9a1385466abff64775eed1e5803 Mon Sep 17 00:00:00 2001 From: Matt Morrissette Date: Wed, 11 Sep 2019 18:54:08 -0700 Subject: [PATCH 41/59] Enhance "metaField" and add "requestProperty" and "responseProperty" config options --- .editorconfig | 23 +++ AUTHORS | 1 + Readme.md | 85 +++++++++- index.js | 265 ++++++++++++++++++------------- package.json | 16 +- test/test.js | 429 ++++++++++++++++++++++++++++++++------------------ 6 files changed, 550 insertions(+), 269 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..22a77f3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_style = space +indent_size = 4 +chaset = utf-8 +trim_trailing_whitespace = true +ij_javascript_use_semicolon_after_statement = true +ij_javascript_space_before_function_left_parenth = true +ij_javascript_space_before_method_left_brace = true +ij_javascript_space_before_method_parentheses = false + +[test/*.js] +indent_size = 2 + +[package.json] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/AUTHORS b/AUTHORS index d852516..70945bf 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,3 +9,4 @@ Ross Brandes Kévin Maschtaler (https://www.kmaschta.me) Matthew Blasius (https://expel.io) Maxime David +Matt Morrissette (https://github.com/yinzara) diff --git a/Readme.md b/Readme.md index 6680652..db1a062 100644 --- a/Readme.md +++ b/Readme.md @@ -76,7 +76,8 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req colorize: Boolean, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red). meta: Boolean, // control whether you want to log the meta data about the request (default to true). baseMeta: Object, // default meta data to be added to log, this will be merged with the meta data. - metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. + metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. Defaults to 'meta'. Set to `null` to store metadata at the root of the log entry. + requestField: [String] // the property of the metadata to store the request under (default 'req'). Set to null to exclude request from metadata statusLevels: Boolean or Object, // different HTTP status codes caused log messages to be logged at different levels (info/warn/error), the default is false. Use an object to control the levels various status codes are logged at. Using an object for statusLevels overrides any setting of options.level. ignoreRoute: function (req, res) { return false; }, // A function to determine if logging is skipped, defaults to returning false. Called _before_ any later middleware. skip: function(req, res) { return false; }, // A function to determine if logging is skipped, defaults to returning false. Called _after_ response has already been sent. @@ -121,7 +122,10 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE winstonInstance: , // a winston logger instance. If this is provided the transports and formats options are ignored. msg: String or function // customize the default logging message. E.g. "{{err.message}} {{res.statusCode}} {{req.method}}" or function(req, res) { return `${res.statusCode} - ${req.method}` } baseMeta: Object, // default meta data to be added to log, this will be merged with the error data. - metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. + meta: Boolean, // control whether you want to log the meta data about the request (default to true). + metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. Defaults to 'meta'. Set to `null` to store metadata at the root of the log entry. + requestField: [String] // the property of the metadata to store the request under (default 'req'). Set to null to exclude request from metadata + responseField: [String] // the property of the metadata to store the response under (default 'res'). If set to the same as 'requestField', filtered response and request properties will be merged. Set to null to exclude request from metadata requestFilter: function (req, propName) { return req[propName]; } // A function to filter/return request values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. requestWhitelist: [String] // Array of request properties to log. Overrides global requestWhitelist for this instance headerBlacklist: [String], // Array of headers to omit from logs. Applied after any previous filters. @@ -136,6 +140,23 @@ To use winston's existing transports, set `transports` to the values (as in key- Alternatively, if you're using a winston logger instance elsewhere and have already set up levels and transports, pass the instance into expressWinston with the `winstonInstance` option. The `transports` option is then ignored. +#### `metaField` option + +In versions of `express-winston` prior to 4.0.0, this field functioned differently. + +Previously the log entry would always have a "meta" field which would be set to the metadata of the request/error. +If `metaField` was set, this information would be stored as an object with the given property on the "meta" object of +the log entry. This prevented the use case where the metadata should be located at the root of the log entry. + +In this version, `metaField` defaults to "meta" which maintains the prior versions behavior of storing the metadata at +a "meta" property of the log entry. + +Explicitly setting the `metaField` to `null` or "null" causes the metadata to be stored at the root of the log entry. + +The `metaField` option now also supports dot separated and array values to store the metadata at a nested location in the log entry. + +

Upgrade Note: For those upgrading from a version of `express-winston` prior to 4.0.0 that use the `metaField` property, to keep the same behavior, prepend `meta.` to your current `metaField` configuration. (i.e. 'foo' would become 'meta.foo')

+ ## Examples ``` js @@ -298,6 +319,65 @@ Browse `/error` will show you how express-winston handles and logs the errors in "message": "middlewareError" } +### StackDriver/Google Cloud Logging + +If using this library with `@google-cloud/logging-winston`, use the following configuration to properly store httpRequest information. + +See https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry + +```javascript +var express = require('express'); +var expressWinston = require('express-winston'); +var LoggingWinston = require('@google-cloud/logging-winston').LoggingWinston; + +const app = express() + +app.use(expressWinston.logger({ + transports: [ new LoggingWinston({}) ], + metaField: null, + requestField: "httpRequest", + responseField: "httpRequest", + requestWhitelist: ["requestMethod", "requestUrl", "protocol", "remoteIp", "requestSize", "userAgent", "referrer"], + responseWhitelist: ["status", "responseSize", "responseTime", "latency"], + requestFilter: function (req, propName) { + switch(propName) { + case "requestMethod": + return req.method; + case "requestUrl": + return `${req.protocol}://${req.hostname}${req.originalUrl}`; + case "protocol": + return req.protocol; + case "remoteIp": + return req.ip; + case "requestSize": + return req.get('Content-Length'); + case "userAgent": + return req.get('User-Agent'); + case "referrer": + return req.get("Referer"); + default: + return undefined; + } + }, + responseFilter: function(res, propName) { + switch (propName) { + case "status": + return res.statusCode; + case "responseSize": + return typeof res.body === 'object' ? + JSON.stringify(res.body).length : + typeof res.body === 'string' ? + res.body.length : undefined; + case "latency": + return res.responseTime; + default: + return undefined; + } + } +})); + +``` + ## Global Whitelists and Blacklists Express-winston exposes three whitelists that control which properties of the `request`, `body`, and `response` are logged: @@ -491,6 +571,7 @@ If you ran into any problems, please use the project [Issues section](https://gi * [Jonathan Lomas](https://github.com/floatingLomas) (https://github.com/floatingLomas) * [Ross Brandes](https://github.com/rosston) (https://github.com/rosston) * [Alex Kaplan](https://github.com/kapalex) (https://github.com/kapalex) +* [Matt Morrissette](https://github.com/yinzara) (https://github.com/yinzara) Also see AUTHORS file, add yourself if you are missing. diff --git a/index.js b/index.js index 89f7290..e8d5c88 100644 --- a/index.js +++ b/index.js @@ -89,10 +89,22 @@ exports.defaultResponseFilter = function (res, propName) { * A default function to decide whether skip logging of particular request. Doesn't skip anything (i.e. log all requests). * @return always false */ -exports.defaultSkip = function() { - return false; +exports.defaultSkip = function () { + return false; }; +/** + * The property of the metadata of the log entry that the filtered HTTP request is stored in (default 'req') + * @type {string} + */ +exports.requestField = 'req'; + +/** + * The property of the metadata of the log entry that the filtered HTTP response is stored in (default 'res') + * @type {string} + */ +exports.responseField = 'res'; + function filterObject(originalObj, whiteList, headerBlacklist, initialFilter) { var obj = {}; @@ -100,32 +112,30 @@ function filterObject(originalObj, whiteList, headerBlacklist, initialFilter) { [].concat(whiteList).forEach(function (propName) { var value = initialFilter(originalObj, propName); - if(typeof (value) !== 'undefined') { _.set(obj, propName, value); fieldsSet = true; - if(propName === 'headers') { + if (propName === 'headers') { [].concat(headerBlacklist).forEach(function (headerName) { var lowerCaseHeaderName = headerName ? headerName.toLowerCase() : null; - if(obj[propName].hasOwnProperty(lowerCaseHeaderName)) { + if (obj[propName].hasOwnProperty(lowerCaseHeaderName)) { delete obj[propName][lowerCaseHeaderName]; } - }) + }); } } - }); - return fieldsSet?obj:undefined; + return fieldsSet ? obj : undefined; } function getTemplate(loggerOptions, templateOptions) { if (loggerOptions.expressFormat) { - var expressMsgFormat = "{{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms"; + var expressMsgFormat = '{{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms'; if (loggerOptions.colorize) { - expressMsgFormat = chalk.grey("{{req.method}} {{req.url}}") + - " {{res.statusCode}} " + - chalk.grey("{{res.responseTime}}ms"); + expressMsgFormat = chalk.grey('{{req.method}} {{req.url}}') + + ' {{res.statusCode}} ' + + chalk.grey('{{res.responseTime}}ms'); } return _.template(expressMsgFormat, templateOptions); @@ -148,7 +158,7 @@ function getTemplate(loggerOptions, templateOptions) { // interpolation, we'll compile a new template for each request. // Warning: this eats a ton of memory under heavy load. return _.template(m, templateOptions)(data); - } + }; } // @@ -156,7 +166,6 @@ function getTemplate(loggerOptions, templateOptions) { // #### @options {Object} options to initialize the middleware. // - exports.errorLogger = function errorLogger(options) { ensureValidOptions(options); @@ -165,18 +174,19 @@ exports.errorLogger = function errorLogger(options) { options.requestFilter = options.requestFilter || exports.defaultRequestFilter; options.headerBlacklist = options.headerBlacklist || exports.defaultHeaderBlacklist; options.winstonInstance = options.winstonInstance || (winston.createLogger({ - transports: options.transports, - format: options.format + transports: options.transports, + format: options.format })); options.msg = options.msg || 'middlewareError'; options.baseMeta = options.baseMeta || {}; - options.metaField = options.metaField || null; + options.metaField = options.metaField === null || options.metaField === 'null' ? null : options.metaField || 'meta'; options.level = options.level || 'error'; - options.dynamicMeta = options.dynamicMeta || function(req, res, err) { return null; }; + options.dynamicMeta = options.dynamicMeta || function (req, res, err) { return null; }; const exceptionHandler = new winston.ExceptionHandler(options.winstonInstance); options.exceptionToMeta = options.exceptionToMeta || exceptionHandler.getAllInfo.bind(exceptionHandler); options.blacklistedMetaFields = options.blacklistedMetaFields || []; options.skip = options.skip || exports.defaultSkip; + options.requestField = options.requestField === null || options.requestField === 'null' ? null : options.requestField || exports.requestField; // backwards comparability. // just in case they're using the same options object as exports.logger. @@ -188,17 +198,29 @@ exports.errorLogger = function errorLogger(options) { return function (err, req, res, next) { // Let winston gather all the error data var exceptionMeta = _.omit(options.exceptionToMeta(err), options.blacklistedMetaFields); - exceptionMeta.req = filterObject(req, options.requestWhitelist, options.headerBlacklist, options.requestFilter); + if (options.meta !== false) { + if (options.requestField !== null) { + exceptionMeta[options.requestField] = filterObject(req, options.requestWhitelist, options.headerBlacklist, options.requestFilter); + } - if(options.dynamicMeta) { - var dynamicMeta = options.dynamicMeta(req, res, err); - exceptionMeta = _.assign(exceptionMeta, dynamicMeta); + if (options.dynamicMeta) { + var dynamicMeta = options.dynamicMeta(req, res, err); + exceptionMeta = _.assign(exceptionMeta, dynamicMeta); + } } if (options.metaField) { - var newMeta = {}; - newMeta[options.metaField] = exceptionMeta; - exceptionMeta = newMeta; + var fields; + if (Array.isArray(options.metaField)) { + fields = options.metaField; + } else { + fields = options.metaField.split('.'); + } + _(fields).reverse().forEach(field => { + var newMeta = {}; + newMeta[field] = exceptionMeta; + exceptionMeta = newMeta; + }); } exceptionMeta = _.assign(exceptionMeta, options.baseMeta); @@ -206,12 +228,11 @@ exports.errorLogger = function errorLogger(options) { var level = _.isFunction(options.level) ? options.level(req, res, err) : options.level; if (!options.skip(req, res, err)) { - // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback - options.winstonInstance.log({ - level, - message: template({err: err, req: req, res: res}), - meta: exceptionMeta - }); + // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback + options.winstonInstance.log(_.merge(exceptionMeta, { + level, + message: template({ err: err, req: req, res: res }), + })); } next(err); @@ -220,12 +241,12 @@ exports.errorLogger = function errorLogger(options) { function levelFromStatus(options) { return function (req, res) { - var level = ""; - if (res.statusCode >= 100) { level = options.statusLevels.success || "info"; } - if (res.statusCode >= 400) { level = options.statusLevels.warn || "warn"; } - if (res.statusCode >= 500) { level = options.statusLevels.error || "error"; } + var level = ''; + if (res.statusCode >= 100) { level = options.statusLevels.success || 'info'; } + if (res.statusCode >= 400) { level = options.statusLevels.warn || 'warn'; } + if (res.statusCode >= 500) { level = options.statusLevels.error || 'error'; } return level; - } + }; } // @@ -246,23 +267,25 @@ exports.logger = function logger(options) { options.responseFilter = options.responseFilter || exports.defaultResponseFilter; options.ignoredRoutes = options.ignoredRoutes || exports.ignoredRoutes; options.winstonInstance = options.winstonInstance || (winston.createLogger({ - transports: options.transports, - format: options.format + transports: options.transports, + format: options.format })); options.statusLevels = options.statusLevels || false; - options.level = options.statusLevels ? levelFromStatus(options) : (options.level || "info"); - options.msg = options.msg || "HTTP {{req.method}} {{req.url}}"; + options.level = options.statusLevels ? levelFromStatus(options) : (options.level || 'info'); + options.msg = options.msg || 'HTTP {{req.method}} {{req.url}}'; options.baseMeta = options.baseMeta || {}; - options.metaField = options.metaField || null; + options.metaField = options.metaField === null || options.metaField === 'null' ? null : options.metaField || 'meta'; options.colorize = options.colorize || false; options.expressFormat = options.expressFormat || false; options.ignoreRoute = options.ignoreRoute || function () { return false; }; options.skip = options.skip || exports.defaultSkip; - options.dynamicMeta = options.dynamicMeta || function(req, res) { return null; }; + options.dynamicMeta = options.dynamicMeta || function (req, res) { return null; }; + options.requestField = options.requestField === null || options.requestField === 'null' ? null : options.requestField || exports.requestField; + options.responseField = options.responseField === null || options.responseField === 'null' ? null : options.responseField || exports.responseField; // Using mustache style templating var template = getTemplate(options, { - interpolate: /\{\{(.+?)\}\}/g + interpolate: /\{\{(.+?)\}\}/g }); return function (req, res, next) { @@ -286,7 +309,7 @@ exports.logger = function logger(options) { // Manage to get information from the response too, just like Connect.logger does: var end = res.end; - res.end = function(chunk, encoding) { + res.end = function (chunk, encoding) { res.responseTime = (new Date) - req._startTime; res.end = end; @@ -296,87 +319,109 @@ exports.logger = function logger(options) { var meta = {}; - if(options.meta !== false) { - var logData = {}; + if (options.meta !== false) { + var logData = {}; + + if (options.requestField !== null) { + var requestWhitelist = options.requestWhitelist.concat(req._routeWhitelists.req || []); + var filteredRequest = filterObject(req, requestWhitelist, options.headerBlacklist, options.requestFilter); + + var bodyWhitelist = _.union(options.bodyWhitelist, (req._routeWhitelists.body || [])); + var blacklist = _.union(options.bodyBlacklist, (req._routeBlacklists.body || [])); + + var filteredBody = null; + + if (req.body !== undefined) { + if (blacklist.length > 0 && bodyWhitelist.length === 0) { + var whitelist = _.difference(Object.keys(req.body), blacklist); + filteredBody = filterObject(req.body, whitelist, options.headerBlacklist, options.requestFilter); + } else if ( + requestWhitelist.indexOf('body') !== -1 && + bodyWhitelist.length === 0 && + blacklist.length === 0 + ) { + filteredBody = filterObject(req.body, Object.keys(req.body), options.headerBlacklist, options.requestFilter); + } else { + filteredBody = filterObject(req.body, bodyWhitelist, options.headerBlacklist, options.requestFilter); + } + } - var requestWhitelist = options.requestWhitelist.concat(req._routeWhitelists.req || []); - var responseWhitelist = options.responseWhitelist.concat(req._routeWhitelists.res || []); + if (filteredRequest) { + if (filteredBody) { + filteredRequest.body = filteredBody; + } else { + delete filteredRequest.body; + } + } - logData.res = res; + logData[options.requestField] = filteredRequest; + } - if (_.includes(responseWhitelist.map(term => term.split('.')[0]), 'body')) { - if (chunk) { - var isJson = (res.getHeader('content-type') - && res.getHeader('content-type').indexOf('json') >= 0); + var responseWhitelist = options.responseWhitelist.concat(req._routeWhitelists.res || []); + if (_.includes(responseWhitelist, 'body')) { + if (chunk) { + var isJson = (res.getHeader('content-type') + && res.getHeader('content-type').indexOf('json') >= 0); + const body = chunk.toString(); + res.body = bodyToString(body, isJson); + } + } - logData.res.body = bodyToString(chunk, isJson); + if (options.responseField !== null) { + var filteredResponse = filterObject(res, responseWhitelist, options.headerBlacklist, options.responseFilter); + if (filteredResponse) { + if (options.requestField === options.responseField) { + logData[options.requestField] = _.assign(filteredRequest, filteredResponse); + } else { + logData[options.responseField] = filteredResponse; + } + } } - } - - logData.req = filterObject(req, requestWhitelist, options.headerBlacklist, options.requestFilter); - logData.res = filterObject(res, responseWhitelist, options.headerBlacklist, options.responseFilter); - - var bodyWhitelist = _.union(options.bodyWhitelist, (req._routeWhitelists.body || [])); - var blacklist = _.union(options.bodyBlacklist, (req._routeBlacklists.body || [])); - - var filteredBody = null; - - if ( req.body !== undefined ) { - if (blacklist.length > 0 && bodyWhitelist.length === 0) { - var whitelist = _.difference(Object.keys(req.body), blacklist); - filteredBody = filterObject(req.body, whitelist, options.headerBlacklist, options.requestFilter); - } else if ( - requestWhitelist.indexOf('body') !== -1 && - bodyWhitelist.length === 0 && - blacklist.length === 0 - ) { - filteredBody = filterObject(req.body, Object.keys(req.body), options.headerBlacklist, options.requestFilter); - } else { - filteredBody = filterObject(req.body, bodyWhitelist, options.headerBlacklist, options.requestFilter); - } - } - - if (logData.req) { - if (filteredBody) { - logData.req.body = filteredBody; - } else { - delete logData.req.body; + + if (!responseWhitelist.includes('responseTime')) { + logData.responseTime = res.responseTime; } - } - logData.responseTime = res.responseTime; + if (options.dynamicMeta) { + var dynamicMeta = options.dynamicMeta(req, res); + logData = _.assign(logData, dynamicMeta); + } - if(options.dynamicMeta) { - var dynamicMeta = options.dynamicMeta(req, res); - logData = _.assign(logData, dynamicMeta); - } + meta = _.assign(meta, logData); + } - if (options.metaField) { - var newMeta = {}; - newMeta[options.metaField] = logData; - logData = newMeta; - } - meta = _.assign(meta, logData); + if (options.metaField) { + var fields; + if (Array.isArray(options.metaField)) { + fields = options.metaField; + } else { + fields = options.metaField.split('.'); + } + _(fields).reverse().forEach(field => { + var newMeta = {}; + newMeta[field] = meta; + meta = newMeta; + }); } meta = _.assign(meta, options.baseMeta); if (options.colorize) { - // Palette from https://github.com/expressjs/morgan/blob/master/index.js#L205 - var statusColor = 'green'; - if (res.statusCode >= 500) statusColor = 'red'; - else if (res.statusCode >= 400) statusColor = 'yellow'; - else if (res.statusCode >= 300) statusColor = 'cyan'; + // Palette from https://github.com/expressjs/morgan/blob/master/index.js#L205 + var statusColor = 'green'; + if (res.statusCode >= 500) statusColor = 'red'; + else if (res.statusCode >= 400) statusColor = 'yellow'; + else if (res.statusCode >= 300) statusColor = 'cyan'; - coloredRes.statusCode = chalk[statusColor](res.statusCode); + coloredRes.statusCode = chalk[statusColor](res.statusCode); } - var msg = template({req: req, res: _.assign({}, res, coloredRes)}); + var msg = template({ req: req, res: _.assign({}, res, coloredRes) }); // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback if (!options.skip(req, res)) { - var level = _.isFunction(options.level) ? options.level(req, res) : options.level; - options.winstonInstance.log({level, message: msg, meta}); + var level = _.isFunction(options.level) ? options.level(req, res) : options.level; + options.winstonInstance.log(_.merge(meta, { level, message: msg })); } }; @@ -401,17 +446,17 @@ function bodyToString(body, isJSON) { } function ensureValidOptions(options) { - if(!options) throw new Error("options are required by express-winston middleware"); - if(!((options.transports && (options.transports.length > 0)) || options.winstonInstance)) - throw new Error("transports or a winstonInstance are required by express-winston middleware"); + if (!options) throw new Error('options are required by express-winston middleware'); + if (!((options.transports && (options.transports.length > 0)) || options.winstonInstance)) + throw new Error('transports or a winstonInstance are required by express-winston middleware'); if (options.dynamicMeta && !_.isFunction(options.dynamicMeta)) { - throw new Error("`dynamicMeta` express-winston option should be a function"); + throw new Error('`dynamicMeta` express-winston option should be a function'); } } function ensureValidLoggerOptions(options) { if (options.ignoreRoute && !_.isFunction(options.ignoreRoute)) { - throw new Error("`ignoreRoute` express-winston option should be a function"); + throw new Error('`ignoreRoute` express-winston option should be a function'); } } diff --git a/package.json b/package.json index ec4009e..3bdb6bd 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.3.0", + "version": "4.0.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" @@ -50,6 +50,15 @@ "engines": { "node": ">= 6" }, + "files": [ + "index.js", + "index.d.ts", + "AUTHORS", + "LICENSE", + "CHANGELOG.md", + "Readme.md", + ".npmrc" + ], "license": "MIT", "contributors": [ { @@ -72,6 +81,11 @@ "name": "Robbie Trencheny", "email": "me@robbiet.us", "url": "http://robbie.io" + }, + { + "name": "Matt Morrissette", + "email": "yinzara@gmail.com", + "url": "https://github.com/yinzara" } ] } diff --git a/test/test.js b/test/test.js index 928dd56..e3efa4f 100644 --- a/test/test.js +++ b/test/test.js @@ -22,6 +22,8 @@ class MockTransport extends Transport { this._test.log.level = info.level; this._test.log.msg = info.message; this._test.log.meta = info.meta; + this._test.log.metaField = info.metaField; + this._test.log.foobar = info.foobar; this.emit('logged'); return cb(); } @@ -151,12 +153,12 @@ describe('express-winston', function () { middleware.length.should.eql(4); }); - it('should use the exported requestWhitelist', function() { + it('should use the exported requestWhitelist', function () { var originalWhitelist = expressWinston.requestWhitelist; expressWinston.requestWhitelist = ['foo']; var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return errorLoggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -181,7 +183,7 @@ describe('express-winston', function () { }); it('should find a custom level of "warn"', function () { - var testHelperOptions = {loggerOptions: {level:'warn'}}; + var testHelperOptions = { loggerOptions: { level: 'warn' } }; return errorLoggerTestHelper(testHelperOptions).then(function (result) { result.log.level.should.eql('warn'); }); @@ -229,7 +231,7 @@ describe('express-winston', function () { }); it('should, use getAllInfo function when not given', function () { - var testHelperOptions = { loggerOptions: { } }; + var testHelperOptions = { loggerOptions: {} }; return errorLoggerTestHelper(testHelperOptions).then(function (result) { result.log.meta.should.have.property('date'); result.log.meta.should.have.property('process'); @@ -251,9 +253,18 @@ describe('express-winston', function () { describe('metaField option', function () { it('should, when using a custom metaField, log the custom metaField', function () { - var testHelperOptions = {loggerOptions: {metaField: 'metaField'}}; + var testHelperOptions = { loggerOptions: { metaField: 'metaField' } }; return errorLoggerTestHelper(testHelperOptions).then(function (result) { - result.log.meta.metaField.req.should.be.ok(); + result.log.metaField.req.should.be.ok(); + }); + }); + }); + + describe('requestField option', function () { + it('should, when using custom requestField, have the request in the alternative property', function () { + var testHelperOptions = { loggerOptions: { requestField: 'httpRequest' } }; + return errorLoggerTestHelper(testHelperOptions).then(function (result) { + result.log.meta.httpRequest.should.be.ok(); }); }); }); @@ -261,7 +272,7 @@ describe('express-winston', function () { describe('requestWhitelist option', function () { it('should default to global requestWhitelist', function () { var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return errorLoggerTestHelper(options).then(function (result) { result.log.meta.req.should.not.have.property('foo'); @@ -270,7 +281,7 @@ describe('express-winston', function () { it('should use specified requestWhitelist', function () { var options = { - req: {foo: "bar"}, + req: { foo: 'bar' }, loggerOptions: { requestWhitelist: ['foo'] } @@ -296,33 +307,37 @@ describe('express-winston', function () { }); describe('dynamicMeta option', function () { - var testHelperOptions = { - req: { - body: { - age: 42, - potato: 'Russet' + var testHelperOptions; + + beforeEach(function () { + testHelperOptions = { + req: { + body: { + age: 42, + potato: 'Russet' + }, + user: { + username: 'john@doe.com', + role: 'operator' + } }, - user: { - username: "john@doe.com", - role: "operator" - } - }, - res: { - custom: 'custom response runtime field' - }, - originalError: new Error('FOO'), - loggerOptions: { - meta: true, - dynamicMeta: function(req, res, err) { - return { - user: req.user.username, - role: req.user.role, - custom: res.custom, - errMessage: err.message + res: { + custom: 'custom response runtime field' + }, + originalError: new Error('FOO'), + loggerOptions: { + meta: true, + dynamicMeta: function (req, res, err) { + return { + user: req.user.username, + role: req.user.role, + custom: res.custom, + errMessage: err.message + }; } } - } - }; + }; + }); it('should contain dynamic meta data if meta and dynamicMeta activated', function () { return errorLoggerTestHelper(testHelperOptions).then(function (result) { @@ -336,6 +351,28 @@ describe('express-winston', function () { it('should work with metaField option', function () { testHelperOptions.loggerOptions.metaField = 'metaField'; + return errorLoggerTestHelper(testHelperOptions).then(function (result) { + result.log.metaField.req.should.be.ok(); + result.log.metaField.user.should.equal('john@doe.com'); + result.log.metaField.role.should.equal('operator'); + result.log.metaField.custom.should.equal('custom response runtime field'); + result.log.metaField.errMessage.should.equal('FOO'); + }); + }); + + it('should work with metaField . separated option', function () { + testHelperOptions.loggerOptions.metaField = 'meta.metaField'; + return errorLoggerTestHelper(testHelperOptions).then(function (result) { + result.log.meta.metaField.req.should.be.ok(); + result.log.meta.metaField.user.should.equal('john@doe.com'); + result.log.meta.metaField.role.should.equal('operator'); + result.log.meta.metaField.custom.should.equal('custom response runtime field'); + result.log.meta.metaField.errMessage.should.equal('FOO'); + }); + }); + + it('should work with metaField array option', function () { + testHelperOptions.loggerOptions.metaField = ['meta', 'metaField']; return errorLoggerTestHelper(testHelperOptions).then(function (result) { result.log.meta.metaField.req.should.be.ok(); result.log.meta.metaField.user.should.equal('john@doe.com'); @@ -357,31 +394,31 @@ describe('express-winston', function () { }); it('should throw an error if dynamicMeta is not a function', function () { - var loggerFn = expressWinston.errorLogger.bind(expressWinston, {dynamicMeta: 12}); + var loggerFn = expressWinston.errorLogger.bind(expressWinston, { dynamicMeta: 12 }); loggerFn.should.throw(); }); }); - describe('skip option', function() { - it('should log error by default', function() { + describe('skip option', function () { + it('should log error by default', function () { var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; - return errorLoggerTestHelper(options).then(function (result) { + return errorLoggerTestHelper(options).then(function (result) { result.transportInvoked.should.eql(true); }); }); - it('should not log error when function returns true', function() { + it('should not log error when function returns true', function () { var options = { - req: {foo: "bar"}, + req: { foo: 'bar' }, loggerOptions: { - skip: function() {return true;} + skip: function () {return true;} } }; - return errorLoggerTestHelper(options).then(function (result) { + return errorLoggerTestHelper(options).then(function (result) { result.transportInvoked.should.eql(false); }); }); @@ -422,6 +459,7 @@ describe('express-winston', function () { function next(req, res, next) { res.end(); } + var testHelperOptions = { next: next, req: { @@ -439,6 +477,7 @@ describe('express-winston', function () { function next(req, res, next) { res.end(); } + var testHelperOptions = { next: next, req: { @@ -470,6 +509,7 @@ describe('express-winston', function () { res.end('{ "message": "Hi! I\'m a chunk!" }'); } + var testHelperOptions = { next: next, loggerOptions: { @@ -493,12 +533,12 @@ describe('express-winston', function () { }); }); - it('should use the exported requestWhitelist', function() { + it('should use the exported requestWhitelist', function () { var originalWhitelist = expressWinston.requestWhitelist; expressWinston.requestWhitelist = ['foo']; var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -509,12 +549,12 @@ describe('express-winston', function () { }); }); - it('should use the exported bodyWhitelist', function() { + it('should use the exported bodyWhitelist', function () { var originalWhitelist = expressWinston.bodyWhitelist; expressWinston.bodyWhitelist = ['foo']; var options = { - req: {body: {foo: 'bar', baz: 'qux'}} + req: { body: { foo: 'bar', baz: 'qux' } } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -525,12 +565,12 @@ describe('express-winston', function () { }); }); - it('should use the exported bodyBlacklist', function() { + it('should use the exported bodyBlacklist', function () { var originalBlacklist = expressWinston.bodyBlacklist; expressWinston.bodyBlacklist = ['foo']; var options = { - req: {body: {foo: 'bar', baz: 'qux'}} + req: { body: { foo: 'bar', baz: 'qux' } } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -541,12 +581,12 @@ describe('express-winston', function () { }); }); - it('should use the exported responseWhitelist', function() { + it('should use the exported responseWhitelist', function () { var originalWhitelist = expressWinston.responseWhitelist; expressWinston.responseWhitelist = ['foo']; var options = { - res: {foo: 'bar', baz: 'qux'} + res: { foo: 'bar', baz: 'qux' } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -557,14 +597,14 @@ describe('express-winston', function () { }); }); - it('should use the exported defaultRequestFilter', function() { + it('should use the exported defaultRequestFilter', function () { var originalRequestFilter = expressWinston.defaultRequestFilter; - expressWinston.defaultRequestFilter = function() { + expressWinston.defaultRequestFilter = function () { return 'foo'; }; var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -574,14 +614,14 @@ describe('express-winston', function () { }); }); - it('should use the exported defaultResponseFilter', function() { + it('should use the exported defaultResponseFilter', function () { var originalResponseFilter = expressWinston.defaultResponseFilter; - expressWinston.defaultResponseFilter = function() { + expressWinston.defaultResponseFilter = function () { return 'foo'; }; var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -591,14 +631,14 @@ describe('express-winston', function () { }); }); - it('should use the exported defaultSkip', function() { + it('should use the exported defaultSkip', function () { var originalSkip = expressWinston.defaultSkip; - expressWinston.defaultSkip = function() { + expressWinston.defaultSkip = function () { return true; }; var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -608,12 +648,12 @@ describe('express-winston', function () { }); }); - it('should use the exported ignoredRoutes', function() { + it('should use the exported ignoredRoutes', function () { var originalIgnoredRoutes = expressWinston.ignoredRoutes; expressWinston.ignoredRoutes = ['/foo-route']; var options = { - req: {url: '/foo-route'} + req: { url: '/foo-route' } }; return loggerTestHelper(options).then(function (result) { // Return to the original value for later tests @@ -635,12 +675,13 @@ describe('express-winston', function () { res.end('{ "message": "Hi! I\'m a chunk!" }'); } + var testHelperOptions = { next: next, req: { body: { - username: "bobby", - password: "top-secret", + username: 'bobby', + password: 'top-secret', age: 42, potato: 'Russet' }, @@ -689,9 +730,10 @@ describe('express-winston', function () { }); }); - describe('when middleware function is invoked on a route that returns JSON', function() { - it('should parse JSON in response body', function() { - var bodyObject = { "message": "Hi! I'm a chunk!" }; + describe('when middleware function is invoked on a route that returns JSON', function () { + it('should parse JSON in response body', function () { + var bodyObject = { 'message': 'Hi! I\'m a chunk!' }; + function next(req, res, next) { // Set Content-Type in a couple different case types, just in case. // Seems like the mock response doesn't quite handle the case @@ -700,12 +742,52 @@ describe('express-winston', function () { res.setHeader('content-type', 'application/json'); res.end(JSON.stringify(bodyObject)); } - return loggerTestHelper({next: next}).then(function(result) { - result.log.meta.res.body.should.eql(bodyObject); + + return loggerTestHelper({ next: next }).then(function (result) { + result.log.meta.res.body.should.eql(bodyObject); }); }); - it('should not blow up when response body is invalid JSON', function() { + it('should be string in response body with alternative property name', function () { + function next(req, res, next) { + // Set Content-Type in a couple different case types, just in case. + // Seems like the mock response doesn't quite handle the case + // translation on these right. + res.end('foo'); + } + + return loggerTestHelper({ next: next, loggerOptions: { responseField: 'response' } }) + .then(function (result) { + result.log.meta.response.body.should.eql('foo'); + }); + }); + + it('should merge filtered request/response under property', function () { + function next(req, res, next) { + res.end('foo'); + } + + return loggerTestHelper( + { + next, + loggerOptions: + { + responseField: 'httpRequest', + requestField: 'httpRequest', + responseWhitelist: [...expressWinston.responseWhitelist, 'responseTime'] + } + }) + .then(function (result) { + result.log.meta.httpRequest.body.should.eql('foo'); + result.log.meta.httpRequest.statusCode.should.eql(200); + should.exist(result.log.meta.httpRequest.responseTime); + should.exist(result.log.meta.httpRequest.url); + should.exist(result.log.meta.httpRequest.headers); + should.exist(result.log.meta.httpRequest.method); + }); + }); + + it('should not blow up when response body is invalid JSON', function () { function next(req, res, next) { // Set Content-Type in a couple different case types, just in case. // Seems like the mock response doesn't quite handle the case @@ -714,13 +796,14 @@ describe('express-winston', function () { res.setHeader('content-type', 'application/json'); res.end('}'); } - return loggerTestHelper({next: next}); + + return loggerTestHelper({ next: next }); }); }); describe('when middleware function is invoked on a route that should be ignored (by .ignoredRoutes)', function () { var testHelperOptions = { - req: {url: '/ignored'} + req: { url: '/ignored' } }; it('should not invoke the transport', function () { @@ -754,7 +837,7 @@ describe('express-winston', function () { }); }); - it('should not emit colors when colorize option is false', function() { + it('should not emit colors when colorize option is false', function () { var testHelperOptions = { loggerOptions: { colorize: false, @@ -771,7 +854,7 @@ describe('express-winston', function () { }); }); - it('should not emit colors when colorize option is not present', function() { + it('should not emit colors when colorize option is not present', function () { var testHelperOptions = { loggerOptions: { colorize: false, @@ -967,7 +1050,7 @@ describe('express-winston', function () { } }; return loggerTestHelper(testHelperOptions).then(function (result) { - result.log.meta.foobar.req.should.be.ok(); + result.log.foobar.req.should.be.ok(); }); }); }); @@ -977,7 +1060,7 @@ describe('express-winston', function () { var testHelperOptions = { loggerOptions: { skip: function (req, res) { - return req.url.indexOf('sandwich') != -1 + return req.url.indexOf('sandwich') != -1; } }, req: { @@ -993,7 +1076,7 @@ describe('express-winston', function () { var testHelperOptions = { loggerOptions: { skip: function (req, res) { - return req.url.indexOf('sandwich') != -1 + return req.url.indexOf('sandwich') != -1; } }, req: { @@ -1072,7 +1155,7 @@ describe('express-winston', function () { res.status(200).end('{ "message": "Hi! I\'m a chunk!" }'); }, loggerOptions: { - statusLevels: {success: 'silly'} + statusLevels: { success: 'silly' } }, transportOptions: { level: 'silly' @@ -1089,7 +1172,7 @@ describe('express-winston', function () { res.status(403).end('{ "message": "Hi! I\'m a chunk!" }'); }, loggerOptions: { - statusLevels: {warn: 'debug'} + statusLevels: { warn: 'debug' } }, transportOptions: { level: 'silly' @@ -1106,7 +1189,7 @@ describe('express-winston', function () { res.status(500).end('{ "message": "Hi! I\'m a chunk!" }'); }, loggerOptions: { - statusLevels: {error: 'verbose'} + statusLevels: { error: 'verbose' } }, transportOptions: { level: 'silly' @@ -1120,56 +1203,56 @@ describe('express-winston', function () { }); describe('when levels set to a function', function () { - it('should have custom status level provided by the function when 100 <= statusCode < 400', function () { - var testHelperOptions = { - next: function (req, res, next) { - res.status(200).end('{ "message": "Hi! I\'m a chunk!" }'); - }, - loggerOptions: { - level: function(req,res) { return 'silly'; } - }, - transportOptions: { - level: 'silly' - } - }; - return loggerTestHelper(testHelperOptions).then(function (result) { - result.log.level.should.equal('silly'); - }); + it('should have custom status level provided by the function when 100 <= statusCode < 400', function () { + var testHelperOptions = { + next: function (req, res, next) { + res.status(200).end('{ "message": "Hi! I\'m a chunk!" }'); + }, + loggerOptions: { + level: function (req, res) { return 'silly'; } + }, + transportOptions: { + level: 'silly' + } + }; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.level.should.equal('silly'); }); + }); - it('should have custom status level provided by the function when 400 <= statusCode < 500', function () { - var testHelperOptions = { - next: function (req, res, next) { - res.status(403).end('{ "message": "Hi! I\'m a chunk!" }'); - }, - loggerOptions: { - level: function(req,res) { return 'silly'; } - }, - transportOptions: { - level: 'silly' - } - }; - return loggerTestHelper(testHelperOptions).then(function (result) { - result.log.level.should.equal('silly'); - }); + it('should have custom status level provided by the function when 400 <= statusCode < 500', function () { + var testHelperOptions = { + next: function (req, res, next) { + res.status(403).end('{ "message": "Hi! I\'m a chunk!" }'); + }, + loggerOptions: { + level: function (req, res) { return 'silly'; } + }, + transportOptions: { + level: 'silly' + } + }; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.level.should.equal('silly'); }); + }); - it('should have custom status level provided by the function when 500 <= statusCode', function () { - var testHelperOptions = { - next: function (req, res, next) { - res.status(500).end('{ "message": "Hi! I\'m a chunk!" }'); - }, - loggerOptions: { - level: function(req,res) { return 'silly'; } - }, - transportOptions: { - level: 'silly' - } - }; - return loggerTestHelper(testHelperOptions).then(function (result) { - result.log.level.should.equal('silly'); - }); + it('should have custom status level provided by the function when 500 <= statusCode', function () { + var testHelperOptions = { + next: function (req, res, next) { + res.status(500).end('{ "message": "Hi! I\'m a chunk!" }'); + }, + loggerOptions: { + level: function (req, res) { return 'silly'; } + }, + transportOptions: { + level: 'silly' + } + }; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.level.should.equal('silly'); }); + }); }); describe('headerBlacklist option', function () { @@ -1209,7 +1292,7 @@ describe('express-winston', function () { it('should not headerBlackList but since a requestFilter is set', function () { const customRequestFilter = (req, propName) => { return (propName !== 'headers') ? req[propName] : undefined; - } + }; var options = { loggerOptions: { requestFilter: customRequestFilter, @@ -1225,7 +1308,7 @@ describe('express-winston', function () { describe('requestWhitelist option', function () { it('should default to global requestWhitelist', function () { var options = { - req: {foo: "bar"} + req: { foo: 'bar' } }; return loggerTestHelper(options).then(function (result) { result.log.meta.req.should.not.have.property('foo'); @@ -1234,7 +1317,7 @@ describe('express-winston', function () { it('should use specified requestWhitelist', function () { var options = { - req: {foo: "bar"}, + req: { foo: 'bar' }, loggerOptions: { requestWhitelist: ['foo'] } @@ -1245,7 +1328,7 @@ describe('express-winston', function () { }); }); - it('should not include a req in the log when there is no request whitelist', function() { + it('should not include a req in the log when there is no request whitelist', function () { var options = { loggerOptions: { requestWhitelist: [], @@ -1258,14 +1341,14 @@ describe('express-winston', function () { }); describe('bodyBlacklist option', function () { - it('should remove the body if it is requestWhitelisted and the bodyBlacklist removes all properties', function() { + it('should remove the body if it is requestWhitelisted and the bodyBlacklist removes all properties', function () { var options = { loggerOptions: { bodyBlacklist: ['foo', 'baz'], requestWhitelist: ['body'], }, req: { - body: {foo: 'bar', baz: 'qux'} + body: { foo: 'bar', baz: 'qux' } } }; return loggerTestHelper(options).then(function (result) { @@ -1277,7 +1360,7 @@ describe('express-winston', function () { describe('responseWhitelist option', function () { it('should default to global responseWhitelist', function () { var options = { - res: {foo: "bar"} + res: { foo: 'bar' } }; return loggerTestHelper(options).then(function (result) { result.log.meta.res.should.not.have.property('foo'); @@ -1286,7 +1369,7 @@ describe('express-winston', function () { it('should use specified responseWhitelist', function () { var options = { - res: {foo: "bar"}, + res: { foo: 'bar' }, loggerOptions: { responseWhitelist: ['foo'] } @@ -1314,7 +1397,7 @@ describe('express-winston', function () { describe('ignoredRoutes option', function () { it('should default to global ignoredRoutes', function () { var options = { - req: {url: "/ignored"} + req: { url: '/ignored' } }; return loggerTestHelper(options).then(function (result) { result.transportInvoked.should.eql(false); @@ -1323,7 +1406,7 @@ describe('express-winston', function () { it('should use specified ignoredRoutes', function () { var options = { - req: {url: "/ignored-option"}, + req: { url: '/ignored-option' }, loggerOptions: { ignoredRoutes: ['/ignored-option'] } @@ -1335,31 +1418,34 @@ describe('express-winston', function () { }); describe('dynamicMeta option', function () { - var testHelperOptions = { - req: { - body: { - age: 42, - potato: 'Russet' + var testHelperOptions; + beforeEach(() => { + testHelperOptions = { + req: { + body: { + age: 42, + potato: 'Russet' + }, + user: { + username: 'john@doe.com', + role: 'operator' + } }, - user: { - username: "john@doe.com", - role: "operator" - } - }, - res: { - custom: 'custom response runtime field' - }, - loggerOptions: { - meta: true, - dynamicMeta: function(req, res) { - return { - user: req.user.username, - role: req.user.role, - custom: res.custom + res: { + custom: 'custom response runtime field' + }, + loggerOptions: { + meta: true, + dynamicMeta: function (req, res) { + return { + user: req.user.username, + role: req.user.role, + custom: res.custom + }; } } - } - }; + }; + }); it('should contain dynamic meta data if meta and dynamicMeta activated', function () { return loggerTestHelper(testHelperOptions).then(function (result) { @@ -1370,8 +1456,35 @@ describe('express-winston', function () { }); }); + it('should contain request under alternative property if defined', function () { + testHelperOptions.loggerOptions.requestField = 'httpRequest'; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.meta.httpRequest.should.be.ok(); + }); + }); + it('should work with metaField option', function () { testHelperOptions.loggerOptions.metaField = 'metaField'; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.metaField.req.should.be.ok(); + result.log.metaField.user.should.equal('john@doe.com'); + result.log.metaField.role.should.equal('operator'); + result.log.metaField.custom.should.equal('custom response runtime field'); + }); + }); + + it('should work with metaField option with . syntax', function () { + testHelperOptions.loggerOptions.metaField = 'meta.metaField'; + return loggerTestHelper(testHelperOptions).then(function (result) { + result.log.meta.metaField.req.should.be.ok(); + result.log.meta.metaField.user.should.equal('john@doe.com'); + result.log.meta.metaField.role.should.equal('operator'); + result.log.meta.metaField.custom.should.equal('custom response runtime field'); + }); + }); + + it('should work with metaField option with array syntax', function () { + testHelperOptions.loggerOptions.metaField = ['meta', 'metaField']; return loggerTestHelper(testHelperOptions).then(function (result) { result.log.meta.metaField.req.should.be.ok(); result.log.meta.metaField.user.should.equal('john@doe.com'); @@ -1391,7 +1504,7 @@ describe('express-winston', function () { }); it('should throw an error if dynamicMeta is not a function', function () { - var loggerFn = expressWinston.logger.bind(expressWinston, {dynamicMeta: 12}); + var loggerFn = expressWinston.logger.bind(expressWinston, { dynamicMeta: 12 }); loggerFn.should.throw(); }); }); @@ -1448,4 +1561,8 @@ describe('express-winston', function () { expressWinston.ignoredRoutes.should.be.an.Array(); }); }); + + describe('google-logging configuration', () => { + + }); }); From 038c839ec8a8447069ebca02defdfce781a35650 Mon Sep 17 00:00:00 2001 From: Matt Morrissette Date: Wed, 11 Sep 2019 18:55:26 -0700 Subject: [PATCH 42/59] Add TypeScript type definitions --- index.d.ts | 114 ++++++++++++++++++++++++++++++++++ package.json | 4 +- test/express-winston-tests.ts | 103 ++++++++++++++++++++++++++++++ tsconfig.json | 19 ++++++ 4 files changed, 239 insertions(+), 1 deletion(-) create mode 100644 index.d.ts create mode 100644 test/express-winston-tests.ts create mode 100644 tsconfig.json diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..274f7fa --- /dev/null +++ b/index.d.ts @@ -0,0 +1,114 @@ +// Type definitions for express-winston 3.0 +// Project: https://github.com/bithavoc/express-winston#readme +// Definitions by: Alex Brick +// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped +// TypeScript Version: 2.3 + +import { ErrorRequestHandler, Handler, Request, Response } from 'express'; +import { Format } from 'logform'; +import * as winston from 'winston'; +import * as Transport from 'winston-transport'; + +export interface FilterRequest extends Request { + [other: string]: any; +} + +export interface FilterResponse extends Response { + [other: string]: any; +} + +export type DynamicMetaFunction = (req: Request, res: Response, err: Error) => object; +export type DynamicLevelFunction = (req: Request, res: Response, err: Error) => string; +export type RequestFilter = (req: FilterRequest, propName: string) => any; +export type ResponseFilter = (res: FilterResponse, propName: string) => any; +export type RouteFilter = (req: Request, res: Response) => boolean; +export type MessageTemplate = string | ((req: Request, res: Response) => string); + +export interface BaseLoggerOptions { + baseMeta?: object; + bodyBlacklist?: string[]; + bodyWhitelist?: string[]; + colorize?: boolean; + dynamicMeta?: DynamicMetaFunction; + expressFormat?: boolean; + format?: Format; + ignoreRoute?: RouteFilter; + ignoredRoutes?: string[]; + level?: string | DynamicLevelFunction; + meta?: boolean; + metaField?: string; + requestField?: string; + responseField?: string; + msg?: MessageTemplate; + requestFilter?: RequestFilter; + requestWhitelist?: string[]; + responseFilter?: ResponseFilter; + responseWhitelist?: string[]; + skip?: RouteFilter; + statusLevels?: { + error?: string; + success?: string; + warn?: string; + }; +} + +export interface LoggerOptionsWithTransports extends BaseLoggerOptions { + transports: Transport[]; +} + +export interface LoggerOptionsWithWinstonInstance extends BaseLoggerOptions { + winstonInstance: winston.Logger; +} + +export type LoggerOptions = LoggerOptionsWithTransports | LoggerOptionsWithWinstonInstance; + +export function logger(options: LoggerOptions): Handler; + +export interface BaseErrorLoggerOptions { + baseMeta?: object; + dynamicMeta?: DynamicMetaFunction; + format?: Format; + level?: string | DynamicLevelFunction; + meta?: boolean; + metaField?: string; + requestField?: string; + msg?: MessageTemplate; + requestFilter?: RequestFilter; + requestWhitelist?: string[]; +} + +export interface ErrorLoggerOptionsWithTransports extends BaseErrorLoggerOptions { + transports: Transport[]; +} + +export interface ErrorLoggerOptionsWithWinstonInstance extends BaseErrorLoggerOptions { + winstonInstance: winston.Logger; +} + +export type ErrorLoggerOptions = ErrorLoggerOptionsWithTransports | ErrorLoggerOptionsWithWinstonInstance; + +export function errorLogger(options: ErrorLoggerOptions): ErrorRequestHandler; + +export let requestWhitelist: string[]; + +export let bodyWhitelist: string[]; + +export let bodyBlacklist: string[]; + +export let responseWhitelist: string[]; + +export let ignoredRoutes: string[]; + +export let defaultRequestFilter: RequestFilter; + +export let defaultResponseFilter: ResponseFilter; + +export function defaultSkip(): boolean; + +export interface ExpressWinstonRequest extends Request { + _routeWhitelists: { + body: string[]; + req: string[]; + res: string[]; + }; +} diff --git a/package.json b/package.json index 3bdb6bd..86b66ad 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "lodash": "^4.17.10" }, "devDependencies": { + "@types/express": "^4.17.1", "eslint": "^5.16.0", "mocha": "^5.2.0", "node-mocks-http": "^1.5.1", @@ -87,5 +88,6 @@ "email": "yinzara@gmail.com", "url": "https://github.com/yinzara" } - ] + ], + "types": "index.d.ts" } diff --git a/test/express-winston-tests.ts b/test/express-winston-tests.ts new file mode 100644 index 0000000..d17693c --- /dev/null +++ b/test/express-winston-tests.ts @@ -0,0 +1,103 @@ +// Not actually used for tests. Used to test the `index.d.ts` typescript definition matches known examples + +import expressWinston = require('..'); +import * as winston from 'winston'; +import express = require('express'); +import { Format } from 'logform'; + +const app = express(); + +// Logger with all options +app.use(expressWinston.logger({ + baseMeta: { foo: 'foo', nested: { bar: 'baz' } }, + bodyBlacklist: ['foo'], + bodyWhitelist: ['bar'], + colorize: true, + dynamicMeta: (req, res, err) => ({ foo: 'bar' }), + expressFormat: true, + format: new Format(), + ignoreRoute: (req, res) => true, + ignoredRoutes: ['foo'], + level: (req, res) => 'level', + meta: true, + metaField: 'metaField', + msg: 'msg', + requestFilter: (req, prop) => req[prop], + requestWhitelist: ['foo', 'bar'], + skip: (req, res) => false, + statusLevels: ({ error: 'error', success: 'success', warn: 'warn' }), + transports: [ + new winston.transports.Console({}) + ] +})); + +// Logger with minimum options (transport) +app.use(expressWinston.logger({ + transports: [ + new winston.transports.Console({}) + ], +})); + +const logger = winston.createLogger(); + +// Logger with minimum options (winstonInstance) +app.use(expressWinston.logger({ + winstonInstance: logger, +})); + +// Error Logger with all options +app.use(expressWinston.errorLogger({ + baseMeta: { foo: 'foo', nested: { bar: 'baz' } }, + dynamicMeta: (req, res, err) => ({ foo: 'bar' }), + format: new Format(), + level: (req, res) => 'level', + metaField: 'metaField', + msg: 'msg', + requestFilter: (req, prop) => true, + requestWhitelist: ['foo', 'bar'], + transports: [ + new winston.transports.Console({}) + ] +})); + +// Error Logger with min options (transports) +app.use(expressWinston.errorLogger({ + transports: [ + new winston.transports.Console({}) + ], +})); + +// Error Logger with min options (winstonInstance) +app.use(expressWinston.errorLogger({ + winstonInstance: logger, +})); + +// Request and error logger with function type msg +app.use(expressWinston.logger({ + msg: (req, res) => `HTTP ${req.method} ${req.url} - ${res.statusCode}`, + transports: [ + new winston.transports.Console({}) + ], +})); + +app.use(expressWinston.errorLogger({ + msg: (req, res) => `HTTP ${req.method} ${req.url} - ${res.statusCode}`, + winstonInstance: logger, +})); + +expressWinston.bodyBlacklist.push('potato'); +expressWinston.bodyWhitelist.push('apple'); +expressWinston.defaultRequestFilter = (req: expressWinston.FilterRequest, prop: string) => req[prop]; +expressWinston.defaultResponseFilter = (res: expressWinston.FilterResponse, prop: string) => res[prop]; +expressWinston.defaultSkip = () => true; +expressWinston.ignoredRoutes.push('/ignored'); +expressWinston.responseWhitelist.push('body'); + +const router = express.Router(); + +router.post('/user/register', (req, res, next) => { + const expressWinstonReq = req as expressWinston.ExpressWinstonRequest; + expressWinstonReq._routeWhitelists.body = ['username', 'email', 'age']; + expressWinstonReq._routeWhitelists.req = ['userId']; + expressWinstonReq._routeWhitelists.res = ['_headers']; +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2e8b7cf --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "module": "commonjs", + "lib": [ + "es6" + ], + "noImplicitAny": true, + "noImplicitThis": true, + "strictNullChecks": true, + "strictFunctionTypes": true, + "types": [], + "noEmit": true, + "forceConsistentCasingInFileNames": true + }, + "files": [ + "index.d.ts", + "test/express-winston-tests.ts" + ] +} From a694259740526ec36d39a48c598d5cce4f8f49a9 Mon Sep 17 00:00:00 2001 From: Matt Morrissette Date: Wed, 11 Sep 2019 21:41:25 -0700 Subject: [PATCH 43/59] Add missing types --- index.d.ts | 2 +- package.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index 274f7fa..f15cf70 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,4 +1,4 @@ -// Type definitions for express-winston 3.0 +// Type definitions for express-winston 4.0 // Project: https://github.com/bithavoc/express-winston#readme // Definitions by: Alex Brick // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped diff --git a/package.json b/package.json index 86b66ad..4d0487a 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ }, "devDependencies": { "@types/express": "^4.17.1", + "@types/logform": "^1.2.0", "eslint": "^5.16.0", "mocha": "^5.2.0", "node-mocks-http": "^1.5.1", From 1716ced4e4d4c5fdbc4fb539830eafa8cf36fe68 Mon Sep 17 00:00:00 2001 From: Matt Morrissette Date: Wed, 11 Sep 2019 22:53:56 -0700 Subject: [PATCH 44/59] Fix readme example for google logging --- Readme.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index db1a062..f558681 100644 --- a/Readme.md +++ b/Readme.md @@ -338,7 +338,7 @@ app.use(expressWinston.logger({ requestField: "httpRequest", responseField: "httpRequest", requestWhitelist: ["requestMethod", "requestUrl", "protocol", "remoteIp", "requestSize", "userAgent", "referrer"], - responseWhitelist: ["status", "responseSize", "responseTime", "latency"], + responseWhitelist: ["status", "responseSize", "latency"], requestFilter: function (req, propName) { switch(propName) { case "requestMethod": @@ -369,7 +369,10 @@ app.use(expressWinston.logger({ typeof res.body === 'string' ? res.body.length : undefined; case "latency": - return res.responseTime; + return { + seconds: Math.round(res.responseTime / 1000), + nanos: (res.responseTime - Math.round(res.responseTime / 1000) * 1000) * 1000000 + }; default: return undefined; } From e309de2c191e13f7abb305f07460efff594cf340 Mon Sep 17 00:00:00 2001 From: Matt Date: Tue, 17 Sep 2019 11:45:17 -0400 Subject: [PATCH 45/59] Updating readme and changelog --- CHANGELOG.md | 9 ++++++ Readme.md | 79 ++++++++++++++++++++++++---------------------------- 2 files changed, 45 insertions(+), 43 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 343f0a3..1a0746b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +## 4.0.0 +* Changed `metaField` configuration property functionality (see Readme.md) ([#209](https://github.com/bithavoc/express-winston/issues/209)) - BREAKING CHANGE +* Moved type definitions to be embedded inside library ([#123](https://github.com/bithavoc/express-winston/issues/123)) +* Added "files" to package.json to reduce unnecessary files in released package +* Added StackDriver/Google Cloud Logging specific instructions to Readme.md +* Added `requestField` and `responseField` options to allow storing the request and response in different metadata fields (or not at all) +* Fixed `meta` configuration option on `errorLogger` (was not functioning at all) +* Added .editorconfig and reformatted library to match + ## 3.3.0 * Added: options.headerBlacklist ([#217](https://github.com/bithavoc/express-winston/pull/217), @maxday) diff --git a/Readme.md b/Readme.md index f558681..216bbe5 100644 --- a/Readme.md +++ b/Readme.md @@ -333,50 +333,43 @@ var LoggingWinston = require('@google-cloud/logging-winston').LoggingWinston; const app = express() app.use(expressWinston.logger({ - transports: [ new LoggingWinston({}) ], - metaField: null, - requestField: "httpRequest", - responseField: "httpRequest", - requestWhitelist: ["requestMethod", "requestUrl", "protocol", "remoteIp", "requestSize", "userAgent", "referrer"], - responseWhitelist: ["status", "responseSize", "latency"], - requestFilter: function (req, propName) { - switch(propName) { - case "requestMethod": - return req.method; - case "requestUrl": - return `${req.protocol}://${req.hostname}${req.originalUrl}`; - case "protocol": - return req.protocol; - case "remoteIp": - return req.ip; - case "requestSize": - return req.get('Content-Length'); - case "userAgent": - return req.get('User-Agent'); - case "referrer": - return req.get("Referer"); - default: - return undefined; - } - }, - responseFilter: function(res, propName) { - switch (propName) { - case "status": - return res.statusCode; - case "responseSize": - return typeof res.body === 'object' ? - JSON.stringify(res.body).length : - typeof res.body === 'string' ? - res.body.length : undefined; - case "latency": - return { - seconds: Math.round(res.responseTime / 1000), - nanos: (res.responseTime - Math.round(res.responseTime / 1000) * 1000) * 1000000 - }; - default: - return undefined; + transports: [new LoggingWinston({})], + metaField: null, //this causes the metadata to be stored at the root of the log entry + responseField: null, // this prevents the response from being included in the metadata (including body and status code) + requestWhitelist: ["headers", "query"], //these are not included in the standard StackDriver httpRequest + responseWhitelist: ["body"], // this populates the `res.body` so we can get the response size (not required) + dynamicMeta: (req, res) => { + const httpRequest = {} + const meta = {} + if (req) { + meta.httpRequest = httpRequest + httpRequest.requestMethod = req.method + httpRequest.requestUrl = `${req.protocol}://${req.hostname}${req.originalUrl}` + httpRequest.protocol = `${req.protocol}/${req.httpVersion}` + // httpRequest.remoteIp = req.ip // this includes both ipv6 and ipv4 addresses separated by ':' + httpRequest.remoteIp = req.ip.indexOf(':') >= 0 ? req.ip.substring(req.lastIndexOf(':') + 1) : req.ip // just ipv4 + httpRequest.requestSize = req.socket.bytesRead + httpRequest.userAgent = req.get("User-Agent") + httpRequest.referrer = req.get("Referrer") + } + + if (res) { + meta.httpRequest = httpRequest + httpRequest.status = res.statusCode + httpRequest.latency = { + seconds: Math.floor(res.responseTime / 1000), + nanos: ( res.responseTime % 1000 ) * 1000000 + } + if (res.body) { + if (typeof res.body === "object") { + httpRequest.responseSize = JSON.stringify(res.body).length + } else if (typeof res.body === "string") { + httpRequest.responseSize = res.body.length + } + } + } + return meta } - } })); ``` From 51ee031436f5c2e8250688abdf059601a500d2cd Mon Sep 17 00:00:00 2001 From: Matt Morrissette Date: Tue, 24 Sep 2019 15:34:05 -0700 Subject: [PATCH 46/59] doc: fix readme typo --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 216bbe5..adba118 100644 --- a/Readme.md +++ b/Readme.md @@ -347,7 +347,7 @@ app.use(expressWinston.logger({ httpRequest.requestUrl = `${req.protocol}://${req.hostname}${req.originalUrl}` httpRequest.protocol = `${req.protocol}/${req.httpVersion}` // httpRequest.remoteIp = req.ip // this includes both ipv6 and ipv4 addresses separated by ':' - httpRequest.remoteIp = req.ip.indexOf(':') >= 0 ? req.ip.substring(req.lastIndexOf(':') + 1) : req.ip // just ipv4 + httpRequest.remoteIp = req.ip.indexOf(':') >= 0 ? req.ip.substring(req.ip.lastIndexOf(':') + 1) : req.ip // just ipv4 httpRequest.requestSize = req.socket.bytesRead httpRequest.userAgent = req.get("User-Agent") httpRequest.referrer = req.get("Referrer") From 434d82eb294e32cbb99fd127e142e9f8d33a5086 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Sun, 29 Sep 2019 20:27:38 -0500 Subject: [PATCH 47/59] Version bump 3.4.0 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 343f0a3..e9aaa37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 3.4.0 +* Added: Nested Whitelists ([#225](https://github.com/bithavoc/express-winston/pull/225), @kapalex) + ## 3.3.0 * Added: options.headerBlacklist ([#217](https://github.com/bithavoc/express-winston/pull/217), @maxday) diff --git a/package.json b/package.json index ec4009e..0941707 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "3.3.0", + "version": "3.4.0", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 565cf5cc908a882f5420735cfc57be0783960ac1 Mon Sep 17 00:00:00 2001 From: Matthew Morrissette Date: Mon, 30 Sep 2019 13:52:17 -0700 Subject: [PATCH 48/59] Typo in Readme.md Fixing 'requestUrl' typo for Google Logging instructions --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index adba118..df7eca5 100644 --- a/Readme.md +++ b/Readme.md @@ -344,8 +344,8 @@ app.use(expressWinston.logger({ if (req) { meta.httpRequest = httpRequest httpRequest.requestMethod = req.method - httpRequest.requestUrl = `${req.protocol}://${req.hostname}${req.originalUrl}` - httpRequest.protocol = `${req.protocol}/${req.httpVersion}` + httpRequest.requestUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}` + httpRequest.protocol = `HTTP/${req.httpVersion}` // httpRequest.remoteIp = req.ip // this includes both ipv6 and ipv4 addresses separated by ':' httpRequest.remoteIp = req.ip.indexOf(':') >= 0 ? req.ip.substring(req.ip.lastIndexOf(':') + 1) : req.ip // just ipv4 httpRequest.requestSize = req.socket.bytesRead From e50d7257cbdae2fd02d93627163f8ea65c5e2cec Mon Sep 17 00:00:00 2001 From: Matthew Morrissette Date: Mon, 30 Sep 2019 14:00:19 -0700 Subject: [PATCH 49/59] Fix quotes in Readme.md for Google Logging --- Readme.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Readme.md b/Readme.md index df7eca5..1ece9db 100644 --- a/Readme.md +++ b/Readme.md @@ -336,8 +336,8 @@ app.use(expressWinston.logger({ transports: [new LoggingWinston({})], metaField: null, //this causes the metadata to be stored at the root of the log entry responseField: null, // this prevents the response from being included in the metadata (including body and status code) - requestWhitelist: ["headers", "query"], //these are not included in the standard StackDriver httpRequest - responseWhitelist: ["body"], // this populates the `res.body` so we can get the response size (not required) + requestWhitelist: ['headers', 'query'], //these are not included in the standard StackDriver httpRequest + responseWhitelist: ['body'], // this populates the `res.body` so we can get the response size (not required) dynamicMeta: (req, res) => { const httpRequest = {} const meta = {} @@ -349,8 +349,8 @@ app.use(expressWinston.logger({ // httpRequest.remoteIp = req.ip // this includes both ipv6 and ipv4 addresses separated by ':' httpRequest.remoteIp = req.ip.indexOf(':') >= 0 ? req.ip.substring(req.ip.lastIndexOf(':') + 1) : req.ip // just ipv4 httpRequest.requestSize = req.socket.bytesRead - httpRequest.userAgent = req.get("User-Agent") - httpRequest.referrer = req.get("Referrer") + httpRequest.userAgent = req.get('User-Agent') + httpRequest.referrer = req.get('Referrer') } if (res) { @@ -361,9 +361,9 @@ app.use(expressWinston.logger({ nanos: ( res.responseTime % 1000 ) * 1000000 } if (res.body) { - if (typeof res.body === "object") { + if (typeof res.body === 'object') { httpRequest.responseSize = JSON.stringify(res.body).length - } else if (typeof res.body === "string") { + } else if (typeof res.body === 'string') { httpRequest.responseSize = res.body.length } } @@ -371,7 +371,6 @@ app.use(expressWinston.logger({ return meta } })); - ``` ## Global Whitelists and Blacklists From bc90297201754f464149f887cca9f2195d06cb28 Mon Sep 17 00:00:00 2001 From: Alok Rajiv Date: Wed, 9 Oct 2019 11:53:21 +0800 Subject: [PATCH 50/59] Adding `headerBlacklist` in LoggerOption interface --- index.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/index.d.ts b/index.d.ts index f15cf70..fbf7b39 100644 --- a/index.d.ts +++ b/index.d.ts @@ -44,6 +44,7 @@ export interface BaseLoggerOptions { requestWhitelist?: string[]; responseFilter?: ResponseFilter; responseWhitelist?: string[]; + headerBlacklist?: string[]; skip?: RouteFilter; statusLevels?: { error?: string; From 7a40390aaee380b71701d5ba122eec75e4595e2e Mon Sep 17 00:00:00 2001 From: bithavoc Date: Wed, 9 Oct 2019 08:30:20 -0500 Subject: [PATCH 51/59] Version bump 4.0.1 --- CHANGELOG.md | 3 +++ package.json | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a10c4ff..1abbd4f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +## 4.0.1 +* Added `headerBlacklist` to BaseLoggerOptions for better typescript support ([#228](https://github.com/bithavoc/express-winston/pull/228)) + ## 4.0.0 * Changed `metaField` configuration property functionality (see Readme.md) ([#209](https://github.com/bithavoc/express-winston/issues/209)) - BREAKING CHANGE * Moved type definitions to be embedded inside library ([#123](https://github.com/bithavoc/express-winston/issues/123)) diff --git a/package.json b/package.json index 4d0487a..9c9c498 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "4.0.0", + "version": "4.0.1", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 74d41d1bf1a5d8b304ab9d8759731f8a9b618442 Mon Sep 17 00:00:00 2001 From: Kanji Tanaka Date: Mon, 9 Dec 2019 15:32:34 +0900 Subject: [PATCH 52/59] Add blacklist fields to BaseErrorLoggerOptions interface --- index.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/index.d.ts b/index.d.ts index fbf7b39..1a74e53 100644 --- a/index.d.ts +++ b/index.d.ts @@ -76,6 +76,8 @@ export interface BaseErrorLoggerOptions { msg?: MessageTemplate; requestFilter?: RequestFilter; requestWhitelist?: string[]; + headerBlacklist?: string[]; + blacklistedMetaFields?: string[]; } export interface ErrorLoggerOptionsWithTransports extends BaseErrorLoggerOptions { From d75fc4e63612fbd63ceff928aa130f9830e24654 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Tue, 17 Dec 2019 09:33:40 -0500 Subject: [PATCH 53/59] Version bump 4.0.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9c9c498..0fad36a 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "4.0.1", + "version": "4.0.2", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" From 6d54c6f8709a964be6eddc24f067dc19473b8d14 Mon Sep 17 00:00:00 2001 From: s0j0hn Date: Fri, 24 Jan 2020 10:56:39 +0100 Subject: [PATCH 54/59] Update lodash to 4.17.15 Update lodash to 4.17.15 https://www.npmjs.com/advisories/1065 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0fad36a..26ce6c6 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "chalk": "^2.4.1", - "lodash": "^4.17.10" + "lodash": "^4.17.15" }, "devDependencies": { "@types/express": "^4.17.1", From df27fb8836c12b48498223dc30f7848ea5fd273b Mon Sep 17 00:00:00 2001 From: Valentin Prugnaud Date: Thu, 6 Feb 2020 03:13:12 -0800 Subject: [PATCH 55/59] Add missing exceptionToMeta and skip to TypeScript definition --- Readme.md | 4 ++-- index.d.ts | 5 +++++ test/express-winston-tests.ts | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 1ece9db..091a64b 100644 --- a/Readme.md +++ b/Readme.md @@ -124,8 +124,8 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE baseMeta: Object, // default meta data to be added to log, this will be merged with the error data. meta: Boolean, // control whether you want to log the meta data about the request (default to true). metaField: String, // if defined, the meta data will be added in this field instead of the meta root object. Defaults to 'meta'. Set to `null` to store metadata at the root of the log entry. - requestField: [String] // the property of the metadata to store the request under (default 'req'). Set to null to exclude request from metadata - responseField: [String] // the property of the metadata to store the response under (default 'res'). If set to the same as 'requestField', filtered response and request properties will be merged. Set to null to exclude request from metadata + requestField: [String] // the property of the metadata to store the request under (default 'req'). Set to null to exclude request from metadata + responseField: [String] // the property of the metadata to store the response under (default 'res'). If set to the same as 'requestField', filtered response and request properties will be merged. Set to null to exclude request from metadata requestFilter: function (req, propName) { return req[propName]; } // A function to filter/return request values, defaults to returning all values allowed by whitelist. If the function returns undefined, the key/value will not be included in the meta. requestWhitelist: [String] // Array of request properties to log. Overrides global requestWhitelist for this instance headerBlacklist: [String], // Array of headers to omit from logs. Applied after any previous filters. diff --git a/index.d.ts b/index.d.ts index 1a74e53..e708216 100644 --- a/index.d.ts +++ b/index.d.ts @@ -17,11 +17,13 @@ export interface FilterResponse extends Response { [other: string]: any; } +export type ExceptionToMetaFunction = (err: Error) => object; export type DynamicMetaFunction = (req: Request, res: Response, err: Error) => object; export type DynamicLevelFunction = (req: Request, res: Response, err: Error) => string; export type RequestFilter = (req: FilterRequest, propName: string) => any; export type ResponseFilter = (res: FilterResponse, propName: string) => any; export type RouteFilter = (req: Request, res: Response) => boolean; +export type ErrorRouteFilter = (req: Request, res: Response, err: Error) => boolean; export type MessageTemplate = string | ((req: Request, res: Response) => string); export interface BaseLoggerOptions { @@ -68,16 +70,19 @@ export function logger(options: LoggerOptions): Handler; export interface BaseErrorLoggerOptions { baseMeta?: object; dynamicMeta?: DynamicMetaFunction; + exceptionToMeta?: ExceptionToMetaFunction; format?: Format; level?: string | DynamicLevelFunction; meta?: boolean; metaField?: string; requestField?: string; + responseField?: string; msg?: MessageTemplate; requestFilter?: RequestFilter; requestWhitelist?: string[]; headerBlacklist?: string[]; blacklistedMetaFields?: string[]; + skip?: ErrorRouteFilter; } export interface ErrorLoggerOptionsWithTransports extends BaseErrorLoggerOptions { diff --git a/test/express-winston-tests.ts b/test/express-winston-tests.ts index d17693c..6f80263 100644 --- a/test/express-winston-tests.ts +++ b/test/express-winston-tests.ts @@ -49,12 +49,19 @@ app.use(expressWinston.logger({ app.use(expressWinston.errorLogger({ baseMeta: { foo: 'foo', nested: { bar: 'baz' } }, dynamicMeta: (req, res, err) => ({ foo: 'bar' }), + exceptionToMeta: function(error){return {}; }, format: new Format(), level: (req, res) => 'level', + meta: true, metaField: 'metaField', + requestField: 'requestField', + responseField: 'responseField', msg: 'msg', requestFilter: (req, prop) => true, requestWhitelist: ['foo', 'bar'], + headerBlacklist: ['foo', 'bar'], + blacklistedMetaFields: ['foo', 'bar'], + skip: (req, res) => false, transports: [ new winston.transports.Console({}) ] From 45ec9e70084023e99c3c9cedc1a2f13d6e026e97 Mon Sep 17 00:00:00 2001 From: bithavoc Date: Thu, 13 Feb 2020 12:36:49 -0500 Subject: [PATCH 56/59] Version bump 4.0.3 --- CHANGELOG.md | 7 +++++++ package.json | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1abbd4f..de624f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 4.0.3 +* Update lodash to 4.17.15 ([#232](https://github.com/bithavoc/express-winston/pull/232)) +* Add missing exceptionToMeta and skip to TypeScript definition ([#234](https://github.com/bithavoc/express-winston/pull/234)) + +## 4.0.2 +* Add blacklist fields to BaseErrorLoggerOptions interface ([#230](https://github.com/bithavoc/express-winston/pull/230)) + ## 4.0.1 * Added `headerBlacklist` to BaseLoggerOptions for better typescript support ([#228](https://github.com/bithavoc/express-winston/pull/228)) diff --git a/package.json b/package.json index 26ce6c6..1f9de3e 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "4.0.2", + "version": "4.0.3", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git" @@ -91,4 +91,4 @@ } ], "types": "index.d.ts" -} +} \ No newline at end of file From a02930b1d80a56fefcf158feafe729ea1ecc9f2c Mon Sep 17 00:00:00 2001 From: Francisco Hanna Date: Mon, 13 Jul 2020 21:09:12 -0300 Subject: [PATCH 57/59] Update statusLevels type Update statusLevels to accept Boolean or StatusLevel object --- index.d.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/index.d.ts b/index.d.ts index e708216..4d5d400 100644 --- a/index.d.ts +++ b/index.d.ts @@ -26,6 +26,12 @@ export type RouteFilter = (req: Request, res: Response) => boolean; export type ErrorRouteFilter = (req: Request, res: Response, err: Error) => boolean; export type MessageTemplate = string | ((req: Request, res: Response) => string); +export interface StatusLevels { + error?: string; + success?: string; + warn?: string; +}; + export interface BaseLoggerOptions { baseMeta?: object; bodyBlacklist?: string[]; @@ -48,12 +54,8 @@ export interface BaseLoggerOptions { responseWhitelist?: string[]; headerBlacklist?: string[]; skip?: RouteFilter; - statusLevels?: { - error?: string; - success?: string; - warn?: string; - }; -} + statusLevels?: Boolean | StatusLevels; +} export interface LoggerOptionsWithTransports extends BaseLoggerOptions { transports: Transport[]; From 9183ca60a80cd11f3e5112d0339ddca41d9829b6 Mon Sep 17 00:00:00 2001 From: AbOuLfOuZ Date: Tue, 14 Jul 2020 09:16:42 +0200 Subject: [PATCH 58/59] Update package.json --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1f9de3e..67278e3 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "chalk": "^2.4.1", - "lodash": "^4.17.15" + "lodash": "^4.17.19" }, "devDependencies": { "@types/express": "^4.17.1", @@ -91,4 +91,4 @@ } ], "types": "index.d.ts" -} \ No newline at end of file +} From 9fe4d4815ce889474723839a6846bb2e911f3cdd Mon Sep 17 00:00:00 2001 From: bithavoc Date: Wed, 22 Jul 2020 08:42:48 -0500 Subject: [PATCH 59/59] Version bump 4.0.4 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de624f7..d4c36ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.0.4 +* Update lodash to 4.17.19 ([#245](https://github.com/bithavoc/express-winston/pull/245)) +* Update statusLevels to accept Boolean or StatusLevel object in TypeScript definition ([#244](https://github.com/bithavoc/express-winston/pull/244)) + ## 4.0.3 * Update lodash to 4.17.15 ([#232](https://github.com/bithavoc/express-winston/pull/232)) * Add missing exceptionToMeta and skip to TypeScript definition ([#234](https://github.com/bithavoc/express-winston/pull/234)) diff --git a/package.json b/package.json index 67278e3..fe05d98 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "middleware", "colors" ], - "version": "4.0.3", + "version": "4.0.4", "repository": { "type": "git", "url": "https://github.com/bithavoc/express-winston.git"