每个程序员都喜欢在有问题的代码中插入一些日志的方法来帮助调试程序,比如System.out.println或console.log。解决后,就会将这些语句删除,周而复始。
但是通过系统日志输出的日志格式都是这种:
// output
console.log("log"); // log
console.info("message"); // message
console.warn("Warning");// warning
现在想象一下,如果你在后端排查问题时,你有数百条这样的信息。这样的日志不仅很难知道消息级别或消息记录的日期,而且过滤和排查日志也是一项艰巨的任务。
我们排查问题时,更期望看到这样的日志:
{"level":"error","message":"Error message","timestamp":"2022-09-20T11:39:33.953Z"}
{"level":"warn","message":"Warning message","timestamp":"2022-09-20T11:39:33.957Z"}
{"level":"info","message":"Info message","timestamp":"2022-09-20T11:39:33.957Z"}
日志API就是为了解决这个问题而设计的。日志API的优点:
● 可以很容易地取消全部日志记录,或者仅仅取消某个级别以下的日志,而且可以很容易地再次打开日志开关。
● 可以很简单的禁止日志记录,因此,将这些日志代码留在程序中的开销很小。
● 日志记录可以被定向到不同的处理器,如在控制台、文件等等。
● 日志记录器和处理器都可以记录进行过滤,过滤器可以根据过滤器实现指定的标准丢弃那些无用的记录。
● 日志记录可以采用不同的方式格式化,例如:纯文本或JSON。
● 应用程序可以使用多个日志记录器。
● 日志系统的配置由配置文件控制。
在 Node.js 应用中进行日志记录是非常重要的,因为它可以帮助我们跟踪应用程序中的问题和错误。接下来介绍 一下Node.js 的日志记录技术。
Winston是一个流行的、功能丰富的、灵活的Node.js日志库。它使用的默认格式是JSON,但可以配置向多个存储设备中发送日志,目前每周下载量接近1千万。
要使用 Winston,首先需要安装它。可以使用 npm 安装 Winston:
npm install winston
安装完成后,在应用程序中引入 Winston:
const winston = require('winston');
// 现在可以创建一个 Winston 实例,并开始记录日志。以下是一个示例:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.Console(),
new winston.transports.File({
filename: 'error.log',
level: 'error'
})
]
});
logger.info('Hello, world!');
在上面的示例中,创建了一个名为 logger 的 Winston 实例。指定了日志级别为 info,表示只记录等级为 info 或更高的日志消息。我们还指定了一个 json 格式化器,以便记录格式化的 JSON 日志消息。定义了两个传输器,一个将日志消息记录到控制台,另一个将错误日志消息记录到名为 error.log 的文件中。
Winston 支持多个日志级别,可以根据日志消息的严重程度选择不同的日志级别来记录。Winston 的日志级别从高到低分别为:
● error:表示发生了错误
● warn:表示警告性的信息
● info:表示常规信息
● verbose:表示详细信息
● debug:表示调试信息
● silly:表示无关紧要的信息
Winston 的格式化器可以帮助我们格式化日志消息,以便更好地组织和分析日志数据。Winston 提供了多个内置的格式化器,例如 json、simple、prettyPrint 等。我们也可以自定义格式化器,根据自己的需求来记录日志数据。
以下是一个使用 json 格式化器记录日志的示例:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.Console()]
});
logger.info({ message: 'Hello, world!', user: 'Alice' });
Winston 支持多个传输器,可以将日志消息记录到不同的目标中,例如控制台、文件、数据库、syslog 等。以下是一个将日志消息记录到文件中的示例:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.simple(),
transports: [
new winston.transports.File({ filename: 'app.log' })
]
});
logger.info('Hello, world!');
在上面的示例中,我们使用 File 传输器将日志消息记录到名为 app.log 的文件中。我们设置日志级别为 info,只记录等级为 info 或更高的日志消息。
除了输出到文件中,Winston还支持输出控制台、http请求以及流的方式。
我们也可以自定义输出的方式,比如输出到邮件、通过接口消息通知等等。
在实际使用过程中,我比较喜欢将console.log进行一次封装。将 console.log 绑定到 logger.info 方法。每次调用 console.log 时,实际上是在调用 logger.info 方法。因此,日志消息将通过Winston的Console传输器发送到标准控制台中和文件中。
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.Console(),
new winston.transports.File()
]
});
// 将 console.log 绑定到 logger.info 方法
console.log = logger.info.bind(logger);
// 使用 console.log 记录日志消息
console.log('Hello, Winston!');
console.log('This is a log message.');
// error 也可以绑定到logger上,但是建议输出file时,指定新的文件名,方便后续的日志查看。
console.error = logger.error.bind(logger);
如果不在需要日志记录时,可以直接设置level为off,就可以停止日志的记录。
日志输出和日志格式解决之后,为了方便检索和更快的排查问题,可以将日志接入es。
下面是接入es后,通过kibana查看的结果:
将日志接入 Elasticsearch (ES) 的好处:
● 日志级别,不要都使用info来使用,要区分不同的日志级别。
● 日志的打印方式:同步和异步。
● 日志体量的大小:LOG系统就是直接写磁盘文件,既然写磁盘文件就牵扯到磁盘IO,而磁盘IO跟内存读写有一个数量级的性能差别。