日志(v2)
本文档为 @midwayjs/logger v2.0 版本的文档。
Midway 为不同场景提供了一套统一的日志接入方式。通过 @midwayjs/logger 包导出的方法,可以方便的接入不同场景的日志系统。
Midway 的日志系统基于社区的 winston,是现在社区非常受欢迎的日志库。
实现的功能有:
- 日志分级
- 按大小和时间自动切割
- 自定义输出格式
- 统一错误日志
日志路径和文件
Midway 会在日志根目录创建一些默认的文件。
midway-core.log框架、组件打印信息的日志,对应coreLogger。midway-app.log应用打印信息的日志,对应appLoggercommon-error.log所有错误的日志(所有 Midway 创建出来的日志,都会将错误重复打印一份到该文件中)
本地开发和服务器部署时的 日志路径 和 日志等级 不同,具体请参考 配置日志根目录 和 框架的默认等级。
默认日志对象
Midway 默认在框架提供了三种不同的日志,对应三种不同的行为。
| 日志 | 释义 | 描述 | 常见使用 |
|---|---|---|---|
| coreLogger | 框架,组件层面的日志 | 默认会输出控制台日志和文本日志 midway-core.log ,并且默认会将错误日志发送到 common-error.log 。 | 框架和组件的错误,一般会打印到其中。 |
| appLogger | 业务层面的日志 | 默认会输出控制台日志和文本日志 midway-app.log ,并且默认会将错误日志发送到 common-error.log 。 | 业务使用的日志,一般业务日志会打印到其中。 |
| 上下文日志(复用 appLogger 的配置) | 请求链路的日志 | 默认使用 appLogger 进行输出,除了会将错误日志发送到 common-error.log 之外,还增加了上下文信息。 | 修改日志输出的标记(Label),不同的框架有不同的请求标记,比如 HTTP 下就会输出路由信息。 |
使用日志
Midway 的常用日志使用方法。
上下文日志
上下文日志是关联框架上下文对象(Context) 的日志。
我们可以通过 获取到 ctx 对象 后,使用 ctx.logger 对象进行日志打印输出。
比如:
ctx.logger.info("hello world");
ctx.logger.debug('debug info');
ctx.logger.warn('WARNNING!!!!');
// 错误日志记录,直接会将错误日志完整堆栈信息记录下来,并且输出到 errorLog 中
// 为了保证异常可追踪,必须保证所有抛出的异常都是 Error 类型,因为只有 Error 类型才会带上堆栈信息,定位到问题。
ctx.logger.error(new Error('custom error'));
在执行后,我们能在两个地方看到日志输出:
- 控制台看到输出。
- 日志目录的 midway-app.log 文件中
输出结果:
2021-07-22 14:50:59,388 INFO 7739 [-/::ffff:127.0.0.1/-/0ms GET /api/get_user] hello world
在注入的形式中,我们也可以直接使用 @Inject() logger 的形式来注入 ctx.logger ,和直接调用 ctx.logger 等价。
比如:
import { Get, Inject, Controller, Provide } from '@midwayjs/core';
import { ILogger } from '@midwayjs/logger';
@Controller()
export class HelloController {
@Inject()
logger: ILogger;
@Inject()
ctx;
@Get("/")
async hello(){
// ...
// this.logger === ctx.logger
}
}
应用日志(App Logger)
如果我们想做一些应用级别的日志记录,如记录启动阶段的一些数据信息,可以通过 App Logger 来完成。
import { Configuration, Logger } from '@midwayjs/core';
import { ILogger } from '@midwayjs/logger';
@Configuration()
export class MainConfiguration implements ILifeCycle {
@Logger()
logger: ILogger;
async onReady(container: IMidwayContainer): Promise<void> {
this.logger.debug('debug info');
this.logger.info('启动耗时 %d ms', Date.now() - start);
this.logger.warn('warning!');
this.logger.error(someErrorObj);
}
}
注意,这里使用的是 @Logger() 装饰器。
CoreLogger
在组件或者框架层面的研发中,我们会使用 coreLogger 来记录日志。
@Configuration()
export class MainConfiguration implements ILifeCycle {
@Logger('coreLogger')
logger: ILogger;
async onReady(container: IMidwayContainer): Promise<void> {
this.logger.debug('debug info');
this.logger.info('启动耗时 %d ms', Date.now() - start);
this.logger.warn('warning!');
this.logger.error(someErrorObj);
}
}
输出方法和格式
Midway 的日志对象继承与 winston 的日志对象,一般情况下,只提供 error() , warn() , info() , debug 四种方法。
示例如下。
logger.debug('debug info');
logger.info('启动耗时 %d ms', Date.now() - start);
logger.warn('warning!');
logger.error(new Error('my error'));
默认的输出行为
在大部分的普通类型下,日志库都能工作的很好。
比如:
logger.info('hello world'); // 输出字符串
logger.info(123); // 输出数字
logger.info(['b', 'c']); // 输出数组
logger.info(new Set([2, 3, 4])); // 输出 Set
logger.info(new Map([['key1', 'value1'], ['key2', 'value2']])); // 输出 Map
Midway 针对 winston 无法输出的
Array,Set,Map类型,做了特殊定制,使其也能够正常的输出。
不过需要注意的是,日志对象在一般情况下,只能传入一个参数,它的第二个参数有其他作用。
logger.info('plain error message', 321); // 会忽略 321
错误输出
针对错误对象,Midway 也对 winston 做了定制,使其能够方便的和普通文本结合到一起输出。
// 输出错误对象
logger.error(new Error('error instance'));
// 输出自定义的错误对象
const error = new Error('named error instance');
error.name = 'NamedError';
logger.error(error);
// 文本在前,加上 error 实例
logger.info('text before error', new Error('error instance after text'));
注意,错误对象只能放在最后,且有且只有一个,其后面的所有参数都会被忽略。
格 式化内容
基于 util.format 的格式化方式。
logger.info('%s %d', 'aaa', 222);
常用的有
%s字符串占位%d数字占位%jjson 占位
更多的占位和详细信息,请参考 node.js 的 util.format 方法。