log4net BufferingForwardingAppender性能问题

时间:2012-07-03 21:16:27

标签: c# performance log4net appender

编辑2:我已经解决了问题(请参阅下面的答案)请注意,该问题可能会影响所有使用BufferingForwardingAppender修补的appender以及从BufferingAppenderSkeleton继承的所有appender(分别为:AdoNetAppender,RemotingAppender,SmtpAppender和SmtpPickupDirAppender) *

我正在做一些非常基本的log4net工作台,我尝试用BufferingForwardingAppender修饰RollingFileAppender。

我通过BufferingForwardingAppender遇到了糟糕的性能,而不是直接通过RollingFileAppender,我真的没有理由。

这是我的配置:

<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
  <file value="c:\" />
  <appendToFile value="false" />
  <rollingStyle value="Composite" />
  <datePattern value="'.'MMdd-HH'.log'" />
  <maxSizeRollBackups value="168" />
  <staticLogFileName value="false" />
  <layout type="log4net.Layout.PatternLayout">      
    <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
  </layout>
</appender>

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

<root>
  <level value="DEBUG" />
  <appender-ref ref="BufferingForwardingAppender" />    
</root>

这是基准(非常简单的代码):

var stopWatch = new Stopwatch();
stopWatch.Start();            
for (int i = 0; i < 100000; i++)            
{
   Log.Debug("Hello");
}
stopWatch.Stop();
Console.WriteLine("Done in {0} ms", stopWatch.ElapsedMilliseconds);

直接通过RollingFileAppender输出:

  

完成511毫秒

然后通过BufferingForwardingAppender装饰RollingFileAppender:

  

于14261毫秒完成

这大约慢了30倍。

我认为通过在将它们写入文件之前缓冲一定量的日志来获得一些速度,但是由于某种原因它会让事情变得更糟。

对我来说似乎配置还可以,所以这真的很奇怪。

任何人都有线索?

谢谢!

编辑1:

通过包装/装饰FileAppender甚至是ConsoleAppender,行为是完全相同的(在log4net官方配置示例中仍然有一个基本的BufferingForwardingAppender包装/装饰ConsoleAppender的示例..并且没有具体提到处理性能)。

经过一些调查/分析后,我可以看到大部分时间都在BufferingForwardingAppender中被破坏,更具体地说是在调用WindowsIdentity.GetCurrent()时被调用...每当我们调用{{1}时调用在上一个示例中(在上面的示例源中为100K次)。

已知对此方法的调用非常昂贵,应该避免或最小化,我真的不明白为什么它会被调用每个日志事件。 我是否真的完全错误配置某些东西/没有看到明显的东西,或者某种错误是某种错误,这就是我现在想要解决的问题......

部分调用堆栈是:

  • AppenderSkeleton.DoAppend
  • BufferingAppenderSkeleton.Append
  • LoggingEvent.FixVolatileData
  • LoggingEvent.get_UserName()

Log.Debug()的调用也在FixVolatileData中完成,也会产生很高的性能(每次捕获堆栈跟踪)。

我现在正在尝试理解为什么这个极其昂贵的FixVolatileData调用(至少对于修复问题)在此上下文中发生的每个日志事件,而直接通过包装的appender(直接通过ConsoleAppender / FileAppender ..)执行这种行动。

即将推出的更新,除非有人得到了所有这些的答案;)

谢谢!

2 个答案:

答案 0 :(得分:39)

我发现了这个问题。

BufferingForwardingAppender继承自BufferingAppenderSkeleton(与使用记录事件缓冲的其他appender一样,AdoNetAppenderRemotingAppenderSmtpAppender ...

BufferingAppenderSkeleton实际上是在满足特定条件(例如缓冲区已满)之后实际将日志事件传递给目标appender之前缓冲日志事件。

根据LoggingEvent类的文档(表示日志记录事件,并包含事件的所有值(message,threadid ...)):

  

某些日志记录事件属性被视为“易失性”,即   在将事件传递给追加者时,值是正确的,   但之后的任何时候都不会保持一致。如果事件是   存储并在稍后处理这些易变的值   必须通过调用FixVolatileData来修复。有表演   通过调用FixVolatileData引起的惩罚但是必不可少的   保持数据一致性

这些“volatile”属性由包含Message,ThreadName,UserName,Identity等标志的FixFlags枚举表示。所有volatile属性。 它还包含标志“None”(不修复任何属性),“All”(修复所有属性)和“Partial”(仅修复某个预定义的dset属性)。

BufferingAppenderSkeleton被实例化时,由DEFAULT将修复设置为“全部”,这意味着所有“易变”属性都应该被修复。

在该上下文中,对于附加到BufferingAppenderSkeleton中的每个LoggingEvent,在将事件插入缓冲区之前,将修复所有“volatile”属性。这包括属性Identity(用户名)和LocationInformation(堆栈跟踪),即使这些属性不包含在布局中(但我想如果布局被更改为稍后在缓冲区中包含这些属性时会有某种意义已经被LoggingEvents填充了。

然而在我的情况下,这真的是HURTS表现。我没有在我的布局中包含Identity和LocationInformation,并且不打算(主要针对性能问题)

现在解决方案......

BufferingAppenderSkeleton中有两个属性可用于控制FixFlags的{​​{1}}标志值(默认情况下再次设置为“ALL”,这不是很真好!) 这两个属性是BufferingAppenderSkeleton(FixFlags类型)和Fix(bool类型)。

要对标记值进行微调或禁用所有修复,应使用OnlyFixPartialEventData属性。 对于特定的部分预定义标志组合(不包括Identity或LocationInfo),可以通过将其设置为“true”来使用Fix

如果我重复使用上面的配置示例(在我的问题中),则对配置进行的唯一更改以释放性能如下所示:

OnlyFixPartialEventData

使用这个修改过的配置,上面我的问题中提出的基准代码执行从大约 14000ms下降到230ms(快60倍)! 如果我使用<appender name="BufferingForwardingAppender" type="log4net.Appender.BufferingForwardingAppender"> <bufferSize value="512" /> <appender-ref ref="RollingLogFileAppender" /> <Fix value="0"/> <!-- Set Fix flag to NONE --> </appender> 而不是禁用所有修复,则需要大约350毫秒。

可悲的是,这个标志没有很好的文档记录(除了在SDK文档中,一点点)..所以我不得不深入研究log4net来源以找到问题。

这尤其成问题,特别是在“参考”配置示例中,此标志无处可见(http://logging.apache.org/log4net/release/config-examples.html)。 因此,为BufferingForwardingAppender和AdoNetAppender(以及继承自BufferingAppenderSkeleton的其他appender)提供的示例将为用户提供可怕的性能,即使他们使用的布局非常小。

答案 1 :(得分:0)

是否可能是因为你没有在BufferingForwardingAppender中指定布局模式,但是你在RollingLogFileAppender,因此BufferingForwardingAppender包含了输出中的所有内容,包括用户名(%用户名)

下面是一篇有趣的博客文章,其中列出了模式布局中的选项,看起来他有几个标记为慢速。

http://www.beefycode.com/post/Log4Net-Tutorial-pt-4-Layouts-and-Patterns.aspx