log4net的LogManager.GetLogger()在自定义TraceListener中使用时抛出NullReferenceException

时间:2012-07-18 22:05:39

标签: c# log4net tracelistener

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

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在此项目中
  4. 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代码库中引发此异常的任何想法吗?

1 个答案:

答案 0 :(得分:0)

@drovani - 出色的描述。我看到了同样的症状。我的问题是app.config中的System.Diagnostics.Trace输出被配置为自定义System.Diagnostics.TraceListener实现,它将跟踪转发到log4net,同时通过log4net.Internal启用log4net的内部调试。在app.config中调试= 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,如果由此记录任何内容,您将最终死锁 - 例如appender错误。在TraceListener的实现中,我确保在实现的构造函数中设置log4net.Util.LogLog.EmitInternalMessages = false,并且可能处理LogLog.LogReceived事件并以另一种方式记录这些消息。令人遗憾的是,由于此设置,将不会记录某些appender错误。 log4net源是你的朋友。

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