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

时间:2016-06-02 15:57:37

标签: c# jdbc log4net ikvm

我正在尝试记录来自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使用正确的NuGets进行编译(NuGet source = 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 个答案:

答案 0 :(得分: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将堆栈跟踪添加到任何错误消息,检索此堆栈跟踪速度要慢得多,但这证明了通过回避默认渲染来避免堆栈溢出的基本思路。