文章目錄
關於提昇 NLog 寫入檔案效能
這是之前一直想做的測試筆記用來比較紀錄 NLog 參數設定對於寫入 log 的效能差異比較,起因是曾經在專案中遇到程式本身沒有問題,卻因為交易龐大產生大量 log 讓 nlog 頻繁讀寫檔案,加上使用傳統硬碟 disk io 速度受限,最後造成系統效能低落,形成嚴重效能瓶頸
剛好同事最近問到相關問題,就來紀錄一下相關比較數據吧
基本設定
- 使用 console project
- 安裝 NLog 及 NLog.config  
- 加入寫 log 程式 - 定義 logger - private static ILogger logger = LogManager.GetCurrentClassLogger();
- 執行 log - static void Main(string[] args) { int logcount = 1000000; for (int i = 0; i < 5; i++) { LogToFile(i,logcount); } } private static void LogToFile(int index,int logcount) { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < logcount; i++) { logger.Debug($"{index}_{i}_{DateTime.Now}"); } sw.Stop(); logger.Info($"{index}_log 筆數:{logcount};總耗時毫秒:{sw.ElapsedMilliseconds}"); LogManager.Flush(); }
 
預設設定
使用原始 NLog.config 所提供的預設設定(僅調整 log folder 跟 layout)
<?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"
    xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
    autoReload="true"
    throwExceptions="false"
    internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
    <targets>
        <target xsi:type="File" name="f" fileName="${basedir}/logs/origin/${shortdate}.${uppercase:${level}}.log"
                layout="${longdate} ${message}" />
    </targets>
    <rules>
        <logger name="*" minlevel="Debug" writeTo="f" />
    </rules>
</nlog>


| 次數 | 1,000 筆 | 100,000 筆 | 
|---|---|---|
| 第一次 | 294 | 24113 | 
| 第二次 | 265 | 26183 | 
| 第三次 | 267 | 28779 | 
| 第四次 | 212 | 30878 | 
| 第五次 | 212 | 32353 | 
| 平均 | 250 | 28461.2 | 
使用 keepFileOpen
在
target中加上keepFileOpen="true"讓 log file 保持開啟,可以節省開檔、關檔的成本
<?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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
    <targets>
        <target xsi:type="File" name="f" fileName="${basedir}/logs/keepFileOpen/${shortdate}.${uppercase:${level}}.log"
                layout="${longdate} ${message}" keepFileOpen="true"/>
    </targets>
    <rules>
        <logger name="*" minlevel="Debug" writeTo="f" />
    </rules>
</nlog>
 )
)

| 次數 | 1,000 筆 | 100,000 筆 | 
|---|---|---|
| 第一次 | 35 | 724 | 
| 第二次 | 6 | 649 | 
| 第三次 | 9 | 649 | 
| 第四次 | 6 | 652 | 
| 第五次 | 6 | 662 | 
| 平均 | 12.4 | 667.2 | 
使用 async
在
targets設定使用 asynchronous,讓 nlog 另起 thread 來寫入 log
<?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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
    <targets async="true">
        <target xsi:type="File" name="f" fileName="${basedir}/logs/async/${shortdate}.${uppercase:${level}}.log"
                layout="${longdate} ${message}" />
    </targets>
    <rules>
        <logger name="*" minlevel="Debug" writeTo="f" />
    </rules>
</nlog>



| 次數 | 1,000 筆 | 100,000 筆 | 1,000,000 筆 | 
|---|---|---|---|
| 第一次 | 6 | 258 | 2100 | 
| 第二次 | 1 | 207 | 2002 | 
| 第三次 | 1 | 196 | 2007 | 
| 第四次 | 1 | 189 | 1987 | 
| 第五次 | 2 | 193 | 1993 | 
| 平均 | 2.2 | 208.6 | 2017.8 | 
使用 async + keepFileOpen
同時使用
async及keepFileOpen
<?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"
      xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd"
      autoReload="true"
      throwExceptions="false"
      internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log">
    <targets async="true">
        <target xsi:type="File" name="f" fileName="${basedir}/logs/asynckeepFileOpen/${shortdate}.${uppercase:${level}}.log"
                layout="${longdate} ${message}" keepFileOpen="true"/>
    </targets>
    <rules>
        <logger name="*" minlevel="Debug" writeTo="f" />
    </rules>
</nlog>



| 次數 | 1,000 筆 | 100,000 筆 | 1,000,000 筆 | 
|---|---|---|---|
| 第一次 | 6 | 264 | 2073 | 
| 第二次 | 2 | 223 | 1963 | 
| 第三次 | 1 | 218 | 1963 | 
| 第四次 | 1 | 196 | 1957 | 
| 第五次 | 1 | 197 | 1979 | 
| 平均 | 2.2 | 219.6 | 1987 | 
總和比較
| 設定 | 1,000 筆 | 100,000 筆 | 1,000,000 筆 | 
|---|---|---|---|
| 預設 | 250 | 28461.2 | - | 
| keepFileOpen | 12.4 | 667.2 | - | 
| async | 2.2 | 208.6 | 2017.8 | 
| async + keepFileOpen | 2.2 | 219.6 | 1987 | 
心得
除了預設設定之外,在少量 log (1000筆) 的情境下,各個設定的耗時差距都很小,但隨著 log 量增加至 10 萬筆時就會發現 async 效能比 keepFileOpen 較快,此時同時使用 async + keepFileOpen 效能上反而沒有單用 async 快,直到筆數來到百萬筆時,同時使用 async + keepFileOpen 效能上就會超越單用 async,但效果並不是非常顯著
另外有個普遍現象是,第一筆 log 的時間都要較久,原因是第一筆 log 包含建立檔案的時間
最後有個需要特別留意的是開檔效能,使用預設設定需要重複開檔、讀檔、寫檔,因此隨著檔案變大,寫入 log 的時間也就愈來愈長,相同的問題在其他設定上則未發生
參考資訊
文章作者 Yowko Tsai
上次更新 2021-11-02
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
 Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。
