【问题标题】:log4net's LogManager.GetLogger() throws NullReferenceException when used in custom TraceListenerlog4net 的 LogManager.GetLogger() 在自定义 TraceListener 中使用时抛出 NullReferenceException
【发布时间】:2012-07-18 22:05:39
【问题描述】:

我正在尝试将大型应用程序从使用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 - 一个类库项目,其中包含许多项目通用的代码。
    • Lo​​g4netTraceListener 在这个项目中

RunShipOrder 项目内部,我可以调用LogManager.GetLogger("Log4netTraceListener"),它按预期工作。但是,如果我尝试在 Trace 上调用方法,则会引发异常。我将其追溯到 Log4netTraceListener 类内部对 LogManager.GetLogger("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 代码库中可能引发此异常的任何想法?

【问题讨论】:

  • 你是否配置了 log4net 来告诉它在哪里寻找它的配置文件?这是一个程序集属性。
  • [assembly: log4net.Config.XmlConfigurator(Watch = false)]RunShipOrder 启动器中。配置看起来应该没问题,因为我可以从启动器中调用GetLogger(string)。我尝试将配置程序集属性添加到CodeLibrary,但没有解决任何问题。

标签: c# log4net tracelistener


【解决方案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,否则会冒着与这个优秀站点类似的异常的风险。

【讨论】:

    猜你喜欢
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 2010-11-13
    • 1970-01-01
    • 2018-01-19
    • 1970-01-01
    • 1970-01-01
    • 2017-01-07
    相关资源
    最近更新 更多