Jul*_*ian 17 c# exception nlog
介绍
当用户在NLog的配置中创建错误(如无效的XML)时,我们(NLog)抛出一个NLogConfigurationException.该例外包含描述错误的内容.
但有时如果第一次调用NLog来自静态字段/属性,则会NLogConfigurationException被"吃掉" System.TypeInitializationException.
例
例如,如果用户有此程序:
using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
namespace TypeInitializationExceptionTest
{
class Program
{
//this throws a NLogConfigurationException because of bad config. (like invalid XML)
private static Logger logger = LogManager.GetCurrentClassLogger();
static void Main()
{
Console.WriteLine("Press any key");
Console.ReadLine();
}
}
}
Run Code Online (Sandbox Code Playgroud)
并且配置中存在错误,NLog抛出:
throw new NLogConfigurationException("Exception occurred when loading configuration from " + fileName, exception);
Run Code Online (Sandbox Code Playgroud)
但是用户会看到:
"将异常详细信息复制到剪贴板":
System.TypeInitializationException未处理消息:mscorlib.dll中发生未处理的类型'System.TypeInitializationException'的异常附加信息:'TypeInitializationExceptionTest.Program'的类型初始值设定项引发异常.
所以消息消失了!
问题
TypeInitializationException吗?像一条消息?我们已经发送了一个innerException.Exception以便报告更多信息?笔记
编辑:
请注意我是图书馆维护者,而不是图书馆的用户.我无法更改调用代码!
Han*_*ant 11
我只想指出你在这里遇到的根本问题.您正在调试调试器中的错误,它有一个非常简单的解决方法.使用工具>选项>调试>常规>勾选"使用托管兼容模式"复选框.同时解开Just My Code以获取最丰富的调试报告:
如果勾选了"仅我的代码",则异常报告的信息量较少,但仍可通过单击"查看详细信息"链接轻松钻取.
选项名称不必要地含糊不清.它真正的作用是告诉Visual Studio使用旧版本的调试引擎.任何使用VS2013或VS2015的人都会遇到新引擎的问题,可能是VS2012.此问题的基本原因还没有在NLog中解决过.
虽然这是一个非常好的解决方法,但它并不容易发现.程序员也不会特别喜欢使用旧引擎,旧引擎不支持返回值调试和64位代码的E + C等闪亮的新功能.这是否真的是一个错误,新引擎的疏忽或技术限制很难猜测.这太难看了,所以不要犹豫,将它标记为"bug",我强烈建议你把它带到connect.microsoft.com.当它被修复时,每个人都会领先,我至少忘记了一次这个问题.通过使用Debug> Windows> Exceptions>勾选当时的CLR异常将其向下钻取.
这种非常不幸的行为的解决办法肯定是丑陋的.你必须延迟提出异常,直到程序执行进展得足够远.我不太了解您的代码库,但是延迟解析配置,直到第一个日志记录命令应该处理它.或者存储异常对象并将其抛出到第一个日志命令上,可能更容易.
我看到的原因是因为入口点类的类型初始化失败.由于没有初始化类型,因此Type loader没有任何关于失败类型的报告TypeInitializationException.
但是如果将logger的Static初始化程序更改为其他类,然后在Entry方法中引用该类.你会在TypeInitialization异常上得到InnerException.
static class TestClass
{
public static Logger logger = LogManager.GetCurrentClassLogger();
}
class Program
{
static void Main(string[] args)
{
var logger = TestClass.logger;
Console.WriteLine("Press any key");
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)
现在您将获得InnerException,因为已加载Entry类型以报告TypeInitializationException.
希望你现在能够保持Entry point的清洁,并从Main()而不是Entry point class的static属性引导应用程序.
更新1
您还可以利用它Lazy<>来避免在声明时执行配置初始化.
class Program
{
private static Lazy<Logger> logger = new Lazy<Logger>(() => LogManager.GetCurrentClassLogger());
static void Main(string[] args)
{
//this will throw TypeInitialization with InnerException as a NLogConfigurationException because of bad config. (like invalid XML)
logger.Value.Info("Test");
Console.WriteLine("Press any key");
Console.ReadLine();
}
}
Run Code Online (Sandbox Code Playgroud)
或者,尝试Lazy<>在LogManager中进行记录器实例化,以便在实际发生第一个日志语句时进行配置初始化.
更新2
我分析了NLog的源代码,看起来它已经实现了,这很有道理.根据对属性的评论"除非LogManager.ThrowExceptionsLogManager.cs中的属性指定,否则NLog不应抛出异常".
修复 - 在LogFactory类中,私有方法GetLogger()具有导致异常发生的初始化语句.如果您通过检查属性引入try catch,ThrowExceptions则可以防止初始化异常.
if (cacheKey.ConcreteType != null)
{
try
{
newLogger.Initialize(cacheKey.Name, this.GetConfigurationForLogger(cacheKey.Name, this.Configuration), this);
}
catch (Exception ex)
{
if(ThrowExceptions && ex.MustBeRethrown())
throw;
}
}
Run Code Online (Sandbox Code Playgroud)
将这些异常/错误存储在某处也是很棒的,这样可以追踪Logger初始化失败的原因,因为它们被忽略了ThrowException.