--- tags: C#, Logger, 翻譯文章 --- # NLog vs log4net vs Serilog: Compare .NET Logging Frameworks 原文:NLog vs log4net vs Serilog: Compare .NET Logging Frameworks 文章出處:<https://stackify.com/nlog-vs-log4net-vs-serilog/> 原文作者: SIMON TIMMS 原文發表時間: 2018/08/27 譯者 : Cymon Dez 翻譯時間 : 2018/08/29 目錄 [TOC] ## 前言 在\.NET中記錄資訊,或者說在任何應用程式產品(production application)是相當重要的。在許多狀況下,開發人員無法直接存取生產環境下的除錯議題。良好的日誌將會讓你有如同 夏洛克。福爾摩斯( Sherlock Holmes) 般的解決問題,而憋腳的日誌則會讓你像 迷糊大偵探(Inspector Jacques Clouseau)一樣拿石頭砸自己腳。 我們在部落格上發表了很多關於\.NET日誌框架的文章。我鼓勵您試著搜尋並閱讀一些我們之前的文章。 ## 頂尖 .NET 日誌框架 比較 本文將介紹在\.NET環境下,最流行的3種日誌框架:log4net、NLog 與 Serilog。 如果您過去閱讀我的文章,您會發現通常將得到一些“這一切都取決於您的情況”諸如此意的結論。 但這次不會!這次我承諾宣布一個勝利者,一個日誌框架來駕馭(rule)它們並在CLR中繫結它們。 --- ## log4net 總覽 ( overview ) 回憶\.NET最初的時候,或者說至少接近最開始的時候,\.NET只有一個日誌框架:log4net。它起始於2001年,是Java框架下的log4j。 它曾經在Sourceforge平台上託管(Hosted on),如果您資歷夠深,應該還記得Sourceforge這個平台。這些年來,Apache Logging Services專案仍持續開發。 在過去的17年中,log4net已被數以萬計的應用程式使用;理所當然,它成為所有現代\.NET日誌框架的老祖宗。 日誌級別(log level),記錄器(logger)和附加器(appender)等概念在日誌記錄框架中幾乎是通用的。這可能是一個禍福兼具之事(mixed blessing):沒有其他框架像log4net那樣經過實戰測試。 在過去,我們提供了一些關於使用log4net的技巧,以及在.NET Core中使用它的快速教學。冒著重複的風險,讓我們至少看一下如何設置記錄器(logger)並記錄資訊。我們將為每個\.NET日誌記錄框架執行相同的操作:生成消息並將其記錄到控制台和日誌文件。本文的所有代碼都在Github上。 [範例](https://github.com/stimms/LoggingDemo) log4net 可以由 NuGet 輕易安裝。 ### log4net 程式範例 我們將針對.NET Core撰寫所有程式碼,因為它是未來趨勢。 我之前提到log4net已經老舊; 作為遺留下來的一部分,它的設定在.NET Core上無法完全正確發揮作用。 您在網路上大多數的地方找到的設定範例都不起作用,但幸運的是,設定的答案就在[這篇部落格文章](https://stackify.com/making-log4net-net-core-work/)中(謝謝,Matt!)。 ```csharp namespace LoggingDemo.Log4Net { class Program { private static readonly ILog log = LogManager.GetLogger(typeof(Program)); static void Main(string[] args) { var logRepository = LogManager.GetRepository(Assembly.GetEntryAssembly()); XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config")); log.Debug("Starting up"); log.Debug("Shutting down"); Console.ReadLine(); } } } ``` 我們將從名為 log4net.config 的檔案載入設定。 這是一個XML設定檔,其中包含用於記錄的設定。 不要忘記將該文件設定為 **複製到輸出資料夾**。 ```xml <log4net> <appender name="Console" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <!-- Pattern to output the caller's file name and line number --> <conversionPattern value="%utcdate %5level [%thread] - %message%newline" /> </layout> </appender> <appender name="RollingFile" type="log4net.Appender.RollingFileAppender"> <file value="logfile.log" /> <appendToFile value="true" /> <maximumFileSize value="100KB" /> <maxSizeRollBackups value="2" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%utcdate %level %thread %logger - %message%newline" /> </layout> <appender> <root> <level value="DEBUG" /> <appender-ref ref="Console" /> <appender-ref ref="RollingFile" /> </root> </log4net> ``` 這個設定檔非常複雜。 定義了兩個附加器(appender):一個用於寫入控制台,另一個用於寫入文件大小為100KB的滾動文件(RollingFile)。 我們必須在參數conversionPattern中 **自定義日誌輸出格式**,因為預設的log4net設定很難派上用場(此處原文是 廢物 trash )。 使用conversionPattern,我們可以獲得看起來像這樣的日誌 ```csharp= 2018-08-18 18:57:37,278 DEBUG 1 LoggingDemo.Log4Net.Program - Starting up 2018-08-18 18:57:37,298 DEBUG 1 LoggingDemo.Log4Net.Program - Shutting down ``` ### log4net 總結 設定檔可說是log4net的最大敗筆。複雜就算了,XML更是令人望之卻步。雖然可以使用程式碼直接進行設定,但缺乏詳細文件。事實上,“沒有良好說明文件”幾乎成了log4net的標語。 這些令人費解的案例 與 記錄到SQL資料庫這樣的事情,像極了2009年常幹的事情。(原文: witch is sooo 2009) log4net有很多不同的附加器(appender),所以你很可能不會為了特定的日誌記錄而枯耗(dry)。 --- ## NLog 總覽 NLog 也是一個歷史悠久的專案。從2006年發佈1.0版,仍積極開發,於2017年12月發佈了最新版本,由於版本趨近穩定,之後18月(本文 章撰寫日 2018/08)未再更新。 NLog的最新版本增加了```.NET Standard```的結構化日誌記錄和支持。 NLog 可以由 NuGet 輕易安裝。 ### NLog 程式範例 ```csharp using NLog; using System; namespace LoggingDemo.Nlog { class Program { static void Main(string[] args) { LogManager.LoadConfiguration("nlog.config"); var log = LogManager.GetCurrentClassLogger(); log.Debug("Starting up"); log.Debug("Shutting down"); Console.ReadLine(); } } } ``` 程式碼與log4net非常相似:讀取設定檔,然後開始記錄。 ```xml <?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <targets> <target name="logfile" xsi:type="File" fileName="logfile.txt" /> <target name="logconsole" xsi:type="Console" /> </targets> <rules> <logger name="*" minlevel="Debug" writeTo="logconsole" /> <logger name="*" minlevel="Debug" writeTo="logfile" /> </rules> </nlog> ``` 這邊可以看出 **NLog** 和 **log4net** 之間的區別。設定檔仍然是用XML,但 **NLog** 更清晰簡潔。使用者沒有必要編寫自己的格式。 日誌格式看起來像這樣: ```csharp 2018-08-18 13:27:10.5022|DEBUG|LoggingDemo.Nlog.Program|Starting up 2018-08-18 13:27:10.5623|DEBUG|LoggingDemo.Nlog.Program|Shutting down ``` NLog還有一種透過用程式碼設定日誌的紀錄方法。 ```csharp var config = new NLog.Config.LoggingConfiguration(); var logfile = new NLog.Targets.FileTarget("logfile") { FileName = "logfile.txt" }; var logconsole = new NLog.Targets.ConsoleTarget("logconsole"); config.AddRule(LogLevel.Debug, LogLevel.Fatal, logconsole); config.AddRule(LogLevel.Debug, LogLevel.Fatal, logfile); NLog.LogManager.Configuration = config; ``` 基於程式碼的設定看起來沒問題,但肯定使用上不會比使用設定檔來得流暢。 ### NLog 總結 NLog和log4net之間只存在一些細微差異。 NLog更易於設定,並且支援比log4net更清晰的程式碼設定方式。個人為NLog中的預設值比log4net更明智。 我認為這兩個框架仍然存在一個問題:如果日誌紀錄器(logger)出現錯誤(個人案例是網際複製設定檔),沒有任何相關的錯誤提示。會這樣設計的想法是因為:日誌問題不應該使應用程式被取消執行。我可以理解這種想法,但如果程式在啟動期間日誌紀錄失敗會是致命的。日誌不是,也不應該是"深思熟慮"的。許多自動化的問題檢測相當依賴於日誌輸出結果,如果沒有日誌輸出是很嚴重的。 ## Serilog 總覽 最新的日誌記錄框架Serilog於2013年發佈。Serilog與其他框架之間的最大區別在於它著重在**開箱即用**的[結構化日誌](https://stackify.com/what-is-structured-logging-and-why-developers-need-it/)。 NLog雖然也支持結構化日誌記錄,但這是一種事後的想法,基準建議([benchmarks suggest](https://www.darylcumbo.net/serilog-vs-nlog-benchmarks/))表明使用它會有一些嚴重的性能影響。 等等! 什麼是結構化日誌記錄? 天!我還以為你永遠不會提起此事。 ### 結構化日誌 ( Structured logging ) 範例 通常日誌將紀錄至少兩件事情,訊息(message)與值(value)。 例如: ```csharp log.Debug($"User id is: ${userId}"); ``` 相較於大多數日誌記錄框架,只能簡單地將資料轉換為日誌檔(log file)中的純文字(text)。 純文字沒啥問題,但是如下例子中,如果能知道被記錄的 userId 的實際值,並且能加以搜索的話,將會非常實用。而Serilog則能將該屬性(property)一直保持。 ```csharp log.Debug("User id is {@userId}", userId); ``` Serilog由商業公司<https://getseq.net/>所提供,他們提供了一個非常好的日誌凝聚工具(log aggregation tool)。 當然,您也可以將日誌發送到我們最喜歡的日誌聚合器:[Retrace](https://stackify.com/retrace-log-management/)。 Retrace也支援保留日誌中的屬性。 安裝Serilog比NLog或log4net稍微複雜一些,因為它力求高度模組化。 對於我們的範例程式,您需要包 + serilog + serilog.sinks.file + serilog.sinks.console ### Serilog 程式範例 這次不再需要任何XML設定檔! Serilog的配置使用流利介面(fluent interface),程式非常漂亮和乾淨。與log4net相比,設置滾動文件(Rolling file)記錄器的難易程度。 ```csharp using Serilog; using System; namespace LoggingDemo.Serilog { class Program { static void Main(string[] args) { Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console() .WriteTo.File("logfile.log", rollingInterval: RollingInterval.Day) .CreateLogger(); Log.Debug("Starting up"); Log.Debug("Shutting down"); Console.ReadLine(); } } } ``` Serilog的日誌記錄輸出如下: ```csharp 2018-08-18 14:37:21.463 -06:00 [DBG] Starting up 2018-08-18 14:37:21.560 -06:00 [DBG] Shutting down ``` ### Serilog 總結 Serilog對於結構化日誌記錄支援地非常好,設定也很簡單。我在使用Seirlog上,將比其他框架更容易上並語開始使用。Serilog中的日誌可以發送往大量目的地。Serilog將這些東西稱為“溝渠(sinks)”,您可以在<https://github.com/serilog/serilog/wiki/Provided-Sinks>查看相當完整的清單。 另一個我在Serilog中非常喜歡的概念是enricher 假設有一段程式碼,它在每個日誌請求時執行(runs for every log request),向其添加新訊息。例如,如果您需要在ASP.NET中包含有關請求的資訊,則可以使用enricher將屬性附加到日誌訊息中。您甚至可以使用enricher來更改日誌項目(entries)。 例如enricher,有一個[enricher](https://github.com/nblumhardt/serilog-enrichers-demystify)可以提高 堆疊追蹤(stack traces)的可理解性。 ## 宣布結果,最佳 日誌框架 我承諾過,我們最終會在本文末尾提出一個優勝者,當然就是:Serilog。Serilog的API更加現代化,更容易設定,更好地維護性,在預設情況下進行結構化日誌記錄。藉由添加enricher的功能使您能夠攔截和修改訊息,這非常實用。 Serilog是我最好的\.NET日誌框架。 記錄是任何應用程式的重要議題,不應被視為事後補上的想法。使用結構化日誌記錄踏出你的第一步(on the right foot)。 您更應該使用日誌聚合器如 [Retrace](https://stackify.com/retrace/),在一個位置查看所有應用程式日誌。