日志框架winston的使用

日志对于问题定位、调试,系统性能调优至关重要,尤其是系统复杂以及在线运行的情况下。之前的项目日志输出一直用 log4js,输出到一个文件,最近对那一块进行重构。

分别考虑了两款 Node.js 框架,分别是BunyanWinston

  • Winston 是 Node.js 最流行的日志框架之一,设计为一个简单通用的日志库,支持多传输
  • Bunyan 以略微不同的方式处理结构化,机器可读性被重点对待。实际上就是 JSON.stringify 的一个输出。

预期效果

  • 日志分级
  • 根据不同的代码分层来产生不同的 log 输出到不同的文件。
  • 输出到 log 文件同时还可以选择输出到标准输出。
  • 自定义格式化日志输出,输出到标准输出的格式便于读取,输出到 log 文件的格式便于分析。
  • 按天自动切割日志文件。

为何选择 winston

  • github start 数多。
  • 更加灵活,可以灵活的组织 transport,来完成比较复杂的日志输出任务。
  • 日志格式为 json 字符串,方便后期分析,当然可以自定义 format.
  • 支持简单的 log 分析,Profiling。
  • 支持 stream Api。
  • 简单 Log Query Api,当然无法和专业的日志分析工具比。

使用方法

定义标准输出 Transport

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class ConsoleTransport extends winston.transports.Console {
constructor(options) {
super(options);

this.format = winston.format.combine(
winston.format(info => {
info.hostname = hostname();
info.pid = process.pid;
info.level = info.level.toUpperCase();
return info;
})(),
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss,SSSS' }),
winston.format.ms(),
winston.format.colorize(),
winston.format.printf(options.formatter),
);
}
}

定义文件输出

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
const winston = require('winston');
require('winston-daily-rotate-file');

class FileTransport extends winston.transports.DailyRotateFile {
constructor(options) {
super(options);

this.datePattern = 'YYYY-MM-DD';
this.zippedArchive = true;
this.maxSize = '100m';
this.maxFiles = '14d';

const defaultFormatter = winston.format.combine(
winston.format(info => {
info.hostname = hostname();
info.pid = process.pid;
info.level = info.level.toUpperCase();
return info;
})(),
winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss,SSSS' }),
winston.format.ms(),
);

if (options.json) {
// 输出json格式
this.format = winston.format.combine(
defaultFormatter,
winston.format.json(options.formatter),
);
} else {
this.format = winston.format.combine(
defaultFormatter,
winston.format.printf(options.formatter),
);
}
}
}

调用方法

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
const options = {
name: 'app',
module: 'app',
filePath: './logs',
formatter: meta => {
return `${meta.timestamp} ${meta.level} ${meta.hostname} ${meta.pid} (${meta.ms}) [${
meta.module
}] ${meta.message}`;
},
consoleLevel: 'error',
json: false,
};

const consoleTransport = new ConsoleTransport({
level: options.consoleLevel,
name: options.name,
formatter: options.consoleFormatter,
});

const infoTransport = new FileTransport({
level: 'info',
name: options.name,
filename: join(options.filePath, `info/${options.name}-info-%DATE%.log`),
formatter: options.formatter,
json: options.json,
});

const errorTransport = new FileTransport({
level: 'error',
name: options.name,
filename: join(options.filePath, 'error/error-%DATE%.log'),
formatter: options.formatter,
json: options.json,
});

const transports = [consoleTransport, infoTransport, errorTransport];
const logger = winston.createLogger({ transports });

logger.info('log1');
logger.error('error 1');

代码地址为:https://github.com/zubincheung/cw-logger-winston

输出效果

1
2
3
4
5
6
7
8
9
10
11
2018-12-30 10:07:28,4605 INFO zubin-pc.local 59468 (+0ms) [app] app log 1
2018-12-30 10:07:28,4695 ERROR zubin-pc.local 59468 (+1ms) [app] Error: app error 1
Error: app error 1
at Object.it (/Users/zubincheung/ciwong/cw-logger-winston/test/cw-logger.test.js:61:35)
at Object.asyncJestTest (/Users/zubincheung/ciwong/cw-logger-winston/node_modules/jest-jasmine2/build/jasmine_async.js:108:37)
at resolve (/Users/zubincheung/ciwong/cw-logger-winston/node_modules/jest-jasmine2/build/queue_runner.js:56:12)
at new Promise (<anonymous>)
at mapper (/Users/zubincheung/ciwong/cw-logger-winston/node_modules/jest-jasmine2/build/queue_runner.js:43:19)
at promise.then (/Users/zubincheung/ciwong/cw-logger-winston/node_modules/jest-jasmine2/build/queue_runner.js:87:41)
at process.internalTickCallback (internal/process/next_tick.js:77:7)