在以前咱們簡單介紹了 .NET Core 中的 DI組件,沒來及瞭解的童鞋能夠翻翻我以前的文章。git
接下來會對 .NET Core 中的 Logging 進行介紹。github
本文中使用了「Microsoft.Extensions.Logging.Console」作爲輸出目標,後續文章會詳解。less
能夠看到 Logging 的核心抽象就是三個接口,分別是:ide
ILogger:負責具體的日誌寫入邏輯,如:FileLogger,ConsoleLogger,SQLLogger,ElasticsearchLogger 等。單元測試
ILoggerProvider:用來建立記錄器,通常和Logger配套使用,至關於單個Logger類型的工廠接口。測試
ILoggerFactory:記錄器工廠,直接面向使用者的,使用者能夠經過記錄器工廠添加記錄器提供程序和建立記錄器。編碼
這幾個核心抽象位於 NuGet包:「Microsoft.Extensions.Logging.Abstractions」中。3d
在.NET Core提供的日誌抽象中提供了7個日誌等級(比通常的日誌組件多提供了一個Trace和None),分別是:調試
Trace日誌
包含最詳細消息的日誌。 這些消息可能包含敏感的應用程序數據。 默認狀況下禁用這些消息,而且不該在生產環境中啓用這些消息。
Debug
在開發過程當中用於交互式調查的日誌。 這些日誌應主要包含對調試有用的信息,不具備長期價值。
Information
跟蹤應用程序的通常流程的日誌。 這些日誌應具備長期價值。
Warning
突出顯示應用程序流中異常或意外事件的日誌,可是不然不會致使應用程序執行中止。
Error
噹噹前執行流程因爲失敗而中止時,會突出顯示的日誌。這些應該指示當前活動中的故障,而不是應用程序範圍的故障。
Critical
描述不可恢復的應用程序或系統崩潰或災難性的日誌失敗須要當即關注。
None
不用於寫日誌消息。 指定記錄類別不該寫任何消息。
CreateLogger 方法的簽名爲
它提供了兩個擴展方法,能夠經過類型做爲分類名稱,以下:
在擴展方法內部使用了「GetTypeDisplayName(Type type)」來根據類型獲取名稱(裏面有一些邏輯處理,但通常是採用「{命名空間}.{類型名稱}」做爲分類名稱)。
logLevel
日誌等級,詳情見上文。
eventId(結構體,必填,能夠傳入 0 或 default(EventId)來充當默認值)
事件ID。
這邊的事件ID是用來追蹤的,相似 ErrorCode、StatusCode。這樣在日誌檢索的時候能夠經過code很方便的找到。
是一個結構體,默認爲:「0」。
state(可爲null)
狀態。
須要記錄的對象,這邊能夠傳入任何類型,這就有點奇怪了日誌不都是字符嗎?
若是我傳一個自建類 UserModel 進去會記錄出什麼信息呢?請接下來看 formatter 參數。
exception(可爲null)
異常。
很少說了,若是當前上下文有異常,你丟進去就行了。
formatter(不可爲null)
格式化器。
這個參數是一個委託能夠看到定義「Func<TState,Exception,string>」,這個就能夠解釋state是非字符的狀況下如何記錄日誌了。
這邊能夠經過你本身的邏輯來重建消息的內容(異常信息都會進行輸出)。
若是傳入null,日誌組件會使用默認的格式化器替換,默認的格式化器邏輯是調用「state.ToString()」
固然Logging組件爲咱們提供了大量擴展方法以簡化咱們的編碼。
如下是方法存根,參數說明能夠對照上文。
日誌域能夠聚合一類的消息,很是適合同一種類型不一樣維度的日誌記錄。
Logging提供了一個包裝實現用來實現日誌過濾,咱們先來看看使用。
能夠看到能夠經過制定 CategoryName 及最小日誌等級來控制日誌是否輸出,這邊有個有趣的事情。
就是 CategoryName 能夠模糊匹配。
在 Logging 組件內部擋識別到 CategoryName 爲:「ConsoleApp.MyClass」時會把這個分類名稱分割爲:
「ConsoleApp.MyClass」
「ConsoleApp」
ps:若是你的命名空間中存在多個「.」符號則還會被分割。
分割完成以後會將這些 Key 拿去與「FilterLoggerSettings」中的字典表進行匹配,優先最大匹配,也就是說若是咱們配置了「ConsoleApp.MyClass」這條項目,則優先使用這條,不然繼續尋找「ConsoleApp」這條項目,若是都沒匹配到則使用默認的規則。
在 NLog、log4jnet 等組件中模糊匹配是採用「.*」的方式,例如:」ConsoleApp.*」,在 .NET Core 中的 Logging 中是不被支持的(把「.*」去掉實現相同的效果),這點須要注意。
「.WithFilter」是使用包裝的方式進行集成,因此內部會單獨維護一個 FilterLogger,也就意味着全部的 LoggerProvider 必須在 FilterFactory 中進行註冊,否則過濾器是不會生效的哦。如下是錯誤的示例:
紅色框框部分的兩句應該對調,「.WithFilter」應該優先調用。
這個我以爲 .NET Core 是從 Orchard「偷」過來的,Orchard 滿地的 NullLogger.Instance。
爲何須要 NullLogger?
在業務系統中,Logger 其實並不影響邏輯,換句話說,Logger若是失敗不該該影響業務。
在單元測試時 Logger 也能夠忽略。
這句話確定是對的,但在遍地DI的項目中 Logger 頗有可能被開發者傳入null,這時候就會影響業務的執行,那麼這時候 NullLogger 很是適合作那個最糟糕的實現者。
用來替換日誌記錄或防止「NullReferenceException」這類異常的發生。
很是惋惜的是,1.1.3版本中沒有提供 NullLogger<T> 這樣的實現。好消息是在 .NET Standard2.0 中已經提供了 NullLogger<T> 的實現。
咱們下面來看看可以使用的場景:
能夠看到在沒有添加 Logging 組件的時候日誌記錄也不會拋出異常。
ps:NullLogger<T> 摘抄至.NET Standard2.0中的 NullLoggerOfT.cs。
不得不感嘆微軟在 .NET Core 中統一了很是多的經常使用組件,爲開發者統一環境提供了極大的方便。
後續的文章會分享如何集成第三方 Logging 組件,好比:NLog、log4jnet、Exceptionless 等。
.NET技術棧QQ羣:384413261(點擊加入 .NET Group)