0%

Koa.js 自定义日志中间件

  1. 前言
  2. 源码

前言

如果安装了别的日志中间件,可以先删除:

1
npm un koa-logger -S

日志中间件主要用于在Koa中输出请求的相关信息,从消息类型看可以分为正常日志信息和异常日志信息,日志的内容取决于其他中间件往上下文ctx中丢了什么(例如请求响应时间、访客信息等)。

为了顾及两类信息,需要使用try...catch结构捕获异常,然后再做处理:

1
2
3
4
5
try {
await next();
} catch (e) {
logger.error(e);
}

使用用例如下:

1
2
3
4
5
6
7
8
const KoaLogger = require('logger.js');
const Koa = require('Koa');
const app = new Koa();

// 使用控制台作为输出
app.use(KoaLogger(console));

app.listen(3000);

源码

logger.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
const chalk = require('chalk');

if (!String.prototype.fade) {
String.prototype.fade = function() {
return this.toString().replace(/\x1B\[[0-9]?[0-9]?m/g, '') + '\r\n';
}
}

const KoaLogger = (logger) => {
return async(ctx, next) => {

/**
* 构建日志内容
*/
const logArr = [];
const req = ctx.request;
const { ip, url } = req;

// GET or POST or ...
logArr.push(chalk.bold(req.method));

if (!logger || !logger.isInitiatial()) throw new Error('日志未初始化');

// 记录可能存在的错误信息
// let errMsg;
try {
await next();
} catch (e) {
logger.error(e);
}

// 处理状态信息 200/404/...
const { status } = ctx.response;
if (status >= 200 && status < 300) {
logArr.push(chalk.bold(chalk.green(status)));
} else if (status >= 400) {
logArr.push(chalk.bold(chalk.red(status)));
} else {
logArr.push(chalk.bold(chalk.yellow(status)));
}

// 响应时间
if (ctx.response.get('X-Response-Time')) {
logArr.push(ctx.response.get('X-Response-Time'));
}

// 请求路径
logArr.push(chalk.magenta(url));

// 访客ip
logArr.push(chalk.cyan(ip));


// 访客信息
if (ctx.request.visitor) {
const v = ctx.request.visitor;

let str = '';
str += `from ${v.from}`;
if (v.from === 'internet') {
str += ` (${v.country}/${v.region}/${v.city})`;
if (v.isNew) {
str += (' ' + chalk.yellow('新访客'));
} else {
str += (' ' + chalk.yellow(`访问次数:${v.visit_count}, 上次访问时间:${v.last_visit_time}`));
}
}

logArr.push(chalk.gray(str));
}

// 返回值为JSON格式的状态信息时
if (ctx.body && typeof ctx.body === 'object' && ctx.request.method !== 'GET') {
logArr.push(chalk.gray(JSON.stringify(ctx.body)));
}

/* 开始打印 */

/**
* 1. 一般请求内容
*/
logger.info(logArr.join(' - '));

/**
* 2. 可能存在的抛出异常
*/
// if (errMsg) {
// logger.error(errMsg);
// }
/**
* 3. 可能存在的请求体
*/
if (ctx.request.body && Object.keys(ctx.request.body).length !== 0) {
let str = '';
if (ctx.request.encBody) {
str += chalk.yellow('Succeed Decrypt! ');
}
str += 'Request Body: ' + chalk.gray(JSON.stringify(ctx.request.body))
logger.info(str);
}

/**
* 4. 记录cookies
*/
if (ctx.request.header.cookie) {
logger.info('Cookies: ' + chalk.gray(ctx.request.header.cookie));
}
}
}

module.exports = KoaLogger;
  • 参数logger为日志控件,用于输出日志信息(到控制台或文件),可以使用console替换;

  • String.prototype.fade用于给字符串褪色,即删除在命令行中显示颜色的控制字符;