Merge pull request #819 from blakambr/master

Feature - Add status code rulesets for connectLogger
This commit is contained in:
Gareth Jones 2018-12-20 08:06:45 +11:00 committed by GitHub
commit 2f738eb5c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 91 additions and 1 deletions

View File

@ -30,6 +30,7 @@ The log4js.connectLogger supports the passing of an options object that can be u
- log level
- log format string or function (the same as the connect/express logger)
- nolog expressions (represented as a string, regexp, or array)
- status code rulesets
For example:
@ -57,6 +58,15 @@ Added automatic level detection to connect-logger, depends on http status respon
app.use(log4js.connectLogger(logger, { level: 'auto' }));
```
The levels of returned status codes can be configured via status code rulesets.
```javascript
app.use(log4js.connectLogger(logger, { level: 'auto', statusRules: [
{ from: 200, to: 299, level: 'debug' },
{ codes: [303, 304], level: 'info' }
]}));
```
The log4js.connectLogger also supports a nolog option where you can specify a string, regexp, or array to omit certain log messages. Example of 1.2 below.
```javascript

View File

@ -165,6 +165,42 @@ function createNoLogCondition(nolog) {
return regexp;
}
/**
* Allows users to define rules around status codes to assign them to a specific
* logging level.
* There are two types of rules:
* - RANGE: matches a code within a certain range
* E.g. { 'from': 200, 'to': 299, 'level': 'info' }
* - CONTAINS: matches a code to a set of expected codes
* E.g. { 'codes': [200, 203], 'level': 'debug' }
* Note*: Rules are respected only in order of prescendence.
*
* @param {Number} statusCode
* @param {Level} currentLevel
* @param {Object} ruleSet
* @return {Level}
* @api private
*/
function matchRules(statusCode, currentLevel, ruleSet) {
let level = currentLevel;
if (ruleSet) {
const matchedRule = ruleSet.find((rule) => {
let ruleMatched = false;
if (rule.from && rule.to) {
ruleMatched = statusCode >= rule.from && statusCode <= rule.to;
} else if (rule.codes) {
ruleMatched = rule.codes.indexOf(statusCode) !== -1;
}
return ruleMatched;
});
if (matchedRule) {
level = levels.getLevel(matchedRule.level, level);
}
}
return level;
}
/**
* Log requests with the given `options` or a `format` string.
*
@ -173,6 +209,7 @@ function createNoLogCondition(nolog) {
* - `format` Format string, see below for tokens
* - `level` A log4js levels instance. Supports also 'auto'
* - `nolog` A string or RegExp to exclude target logs
* - `statusRules` A array of rules for setting specific logging levels base on status codes
*
* Tokens:
*
@ -238,6 +275,7 @@ module.exports = function getLogger(logger4js, options) {
} else {
level = levels.getLevel(options.level, levels.INFO);
}
level = matchRules(code, level, options.statusRules);
};
// hook on end request to emit the log entry of the HTTP request.
@ -249,6 +287,7 @@ module.exports = function getLogger(logger4js, options) {
if (res.statusCode >= 300) level = levels.WARN;
if (res.statusCode >= 400) level = levels.ERROR;
}
level = matchRules(res.statusCode, level, options.statusRules);
if (thisLogger.isLevelEnabled(level)) {
const combinedTokens = assembleTokens(req, res, options.tokens || []);

View File

@ -158,6 +158,47 @@ test('log4js connect logger', (batch) => {
t.end();
});
batch.test('logger with status code rules applied', (t) => {
const ml = new MockLogger();
ml.level = levels.DEBUG;
const clr = [
{ codes: [201, 304], level: levels.DEBUG.toString() },
{ from: 200, to: 299, level: levels.DEBUG.toString() },
{ from: 300, to: 399, level: levels.INFO.toString() }
];
const cl = clm(ml, { level: 'auto', format: ':method :url', statusRules: clr });
request(cl, 'GET', 'http://meh', 200);
request(cl, 'GET', 'http://meh', 201);
request(cl, 'GET', 'http://meh', 302);
request(cl, 'GET', 'http://meh', 304);
request(cl, 'GET', 'http://meh', 404);
request(cl, 'GET', 'http://meh', 500);
const messages = ml.messages;
t.test('should use DEBUG for 2xx', (assert) => {
assert.ok(levels.DEBUG.isEqualTo(messages[0].level));
assert.ok(levels.DEBUG.isEqualTo(messages[1].level));
assert.end();
});
t.test('should use WARN for 3xx, DEBUG for 304', (assert) => {
assert.ok(levels.INFO.isEqualTo(messages[2].level));
assert.ok(levels.DEBUG.isEqualTo(messages[3].level));
assert.end();
});
t.test('should use ERROR for 4xx', (assert) => {
assert.ok(levels.ERROR.isEqualTo(messages[4].level));
assert.end();
});
t.test('should use ERROR for 5xx', (assert) => {
assert.ok(levels.ERROR.isEqualTo(messages[5].level));
assert.end();
});
t.end();
});
batch.test('format using a function', (t) => {
const ml = new MockLogger();
ml.level = levels.INFO;

2
types/log4js.d.ts vendored
View File

@ -19,7 +19,7 @@ export function configure(config: Configuration): Log4js;
export function addLayout(name: string, config: (a: any) => (logEvent: LoggingEvent) => string): void;
export function connectLogger(logger: Logger, options: { format?: Format; level?: string; nolog?: any; }): any; // express.Handler;
export function connectLogger(logger: Logger, options: { format?: Format; level?: string; nolog?: any; statusRules?: any[] }): any; // express.Handler;
export const levels: Levels;