C# 记录java.sql.SQLException会导致溢出

C# 记录java.sql.SQLException会导致溢出,c#,jdbc,log4net,ikvm,C#,Jdbc,Log4net,Ikvm,我试图记录来自IKVM.OpenJDK.Jdbc程序集的异常。发生的一个异常是java.sql.SQLException(和子类)。log4Net似乎在这个问题上出错了,因为java.sql.SQLException实现了IEnumerable,而它的实现只是返回它自己。我已经详细介绍了log4net代码,它看起来像log4net所说的: “这里有一个要记录的SQLException。它是否包含我应该记录的任何信息?哦,是的。有一个IEnumerable,我最好把东西记录在IEnumerable

我试图记录来自IKVM.OpenJDK.Jdbc程序集的异常。发生的一个异常是
java.sql.SQLException
(和子类)。log4Net似乎在这个问题上出错了,因为
java.sql.SQLException
实现了
IEnumerable
,而它的实现只是返回它自己。我已经详细介绍了log4net代码,它看起来像log4net所说的:

“这里有一个要记录的SQLException。它是否包含我应该记录的任何信息?哦,是的。有一个IEnumerable,我最好把东西记录在IEnumerable里面。那么,在这个IEnumerable中是什么呢?哦,看,这是一个SQLException日志。它是否包含任何我应该记录的内容?哦,是的。有一个IEnumerable,我最好把东西记录在IEnumerable里面。那么,在这个IEnumerable中是什么呢?哦,看,这是个例外……”

最终我们得到了一个
堆栈溢出异常

以前有人解决过这个问题吗

最小可验证完整示例:

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();
}
我使用packages.config获取正确的NuGet以使其编译(NuGet source=):



有一个异常实现
IEnumerable
可以说是一个设计错误——在.NET中,对于一般情况,这是通过
exception.InnerException
实现的,对于数据库错误,尤其是
SqlException.Errors
实现的。然而,提交错误报告可能对您没有好处,因为IKVM的目标是在.NET中使用Java,因此它希望紧跟其根

使用log4net提交bug报告的成功几率稍高一些,但不会太高,因为以完全通用的方式处理这个问题需要在可枚举中进行周期检测,这是一个巨大的难题

幸运的是,有一个更简单的解决方法;log4net是高度可定制的,通过使用自定义对象渲染器,我们可以随心所欲地处理这些异常。由于可枚举异常的奇怪之处似乎仅限于IKVM,因此我们可能不必将其特定于可丢弃的

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);
    }
  }
}
在上面的实现中,我定制了
Throwable
s的呈现,首先将
Throwable
本身呈现为平面对象,然后是链中的任何嵌套异常,但不是第一个异常本身。您可能希望进一步定制它,因为与标准
exception
不同,
Throwable.ToString()
似乎不包含堆栈跟踪(虽然您可以让log4net将堆栈跟踪添加到任何错误消息中,但检索此堆栈跟踪的速度要慢得多),但这演示了通过避开默认渲染来避免堆栈溢出的基本思想

var rendererMap = log4net.LogManager.GetRepository().RendererMap;
rendererMap.Put(typeof(Throwable), new ThrowableRenderer(rendererMap));