擁抱.NET Core系列:Logging (1)


在之前我們簡單介紹了 .NET Core 中的 DI組件,沒來及了解的童鞋可以翻翻我之前的文章。

接下來會對 .NET Core 中的 Logging 進行介紹。

本文中使用了“Microsoft.Extensions.Logging.Console”做為輸出目標,后續文章會詳解。

Logging 中的三劍客

image

可以看到 Logging 的核心抽象就是三個接口,分別是:

ILogger:負責具體的日志寫入邏輯,如:FileLogger,ConsoleLogger,SQLLogger,ElasticsearchLogger 等。

ILoggerProvider:用來創建記錄器,一般和Logger配套使用,相當於單個Logger類型的工廠接口。

ILoggerFactory:記錄器工廠,直接面向使用者的,使用者可以通過記錄器工廠添加記錄器提供程序和創建記錄器。

這幾個核心抽象位於 NuGet包:“Microsoft.Extensions.Logging.Abstractions”中。

日志等級

在.NET Core提供的日志抽象中提供了7個日志等級(比一般的日志組件多提供了一個Trace和None),分別是:

Trace

包含最詳細消息的日志。 這些消息可能包含敏感的應用程序數據。 默認情況下禁用這些消息,並且不應在生產環境中啟用這些消息。

Debug

在開發過程中用於交互式調查的日志。 這些日志應主要包含對調試有用的信息,不具有長期價值。

Information

跟蹤應用程序的一般流程的日志。 這些日志應具有長期價值。

Warning

突出顯示應用程序流中異常或意外事件的日志,但是否則不會導致應用程序執行停止。

Error

當當前執行流程由於失敗而停止時,會突出顯示的日志。這些應該指示當前活動中的故障,而不是應用程序范圍的故障。

Critical

描述不可恢復的應用程序或系統崩潰或災難性的日志失敗需要立即關注。

None

不用於寫日志消息。 指定記錄類別不應寫任何消息。

簡單的使用

image

image

CreateLogger 方法

CreateLogger 方法的簽名為

image

它提供了兩個擴展方法,可以通過類型作為分類名稱,如下:

image

如何根據類型確定分類名稱?

在擴展方法內部使用了“GetTypeDisplayName(Type type)”來根據類型獲取名稱(里面有一些邏輯處理,但一般是采用“{命名空間}.{類型名稱}”作為分類名稱)。

image

實現傳送門:https://github.com/aspnet/Logging/blob/patch/1.1.3/src/Microsoft.Extensions.Logging.Abstractions/Internal/TypeNameHelper.cs

Log方法

image

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組件為我們提供了大量擴展方法以簡化我們的編碼。

以下是方法存根,參數說明可以對照上文。

image

EventId效果

image

日志域

image

日志域可以聚合一類的消息,非常適合同一種類型不同維度的日志記錄。

日志過濾器

Logging提供了一個包裝實現用來實現日志過濾,我們先來看看使用。

image

可以看到可以通過制定 CategoryName 及最小日志等級來控制日志是否輸出,這邊有個有趣的事情。

就是 CategoryName 可以模糊匹配。

在 Logging 組件內部擋識別到 CategoryName 為:“ConsoleApp.MyClass”時會把這個分類名稱分割為:

“ConsoleApp.MyClass”

“ConsoleApp”

ps:如果你的命名空間中存在多個“.”符號則還會被分割。

分割完成之后會將這些 Key 拿去與“FilterLoggerSettings”中的字典表進行匹配,優先最大匹配,也就是說如果我們配置了“ConsoleApp.MyClass”這條項目,則優先使用這條,否則繼續尋找“ConsoleApp”這條項目,如果都沒匹配到則使用默認的規則。

實現傳送門:https://github.com/aspnet/Logging/blob/patch/1.1.3/src/Microsoft.Extensions.Logging.Filter/Internal/FilterLogger.cs

在 NLog、log4jnet 等組件中模糊匹配是采用“.*”的方式,例如:”ConsoleApp.*”,在 .NET Core 中的 Logging 中是不被支持的(把“.*”去掉實現相同的效果),這點需要注意。

注意

“.WithFilter”是使用包裝的方式進行集成,所以內部會單獨維護一個 FilterLogger,也就意味着所有的 LoggerProvider 必須在 FilterFactory 中進行注冊,不然過濾器是不會生效的哦。以下是錯誤的示例:

image

紅色框框部分的兩句應該對調,“.WithFilter”應該優先調用。

特殊的Logger => NullLogger

這個我覺得 .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> 的實現。

我們下面來看看可使用的場景:

image

可以看到在沒有添加 Logging 組件的時候日志記錄也不會拋出異常。

ps:NullLogger<T> 摘抄至.NET Standard2.0中的 NullLoggerOfT.cs。

地址:https://github.com/aspnet/Logging/blob/dev/src/Microsoft.Extensions.Logging.Abstractions/NullLoggerOfT.cs

Logger in DependencyInjection

不使用 Filter

image

使用 Filter

image

寫在最后

不得不感嘆微軟在 .NET Core 中統一了非常多的常用組件,為開發者統一環境提供了極大的方便。

后續的文章會分享如何集成第三方 Logging 組件,比如:NLog、log4jnet、Exceptionless 等。

.NET技術棧QQ群:384413261(點擊加入 .NET Group


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM