跟同事討論到高負載 .NET Logging 議題。某程式有巨量 Log 寫入需求,執行幾小時 Log 量可達數 GB,甚至懷疑寫 Log 可能成為效率瓶頸之一,程式目前使用 log4net,考是是否應更換成其他 Log 程式庫?如果要換,有哪些選擇?

早年用過 log4net,複雜的 XML 設定檔常讓我吐血,直到發現 NLog,二話不說立刻跳槽沿用至今,沒想過要換也就沒再留意其他 Log 程式庫,趁此機會做點功課。

把研究議題訂為「高負載 .NET Logging 解決方案評估」,如前述專案需求,以效能為首要考量。網路上討論較多的 .NET Log 程式庫有以下幾種:

整理爬文心得如下:

2016 年的評測,比較 log4net、ELMAH、NLog、MS Enterprise Library、NSpring,結論:NLog 最快,效能是 log4net 的五倍,第二名是 MS Enterprise Library,NSpring 第三,ELMAH 殿後,效能只有 NLog 的 1/10。
在設定及便捷性方面,NLog、log4net 較佳,NSpring 較麻煩,Enterprise Library 不辱其「企業級」之名,一整個複雜。ELMAH 只能搭配 ASP.NET 應用範圍較窄,每個事件以 XML 保存推測是效能較差原因。

.NET 低延遲 Log 測試

作者設計了一套評測程式,比較了 NLog, log4net, log4j2, 但偏重對 log4net 的設定調校。

Serilog vs NLog Benchmarks

Serilog 是近年掘起的 .NET Log 程式庫,標榜 Log 以結構化格式(JSON)儲存,有利事後查詢整理。作者沿用上一則文章的測試方法,在這份評測中,Serilog 的效能為 NLog 的兩倍。但有網友留言提到測試所用的 Liblog 抽象架構及設定差異嚴重影響評測結果,NLog 數據有大幅修正空間,細節留待後面說明。

NLog BufferingWrapper

NLog 有個 BufferingWrapper,允許累積一定筆數再批次寫入儲存媒體或傳送,可降低頻繁開啟關閉檔案或連線的成本,或減少通知頻率過高的困擾。(註:NLog 另外也有 AsyncWrapper 支援非同步寫入,意指程式不需停下來等 Log 寫完,可避免為了寫 Log 拖累核心作業執行。)

NLog FileTarget 效能參數

FileTarget 有多個參數可用於提高效能:

  • keepFileOpen
    設為 true 可保持檔案開啟,取代每次寫 Log 就開啟關閉一次,如此可大幅提高效能,缺點是會鎖定檔案,建議配合 openFileCacheTimeout = 30 服用,預設為 false。
  • concurrentWrites
    當 keepFileOpen = true 時允許同主機上其他 Process 也寫到同一 Log 檔(背後有黑魔法),若只有單一 Process 會寫入該 Log 檔,建議設 false 提高效率。
  • openFileCacheTimeout
    檔案保持開啟的時限,超過一段時間未使用便將檔案關閉,設為 -1 表永不關閉。預設為 -1。
  • openFileCacheSize
    當單一 File Target 寫入多個檔案(例如:依 Level 區分多個檔案),保持開啟檔案數目的上限(超過此數量時關閉最久沒使用的檔案),加大有助提升效能(但勿超過 10-15,以免佔用系統資源)。預設為 5。
  • optimizeBufferReuse
    寫入 Log 訊息時重複使用緩衝記憶體,預設為 true。(NLog 4.4.2+)
  • networkWrites
    網路上如有其他 Process 要同時寫入 Log 檔,設為 true 可防止檔案持續開啟,預設為 false。
  • concurrentWriteAttemptDelay
    寫入檔案失敗再次嘗試前的等待亳秒(ms)數,實際應用時取 0 到此延遲數字的亂數值,例如:設為 10 表示取 0-10ms 間的亂數。若再次失敗,延遲時間乘以 2 取 0-20ms 亂數,再失敗再取 0 - 40ms 亂數,以此類推。預設為 1。
  • concurrentWriteAttempts
    寫入失敗嘗試次數上限,超過次數則丟棄訊息。預設為 10。
  • cleanupFileName
    寫入檔案前要檢查檔案是否合法(依 OS 有別),若遇大量寫入時此檢查將消耗資源,設為 false 可提升效能。預設為 true。(NLog 4.2.3+)
  • bufferSize
    緩衝區大小,預設值為 32768
  • autoFlush
    是否每次寫 Log 都將緩衝區內容寫入檔案,設為 false 可提升效能(需搭配 openFileFlushTimeout),預設為 true。
  • openFileFlushTimeout
    當 autoFlush = false 時,設定每隔幾秒強制寫入檔案清空緩衝區,預設為 0。

NLog FileTarget 效能最佳化

Github 看到一則不錯的討論,參考其中建議:

 var fileTarget = new FileTarget 
 { 
     Name = "FileTarget", 
     FileName = "Log.txt",
     KeepFileOpen = true,
     ConcurrentWrites = false,
     AutoFlush = false,
     OpenFileFlushTimeout = 1,
}; 

