我的自定义 Log4net 异步故障转移附加程序跳过消息

My Custom Log4net Asynch Failover Appender Skips Messages

我正在尝试为 Log4net 编写异步 Failover Appender。我快到了,但有一些小问题。

appender 需要尝试登录到我们的内部 Web 服务,然后,如果失败,则写入文件。

这是我需要帮助的地方...

症状:

我的附加程序代码如下所示:
最终,我将动态解析客户端,但现在...

namespace Testing.Appenders
{
    using log4net.Appender;
    using log4net.Core;
    using System;
    using System.IO;
    using System.ServiceModel;
    using System.Threading.Tasks;
    using wsEventManager;

    public class AsynchWebServiceFailoverAppender : AppenderSkeleton
    {
        #region <Properties>

        public string ServiceUrl { get; set; }

        public string File { get; set; }

        #endregion

        #region <Methods>

        #region protected

        protected override void Append(LoggingEvent loggingEvent)
        {
            if (loggingEvent == null)
                throw new ArgumentNullException("LoggingEvent");

            if (string.IsNullOrEmpty(File))
                throw new ArgumentNullException("File");

            if (string.IsNullOrEmpty(ServiceUrl))
                throw new ArgumentNullException("ServiceUrl");

            Task.Factory.StartNew(() =>
            {
                var uri = new Uri(ServiceUrl);
                var myBinding = new BasicHttpBinding(BasicHttpSecurityMode.None);
                var endpoint = new EndpointAddress(uri);
                var client = new EventManagerSoapClient(myBinding, endpoint);

                var eventType = GetEventType(loggingEvent);
                var log = GetEvent(eventType);

                client.Log(log);
                client.Close();
                client = null;
            }) 
            .ContinueWith(task => 
            {    
                using (var streamWriter = new StreamWriter(File, true))
                {
                    using (var textWriter = TextWriter.Synchronized(streamWriter))
                    {
                        textWriter.WriteLine(base.RenderLoggingEvent(loggingEvent));
                        textWriter.FlushAsync();
                    }
                }
            }, TaskContinuationOptions.OnlyOnFaulted);
        }

        #endregion

        #endregion
    }
}

我的配置有效,但这里是:

<?xml version="1.0"?>
<configuration>

  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>

  <log4net>
    <appender name="WebServiceFailoverAppender" type="Testing.Appenders.AsynchWebServiceFailoverAppender , Testing">
      <ServiceUrl value="http://localhost/DiagnosticsWeb/BadUrl.asmx" />
      <file value="C:\Logs\Support\Diagnostics.Appender\log4net.log" />
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="&lt;%level date=&quot;%date{MM-dd-yyyy}&quot; time=&quot;%date{h:mm:ss tt}&quot; file=&quot;%class&quot; thread=&quot;%thread&quot;&gt;%message&lt;/%level&gt;%newline" />
      </layout>
    </appender>
    <root>
      <level value="ALL" />
      <appender-ref ref="WebServiceFailoverAppender" />
    </root>
  </log4net>

</configuration>

结果:

如果您正在使用 asynchronous 调用,您应该等待它们,否则,当您离开 scope 时,它可能无法完成。 我的猜测是 flush 确实会对您遇到的这种竞争状况产生任何影响。

要解决此问题,您应该 await flush。在您的 task 延续中,我将介绍 await async 架构:

ContinueWith(async task => 
{    
    using (var streamWriter = new StreamWriter(File, true))
    {
       using (var textWriter = TextWriter.Synchronized(streamWriter))
       {
           textWriter.WriteLine(base.RenderLoggingEvent(loggingEvent));
           await textWriter.FlushAsync();
        }
     }
}, TaskContinuationOptions.OnlyOnFaulted);

如果有效,请告诉我。

写入文件时存在竞争条件。您的 TextWriter.Synchronized 没有效果,因为您每次都创建新的编写器,并且创建此构造是为了将写入同步到 TextWriter 的单个实例。要验证尝试锁定整个文件写入块。如果您想以即发即弃的异步方式从多个线程写入单个日志文件 - 请参阅我的其他答案 .