winston-middleware 1.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of winston-middleware might be problematic. Click here for more details.

package/.travis.yml CHANGED
@@ -1,13 +1,6 @@
1
1
  sudo: false
2
2
  language: node_js
3
3
  node_js:
4
- - "0.10"
5
- - "0.11"
6
- - "0.12"
7
- - "4.2"
8
- - "5.3"
9
- - "iojs"
10
-
11
- script:
12
- - "test $TRAVIS_NODE_VERSION = '0.6' || npm test"
13
- - "test $TRAVIS_NODE_VERSION != '0.6' || npm run-script test-coverage"
4
+ - "6"
5
+ - "8"
6
+ - "10"
package/AUTHORS CHANGED
@@ -1,4 +1,4 @@
1
- Heapsource <npm@heapsource.com> (http://www.heapsource.com)
1
+ Bithavoc <im@bithavoc.io> (https://bithavoc.io)
2
2
  Lars Jacob (http://jaclar.net)
3
3
  Jonathan Lomas (http://floatinglomas.ca)
4
4
  Xavier Damman (http://xdamman.com)
@@ -6,3 +6,4 @@ Quentin Rossetti <quentin.rossetti@gmail.com>
6
6
  Damian Kaczmarek <rush@rushbase.net>
7
7
  Robbie Trencheny <me@robbiet.us> (http://robbie.io)
8
8
  Ross Brandes <ross.brandes@gmail.com>
9
+ Kévin Maschtaler (https://www.kmaschta.me)
package/CHANGELOG.md ADDED
@@ -0,0 +1,69 @@
1
+ ## 3.0.0
2
+ winston-middleware@3 shouldn't have any breaking changes _of its own_, but there are breaking changes as a result of upgrading winston and Node.js.
3
+
4
+ winston-middleware@2.6.0 will be the last version to support winston@2.
5
+
6
+ #### Breaking changes
7
+ * 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.
8
+ * 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).
9
+
10
+ ## 2.6.0
11
+ * Add `exceptionToMeta` and `blacklistedMetaFields` for filtering returned meta
12
+ object ([#173](https://github.com/bithavoc/winston-middleware/pull/173), @cubbuk)
13
+
14
+ ## 2.5.1
15
+ * Allow `msg` to be a function ([#160](https://github.com/bithavoc/winston-middleware/pull/160), @brendancwood)
16
+
17
+ ## 2.5.0
18
+ * Reduce memory usage ([#164](https://github.com/bithavoc/winston-middleware/pull/164), @Kmaschta)
19
+
20
+ ## 2.4.0
21
+ * Allow `options.level` to be a function for dynamic level setting ([#148](https://github.com/bithavoc/winston-middleware/pull/148), @CryptArchy)
22
+
23
+ ## 2.3.0
24
+ * Allow line breaks inside `msg` interpolation ([#143](https://github.com/bithavoc/winston-middleware/pull/143), @ltegman)
25
+
26
+ ## 2.2.0
27
+ * Add dynamic metadata support to error logger ([#139](https://github.com/bithavoc/winston-middleware/issues/139), @scarlettsteph)
28
+
29
+ ## 2.1.3
30
+ * Re-enable logging of req.body when request whitelist contains body and there is no no body whitelist/blacklist (broken in 2.1.1) ([#136](https://github.com/bithavoc/winston-middleware/issues/136))
31
+
32
+ ## 2.1.2
33
+ * Fix error throwing when no req is logged ([#130](https://github.com/bithavoc/winston-middleware/issues/130))
34
+
35
+ ## 2.1.1
36
+ * Fix `bodyBlacklist` not working when `requestWhitelist` contains `body` ([#128](https://github.com/bithavoc/winston-middleware/issues/128), @jacobcabantomski-ct)
37
+
38
+ ## 2.1.0
39
+ * Add dynamic metadata support ([#124](https://github.com/bithavoc/winston-middleware/issues/124), @jpdelima)
40
+ * Add support for `colorize`-ing status code, no `expressFormat` required ([#121](https://github.com/bithavoc/winston-middleware/issues/121))
41
+
42
+ ## 2.0.0
43
+ #### Breaking changes
44
+ * Make winston a peer dependency. `npm install --save winston` if you haven't already.
45
+ * `expressFormat` has no color by default. Add `colorize: true` to winston-middleware
46
+ options to enable the previous colorized output. ([#86](https://github.com/bithavoc/winston-middleware/issues/86))
47
+ * Drop support for inherited properties on the object provided to the `baseMeta` option. This is unlikely to actually break anyone's real-world setup.
48
+
49
+ ## 1.4.2
50
+ * Upgrade winston to 1.1 ([#114](https://github.com/bithavoc/winston-middleware/issues/114))
51
+
52
+ ## 1.4.1
53
+ * Don't throw exception on invalid JSON in response body ([#112](https://github.com/bithavoc/winston-middleware/issues/112))
54
+
55
+ ## 1.4.0
56
+ * Allow custom log level for error logger ([#111](https://github.com/bithavoc/winston-middleware/pull/111))
57
+
58
+ ## 1.3.1
59
+ * underscore -> lodash ([#88](https://github.com/bithavoc/winston-middleware/issues/88))
60
+
61
+ ## 1.3.0
62
+ * Allow custom status levels ([#102](https://github.com/bithavoc/winston-middleware/pull/102))
63
+ * Add per-instance equivalents of all global white/blacklists ([#105](https://github.com/bithavoc/winston-middleware/pull/105))
64
+ * Allow user to override module-level whitelists and functions without having to worry about using the right object reference ([#92](https://github.com/bithavoc/winston-middleware/issues/92))
65
+
66
+ ## 1.2.0
67
+ * Add `baseMeta` and `metaField` options ([#91](https://github.com/bithavoc/winston-middleware/pull/91))
68
+ * Document `requestFilter` and `responseFilter` options
69
+ * Drop support for node 0.6 and 0.8
package/Readme.md CHANGED
@@ -1,17 +1,15 @@
1
1
  # winston-middleware
2
2
  [![Monthly Downloads](https://img.shields.io/npm/dm/winston-middleware.svg)](https://www.npmjs.com/package/winston-middleware) [![Build Status](https://secure.travis-ci.org/bithavoc/winston-middleware.png)](http://travis-ci.org/bithavoc/winston-middleware)
3
3
 
4
- > [winston](https://github.com/flatiron/winston) middleware for express.js
4
+ > [winston](https://github.com/winstonjs/winston) middleware for express.js
5
5
 
6
- ## Installation
7
-
8
- Newcomers should start with the latest branch which makes use of winston 1.x.x and supports node >= 0.10:
6
+ [Changelog](CHANGELOG.md)
9
7
 
10
- npm install winston-middleware
8
+ ## Installation
11
9
 
12
- If you're already using winston-middleware, and want to stick with the stable version based on winston 0.9.x, you should instead do:
10
+ npm install winston winston-middleware
13
11
 
14
- npm install winston-middleware@0.x.x --save
12
+ (supports node >= 6)
15
13
 
16
14
  ## Usage
17
15
 
@@ -25,8 +23,8 @@ In `package.json`:
25
23
  {
26
24
  "dependencies": {
27
25
  "...": "...",
28
- "winston": "0.6.x",
29
- "winston-middleware": "0.2.x",
26
+ "winston": "^3.0.0",
27
+ "winston-middleware": "^3.0.0",
30
28
  "...": "..."
31
29
  }
32
30
  }
@@ -55,8 +53,8 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
55
53
  ],
56
54
  meta: true, // optional: control whether you want to log the meta data about the request (default to true)
57
55
  msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
58
- expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
59
- colorStatus: true, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
56
+ 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
57
+ colorize: false, // Color the text and status code, using the Express/morgan color palette (text: gray, status: default green, 3XX cyan, 4XX yellow, 5XX red).
60
58
  ignoreRoute: function (req, res) { return false; } // optional: allows to skip some log messages based on request and/or response
61
59
  }));
62
60
 
@@ -68,14 +66,14 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
68
66
  ``` js
69
67
  transports: [<WinstonTransport>], // list of all winston transports instances to use.
70
68
  winstonInstance: <WinstonLogger>, // a winston logger instance. If this is provided the transports option is ignored.
71
- level: String, // log level to use, the default is "info".
72
- msg: String // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}".
73
- expressFormat: Boolean, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
74
- colorStatus: Boolean, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
69
+ 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.
70
+ 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}` }
71
+ 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
72
+ 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).
75
73
  meta: Boolean, // control whether you want to log the meta data about the request (default to true).
76
74
  baseMeta: Object, // default meta data to be added to log, this will be merged with the meta data.
77
75
  metaField: String, // if defined, the meta data will be added in this field instead of the meta root object.
78
- 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.
76
+ 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.
79
77
  ignoreRoute: function (req, res) { return false; } // A function to determine if logging is skipped, defaults to returning false. Called _before_ any later middleware.
80
78
  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.
81
79
  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.
@@ -85,6 +83,7 @@ Use `expressWinston.logger(options)` to create a middleware to log your HTTP req
85
83
  bodyWhitelist: [String] // Array of body properties to log. Overrides global bodyWhitelist for this instance
86
84
  bodyBlacklist: [String] // Array of body properties to omit from logs. Overrides global bodyBlacklist for this instance
87
85
  ignoredRoutes: [String] // Array of paths to ignore/skip logging. Overrides global ignoredRoutes for this instance
86
+ 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
88
87
 
89
88
  ```
90
89
 
@@ -113,12 +112,15 @@ The logger needs to be added AFTER the express router(`app.router)`) and BEFORE
113
112
  ``` js
114
113
  transports: [<WinstonTransport>], // list of all winston transports instances to use.
115
114
  winstonInstance: <WinstonLogger>, // a winston logger instance. If this is provided the transports option is ignored
116
- msg: String // customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}", "HTTP {{req.method}} {{req.url}}".
115
+ 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}` }
117
116
  baseMeta: Object, // default meta data to be added to log, this will be merged with the error data.
118
117
  metaField: String, // if defined, the meta data will be added in this field instead of the meta root object.
119
118
  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.
120
119
  requestWhitelist: [String] // Array of request properties to log. Overrides global requestWhitelist for this instance
121
- level: String // custom log level for errors (default is 'error')
120
+ 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.
121
+ 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
122
+ 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
123
+ blacklistedMetaFields: [String] // fields to blacklist from meta data
122
124
  ```
123
125
 
124
126
  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)`.
@@ -143,12 +145,12 @@ Alternatively, if you're using a winston logger instance elsewhere and have alre
143
145
  return next(new Error("This is an error and it should be logged to the console"));
144
146
  });
145
147
 
146
- app.get('/', function(req, res, next) {
148
+ router.get('/', function(req, res, next) {
147
149
  res.write('This is a normal request, it should be logged to the console too');
148
150
  res.end();
149
151
  });
150
152
 
151
- // winston-middleware logger makes sense BEFORE the router.
153
+ // winston-middleware logger makes sense BEFORE the router
152
154
  app.use(expressWinston.logger({
153
155
  transports: [
154
156
  new winston.transports.Console({
@@ -370,7 +372,7 @@ Blacklisting supports only the `body` property.
370
372
  });
371
373
  ```
372
374
 
373
- If both `req._bodyWhitelist.body` and `req._bodyBlacklist.body` are set the result will be the white listed properties
375
+ If both `req._routeWhitelists.body` and `req._routeBlacklists.body` are set the result will be the white listed properties
374
376
  excluding any black listed ones. In the above example, only 'email' and 'age' would be included.
375
377
 
376
378
 
@@ -385,6 +387,44 @@ If you set statusLevels to true winston-middleware will log sub 400 responses at
385
387
  }
386
388
  ```
387
389
 
390
+ ## Dynamic Status Levels
391
+
392
+ If you set statusLevels to false and assign a function to level, you can customize the log level for any scenario.
393
+
394
+ ```js
395
+ statusLevels: false // default value
396
+ level: function (req, res) {
397
+ var level = "";
398
+ if (res.statusCode >= 100) { level = "info"; }
399
+ if (res.statusCode >= 400) { level = "warn"; }
400
+ if (res.statusCode >= 500) { level = "error"; }
401
+ // Ops is worried about hacking attempts so make Unauthorized and Forbidden critical
402
+ if (res.statusCode == 401 || res.statusCode == 403) { level = "critical"; }
403
+ // No one should be using the old path, so always warn for those
404
+ if (req.path === "/v1" && level === "info") { level = "warn"; }
405
+ return level;
406
+ }
407
+ ```
408
+
409
+
410
+ ## Dynamic meta data from request or response
411
+
412
+ If you set dynamicMeta function you can extract additional meta data fields from request or response objects.
413
+ The function can be used to either select relevant elements in request or response body without logging them as a whole
414
+ or to extract runtime data like the user making the request. The example below logs the user name and role as assigned
415
+ by the passport authentication middleware.
416
+
417
+ ```js
418
+ meta: true,
419
+ dynamicMeta: function(req, res) {
420
+ return {
421
+ user: req.user ? req.user.username : null,
422
+ role: req.user ? req.user.role : null,
423
+ ...
424
+ }
425
+ }
426
+ ```
427
+
388
428
  ## Tests
389
429
 
390
430
  Run the basic Mocha tests:
package/index.js CHANGED
@@ -117,44 +117,69 @@ exports.errorLogger = function errorLogger(options) {
117
117
 
118
118
  options.requestWhitelist = options.requestWhitelist || exports.requestWhitelist;
119
119
  options.requestFilter = options.requestFilter || exports.defaultRequestFilter;
120
- options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports }));
120
+ options.winstonInstance = options.winstonInstance || (winston.createLogger ({ transports: options.transports }));
121
121
  options.msg = options.msg || 'middlewareError';
122
122
  options.baseMeta = options.baseMeta || {};
123
123
  options.metaField = options.metaField || null;
124
124
  options.level = options.level || 'error';
125
+ options.dynamicMeta = options.dynamicMeta || function(req, res, err) { return null; };
126
+ const exceptionHandler = new winston.ExceptionHandler(options.winstonInstance);
127
+ options.exceptionToMeta = options.exceptionToMeta || exceptionHandler.getAllInfo.bind(exceptionHandler);
128
+ options.blacklistedMetaFields = options.blacklistedMetaFields || [];
125
129
 
126
130
  // Using mustache style templating
127
- var template = _.template(options.msg, {
128
- interpolate: /\{\{(.+?)\}\}/g
129
- });
131
+ var getTemplate = function(msg, data) {
132
+ return _.template(msg, {interpolate: /\{\{([\s\S]+?)\}\}/g})(data)
133
+ };
130
134
 
131
135
  return function (err, req, res, next) {
132
136
 
133
- // Let winston gather all the error data.
134
- var exceptionMeta = winston.exception.getAllInfo(err);
137
+ // Let winston gather all the error data
138
+ var exceptionMeta = _.omit(options.exceptionToMeta(err), options.blacklistedMetaFields);
135
139
  exceptionMeta.req = filterObject(req, options.requestWhitelist, options.requestFilter);
136
140
 
141
+ if(options.dynamicMeta) {
142
+ var dynamicMeta = options.dynamicMeta(req, res, err);
143
+ exceptionMeta = _.assign(exceptionMeta, dynamicMeta);
144
+ }
145
+
137
146
  if (options.metaField) {
138
147
  var newMeta = {};
139
148
  newMeta[options.metaField] = exceptionMeta;
140
149
  exceptionMeta = newMeta;
141
150
  }
142
151
 
143
- exceptionMeta = _.extend(exceptionMeta, options.baseMeta);
152
+ exceptionMeta = _.assign(exceptionMeta, options.baseMeta);
153
+
154
+ var level = _.isFunction(options.level) ? options.level(req, res, err) : options.level;
155
+
156
+ options.msg = typeof options.msg === 'function' ? options.msg(req, res) : options.msg;
144
157
 
145
158
  // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback
146
- options.winstonInstance.log(options.level, template({err: err, req: req, res: res}), exceptionMeta);
159
+ options.winstonInstance.log({
160
+ level,
161
+ message: getTemplate(options.msg, {err: err, req: req, res: res}),
162
+ meta: exceptionMeta
163
+ });
147
164
 
148
165
  next(err);
149
166
  };
150
167
  };
151
168
 
169
+ function levelFromStatus(options) {
170
+ return function (req, res) {
171
+ var level = "";
172
+ if (res.statusCode >= 100) { level = options.statusLevels.success || "info"; }
173
+ if (res.statusCode >= 400) { level = options.statusLevels.warn || "warn"; }
174
+ if (res.statusCode >= 500) { level = options.statusLevels.error || "error"; }
175
+ return level;
176
+ }
177
+ }
178
+
152
179
  //
153
180
  // ### function logger(options)
154
181
  // #### @options {Object} options to initialize the middleware.
155
182
  //
156
-
157
-
158
183
  exports.logger = function logger(options) {
159
184
 
160
185
  ensureValidOptions(options);
@@ -167,23 +192,35 @@ exports.logger = function logger(options) {
167
192
  options.requestFilter = options.requestFilter || exports.defaultRequestFilter;
168
193
  options.responseFilter = options.responseFilter || exports.defaultResponseFilter;
169
194
  options.ignoredRoutes = options.ignoredRoutes || exports.ignoredRoutes;
170
- options.winstonInstance = options.winstonInstance || (new winston.Logger ({ transports: options.transports }));
171
- options.level = options.level || "info";
195
+ options.winstonInstance = options.winstonInstance || (winston.createLogger ({ transports: options.transports }));
172
196
  options.statusLevels = options.statusLevels || false;
197
+ options.level = options.statusLevels ? levelFromStatus(options) : (options.level || "info");
173
198
  options.msg = options.msg || "HTTP {{req.method}} {{req.url}}";
174
199
  options.baseMeta = options.baseMeta || {};
175
200
  options.metaField = options.metaField || null;
176
- options.colorStatus = options.colorStatus || false;
201
+ options.colorize = options.colorize || false;
177
202
  options.expressFormat = options.expressFormat || false;
178
203
  options.ignoreRoute = options.ignoreRoute || function () { return false; };
179
204
  options.skip = options.skip || exports.defaultSkip;
205
+ options.dynamicMeta = options.dynamicMeta || function(req, res) { return null; };
206
+
207
+ var expressMsgFormat = "{{req.method}} {{req.url}} {{res.statusCode}} {{res.responseTime}}ms";
208
+ if (options.colorize) {
209
+ expressMsgFormat = chalk.grey("{{req.method}} {{req.url}}") +
210
+ " {{res.statusCode}} " +
211
+ chalk.grey("{{res.responseTime}}ms");
212
+ }
213
+
214
+ var msgFormat = !options.expressFormat ? options.msg : expressMsgFormat;
180
215
 
181
216
  // Using mustache style templating
182
- var template = _.template(options.msg, {
217
+ var template = _.template(msgFormat, {
183
218
  interpolate: /\{\{(.+?)\}\}/g
184
219
  });
185
220
 
186
221
  return function (req, res, next) {
222
+ var coloredRes = {};
223
+
187
224
  var currentUrl = req.originalUrl || req.url;
188
225
  if (currentUrl && _.includes(options.ignoredRoutes, currentUrl)) return next();
189
226
  if (options.ignoreRoute(req, res)) return next();
@@ -210,21 +247,6 @@ exports.logger = function logger(options) {
210
247
 
211
248
  req.url = req.originalUrl || req.url;
212
249
 
213
- if (options.statusLevels) {
214
- if (res.statusCode >= 100) { options.level = options.statusLevels.success || "info"; }
215
- if (res.statusCode >= 400) { options.level = options.statusLevels.warn || "warn"; }
216
- if (res.statusCode >= 500) { options.level = options.statusLevels.error || "error"; }
217
- };
218
-
219
- if (options.colorStatus || options.expressFormat) {
220
- // Palette from https://github.com/expressjs/morgan/blob/master/index.js#L205
221
- var statusColor = 'green';
222
- if (res.statusCode >= 500) statusColor = 'red';
223
- else if (res.statusCode >= 400) statusColor = 'yellow';
224
- else if (res.statusCode >= 300) statusColor = 'cyan';
225
- var coloredStatusCode = chalk[statusColor](res.statusCode);
226
- }
227
-
228
250
  var meta = {};
229
251
 
230
252
  if(options.meta !== false) {
@@ -240,7 +262,7 @@ exports.logger = function logger(options) {
240
262
  var isJson = (res._headers && res._headers['content-type']
241
263
  && res._headers['content-type'].indexOf('json') >= 0);
242
264
 
243
- logData.res.body = isJson ? JSON.parse(chunk) : chunk.toString();
265
+ logData.res.body = bodyToString(chunk, isJson);
244
266
  }
245
267
  }
246
268
 
@@ -256,35 +278,70 @@ exports.logger = function logger(options) {
256
278
  if (blacklist.length > 0 && bodyWhitelist.length === 0) {
257
279
  var whitelist = _.difference(Object.keys(req.body), blacklist);
258
280
  filteredBody = filterObject(req.body, whitelist, options.requestFilter);
281
+ } else if (
282
+ requestWhitelist.indexOf('body') !== -1 &&
283
+ bodyWhitelist.length === 0 &&
284
+ blacklist.length === 0
285
+ ) {
286
+ filteredBody = filterObject(req.body, Object.keys(req.body), options.requestFilter);
259
287
  } else {
260
288
  filteredBody = filterObject(req.body, bodyWhitelist, options.requestFilter);
261
289
  }
262
290
  }
263
291
 
264
- if (filteredBody) logData.req.body = filteredBody;
292
+ if (logData.req) {
293
+ if (filteredBody) {
294
+ logData.req.body = filteredBody;
295
+ } else {
296
+ delete logData.req.body;
297
+ }
298
+ }
265
299
 
266
300
  logData.responseTime = res.responseTime;
267
301
 
302
+ if(options.dynamicMeta) {
303
+ var dynamicMeta = options.dynamicMeta(req, res);
304
+ logData = _.assign(logData, dynamicMeta);
305
+ }
306
+
268
307
  if (options.metaField) {
269
- var newMeta = {}
308
+ var newMeta = {};
270
309
  newMeta[options.metaField] = logData;
271
310
  logData = newMeta;
272
311
  }
273
- meta = _.extend(meta, logData);
312
+ meta = _.assign(meta, logData);
274
313
  }
275
314
 
276
- meta = _.extend(meta, options.baseMeta);
315
+ meta = _.assign(meta, options.baseMeta);
316
+
317
+ if (options.colorize) {
318
+ // Palette from https://github.com/expressjs/morgan/blob/master/index.js#L205
319
+ var statusColor = 'green';
320
+ if (res.statusCode >= 500) statusColor = 'red';
321
+ else if (res.statusCode >= 400) statusColor = 'yellow';
322
+ else if (res.statusCode >= 300) statusColor = 'cyan';
323
+
324
+ coloredRes.statusCode = chalk[statusColor](res.statusCode);
325
+ }
277
326
 
278
- if(options.expressFormat) {
279
- var msg = chalk.grey(req.method + " " + req.url || req.url)
280
- + " " + chalk[statusColor](res.statusCode)
281
- + " " + chalk.grey(res.responseTime+"ms");
327
+ var msgFormat
328
+ if (!options.expressFormat) {
329
+ msgFormat = typeof options.msg === 'function' ? options.msg(req, res) : options.msg
282
330
  } else {
283
- var msg = template({req: req, res: res});
331
+ msgFormat = expressMsgFormat
284
332
  }
333
+
334
+ // Using mustache style templating
335
+ var template = _.template(msgFormat, {
336
+ interpolate: /\{\{(.+?)\}\}/g
337
+ });
338
+
339
+ var msg = template({req: req, res: _.assign({}, res, coloredRes)});
340
+
285
341
  // This is fire and forget, we don't want logging to hold up the request so don't wait for the callback
286
342
  if (!options.skip(req, res)) {
287
- options.winstonInstance.log(options.level, msg, meta);
343
+ var level = _.isFunction(options.level) ? options.level(req, res) : options.level;
344
+ options.winstonInstance.log({level, message: msg, meta});
288
345
  }
289
346
  };
290
347
 
@@ -292,10 +349,30 @@ exports.logger = function logger(options) {
292
349
  };
293
350
  };
294
351
 
352
+ function safeJSONParse(string) {
353
+ try {
354
+ return JSON.parse(string);
355
+ } catch (e) {
356
+ return undefined;
357
+ }
358
+ }
359
+
360
+ function bodyToString(body, isJSON) {
361
+ var stringBody = body && body.toString();
362
+ if (isJSON) {
363
+ return (safeJSONParse(body) || stringBody);
364
+ }
365
+ return stringBody;
366
+ }
367
+
295
368
  function ensureValidOptions(options) {
296
369
  if(!options) throw new Error("options are required by winston-middleware middleware");
297
370
  if(!((options.transports && (options.transports.length > 0)) || options.winstonInstance))
298
371
  throw new Error("transports or a winstonInstance are required by winston-middleware middleware");
372
+
373
+ if (options.dynamicMeta && !_.isFunction(options.dynamicMeta)) {
374
+ throw new Error("`dynamicMeta` winston-middleware option should be a function");
375
+ }
299
376
  }
300
377
 
301
378
  function ensureValidLoggerOptions(options) {
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "middleware",
18
18
  "colors"
19
19
  ],
20
- "version": "1.4.0",
20
+ "version": "3.0.0",
21
21
  "repository": {
22
22
  "type": "git",
23
23
  "url": "https://github.com/bithavoc/winston-middleware.git"
@@ -47,20 +47,23 @@
47
47
  }
48
48
  },
49
49
  "dependencies": {
50
- "chalk": "~0.4.0",
51
- "lodash": "~4.11.1",
52
- "winston": "~1.0.0"
50
+ "chalk": "^2.4.1",
51
+ "lodash": "^4.17.10"
53
52
  },
54
53
  "devDependencies": {
55
54
  "blanket": "^1.2.2",
56
- "mocha": "^2.4.5",
55
+ "mocha": "^5.2.0",
57
56
  "node-mocks-http": "^1.5.1",
58
- "promise": "^7.1.1",
59
- "should": "^8.2.2",
60
- "travis-cov": "^0.2.5"
57
+ "should": "^13.2.3",
58
+ "travis-cov": "^0.2.5",
59
+ "winston": ">=3.x <4",
60
+ "winston-transport": "^4.2.0"
61
+ },
62
+ "peerDependencies": {
63
+ "winston": ">=3.x <4"
61
64
  },
62
65
  "engines": {
63
- "node": ">=0.10.0"
66
+ "node": ">= 6"
64
67
  },
65
68
  "license": "MIT",
66
69
  "contributors": [
package/test/test.js CHANGED
@@ -1,10 +1,10 @@
1
1
  var util = require('util');
2
2
 
3
3
  var mocks = require('node-mocks-http');
4
- var Promise = require('promise/lib/es6-extensions');
5
4
  var should = require('should');
6
5
  var _ = require('lodash');
7
6
  var winston = require('winston');
7
+ var Transport = require('winston-transport');
8
8
 
9
9
  var expressWinston = require('../index.js');
10
10
 
@@ -12,21 +12,23 @@ expressWinston.ignoredRoutes.push('/ignored');
12
12
  expressWinston.responseWhitelist.push('body');
13
13
  expressWinston.bodyBlacklist.push('potato');
14
14
 
15
- var MockTransport = function (test, options) {
16
- test.transportInvoked = false;
15
+ class MockTransport extends Transport {
16
+ constructor(test, options) {
17
+ super(options || {});
17
18
 
18
- winston.Transport.call(this, options || {});
19
+ this._test = test;
20
+ this._test.transportInvoked = false;
21
+ }
19
22
 
20
- this.log = function (level, msg, meta, cb) {
21
- test.transportInvoked = true;
22
- test.log.level = level;
23
- test.log.msg = msg;
24
- test.log.meta = meta;
23
+ log(info, cb) {
24
+ this._test.transportInvoked = true;
25
+ this._test.log.level = info.level;
26
+ this._test.log.msg = info.message;
27
+ this._test.log.meta = info.meta;
25
28
  this.emit('logged');
26
29
  return cb();
27
- };
28
- };
29
- util.inherits(MockTransport, winston.Transport);
30
+ }
31
+ }
30
32
 
31
33
  function mockReq(reqMock) {
32
34
  var reqSpec = _.extend({
@@ -119,6 +121,7 @@ function errorLoggerTestHelper(providedOptions) {
119
121
  }
120
122
 
121
123
  describe('winston-middleware', function () {
124
+
122
125
  describe('.errorLogger()', function () {
123
126
  it('should be a function', function () {
124
127
  expressWinston.errorLogger.should.be.a.Function();
@@ -228,6 +231,47 @@ describe('winston-middleware', function () {
228
231
  });
229
232
  });
230
233
 
234
+ describe('exceptionToMeta option', function () {
235
+ it('should, use exceptionToMeta function when given', function () {
236
+ function exceptionToMeta(error) {
237
+ return {
238
+ stack: error.stack && error.stack.split('\n')
239
+ };
240
+ }
241
+
242
+ var testHelperOptions = { loggerOptions: { exceptionToMeta: exceptionToMeta } };
243
+ return errorLoggerTestHelper(testHelperOptions).then(function (result) {
244
+ result.log.meta.stack.should.be.ok();
245
+ result.log.meta.should.not.have.property('trace');
246
+ });
247
+ });
248
+
249
+ it('should, use getAllInfo function when not given', function () {
250
+ var testHelperOptions = { loggerOptions: { } };
251
+ return errorLoggerTestHelper(testHelperOptions).then(function (result) {
252
+ result.log.meta.should.have.property('date');
253
+ result.log.meta.should.have.property('process');
254
+ result.log.meta.should.have.property('os');
255
+ result.log.meta.should.have.property('trace');
256
+ result.log.meta.should.have.property('stack');
257
+ });
258
+ });
259
+ });
260
+
261
+ describe('blacklistedMetaFields option', function () {
262
+ it('should, remove given fields from the meta result', function () {
263
+ var testHelperOptionsWithBlacklist = { loggerOptions: { blacklistedMetaFields: ['trace'] } };
264
+ return errorLoggerTestHelper(testHelperOptionsWithBlacklist).then(function (result) {
265
+ result.log.meta.should.not.have.property('trace');
266
+ });
267
+
268
+ var testHelperOptionsWithoutBlacklist = { loggerOptions: {} };
269
+ return errorLoggerTestHelper(testHelperOptionsWithoutBlacklist).then(function (result) {
270
+ result.log.meta.should.have.property('trace');
271
+ });
272
+ });
273
+ });
274
+
231
275
  describe('metaField option', function () {
232
276
  it('should, when using a custom metaField, log the custom metaField', function () {
233
277
  var testHelperOptions = {loggerOptions: {metaField: 'metaField'}};
@@ -260,6 +304,73 @@ describe('winston-middleware', function () {
260
304
  });
261
305
  });
262
306
  });
307
+
308
+ describe('dynamicMeta option', function () {
309
+ var testHelperOptions = {
310
+ req: {
311
+ body: {
312
+ age: 42,
313
+ potato: 'Russet'
314
+ },
315
+ user: {
316
+ username: "john@doe.com",
317
+ role: "operator"
318
+ }
319
+ },
320
+ res: {
321
+ custom: 'custom response runtime field'
322
+ },
323
+ originalError: new Error('FOO'),
324
+ loggerOptions: {
325
+ meta: true,
326
+ dynamicMeta: function(req, res, err) {
327
+ return {
328
+ user: req.user.username,
329
+ role: req.user.role,
330
+ custom: res.custom,
331
+ errMessage: err.message
332
+ }
333
+ }
334
+ }
335
+ };
336
+
337
+ it('should contain dynamic meta data if meta and dynamicMeta activated', function () {
338
+ return errorLoggerTestHelper(testHelperOptions).then(function (result) {
339
+ result.log.meta.req.should.be.ok();
340
+ result.log.meta.user.should.equal('john@doe.com');
341
+ result.log.meta.role.should.equal('operator');
342
+ result.log.meta.custom.should.equal('custom response runtime field');
343
+ result.log.meta.errMessage.should.equal('FOO');
344
+ });
345
+ });
346
+
347
+ it('should work with metaField option', function () {
348
+ testHelperOptions.loggerOptions.metaField = 'metaField';
349
+ return errorLoggerTestHelper(testHelperOptions).then(function (result) {
350
+ result.log.meta.metaField.req.should.be.ok();
351
+ result.log.meta.metaField.user.should.equal('john@doe.com');
352
+ result.log.meta.metaField.role.should.equal('operator');
353
+ result.log.meta.metaField.custom.should.equal('custom response runtime field');
354
+ result.log.meta.metaField.errMessage.should.equal('FOO');
355
+ });
356
+ });
357
+
358
+ it('should not contain dynamic meta data if dynamicMeta activated but meta false', function () {
359
+ testHelperOptions.loggerOptions.meta = false;
360
+ return errorLoggerTestHelper(testHelperOptions).then(function (result) {
361
+ should.not.exist(result.log.meta.req);
362
+ should.not.exist(result.log.meta.user);
363
+ should.not.exist(result.log.meta.role);
364
+ should.not.exist(result.log.meta.custom);
365
+ should.not.exist(result.log.meta.errMessage);
366
+ });
367
+ });
368
+
369
+ it('should throw an error if dynamicMeta is not a function', function () {
370
+ var loggerFn = expressWinston.errorLogger.bind(expressWinston, {dynamicMeta: 12});
371
+ loggerFn.should.throw();
372
+ });
373
+ });
263
374
  });
264
375
 
265
376
  describe('.logger()', function () {
@@ -309,6 +420,34 @@ describe('winston-middleware', function () {
309
420
  });
310
421
  });
311
422
 
423
+ it('should log entire body when request whitelist contains body and there is no body whitelist or blacklist', function () {
424
+ function next(req, res, next) {
425
+ res.end();
426
+ }
427
+ var testHelperOptions = {
428
+ next: next,
429
+ req: {
430
+ body: {
431
+ foo: 'bar',
432
+ baz: 'qux'
433
+ },
434
+ routeLevelAddedProperty: 'value that should be logged',
435
+ url: '/hello'
436
+ },
437
+ loggerOptions: {
438
+ bodyBlacklist: [],
439
+ bodyWhitelist: [],
440
+ requestWhitelist: expressWinston.requestWhitelist.concat('body')
441
+ }
442
+ };
443
+ return loggerTestHelper(testHelperOptions).then(function (result) {
444
+ result.log.meta.req.body.should.eql({
445
+ foo: 'bar',
446
+ baz: 'qux'
447
+ });
448
+ });
449
+ });
450
+
312
451
  it('should not invoke the transport when invoked on a route with transport level of "error"', function () {
313
452
  function next(req, res, next) {
314
453
  req._routeWhitelists.req = ['routeLevelAddedProperty'];
@@ -372,7 +511,7 @@ describe('winston-middleware', function () {
372
511
  });
373
512
 
374
513
  it('should use the exported bodyBlacklist', function() {
375
- var originalWhitelist = expressWinston.bodyBlacklist;
514
+ var originalBlacklist = expressWinston.bodyBlacklist;
376
515
  expressWinston.bodyBlacklist = ['foo'];
377
516
 
378
517
  var options = {
@@ -380,7 +519,7 @@ describe('winston-middleware', function () {
380
519
  };
381
520
  return loggerTestHelper(options).then(function (result) {
382
521
  // Return to the original value for later tests
383
- expressWinston.bodyBlacklist = originalWhitelist;
522
+ expressWinston.bodyBlacklist = originalBlacklist;
384
523
 
385
524
  result.log.meta.req.body.should.not.have.property('foo');
386
525
  result.log.meta.req.body.should.have.property('baz');
@@ -535,6 +674,35 @@ describe('winston-middleware', function () {
535
674
  });
536
675
  });
537
676
 
677
+ describe('when middleware function is invoked on a route that returns JSON', function() {
678
+ it('should parse JSON in response body', function() {
679
+ var bodyObject = { "message": "Hi! I\'m a chunk!" };
680
+ function next(req, res, next) {
681
+ // Set Content-Type in a couple different case types, just in case.
682
+ // Seems like the mock response doesn't quite handle the case
683
+ // translation on these right.
684
+ res.setHeader('Content-Type', 'application/json');
685
+ res.setHeader('content-type', 'application/json');
686
+ res.end(JSON.stringify(bodyObject));
687
+ }
688
+ return loggerTestHelper({next: next}).then(function(result) {
689
+ result.log.meta.res.body.should.eql(bodyObject);
690
+ });
691
+ });
692
+
693
+ it('should not blow up when response body is invalid JSON', function() {
694
+ function next(req, res, next) {
695
+ // Set Content-Type in a couple different case types, just in case.
696
+ // Seems like the mock response doesn't quite handle the case
697
+ // translation on these right.
698
+ res.setHeader('Content-Type', 'application/json');
699
+ res.setHeader('content-type', 'application/json');
700
+ res.end('}');
701
+ }
702
+ return loggerTestHelper({next: next});
703
+ });
704
+ });
705
+
538
706
  describe('when middleware function is invoked on a route that should be ignored (by .ignoredRoutes)', function () {
539
707
  var testHelperOptions = {
540
708
  req: {url: '/ignored'}
@@ -557,6 +725,7 @@ describe('winston-middleware', function () {
557
725
  it('should match the Express format when logging', function () {
558
726
  var testHelperOptions = {
559
727
  loggerOptions: {
728
+ colorize: true,
560
729
  expressFormat: true
561
730
  },
562
731
  req: {
@@ -569,6 +738,115 @@ describe('winston-middleware', function () {
569
738
  resultMsg.should.endWith('ms\u001b[39m');
570
739
  });
571
740
  });
741
+
742
+ it('should not emit colors when colorize option is false', function() {
743
+ var testHelperOptions = {
744
+ loggerOptions: {
745
+ colorize: false,
746
+ expressFormat: true
747
+ },
748
+ req: {
749
+ url: '/all-the-things'
750
+ }
751
+ };
752
+ return loggerTestHelper(testHelperOptions).then(function (result) {
753
+ var resultMsg = result.log.msg;
754
+ resultMsg.should.startWith('GET /all-the-things 200 ');
755
+ resultMsg.should.endWith('ms');
756
+ });
757
+ });
758
+
759
+ it('should not emit colors when colorize option is not present', function() {
760
+ var testHelperOptions = {
761
+ loggerOptions: {
762
+ colorize: false,
763
+ expressFormat: true
764
+ },
765
+ req: {
766
+ url: '/all-the-things'
767
+ }
768
+ };
769
+ return loggerTestHelper(testHelperOptions).then(function (result) {
770
+ var resultMsg = result.log.msg;
771
+ resultMsg.should.startWith('GET /all-the-things 200 ');
772
+ resultMsg.should.endWith('ms');
773
+ });
774
+ });
775
+ });
776
+
777
+ describe('colorize option', function () {
778
+ it('should make status code text green if < 300', function () {
779
+ var testHelperOptions = {
780
+ loggerOptions: {
781
+ colorize: true,
782
+ msg: '{{res.statusCode}} {{req.method}} {{req.url}}'
783
+ },
784
+ req: {
785
+ url: '/all-the-things'
786
+ }
787
+ };
788
+ return loggerTestHelper(testHelperOptions).then(function (result) {
789
+ var resultMsg = result.log.msg;
790
+ resultMsg.should.eql('\u001b[32m200\u001b[39m GET /all-the-things');
791
+ });
792
+ });
793
+
794
+ it('should make status code text cyan if >= 300 and < 400', function () {
795
+ var testHelperOptions = {
796
+ loggerOptions: {
797
+ colorize: true,
798
+ msg: '{{res.statusCode}} {{req.method}} {{req.url}}'
799
+ },
800
+ req: {
801
+ url: '/all-the-things'
802
+ },
803
+ res: {
804
+ statusCode: 302
805
+ }
806
+ };
807
+ return loggerTestHelper(testHelperOptions).then(function (result) {
808
+ var resultMsg = result.log.msg;
809
+ resultMsg.should.eql('\u001b[36m302\u001b[39m GET /all-the-things');
810
+ });
811
+ });
812
+
813
+ it('should make status code text yellow if >= 400 and < 500', function () {
814
+ var testHelperOptions = {
815
+ loggerOptions: {
816
+ colorize: true,
817
+ msg: '{{res.statusCode}} {{req.method}} {{req.url}}'
818
+ },
819
+ req: {
820
+ url: '/all-the-things'
821
+ },
822
+ res: {
823
+ statusCode: 420
824
+ }
825
+ };
826
+ return loggerTestHelper(testHelperOptions).then(function (result) {
827
+ var resultMsg = result.log.msg;
828
+ resultMsg.should.eql('\u001b[33m420\u001b[39m GET /all-the-things');
829
+ });
830
+ });
831
+
832
+ it('should make status code text red if >= 500', function () {
833
+ var testHelperOptions = {
834
+ loggerOptions: {
835
+ colorize: true,
836
+ msg: '{{res.statusCode}} {{req.method}} {{req.url}}'
837
+ },
838
+ req: {
839
+ url: '/all-the-things'
840
+ },
841
+ res: {
842
+ statusCode: 500
843
+ }
844
+ };
845
+ return loggerTestHelper(testHelperOptions).then(function (result) {
846
+ var resultMsg = result.log.msg;
847
+ resultMsg.should.eql('\u001b[31m500\u001b[39m GET /all-the-things');
848
+ });
849
+ });
572
850
  });
573
851
 
574
852
  describe('msg option', function () {
@@ -798,6 +1076,59 @@ describe('winston-middleware', function () {
798
1076
  });
799
1077
  });
800
1078
 
1079
+ describe('when levels set to a function', function () {
1080
+ it('should have custom status level provided by the function when 100 <= statusCode < 400', function () {
1081
+ var testHelperOptions = {
1082
+ next: function (req, res, next) {
1083
+ res.status(200).end('{ "message": "Hi! I\'m a chunk!" }');
1084
+ },
1085
+ loggerOptions: {
1086
+ level: function(req,res) { return 'silly'; }
1087
+ },
1088
+ transportOptions: {
1089
+ level: 'silly'
1090
+ }
1091
+ };
1092
+ return loggerTestHelper(testHelperOptions).then(function (result) {
1093
+ result.log.level.should.equal('silly');
1094
+ });
1095
+ });
1096
+
1097
+ it('should have custom status level provided by the function when 400 <= statusCode < 500', function () {
1098
+ var testHelperOptions = {
1099
+ next: function (req, res, next) {
1100
+ res.status(403).end('{ "message": "Hi! I\'m a chunk!" }');
1101
+ },
1102
+ loggerOptions: {
1103
+ level: function(req,res) { return 'silly'; }
1104
+ },
1105
+ transportOptions: {
1106
+ level: 'silly'
1107
+ }
1108
+ };
1109
+ return loggerTestHelper(testHelperOptions).then(function (result) {
1110
+ result.log.level.should.equal('silly');
1111
+ });
1112
+ });
1113
+
1114
+ it('should have custom status level provided by the function when 500 <= statusCode', function () {
1115
+ var testHelperOptions = {
1116
+ next: function (req, res, next) {
1117
+ res.status(500).end('{ "message": "Hi! I\'m a chunk!" }');
1118
+ },
1119
+ loggerOptions: {
1120
+ level: function(req,res) { return 'silly'; }
1121
+ },
1122
+ transportOptions: {
1123
+ level: 'silly'
1124
+ }
1125
+ };
1126
+ return loggerTestHelper(testHelperOptions).then(function (result) {
1127
+ result.log.level.should.equal('silly');
1128
+ });
1129
+ });
1130
+ });
1131
+
801
1132
  describe('requestWhitelist option', function () {
802
1133
  it('should default to global requestWhitelist', function () {
803
1134
  var options = {
@@ -820,6 +1151,34 @@ describe('winston-middleware', function () {
820
1151
  result.log.meta.req.should.not.have.property('method');
821
1152
  });
822
1153
  });
1154
+
1155
+ it('should not include a req in the log when there is no request whitelist', function() {
1156
+ var options = {
1157
+ loggerOptions: {
1158
+ requestWhitelist: [],
1159
+ }
1160
+ };
1161
+ return loggerTestHelper(options).then(function (result) {
1162
+ should.not.exist(result.log.meta.req);
1163
+ });
1164
+ });
1165
+ });
1166
+
1167
+ describe('bodyBlacklist option', function () {
1168
+ it('should remove the body if it is requestWhitelisted and the bodyBlacklist removes all properties', function() {
1169
+ var options = {
1170
+ loggerOptions: {
1171
+ bodyBlacklist: ['foo', 'baz'],
1172
+ requestWhitelist: ['body'],
1173
+ },
1174
+ req: {
1175
+ body: {foo: 'bar', baz: 'qux'}
1176
+ }
1177
+ };
1178
+ return loggerTestHelper(options).then(function (result) {
1179
+ result.log.meta.req.should.not.have.property('body');
1180
+ });
1181
+ });
823
1182
  });
824
1183
 
825
1184
  describe('responseWhitelist option', function () {
@@ -868,6 +1227,68 @@ describe('winston-middleware', function () {
868
1227
  });
869
1228
  });
870
1229
  });
1230
+
1231
+ describe('dynamicMeta option', function () {
1232
+ var testHelperOptions = {
1233
+ req: {
1234
+ body: {
1235
+ age: 42,
1236
+ potato: 'Russet'
1237
+ },
1238
+ user: {
1239
+ username: "john@doe.com",
1240
+ role: "operator"
1241
+ }
1242
+ },
1243
+ res: {
1244
+ custom: 'custom response runtime field'
1245
+ },
1246
+ loggerOptions: {
1247
+ meta: true,
1248
+ dynamicMeta: function(req, res) {
1249
+ return {
1250
+ user: req.user.username,
1251
+ role: req.user.role,
1252
+ custom: res.custom
1253
+ }
1254
+ }
1255
+ }
1256
+ };
1257
+
1258
+ it('should contain dynamic meta data if meta and dynamicMeta activated', function () {
1259
+ return loggerTestHelper(testHelperOptions).then(function (result) {
1260
+ result.log.meta.req.should.be.ok();
1261
+ result.log.meta.user.should.equal('john@doe.com');
1262
+ result.log.meta.role.should.equal('operator');
1263
+ result.log.meta.custom.should.equal('custom response runtime field');
1264
+ });
1265
+ });
1266
+
1267
+ it('should work with metaField option', function () {
1268
+ testHelperOptions.loggerOptions.metaField = 'metaField';
1269
+ return loggerTestHelper(testHelperOptions).then(function (result) {
1270
+ result.log.meta.metaField.req.should.be.ok();
1271
+ result.log.meta.metaField.user.should.equal('john@doe.com');
1272
+ result.log.meta.metaField.role.should.equal('operator');
1273
+ result.log.meta.metaField.custom.should.equal('custom response runtime field');
1274
+ });
1275
+ });
1276
+
1277
+ it('should not contain dynamic meta data if dynamicMeta activated but meta false', function () {
1278
+ testHelperOptions.loggerOptions.meta = false;
1279
+ return loggerTestHelper(testHelperOptions).then(function (result) {
1280
+ should.not.exist(result.log.meta.req);
1281
+ should.not.exist(result.log.meta.user);
1282
+ should.not.exist(result.log.meta.role);
1283
+ should.not.exist(result.log.meta.custom);
1284
+ });
1285
+ });
1286
+
1287
+ it('should throw an error if dynamicMeta is not a function', function () {
1288
+ var loggerFn = expressWinston.logger.bind(expressWinston, {dynamicMeta: 12});
1289
+ loggerFn.should.throw();
1290
+ });
1291
+ });
871
1292
  });
872
1293
 
873
1294
  describe('.requestWhitelist', function () {