日常普通使用
首先我們學會 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',
},
};