keepFileOpen 設 true,concurrentWrites 設 false,autoFlush 設 false,openFileFlushTimeout 設 1 秒。在此設定下,NLog 之效能數字優於 Serilog。
另外,BufferingTargetWrapper 主要用於減少觸發外部通知管道次數(例如:第三方通訊軟體的 WebAPI、Email... 等),不利於檔案最大輸出,拼效能時應移除。 經過上述調整,NLog 效能明顯勝過 Serilog。(首頁的數據已提 PR 等待更新)

NLog vs log4net vs Serilog: Compare .NET Logging Frameworks

這是另一篇 NLog、log4net 與 Serilog 評比,聚焦於使用面而非效能面。
log4net 設定檔又臭又長眾所皆知,且更新緩慢(已 18 月未更新)已顯老態。 NLog 專案相對有活力,仍在持續改進,最新版加入結構化 Log 並支援 .NET Standard。(作者提到一項缺點是 NLog 出錯訊息易被忽略,此點可修改設定克服)。
Serilog 很新,2013 才誕生,強調 Log 資料 JSON 化,方便事後查詢、解析。由於年輕,Serilog 的設定語法更貼近未來趨勢,與 .NET Core 的語法很像,

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .WriteTo.Console()
    .WriteTo.File("logfile.log", rollingInterval: RollingInterval.Day)
    .CreateLogger();

已走進新時代的開發者應會感覺格外親切,但對老人來說,則需要一點時間學習適應。

結論

綜合以上討論,結論如下:

  1. log4net、NLog 及 Serilog 是 .NET 寫 Log 最主流的三項選擇。
  2. log4net 更新腳步慢,可怕的 XML 設定檔是其致命傷,繼續放生。
  3. 回到效能議題,依目前評測數據,NLog 與 Serilog 不相上下,但如何調校很重要,沒調好數字可差 N 倍。
  4. NLog 有一些重要效能參數 keepFileOpen、concurrentWrites、autoFlush、openFileFlushTimeout,避用 BufferingWrapper,是大幅提升寫 Log 效能的關鍵。
  5. Serilog 更貼新新一代 .NET 程式撰寫風格,強調結構化 Log 及高效能,貌似明日之星。(註:新版 NLog 也已支援結構化 Log,至於效能評比數據,若經妥善調校,NLog 與 Serilog 並無壓倒性差異)
  6. 個人觀點,手上專案仍會選擇 NLog,理由是其成熟度、使用簡便、彈性大及高效能。但會觀注 Serilog,未來伺機嘗試轉換。

A survey of .NET logging solution for high capacity file logging, including log4net, NLog and Serilog. NLog seems the best and need some optimization on configuration and Serilog is the rising star.


Comments

# by 小熊子

可以試試fluentd

# by Jeffrey

to 小熊子, 感謝補充,已筆記,擇空研究。

# by Bruce

請問黑大,如果NLog也想做資料JSON化,有沒有比較簡單的做法或範例可以參考呢?謝謝

# by Jeffrey

to Bruce, 官方文件有介紹 https://github.com/NLog/NLog/wiki/How-to-use-structured-logging 但略顯粗淺。Log 結構化後要搭配後端查詢工具才能展現威力,這段很少人自己刻,最流行的做法是搭配 Elastic Stack(ElasticSearch, Logstash, Kibana, ELK),但講到 ELK 會扯出另一大串粽子。用關鍵字查 NLog ElasticSerach 可以找到不少文章。我還沒研究到這一段,暫無法提供實務建議。

# by Bruce

感謝黑大提供的資訊,我再來好好研究一下,Thanks~~

# by 星寂

最近因為想弄個log server,然後發現 serilog 可以掛 elasticsearch,配合kibana, 我覺得蠻好用的。 然後如果喜歡nlog, 也可以用serilog 再把nlog 掛進來同時寫。 XD

# by Victor Tseng

4 年過去了,有點好奇黑大當年的結論 6. 個人觀點,手上專案仍會選擇 NLog,理由是其成熟度、使用簡便、彈性大及高效能。但會觀注 Serilog,未來伺機嘗試轉換。 現在有沒有開始要進伺機嘗試轉換的階段呢? 最近也在評比 NLog vs serilog,基本使用,包括 structured logging,大致上沒有說天差地遠,不過 serilog 微妙的在 nuget 的下載數海放 NLog,Google Trend 上面也出現熱度超車 NLog,不知道黑大有沒有什麼想法?

# by Jeffrey

to Victor Tseng, 如果後端會串接 ELK 之類的 Log 蒐集整合機制,絕對就是選 Serilog,結構化資料更有利於後續程式處理。反之,若只想簡單記錄執行軌跡,或事後查錯誤訊息,Log 主要供人工調閱,NLog 一筆一行的形式更友善,我的話會依據 Log 用途選擇。

# by 阿光

黑大 最近發生Log在一秒內大量寫入導致檔案鎖定,想改用NLog的BufferingWrapper看看

Post a comment