.NetCore中的日誌(1)日誌組件解析 0x00 問題的產生 日誌記錄功能在開發中很常用,可以記錄程式運行的細節,也可以記錄用戶的行為。在之前開發時我一般都是用自己寫的小工具來記錄日誌,輸出目標包含控制台、文本文件、資料庫,一般都是創建全局的Logger,在需要記錄日誌的地方調用相應的Logge ...
.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。