日常普通使用
首先我们学会 Midway 的日常日志使用方法。
import { Get } from ‘@midwayjs/decorator‘;
import { Inject, Controller, Provide } from ‘@midwayjs/decorator‘;
@Provide()
@Controller()
export class HelloController {
@Inject()
logger;
@Inject()
ctx;
@Get(‘/‘)
async ctx() {
this.logger.info(‘hello world‘);
this.ctx.body = ‘hello world‘;
}
}
访问后,我们能在两个地方看到日志输出:
- console 栏看到输出。
- 日志目录的 midway-app.log 文件中。
输出结果:
2021-07-22 14:50:59,388 INFO 7739 [-/::ffff:127.0.0.1/-/0ms GET /api/get_user] hello world
以上是用户在项目开发的基本使用。如果有更多高阶用法,请继续阅读接下来的章节。
简介
Midway 为不同场景提供了一套统一的日志接入方式。通过 @midwayjs/logger
包导出的方法,可以方便的接入不同场景的日志系统。
Midway 的日志系统基于社区的 winston,是现在社区非常受欢迎的日志库。
默认日志对象
默认情况下,Midway 已经将日志库埋入到整个框架中,在框架启动时已经能够自动输出信息到控制台,以及输出到文件。
Midway 日志的默认逻辑为:
- 将日志输出到控制台和写入文件
- 按日期每天切割,以及按大小切割
- 将错误(
.error()
输出的内容)统一输出到一个固定的错误文件
Midway 默认在框架提供了三种不同的日志,对应三种不同的行为。
框架,组件层面的日志,我们叫他 coreLogger |
默认会输出控制台日志和文本日志 midway-core.log ,并且默认会将错误日志发送到 common-error.log 。 |
|
业务层面的日志,我们叫他 appLogger |
默认会输出控制台日志和文本日志 midway-app.log ,并且默认会将错误日志发送到 common-error.log 。 |
|
请求链路的日志,我们叫它上下文日志对象(ContextLogger) |
默认使用 appLogger 进行输出,除了会将错误日志发送到 common-error.log 之外,还增加了上下文信息。 |
修改日志输出的标记(Label),不同的框架有不同的请求标记,比如 HTTP 下就会输出路由信息。 |
日志路径和文件
默认情况下,Midway 会在本地开发和服务器部署时输出日志到日志根目录。
- 本地的日志根目录为
${app.appDir}/logs/项目名
目录下
- 服务器的日志根目录为用户目录
${process.env.HOME}/logs/项目名
(Linux/Mac)以及 ${process.env.USERPROFILE}/logs/项目名
(Windows)下,例如 /home/admin/logs/example-app
。
Midway 会在日志根目录创建一些默认的文件。
-
midway-core.log
框架、组件打印信息的日志,对应 coreLogger
。
-
midway-app.log
应用打印信息的日志,对应 appLogger
-
common-error.log
所有错误的日志(所有 Midway 创建出来的日志,都会将错误重复打印一份到该文件中)
在 EggJS 下,为了兼容以前的日志,依旧处理将日志打印在 `midway-web.log` 下。
使用日志对象
一般来说,框架开发者需要获取到 coreLogger
,记录框架,组件层面的日志。而业务开发者,需要获取到 appLogger
来记录业务日志。在业务和请求相关的流程中,需要拿到上下文日志对象,方便追踪请求。
使用装饰器获取日志对象
在任意类中,我们可以通过装饰器来获取日志对象。下面是一个通过装饰器获取各种默认日志对象的方式。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { ILogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@Logger()
coreLogger: ILogger;
@Logger(‘coreLogger‘)
anotherLogger: ILogger;
async getUser() {
this.coreLogger.warn(‘warn message‘);
}
}
为了使用更简单,我们将 appLogger
的 key 变为了最为普通的 logger
。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { ILogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@Logger()
logger: ILogger;
async getUser() {
this.logger.info(‘hello user‘);
this.coreLogger.warn(‘warn message‘);
}
}
- 3、获取上下文日志对象(Context Logger)
上下文日志是在每个请求实例中动态创建的日志对象,因此它和请求作用域绑定,即和请求实例绑定。
Midway 默认会将上下文日志对象挂载到上下文(ctx)上,即 ctx.logger
。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { ILogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@Inject()
logger: ILogger;
async getUser() {
this.logger.info(‘hello user‘);
}
}
和全局的日志不同,上下文日志对象,默认会放在请求作用域的依赖注入容器中,它的 key 为 logger,所以可以使用 `Inject` 装饰器注入它。
使用 API 接口获取日志对象
有时候,我们不在 Class 的场景下,我们可以从 app
上的方法来获取这些默认的日志对象。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { ILogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@App()
app: IMidwayApplication;
@Inject()
ctx;
async getUser() {
this.app.getLogger(‘logger‘).info(‘hello user‘);
this.app.getLogger(‘coreLogger‘).warn(‘warn message‘);
}
}
使用全局 API 获取
当上述两种方法都无法获取的时候,比如在静态方法中,我们可以从全局的 日志容器 中获取日志对象。
import { loggers } from ‘@midwayjs/logger‘;
loggers.getLogger(‘coreLogger‘);
loggers.getLogger(‘logger‘);
更多的信息,可以查看 日志容器 的介绍。
输出方法和格式
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]));
logger.info(
new Map([
[‘key1‘, ‘value1‘],
[‘key2‘, ‘value2‘],
]),
);
Midway 针对 winston 无法输出的 `Array` , `Set` , `Map` 类型,做了特殊定制,使其也能够正常的输出。
不过需要注意的是,日志对象在一般情况下,只能传入一个参数,它的第二个参数有其他作用。
logger.info(‘plain error message‘, 321);
错误输出
针对错误对象,Midway 也对 winston 做了定制,使其能够方便的和普通文本结合到一起输出。
logger.error(new Error(‘error instance‘));
const error = new Error(‘named error instance‘);
error.name = ‘NamedError‘;
logger.error(error);
logger.info(‘text before error‘, new Error(‘error instance after text‘));
注意,错误对象只能放在最后,且有且只有一个,其后面的所有参数都会被忽略。
格式化内容
基于 util.format
的格式化方式。
logger.info(‘%s %d‘, ‘aaa‘, 222);
常用的有
-
%s
字符串占位
-
%d
数字占位
-
%j
json 占位
更多的占位和详细信息,请参考 node.js 的 util.format 方法。
输出自定义对象或者复杂类型
基于性能考虑,Midway(winston)大部分时间只会输出基本类型,所以当输出的参数为高级对象时,需要用户手动转换为需要打印的字符串。
如下示例,将不会得到希望的结果。
const obj = { a: 1 };
logger.info(obj);
需要手动输出希望打印的内容。
const obj = {a: 1};
logger.info(JSON.stringify(obj));
logger.info(a.1);
logger.info(‘%j‘, a);
纯输出内容
特殊场景下,我们需要单纯的输出内容,不希望输出时间戳,label 等和格式相关的信息。这种需求我们可以使用 write
方法。
write
方法是个非常底层的方法,并且不管什么级别的日志,它都会写入到文件中。
虽然 write
方法在每个 logger 上都有,但是我们只在 IMidwayLogger
定义中提供它,我们希望你能明确的知道自己希望调用它。
(logger as IMidwayLogger).write(‘hello world‘);
日志定义
默认的情况,用户应该使用最简单的 ILogger
定义。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { ILogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@Inject()
logger: ILogger;
async getUser() {
this.logger.info(‘hello user‘);
}
}
ILogger
定义只提供最简单的 debug
, info
, warn
以及 error
方法。
在某些场景下,我们需要更为复杂的定义,比如修改日志属性或者动态调节,这个时候需要使用更为复杂的 IMidwayLogger
定义。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { IMidwayLogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@Inject()
logger: IMidwayLogger;
async getUser() {
this.logger.disableConsole();
this.logger.info(‘hello user‘);
this.logger.enableConsole();
this.logger.info(‘hello user‘);
}
}
IMidwayLogger
现有完整定义如下,下面文档介绍的方法,都在其中。
export interface IMidwayLogger extends ILogger {
disableConsole();
enableConsole();
disableFile();
enableFile();
disableError();
enableError();
isEnableFile(): boolean;
isEnableConsole(): boolean;
isEnableError(): boolean;
updateLevel(level: LoggerLevel): void;
updateFileLevel(level: LoggerLevel): void;
updateConsoleLevel(level: LoggerLevel): void;
updateDefaultLabel(defaultLabel: string): void;
updateDefaultMeta(defaultMeta: object): void;
updateTransformableInfo(customInfoHandler: LoggerCustomInfoHandler): void;
getDefaultLabel(): string;
getDefaultMeta(): Record<string, unknown>;
write(...args): boolean;
add(transport: any): any;
remove(transport: any): any;
close(): any;
}
日志等级
winston 的日志等级分为下面几类,日志等级依次降低(数字越大,等级越低):
const levels = {
all: 0,
error: 1,
warn: 2,
info: 3,
verbose: 4,
debug: 5,
silly: 6,
};
在 Midway 中,为了简化,一般情况下,我们只会使用 error
, warn
, info
, debug
这四种等级。
日志等级表示当前可输出日志的最低等级。比如当你的日志 level 设置为 warn
时,仅 warn
以及更高的 error
等级的日志能被输出。
框架的默认等级
在 Midway 中,有着自己的默认日志等级。
- 在开发环境下(local,test,unittest),日志等级统一为
info
。
- 在服务器环境(除开发环*),为减少日志数量,日志等级统一为
warn
。
动态调整等级
在开发调试时,我们往往有动态调整等级的诉求。在 Midway 的日志下,我们可以使用方法动态的调整日志等级。
logger.updateLevel(‘debug‘);
也可以单独调整文本和控制台输出的等级。
logger.updateFileLevel(‘warn‘);
logger.updateConsoleLevel(‘error‘);
日志输出管道(Transport)
Midway 的日志对象基于 Winston 日志,默认包含三个日志管道。
-
ConsoleTransport
用于向控制台输出日志
-
FileTransport
用于向文件写入日志
-
ErrorTransport
用于将 Error 级别输出到特定的错误日志
我们可以通过方法动态更新这三个管道,控制输出。
logger.enableFile();
logger.disableFile();
logger.enableConsole();
logger.disableConsole();
logger.enableError();
logger.disableError();
同时,还提供了判断的 API。
logger.isEnableConsole();
logger.isEnableFile();
logger.isEnableError();
日志切割(轮转)
默认行为下,同一个日志对象会生成两个文件。
以 midway-core.log
为例,应用启动时会生成一个带当日时间戳 midway-core.YYYY-MM-DD
格式的文件,以及一个不带时间戳的 midway-core.log
的软链文件。
为方便配置日志采集和查看,该软链文件永远指向最新的日志文件。
当凌晨 00:00
时,会生成一个以当天日志结尾 midway-core.log.YYYY-MM-DD
的形式的新文件。
同时,当单个日志文件超过 200M 时,也会自动切割,产生新的日志文件。
日志标签(label)
日志标签(label)指的是日志输出时,带有 [xx]
的部分。
默认的日志标签
默认情况下,Midway 对 coreLogger
的标签做了特殊处理,使用 coreLogger
输出的日志,会自带当前的框架信息。
比如:
2021-01-22 12:34:24,354 INFO 34458 [midway:gRPC] Find 1 class has gRPC provider decorator
2021-01-22 12:34:24,372 INFO 34458 [midway:gRPC] Proto helloworld.Greeter found and add to gRPC server
2021-01-22 12:34:24,381 INFO 34458 [midway:gRPC] Server port = 6565 start success
2021-01-22 12:34:24,416 INFO 34458 [midway:gRPC] Server shutdown success
Midway 没有对 appLogger
做特殊处理,即输出的日志不带标签。
Midway 对 contextLogger
做了特殊处理,默认的标签会关联上下文信息。
比如在 Web 下,会输出 ip,method,path 等:
2021-01-20 15:13:25,408 INFO 66376 [-/127.0.0.1/-/5ms GET /] xxxx
修改日志标签
有两个地方可以修改日志标签。
1、初始化时
const logger = this.app.createLogger(‘custom‘, {
level: ‘info‘,
defaultLabel: ‘main label‘,
});
logger.info(‘hello world‘);
2、动态调整
const logger = this.app.createLogger(‘custom‘, {
level: ‘info‘,
});
logger.info(‘hello world‘, { label: ‘UserService‘ });
logger.info(‘hello world‘, { label: [‘a‘, ‘b‘] });
注意,动态调整标签,不会影响默认的标签,即下一次如果不带 {label: xxx},依旧会输出默认标签。
自定义日志
如果用户不满足于默认的日志对象,也可以自行创建。
创建日志有两种方法:
- 1、从 app/framework 创建
- 2、从日志库
@midwayjs/logger
创建
不管哪一种,都是代理自日志容器的 createLogger
方法。
日志容器
日志容器是来存放日志对象以及管理日志对象,你可以理解为一个 Map。key 为日志对象的名称,value 为日志对象本身。
默认情况下,引入 @midwayjs/logger
库时,会在全局创建一个日志容器。
import { loggers } from ‘@midwayjs/logger‘;
console.log(loggers);
所有通过框架以及自定义创建的日志对象,都会存储其中。
每次使用 @Logger
装饰器以及 app.getLogger()
获取日志的行为,本质上都是从默认的日志容器或获取同名的日志对象。
import { Provide, Logger } from ‘@midwayjs/decorator‘;
import { ILogger } from ‘@midwayjs/logger‘;
@Provide()
export class UserService {
@App()
app: IMidwayApplication;
@Logger()
logger;
async getUser() {
this.app.getLogger(‘coreLogger‘).warn(‘warn message‘);
}
}
除了 getLogger
之外,还有其他一些方法,这些最基础的方法,可以以最原始的方式来获取、修改日志对象。
import { loggers, ILogger } from ‘@midwayjs/logger‘;
const customLogger = loggers.createLogger(‘customLogger‘, {
});
customLogger.info(‘hello world‘);
loggers.getLogger(‘customLogger‘);
loggers.addLogger(‘anotherLogger‘, customLogger);
loggers.removeLogger(‘customLogger‘);
loggers.close();
这种方法是一般用于和框架无关的场景,需要传递相对完整的参数,比如日志文件的路径等。
同时, @midway/logger
也提供两个简化的方法,用于快速创建日志。
import { createLogger, createConsoleLogger } from ‘@midwayjs/logger‘;
const consoleLogger = createConsoleLogger(‘customConsoleLogger‘);
const onlyFileLogger = createFileLogger(‘customOnlyFileConsoleLogger‘, {
dir: logsDir,
fileLogName: ‘test-logger.log‘,
});
const fileLogger = createLogger(‘customFileLogger‘, {
level: ‘warn‘,
dir: __dirname,
});
注意,如果创建同名的日志,日志容器会自动判断重名,跳过创建,并返回原有日志对象。
const customLogger1 = loggers.createLogger(‘customLogger‘, {
});
const customLogger2 = loggers.createLogger(‘customLogger‘, {
});
这个特性很有用,使得在不同场景下,能够让业务使用到同一个日志对象。
从当前框架、App 创建日志
在大多数请下,用户会使用这种方式创建日志。
Midway 在 app
上增加了 createLogger
方法,以方便用户快速基于框架默认的日志配置创建自己的日志实例。
比如在入口的 configuration.ts
中,我们可以创建出自己的日志。
export class AutoConfiguration {
@App()
app: IMidwayApplication;
async onReady() {
this.app.createLogger(‘custom1‘);
this.app.createLogger(‘custom2‘, {
level: ‘error‘,
disableFile: true,
disableError: true,
});
this.app.createLogger(‘custom3‘, {
fileLevel: ‘warn‘,
disableConsole: true,
});
}
}
这样创建出日志会自动绑定到框架中,并且使用框架默认的路径创建日志,后期可以直接根据日志名获取使用。
import { ILogger } from ‘@midwayjs/logger‘;
export class UserService {
@Logger(‘custom1‘)
custom1Logger: ILogger;
@Logger(‘custom2‘)
custom2Logger: ILogger;
@Logger(‘custom3‘)
custom3Logger: ILogger;
}
所有创建的日志,在全局日志容器 `loggers` 中都能获取到。
创建日志,等价于在全局日志容器中调用 loggers.createLogger()
方法。
创建日志选项
createLogger
方法的所有参数如下,用户可以自行调整。
export interface LoggerOptions {
dir?: string;
fileLogName?: string;
errorLogName?: string;
label?: string;
disableConsole?: boolean;
disableFile?: boolean;
disableError?: boolean;
consoleLevel?: LoggerLevel;
fileLevel?: LoggerLevel;
fileMaxSize?: string;
fileMaxFiles?: string;
fileDatePattern?: string;
errMaxSize?: string;
errMaxFiles?: string;
errDatePattern?: string;
disableFileSymlink?: boolean;
disableErrorSymlink?: boolean;
printFormat?: (info) => string;
format?: logform.format;
eol?: string;
}
| 参数名 | 参数类型 | 默认值 | 描述 | | ------------------------------ | ---------------------------------------- | ---------------------------------- | ------------------------------------------------------------------------------ | ----- | --- | ------------ | | dir | string | window: process.env.USERPROFILE
| | Linux/mac: process.env.HOME
| 文本日志的根目录,默认为当前的用户根目录 | | level | debug | info | warn | error | | 全局日志等级 | | fileLogName | string | midway-core.log | 文本日志写入的文件名 | | errorLogName | string | common-error.log | 错误日志写入的文件名 | | defaultLabel | string | undefined | 输出的默认标签,[] 中的值 | | disableConsole | boolean | false | 禁止控制台输出 | | disableFile | boolean | false | 禁止文本日志输出 | | disableError | boolean | false | 禁止错误日志输出 | | disableFileSymlink | boolean | false | 禁止生成软链,默认情况下,会生成带有时间戳的文件加上一个没有时间戳的软链文件。 | | disableErrorSymlink | boolean | false | 禁止生成软链,默认情况下,会生成带有时间戳的文件加上一个没有时间戳的软链文件。 | | consoleLevel | string | silly | 最低的控制台日志可见等级,可覆盖全局的日志等级 | | fileLevel | string | silly | 最低的文本日志可见等级,可覆盖全局的日志等级 | | fileMaxSize | string | 200m | 日志切割的最大尺寸,默认 200m
| | fileMaxFiles | string | 31d(31 天) | 最多保留的文件时间,默认 31d
| | fileDatePattern | string | YYYY-MM-DD | 文件后缀时间戳格式 | | errMaxSize | string | 200m | 日志切割的最大尺寸,默认 200m
| | errMaxFiles | string | 31d(31 天) | 最多保留的文件时间,默认 31d
| | errDatePattern | string | YYYY-MM-DD | 错误日志文件后缀时间戳格式 | | printFormat | (info: any) => string; | midway 默认显示格式 | 默认的日志输出显示格式,传入一个回调函数进行覆盖。 | | format | logform.Format | midway 默认 format | 默认的 winston format 格式。 | | eol | string | os.EOL | 默认是操作系统的换行符 |
修改显示格式(Display)
显示格式指的是日志输出时单行文本的字符串结构。Miidway 对 Winston 的日志做了定制,提供了一些默认对象。
显示格式是一个返回字符串结构的方法,参数为 Winston 的 info 对象。
默认情况下,我们的显示格式为:
(info) => {
return `${info.timestamp} ${info.LEVEL} ${info.pid} ${info.labelText}${info.message}`;
};
输出如下:
2020-12-30 07:50:10,453 ERROR 3847 [customLabel] Error: another test error
at Object.<anonymous> (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18)
info 对象的默认属性如下:
属性名 |
描述 |
示例 |
timestamp |
时间戳,默认为 ‘YYYY-MM-DD HH:mm:ss,SSS 格式。 |
2020-12-30 07:50:10,453 |
level |
小写的日志等级 |
info |
LEVEL |
大写的日志等级 |
INFO |
pid |
当前进程 pid |
3847 |
labelText |
标签的聚合文本 |
[a:b:c] |
message |
普通消息 + 错误消息 + 错误堆栈的组合 |
1、普通文本,如 123456 , hello world 2、错误文本(错误名+堆栈)Error: another test error at Object. (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) 3、普通文本+错误文本 hello world Error: another test error at Object. (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) |
stack |
错误堆栈 |
Error: another test error at Object. (/home/runner/work/midway/midway/packages/logger/test/index.test.ts:224:18) |
originError |
原始错误对象 |
错误实例本身 |
originArgs |
原始的用户入参 |
[ ‘a‘, ‘b‘, ‘c‘ ] |
示例,创建一个自定义格式的 Logger。
export class AutoConfiguration {
@App()
app: IMidwayApplication;
async onReady() {
this.app.createLogger(‘custom1‘, {
printFormat: (info) => {
return `${info.timestamp} ${info.level} ${info.message}`;
},
});
this.app.getLogger(‘custom1‘).info(‘hello world‘);
}
}
这样该日志的输出效果则为:
2020-12-30 07:50:10,453 info hello world
在某些场景下,我们无法在初始化修改日志对象 ,如果希望可以修改输出内容,也可以使用动态修改 info 对象的值来达到类似的效果。
logger.updateTransformableInfo((info) => {
info.timestamp = ‘123‘;
return info;
});
在原有输出的时间字段的位置则会变成
123,408 INFO 66376 [-/127.0.0.1/-/5ms GET /] xxxx
注意,该方法只能修改属性值,但是不能修改输出结构。
一般来说修改展示的效果已经足够,在 winston 中,还有另一种完全自定义输出的方式,修改 logform。通过修改 logform,基本上可以达到任意的效果。
你可以使用如下的 winston 自带的 format。
Formats
以及,midway 为 winston 定制的几个 format。
1、displayCommonMessage
displayCommonMessage 用于对常用输入的规则化处理,做了以下一些事情
- 1、对数组,Set,Map 的输出处理
- 2、Error 的堆栈拼装,以及增加原始的 error 对象
- 3、增加 pid
- 4、增加大写的 level
它的 options 如下:
属性名 |
类型 |
描述 |
defaultMeta |
object |
默认输出的元信息,对象 key/value 结构 |
uppercaseLevel |
boolean |
是否开启大写,默认 true |
2、displayLabels
按照一定的分隔符聚合标签(labels),它的 options 如下:
属性名 |
类型 |
描述 |
defaultLabels |
string[] |
标签信息数组 |
labelSplit |
string |
标签分隔符,默认为 : |
示例:
import { format, displayCommonMessage, displayLabels } from ‘@midwayjs/logger‘;
export class AutoConfiguration {
@App()
app: IMidwayApplication;
async onReady() {
this.app.createLogger(‘custom1‘, {
format: format.combine(
displayCommonMessage({
uppercaseLevel: true,
defaultMeta: {
group: ‘defaultGroup‘,
},
}),
displayLabels({
defaultLabels: this.labels,
}),
format.timestamp({
format: ‘YYYY-MM-DD HH:mm:ss,SSS‘,
}),
format.splat(),
format.printf(
(info) =>
`${info.timestamp} ${info.LEVEL} ${info.pid} ${info.labelText}- ${info.group} ${info.message}`,
),
),
});
this.app.getLogger(‘custom1‘).info(‘hello world‘);
}
}
清理全局日志容器
midway 提供了一个方法用于一次性清理所有的日志对象。
import { clearAllLoggers } from ‘@midwayjs/logger‘;
clearAllLoggers();
loggers.getLogger(‘coreLogger‘);
配置框架日志
Midway 在框架中提供了默认日志,如果需要修改默认日志的行为,可以在初始化框架时修改,传入不同的日志对象。
覆盖框架日志
框架的初始化入口一般为下面的代码,在其中创建日志实例,替换即可。
const { Bootstrap } = require(‘@midwayjs/bootstrap‘);
import { Framework } from ‘@midwayjs/koa‘;
import { createLogger, createConsoleLogger } from ‘@midwayjs/logger‘;
const consoleLogger = createConsoleLogger(‘customConsoleLogger‘);
const fileLogger = createLogger(‘customFileLogger‘, {
dir: __dirname,
});
const koaFramework = new Framework().configure({
port: 7001,
logger: consoleLogger,
});
Bootstrap.load(koaFramework).run();
覆盖请求链路日志的 Label
每个框架(Framework)可能会有默认的上下文日志输出,ContextLogger 是基于 appLogger 来打日志的,会复用 appLogger 的所有信息,唯一不同的是,ContextLogger 会输出特殊的 label。
比如 HTTP 下的默认输出为:
2021-01-20 15:13:25,408 INFO 66376 [-/127.0.0.1/-/5ms GET /] xxxx
label 为 -/127.0.0.1/-/5ms GET /
这一部分。
我们可以通过重写上下文日志类来修改上下文输出信息(ContextLogger)的 label。
首先,你需要定义一个文件,继承默认的 MidwayContextLogger
类,实现 formatContextLabel
来返回 label 内容。比如 HTTP 下:
import { MidwayContextLogger } from ‘@midwayjs/logger‘;
import { Context } from ‘egg‘;
export class MidwayCustomContextLogger extends MidwayContextLogger<Context> {
formatContextLabel() {
const ctx = this.ctx;
return `${Date.now() - ctx.startTime}ms ${ctx.method}`;
}
}
Midway 为每个框架的 app 增加了一个 setContextLoggerClass
方法,用于覆盖默认的 ctx.logger
输出的 label。
你可以在启动时进行覆盖。
import { Configuration } from ‘@midwayjs/decorator‘;
import { ILifeCycle, IMidwayContainer } from ‘@midwayjs/core‘;
import { MidwayCustomContextLogger } from ‘./custom/logger‘;
import { Application } from ‘egg‘;
@Configuration()
export class ContainerConfiguration implements ILifeCycle {
@App()
app: Application;
async onReady(container: IMidwayContainer): Promise<void> {
this.app.setContextLoggerClass(MidwayCustomContextLogger);
}
}
则你在使用 ctx.logger
输出时,会默认变成你 format 的样子。
ctx.logger.info(‘hello world‘);
@midwayjs/web(EggJS)下特殊情况
在 2021-01-28 之前的创建的项目,默认使用 egg-logger,之后创建的项目,将使用 @mdwayjs/logger。
兼容配置
由于 Egg 下原日志配置是非 API 形式,统一放在 config 文件中,在这一场景下,我们依旧支持大部分的参数,用于快速将应用迁移到新的日志体系。
以下配置只在 Egg 下生效。
config.logger
dir |
日志根目录 |
level |
文本日志等级 |
consoleLevel |
控制台日志等级 |
fileLogName |
文本日志文件名 |
coreLogName |
core 日志文件名 |
agnetLogName |
agent 日志名 |
appLogName |
应用日志名 |
disableConsoleAfterReady |
ready 之后禁止控制台输出 |
config.customLogger
dir |
日志根目录 |
file |
日志文件名 |
level |
文本日志等级 |
consoleLevel |
控制台日志等级 |
替换日志库
默认情况下,脚手架生成的日志库为 @midwayjs/logger
,并且默认关闭 egg 的日志切割能力(因为 midway 的日志库自带了),如果希望继续使用 egg-logger
,可以通过配置改回。
export const midwayFeature = {
replaceEggLogger: false,
};
同时,由于 egg-logger 日志需要额外开启切割能力,需要开启切割插件。
import { EggPlugin } from ‘egg‘;
export default {
logrotator: true,
static: false,
} as EggPlugin;
调整默认 level
export const logger = {
level: ‘INFO‘,
consoleLevel: ‘WARN‘,
};
启动输出
开发时,框架的默认输出都使用的是 coreLogger
,egg 默认的 coreLogger
的 consoleLevel
为 WARN
,如有查看的需求,可以覆盖默认的 egg 配置。
export const logger = {
coreLogger: {
consoleLevel: ‘INFO‘,
},
};