原文地址:Comparing Winston and Bunyan Node.js Logging
如果你正在编写一些生存周期比较长的应用程序,详细的日志记录对于发现问题和调试代码来说非常重要。没有日志,你无法得知你的应用正在做什么,有没有发生错误,应用已经完成了预期的工作还是在你看不见的地方偷懒。
要求
我们先列出一些要求来确定选用哪个框架:
- 每个日志行都应该有一个时间戳。这很好解释——你应该能够从日志中得知每个动作发生的时间。
- 日志格式应当很容易被人和机器解读。
- 允许多个可配置的目标流。例如,你可能会将跟踪日志写入一个文件,而当有错误发生时,将错误信息写入同一个文件和错误日志文件,并且第一时间使用邮件将错误信息发送出去。
基于这些要求,有两个Node.js
的日志库值得一试:
- 由
Trent Mick
开发的Bunyan
- 作为
Flatiron.js
的一部分并由nodejitstu
赞助的Winston
控制台
在我们开始介绍Winston
之前,先让我们回顾一下我们的老朋友控制台。你使用得最多的打印日志的方式可能是console.log()
和console.error()
。这比什么都不做要好,但并不是最好的解决方案。控制台将以上信息分别写入标准输出stdout
和标准错误stderr
中。
当目标是终端或文件时,控制台函数是同步的(避免丢失提早退出应用程序的信息),而当目标是管道时,它们是异步的(避免长时间阻塞)。
也就是说,在下面的例子中,stdout
是非阻塞的,而stderr
则是阻塞的:
$ nodescript.js2>error.log | teeinfo.log
这种方式依赖于个人进行配置和管理每一件事情,既耗时又容易出错。你可能想要专注于你的应用程序功能而不是这些琐事。考虑到有持续维护并且开源的日志库,如果你希望将注意力集中在开发功能上,大可不必在这上面消耗精力。
Winston
Winston
是Node.js
上最流行的日志库之一。它被设计为一个简单通用的日志库,支持多种传输(一种传输实际上就是一种存储设备,例如日志存储在哪里)。Winston
中的每一个logger
实例在不同的日志级别可以存在多个传输配置。
安装
npm install winston
使用
Winston
最基本的使用方法就是导入Winston
模块后使用默认实例。
var winston = require('winston');
winston.log('info', 'Hello distributed log files!');
winston.info('Hello again distributed logs');
相当于:
var winston = require('winston');
var logger = new winston.Logger();
logger.log('info', 'Hello distributed log files!');
logger.info('Hello again distributed logs');
上面两个例子都会产生以下输出:
info: Hello distributed log files!
info: Hello again distributed logs
格式化
我个人对默认格式化程序的缺乏细节有点小疑惑。没有时间戳、机器名或进程ID,而且输出格式并不适合机器分析。话虽如此,你可以通过增加一点额外的工作量来得到所有的信息。
winston.info('Hello world!', {timestamp: Date.now(), pid: process.pid});
执行上面语句会得到以下信息更为丰富的输出结果,但仍然不是非常适合机器进行分析。
info: Hello world! timestamp=1402286804314, pid=80481
最后,日志方法提供了相同的字符串插值方法util.format
,例如:
winston.log('info', 'test message %d', 123);
Transporters
Winston
可以通过构造函数或项目的Github页面上详细介绍的接口来进行配置。大部分配置项都围绕着传输。开箱即用的Winston
配备了console
对象和基于文件的传输,如果你打开npmjs.org的话会发现,从MongoDB
到第三方社区平台,一切你能想到的模块都有。
在我看来最值得关注的transporters
之一是Nathan Zadoks
开发的winstron-irc
,可以用于向团队的IRC频道记录错误日志,我觉得这非常方便。
winston.add(require('winston-irc'), {
host: 'irc.somewhere.net',
nick: 'logger',
pass: 'hunter2',
channels: {
'#logs': true,
'sysadmin': ['warn', 'error']
}
});
多个记录器
一旦你的应用开始增长,你可能会希望有多个不同配置的记录器,每个记录器负责不同的功能区。Winston
有两种方式支持以上做法:通过winston.loggers
或者winston.Container
的实例。事实上,winston.loggers
只是一个预定义的winston.Container
实例。
winston.loggers.add('category1', {console: {...}, file: {...}});
winston.loggers.add('category2', {irc: {...}, file: {...}});
既然你的记录器已经配置好了,你可以在任意文件中引入Winston
并访问这些预配置的记录器:
var category1 = winston.loggers.get('category1');
category1.info('logging from your IoC container-based logger');
更多
以上是最基本的Winston
使用方法,它还有一些其他的功能,其中最值得注意的是:
- Profiling
- String interpolation
- Querying and streaming
- Handling exeptions