我正在尝试记录来自IKVM.OpenJDK.Jdbc程序集的异常.发生的一种异常是java.sql.SQLException(和子类). log4Net似乎在此之上绊倒了,因为java.sql.SQLException实现了IEnumerable,而该实现仅返回自身.我已经逐步完成了log4net代码,而log4net看起来像这样:
“Here is a SQLException to log. Does it contain any information that I should log? Oh, yes it does. There is an IEnumerable, I had better log the stuff inside the IEnumerable. So what is in that IEnumerable? Oh, look it is a SQLException to log. Does it contain any that I should log? Oh, yes it does. There is an IEnumerable, I had better log the stuff inside the IEnumerable. So what is in that IEnumerable? Oh, look it is a SQLException…”
最终,我们得到一个*Exception.
有人曾经解决过这个问题吗?
最少可验证的完整示例:
static void Main(string[] args)
{
java.sql.SQLException ex = new java.sql.SQLException();
log4net.Config.BasicConfigurator.Configure();
log4net.ILog logger = log4net.LogManager.GetLogger("foo");
logger.Error("This exception overflows the stack -> ", ex); // Exception here
Console.WriteLine("Finished. Press any key...");
Console.ReadKey();
}
我曾经使用正确的NuGets的packages.config进行编译(NuGet源= https://www.nuget.org/api/v2/):
<packages>
<package id="log4net" version="2.0.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Jdbc" version="7.2.4630.5" targetFramework="net452" />
<!-- The packages below are dependencies from manually getting the packages above. -->
<package id="IKVM.OpenJDK.Charsets" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Core" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Misc" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Naming" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Remoting" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Security" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.SwingAWT" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Text" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.Util" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.OpenJDK.XML.API" version="7.2.4630.5" targetFramework="net452" />
<package id="IKVM.Runtime" version="7.2.4630.5" targetFramework="net452" />
</packages>
解决方法:
拥有实现IEnumerable的异常可以说是设计错误-在.NET中,这是通过Exception.InnerException(对于一般情况)以及SqlException.Errors(对于数据库错误)来实现的.但是,提交错误报告可能对您没有好处,因为IKVM的目标是在.NET中实现Java,因此它将希望紧贴其根源.
使用log4net提交错误报告的机会更大,但成功的机会不多,因为以完全通用的方式处理错误报告将需要对可枚举对象进行循环检测,这是一个巨大的痛苦.
幸运的是,有一个更简单的解决方法. log4net是高度可定制的,并且通过使用自定义对象渲染器,我们可以随心所欲地处理这些异常.由于可枚举异常的奇异性似乎仅限于IKVM,因此我们可以摆脱使其仅适用于Throwable的情况:
class ThrowableRenderer : log4net.ObjectRenderer.IObjectRenderer {
private readonly IObjectRenderer fallback;
public ThrowableRenderer(RendererMap rendererMap) {
this.fallback = rendererMap.Get(typeof(Throwable));
}
public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer) {
var filteredEnumerable = (obj as IEnumerable)?.Cast<object>().Skip(1);
if (filteredEnumerable != null) {
rendererMap.FindAndRender(obj.ToString(), writer);
if (filteredEnumerable.Any()) {
writer.WriteLine();
rendererMap.FindAndRender(filteredEnumerable, writer);
}
} else {
fallback.RenderObject(rendererMap, obj, writer);
}
}
}
然后像这样配置自定义渲染器:
var rendererMap = log4net.LogManager.GetRepository().RendererMap;
rendererMap.Put(typeof(Throwable), new ThrowableRenderer(rendererMap));
在以上实现中,我自定义了Throwables的呈现方式,因为首先将Throwable本身呈现为平面对象,然后呈现链中的任何嵌套异常,但不是第一个异常本身.您可能希望进一步自定义它,因为与标准Exception不同,Throwable.ToString()似乎不包括堆栈跟踪(虽然您可以让log4net将堆栈跟踪添加到任何错误消息中,但是检索此堆栈跟踪的速度要慢得多),但这展示了通过避开默认渲染来避免堆栈溢出的基本思想.