如何为log4net创建异步包装器

本文关键字:异步 包装 创建 log4net | 更新日期: 2023-09-27 18:05:19

默认情况下,log4net是一种同步日志记录机制,我想知道是否有一种方法可以使用log4net进行异步日志记录?

如何为log4net创建异步包装器

如果你去log4net网站,你可以找到一些例子,其中至少有一个是异步Appender。

http://logging.apache.org/log4net/release/example-apps.html

请注意,我没有使用过这些例子中的任何一个,所以我不能以任何方式为它们担保。

以下是来自log4net的实际异步附加程序的链接。示例代码存储库中的区域:

http://svn.apache.org/viewvc/logging/log4net/trunk/examples/net/2.0/Appenders/SampleAppendersApp/cs/src/Appender/AsyncAppender.cs?view=markup

我简单地看了一下,它显然是一个包装一个或多个"传统"的Appenders。在每个日志记录请求(包含一个或多个LoggingEvent对象(上,ThreadPool线程用于将LoggingEvent转发到包装的Appenders列表。

只是想提供我的完整解决方案供参考。有几个重要的项目,FixFlags可以让您捕获实际进行日志记录的线程。阻塞集合在ReactiveExtensions中。这里的问题是,你的转发附加程序处理异步的东西,然后只将LoggingEvent转发到标准的Log4Net附加程序,它可以让Log4Net做它擅长的所有事情。不需要重新发明轮子。

/// <summary>
/// Provides an extension for the log4net libraries to provide ansynchronous logging capabilities to the log4net architecture
/// </summary>
public class AsyncLogFileAppender : log4net.Appender.ForwardingAppender
{
    private static int _asyncLogFileAppenderCount = 0;
    private readonly Thread _loggingThread;
    private readonly BlockingCollection<log4net.Core.LoggingEvent> _logEvents = new BlockingCollection<log4net.Core.LoggingEvent>();
    protected override void Append(log4net.Core.LoggingEvent loggingEvent)
    {
        loggingEvent.Fix = FixFlags.ThreadName;
        _logEvents.Add(loggingEvent);
    }
    public AsyncLogFileAppender()
    {
        _loggingThread = new Thread(LogThreadMethod) { IsBackground = true, Name = "AsyncLogFileAppender-" + Interlocked.Increment(ref _asyncLogFileAppenderCount), };
        _loggingThread.Start();
    }
    private void LogThreadMethod()
    {
        while (true)
        {
            LoggingEvent le = _logEvents.Take();
            foreach (var appender in Appenders)
            {
                appender.DoAppend(le);
            }
        }
    }
}

然后,在您的log4net.xml中,您可以这样设置附加程序

<!-- Standard form output target location and form -->
<appender name="StandardAppender" type="TSUIC.Logging.AsyncLogFileAppender">
<appender-ref ref="StandardAppenderSync" />
</appender>
<appender name="StandardAppenderSync" type="log4net.Appender.RollingFileAppender">
    <!-- The standard pattern layout to use -->
    <file value="log'Log_" />
    <appendToFile value="true" />
    <rollingStyle value="Date" />
    <maxSizeRollBackups value="-1" />
    <maximumFileSize value="5GB" />
    <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
    <staticLogFileName value="false" />
    <datePattern value="yyyyMMdd'.txt'" />
    <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
    </layout>
</appender>

更新:

如果您想在log4net中使用上下文,如"log4net.ThreadContext.Properties["CustomColumn"]">

然后你需要像一样更新上面的代码

loggingEvent.Fix = FixFlags.All;

我就是这样做的:

Task.Factory.StartNew(() => log.Info("My Info"));

通过这种方式,log4net异步地在一个单独的线程上执行日志记录。。。

BTW,Task类在System.Threading.Tasks命名空间中。

这里的一些想法是不正确的,会导致数据无效/过时、日志记录不正常或性能非常差。例如,公认的答案建议使用log4net AsyncAppender,它使用ThreadPool,导致条目无序,这对一些人来说可能不是问题,但我当然希望我的日志事件是一个接一个的,它也可能具有糟糕的性能,给ThreadPool带来太大的压力,而且它不批处理日志条目。Jonathan提出的答案当然是一个更好的解决方案,但仍然缺乏最佳性能。

关于如何实现这一点的一个很好的例子可以在这里找到,基准测试结果和解释也在这里。

该解决方案的另一个好特性是,它被实现为Forwarder而不是Appender,允许用户包括多个Appender并同时登录到它们中的每一个。

本周我遇到了这个问题,但我不想一直向线程池发送请求,因为它最终可能会使应用程序的其他线程挨饿,所以我想出了一个异步追加器,它运行一个通过缓冲区馈送的专用线程进行追加。点击此处查看:https://github.com/cjbhaines/Log4Net.Async

https://github.com/cjbhaines/Log4Net.Async

我们现在有异步log4net方法可用。对于那些正在寻找最新答案的人。

https://www.nuget.org/packages/Log4Net.Async/