log4net - BufferingForwardingAppender

為了調效 log4net RollingFileAppender 的性能做了個簡單的測試,測試程式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using System;
using System.Diagnostics;
using log4net;
using log4net.Config;

namespace ConsoleApplication27
{
class Program
{
static void Main(string[] args)
{
XmlConfigurator.Configure();

var count = 100000;
var logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

var sw = Stopwatch.StartNew();
for (var idx = 0; idx < count; ++idx)
{
logger.Debug(idx.ToString());
}

Console.WriteLine(sw.ElapsedMilliseconds);
}
}
}



套上 BufferingForwardingAppender。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<log4net>
<root>
<level value="ALL"/>
<appender-ref ref="BufferingForwardingAppender"/>
</root>

<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender">
<bufferSize value="512" />
<appender-ref ref="RollingFileAppender" />
</appender>

<appender name="RollingFileAppender" type="log4net.Appender.RollingFileAppender">
...
</appender>
</log4net>
</configuration>


整個性能反而嚴重下降。


這邊可進一步調整 Fix 參數,像是如果我們將之設為 0,效能就會提升上去。


但是如果設為 0,Log 的資料會不正確,像是圖中的 Thread ID 就無法正確紀錄。


實際使用時我們應該參閱 FixFlags Enumeration,依需求下去設定。


像是可以設定為 12,使其修正 Message 與 ThreadName。


效能問題解決了,仍需考慮 Buffer 的資料是否可以正確的寫入。


一樣做個簡單的實驗去測試,設定 Buffer 大小為 512 筆,然後程式這邊在第 511 筆時丟出例外,可以看到這邊 Buffer 的資料並未正確的紀錄。


所以我們需要在程式中適當的時機點,像是在 Application_End 或是在全域例外處理這邊,將 Buffer Flush 讓 Log 可被寫入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
...
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
...
}

private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
FlushBuffers();
}

public static void FlushBuffers()
{
var rep = LogManager.GetRepository();
foreach (IAppender appender in rep.GetAppenders())
{
var buffered = appender as BufferingAppenderSkeleton;
if (buffered != null)
{
buffered.Flush();
}
}
}
...