.NetCore中的日志(1)日志組件解析
.NetCore中的日志(1)日志組件解析
0x00 問題的產生
日志記錄功能在開發中很常用,可以記錄程序運行的細節,也可以記錄用戶的行為。在之前開發時我一般都是用自己寫的小工具來記錄日志,輸出目標包含控制臺、文本文件、數據庫,一般都是創建全局的Logger,在需要記錄日志的地方調用相應的Logger輸出至相應目標。遇到輸出目標多了有時候也感覺挺麻煩的,不過也還能接受。開始學習.NetCore后接觸到了日志記錄框架(Logging組件),雖然完全可以用之前的方式記錄日志,不過應該使用更通用的方式,把日志記錄和具體的輸出目標解耦。所以學習了.NetCore中Logging組件,并嘗試實現了自定義的LoggerProvider,以及在.NetCore的Logging框架中使用現有完善的第三方日志記錄工具NLog。寫一篇博客作為學習記錄,同時也希望對有這方面需求的園友有所幫助。
0x01 .NetCore中的Logging
正如在上面部分寫到的那樣,當日志輸出的目標多起來后,寫日志就會變得麻煩。仔細想一下,日志輸出這個動作是不變的,變的只是不同的輸出目標(控制臺、文本文件、數據庫等),所以可以把日志記錄這個動作抽象出來,日志記錄器包含多個可輸出目標,當我們調用Log方法寫日志時,由Log方法依次調用Logger中的XxxLogger,把日志寫到具體的目標上。過程如下圖所示:

那么如何創建出這樣的一個Logger呢,我們可以創建一個工廠叫LoggerFactory用來生產Logger,Logger中包還含了ConsoleLogger、FileLogger等,這些XxxLogger可以通過XxxLoggerProvider創建。進一步的,可以把Logger、LoggerFactory和LoggerProvider的行為抽象為接口ILogger、ILoggerFactory、ILoggerProvider。

其中:
ILogger中的Log()方法可以記錄日志;
ILoggerProvider可以創建ILogger,用于向特定的目標寫入日志;
ILoggerFactory可以添加多個IloggerProvider,并可以創建我們最終使用的ILogger;
下圖為使用LoggerFactory中使用AddProvider方法添加ILoggerProvider:

下圖為LoggerFactory中使用CreateLogger方法創建Logger:

下圖為Logger的構造函數,使用傳入的LoggerFactory中的providers,依次調用其中的ILoggerProvider來創建XxxLogger。

這里需要特別說明一下,ILoggerFactory和ILoggerProvider都產生ILogger,看上去讓人迷惑,但實際上這兩種ILogger的實現細節是不一樣的,不同的實現中Log()方法的意義不同。
對于ILoggerFactory產生的是Logger類型(也就是我們最終使用的Logger),其Log()方法是依次調用Logger中包含的_loggers數組中的ILogger。

而ILoggerProvider產生的為各類不同的XxxLogger(也就是上面說的Logger中的_loggers數組包含的如ConsoleLogger、DebugLogger),其Log()方法是把日志寫到具體的目標上去。下圖為ConsoleLogger的Log()方法:

在有時候我們可能不希望某些日志被寫入到所有的目標上。例如只想把某些特定的日志寫入數據庫。這時可以在XxxdLoggerProvider構造函數中傳入
Func<string, LogLevel, bool> filter
形式的委托,當返回true時寫入日志,返回false則不寫入日志。
此外針對不同的LoggerProvider有不同的配置方式,這里就不一一說明了。
0x02 泛型的Logger<T>
前面我們看到了,Logger用name來標識其唯一性。在日志記錄時我們很多情況下都希望記錄日志產生時所在的命名空間和類型,因此使用完整的類型名稱來作為Logger的name既保證了唯一性又記錄了日志產生時所在的命名空間和類型是一個很好的選擇。當創建Logger<T>對象時,實際上就是創建了一個用T的完整類型名稱作為name的Logger并進行了包裝,把Logger<T>的Log方法原封不動傳入了創建的Logger的Log方法。

這樣一來像NLog這樣基于name的路由也很容易集成了。
0x03 使用日志記錄
ILoggerFactory默認就已經被添加到IServiceCollection容器中了,我們只需要添加需要的ILoggerProvider即可。為了讓代碼更簡潔更具備自解釋的能力,Logging組件還給ILoggerFactory添加了擴展方法,例如只要使用以下代碼

就可以完成ConsoleLoggerProvider和DebugLoggerProvider的添加。
此外對Logger復雜的Log方法也進行了封裝(LogTrace、LogDebug、LogError等等),以滿足不同需求。
在使用Logger時可以通過依賴注入的方式獲取Logger,可以有兩種方法獲取:

以及

這兩種方法沒有本質區別,如下圖所示CreateLogger<T>方法也是調用Logger<T>構造函數來創建Logger<T>的。

所以只要根據喜好選擇就行。
0x04 寫在最后
.NetCore的Logging組件提供了日志記錄的框架,只要實現了ILoggerProvider接口的日志記錄工具都可以集成到Logger中,這極大方便了成熟的第三方日志記錄工具的集成。通過Logging組件,把日志記錄邏輯和具體的記錄行為解耦了,可以任意更換日志記錄工具而不需要修改日志記錄邏輯,同樣的,只要實現了框架的接口,不同日志記錄工具也可以混用。所以雖然.NetCore本身只實現了Console、Debug等幾個有限的Logger,但借助于豐富的第三方日志記錄工具,我們有非常多的選擇。即使需求極其奇葩,只要實現框架中的接口,我們很容易集成自己寫的日志記錄工具。下一篇將以NLog為例說一下第三方日志記錄工具的集成,此外還將編寫和集成一個自己寫的Logger。

浙公網安備 33010602011771號