记录java.sql.SQLException导致溢出。

3
我正在尝试记录来自IKVM.OpenJDK.Jdbc程序集的异常。其中一个发生的异常是java.sql.SQLException(和子类)。log4Net似乎会因为java.sql.SQLException实现了IEnumerable而出现问题,而其实现只返回它本身。我已经逐步检查了这个的log4net代码,看起来像是log4net说:

"这里有一个需要记录的SQLException。它包含任何应该记录的信息吗?哦,是的,它有。有一个IEnumerable,我最好记录IEnumerable内部的内容。那么在IEnumerable里面有什么?噢,它是一个需要记录的SQLException。它包含任何需要记录的信息吗?哦,是的,它有。有一个IEnumerable,我最好记录IEnumerable内部的内容。那么在IEnumerable里面有什么?噢,它是一个SQLException..."

最终我们得到了一个StackOverflowException

之前有人遇到并解决过这个问题吗?

最小可验证完整示例:

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来源= 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>
1个回答

3
拥有实现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));

在上述实现中,我已经定制了Throwable的渲染方式为首先将Throwable本身渲染为一个平坦的对象,然后再渲染任何嵌套的异常链,但不包括第一个异常本身。您可能希望进一步自定义此内容,因为与标准的Exception不同,Throwable.ToString()似乎不包括堆栈跟踪(虽然您可以让log4net将堆栈跟踪添加到任何错误消息中,但检索此堆栈跟踪会更慢),但这演示了通过规避默认渲染来避免堆栈溢出的基本思想。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接