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 +3 -10
- package/AUTHORS +2 -1
- package/CHANGELOG.md +69 -0
- package/Readme.md +61 -21
- package/index.js +117 -40
- package/package.json +12 -9
- package/test/test.js +435 -14
package/.travis.yml
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
sudo: false
|
|
2
2
|
language: node_js
|
|
3
3
|
node_js:
|
|
4
|
-
- "
|
|
5
|
-
- "
|
|
6
|
-
- "
|
|
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
|
-
|
|
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
|
[](https://www.npmjs.com/package/winston-middleware) [](http://travis-ci.org/bithavoc/winston-middleware)
|
|
3
3
|
|
|
4
|
-
> [winston](https://github.com/
|
|
4
|
+
> [winston](https://github.com/winstonjs/winston) middleware for express.js
|
|
5
5
|
|
|
6
|
-
|
|
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
|
-
|
|
8
|
+
## Installation
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
npm install winston winston-middleware
|
|
13
11
|
|
|
14
|
-
|
|
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.
|
|
29
|
-
"winston-middleware": "0.
|
|
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
|
|
59
|
-
|
|
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
|
|
74
|
-
|
|
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. "{{
|
|
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
|
-
|
|
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.
|
|
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 || (
|
|
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
|
|
128
|
-
interpolate: /\{\{(
|
|
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
|
-
|
|
134
|
-
var exceptionMeta =
|
|
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 = _.
|
|
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(
|
|
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 || (
|
|
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.
|
|
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(
|
|
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 =
|
|
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 (
|
|
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 = _.
|
|
312
|
+
meta = _.assign(meta, logData);
|
|
274
313
|
}
|
|
275
314
|
|
|
276
|
-
meta = _.
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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": "
|
|
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": "
|
|
51
|
-
"lodash": "
|
|
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.
|
|
55
|
+
"mocha": "^5.2.0",
|
|
57
56
|
"node-mocks-http": "^1.5.1",
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
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": ">=
|
|
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
|
-
|
|
16
|
-
test
|
|
15
|
+
class MockTransport extends Transport {
|
|
16
|
+
constructor(test, options) {
|
|
17
|
+
super(options || {});
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
this._test = test;
|
|
20
|
+
this._test.transportInvoked = false;
|
|
21
|
+
}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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 =
|
|
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 () {
|