1 基本概念
Dotnet core 一個重要的特征是 Dependency injection ,中文一般是依賴注入,可以簡單理解為一個集合,在應用程序啟動時,定義各種具體的實現類型並將其放到集合中;在應用程序運行時,從集合中取出之前放入的類型。
Logging 的實現就采用這種方式,寫日志分為兩步:創建寫日志的對象;用創建的對象寫日志。ILoggerProvider 創建寫日志的對象 ILogger ,即在應用程序啟動時,把實現了 ILoggerProvider 接口的類型放到集合中,在應用程序運行期間,需要寫日志,先去集合中取 ILoggerProvider ,使用其創建 ILogger 對象,然后就可以寫日志了。
1.1 ILogger
寫日志的定義:
void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter);
除了 logLevel 有點熟,其它都感覺無用,只是我們並沒有直接使用,並不是其真的無用。
1.2 ILoggerProvider
創建寫日志對接定義:
ILogger CreateLogger(string categoryName);
通過定義可以知道,所有的 ILogger 對象都有 categoryName 屬性。屬性值不能為 Null。
到這里其實可以寫日志了,也僅只是寫日志而已,還有兩個比較重要的問題:1 寫的日志在哪里可以看?2 如何管理寫日志?
問題1 是由 ILoggerProvider 接口的實現來決定,官方的實現有:Console 、Debug 、EventSource 、EventLog 、TraceSource 、Azure App Service,還有一些第三方實現,如此我們自己也是可以實現的。
例如 ConsoleLoggerProvider 是將日志在命令行中打印出來,如果是控制台程序或者是 self host 啟動 web 程序便可以在命令行中看到打印的日志信息。
問題2由 log Level、EventId、Logger Provider、Log filtering、Log category、Log scopes 合作解決。好像是很復雜,其實比較簡單。
1.3 Log Level
定義如下,數值越大級別越高。:
Trace = 0
Debug = 1
Information = 2
Warning = 3
Error = 4
Critical = 5
None = 6
例如:
_logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
日志的 logLevel 是 Warning ,eventId 是的 GetItemNotFound,內容是“GetById({ID}) NOT FOUND”,這個對應接口定義中 formatter 的返回值 string。
2 log filtering 說明
為了說明日志過濾,先說明下日志的整體框架,在應用程序啟動時,可以注冊多個不同的 ILoggerProvider 實現,而在寫日志時,只調用一次寫,但所有注冊的 ILoggerProvider 都能收到寫的日志,這背后是由框架實現的。具體的簡單點說,我們調用 log()后,它再遍歷所有注冊的 ILoggerProvider ,再分別調用它們的 log()方法。
這樣在一個正常的系統中會有多個 ILoggerProvider 實現,而每個 ILoggerProvider 創建的 ILogger 又有一個類別 category;也就是說在調用log()寫日志時,ILogger 對象具有 Provider 和 Category,這點很重要!
而過濾便是針對 Provider 和 Category 設置的。
2.1 過濾類型
可以為某個 Provider 的某個 Category 設置一條過濾規則,也可以針對所有的Provider 和所有的Category 設置一條過濾規則。
這樣過濾規則的類型也就可以如下所示:
表1:
|
Provider 限定 |
Provider 不限定 |
Category 限定 |
1 |
3 |
Category 不限定 |
2 |
4 |
2.2 過濾規則
(1)minimum level
最低級別的意思是框架只會將高於(包括等於)此級別的日志傳遞給 Provider,進而 Provider 調用 log()方法處理日志。
(2)Filter functions
Func<string, LogLevel, bool> categoryLevelFilter
這是委托簽名,入參是類別 Category 名稱和寫日志的級別,返回是 bool 類型,也就是說可以根據類別和消息級別來決定是否寫日志。
2.3 最佳過濾規則
至此,我可以可以添加一條過濾規則,其簽名如下:
LoggerFilterRule(string providerName, string categoryName, LogLevel? logLevel, Func<string, string, LogLevel, bool> filter)
比如:.AddFilter<ConsoleLoggerProvider>("System", LogLevel.Critical) 便等同於:
LoggerFilterRule("ConsoleLoggerProvider", "System", LogLevel.Critical,null)
如果我們添加了多條過濾規則,但每個 Provider 只能對應一條過濾規則,這時應當使用以下規則:
(1)1>2>3>4 (參考表1)
(2)同一等級后注冊高於先注冊
(3)無可用過濾規則,則不過濾
補充,在設置過濾規則時可以同時設置 minimum level 和 Filter functions,先執行minimum level,如有必要再執行 Filter functions。
在匹配類別 Category 時,不是字符串相同比較,是字符串前綴包含即可。
正常的系統,對象間關系簡單示意如下:
圖1
3 配置示例
設置過濾規則有兩種方式:代碼和配置文件
3.1 代碼
ILoggingBuilder AddFilter<T>(this ILoggingBuilder builder, string category, LogLevel level)
還有一些類似的簽名,T 是 Provider 類型,可選, category 必選。可以實現表1中:1、2、3類型設置,4類型沒有找到(應該是沒有提供)
3.2 配置文件
{
"Logging": {
"LogLevel": {
"Default": "Trace",
"Microsoft": "Critical"
},
"Console": {
"LogLevel": {
"Default": "Debug",
"System": "Warning"
}
}
}
}
這是一實例配置,從上往下定義了4個過來規則,其對應的表1中的級別分別是:4、3、2、1
4 實踐一下
(1)注冊 Provider
(2)注冊 Filter
(3)獲取 ILogger
(4)寫日志
我沒有貼代碼,請參考下面的git中的SampleApp。
5 參考
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-2.1
https://github.com/aspnet/Logging