2

我正在尝试将大型应用程序从使用Trace类转换为通过log4net. 因此,我编写了一个自定义TraceListener来将输出重定向到 log4net 的消息中(受这篇文章的启发)。

public class Log4netTraceListener : TraceListener
{
    private static readonly ILog logger = LogManager.GetLogger("Log4netTraceListener"); // Line 19

    public Log4netTraceListener() { /* nothing special */ }

    public override void TraceEvent(TraceEventCache eventCache, string source, TraceEventType eventType, int id, string message)
    {
        // some switching based on the TraceLevel, but eventually something like:
        logger.Error(message);
    }
}

控制台应用解决方案是三个项目:

  1. RunShipOrder - 作为应用程序入口点的控制台项目
  2. ShipOrderAPI - 一个包含与此流程相关的所有代码的类库项目
  3. CodeLibrary - 一个包含许多项目通用代码的类库项目。
    • Log4netTraceListener 在这个项目中

RunShipOrder项目内部,我可以调用LogManager.GetLogger("Log4netTraceListener"),它按预期工作。但是,如果我尝试对其调用方法,Trace则会引发异常。我已将其追溯到LogManager.GetLogger("Log4netTraceListener")对类内部的初始调用Log4netTraceListener

抛出的异常是ConfigurationErrorsException,内部异常为TypeInitializationException,内部异常为NullReferenceException。最里面的异常的堆栈跟踪是:

   at log4net.Core.LoggerManager.GetLogger(Assembly repositoryAssembly, String name)
   at log4net.LogManager.GetLogger(Assembly repositoryAssembly, String name)
   at log4net.LogManager.GetLogger(String name)
   at CodeLibrary.Diagnostics.Log4netTraceListener..cctor() in Log4netTraceListener.cs:line 19

关于从 log4net 代码库中可能引发此异常的任何想法?

4

1 回答 1

1

@drovani - 很好的描述。我看到了同样的症状。我的问题是 app.config 中的 System.Diagnostics.Trace 输出被配置为自定义 System.Diagnostics.TraceListener 实现,该实现将跟踪转发到 log4net,同时通过 log4net.Internal 启用 log4net 的内部调试。 app.config 中的 Debug = True(或通过 log4net.Util.LogLog.InternalDebugging = true 的代码)。

可能发生的情况是您在静态初始化期间或就在记录之前在自定义 System.Diagnostics.TraceListener 实现中调用 LoggerManager.GetLogger(),这会产生静态初始化排序问题。log4net 在初始化时尝试记录某些内容,但在 LoggerManager 初始化之前。因此 LoggerManager.GetLogger() 抛出空引用异常。

在您的自定义 TraceListener 中,您可以通过使用静态属性在首次使用而不是在字段中初始化 ILogger 来防止这种情况,但您需要在这里小心。最好的办法是不记录,或者如果 log4net.Util.LogLog.InternalDebugging 为 true,则使用其他机制记录。这将是首选的解决方案。

查看 log4net.Util.LogLog 类的源代码。默认情况下,这会记录到 Trace,如果由此记录任何内容(例如附加程序错误),您最终将陷入死锁。在您的 TraceListener 实现中,我会确保在您的实现的构造函数中设置 log4net.Util.LogLog.EmitInternalMessages = false,并可能处理 LogLog.LogReceived 事件并以另一种方式记录这些消息。可悲的是,由于此设置,一些附加程序错误将不会被记录。log4net 源是你的朋友。

如果您想变得非常棘手,对性能产生巨大影响,则需要在记录消息和创建记录器之前检查 log4net.Util.LogLog.InternalDebugging 的设置。如果启用,您可以获得堆栈跟踪并遍历它以确保在调用 log4net 之前 log4net 不在堆栈中。如果 log4net 在堆栈跟踪中,您可以 pinvoke OutputDebugString() 或使用其他一些日志记录机制来跟踪 log4net 内部日志输出。您绝对不想将内部 log4net 调试消息重定向到 log4net,否则可能会出现与这个优秀站点类似的异常。

于 2015-07-20T15:46:45.067 回答