---
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/),在一個位置查看所有應用程式日誌。