使用Log4Net记录Exception.Data

Pau*_*eld 17 log4net exception

我们刚刚开始使用Log4Net(并且希望我们之前完成它).虽然我们可以看到内部异常等,但是在记录异常时输出中似乎缺少的一件事是"Exception.Data"中保存的任何键/值信息.无论如何我们可以"开箱即用"吗?如果没有,因为我们真的只是刚刚开始寻找实现此功能的方法?

作为示例,请参阅下面的基本伪代码.我们不想用上下文信息污染异常消息只是问题所在(我们可能已经丢失了更多有助于调查实际问题的数据信息).但是现在我们在日志中看到的只是异常类型,消息,任何堆栈跟踪 - 但没有异常"数据".这意味着在我们的日志中我们丢失了客户ID等.我们如何轻松地将这些信息记录到我们的日志中(无需在每个异常捕获中手动编写代码).

try
{
   var ex = new ApplicationException("Unable to update customer");
   ex.Data.Add("id", customer.Id);
   throw ex;
}
catch(ApplicationException ex)
{
   logger.Error("An error occurred whilst doing something", ex);
   throw;
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*n K 20

继Stefan的领导之后:

namespace YourNamespace {
    public sealed class ExceptionDataPatternConverter : PatternLayoutConverter {

        protected override void Convert(TextWriter writer, LoggingEvent loggingEvent) {
            var data = loggingEvent.ExceptionObject.Data;
            if (data != null) {
                foreach(var key in data.Keys) {
                    writer.Write("Data[{0}]={1}" + Environment.NewLine, key, data[key]);
                }
            }

        }
    }   
}
Run Code Online (Sandbox Code Playgroud)

在您的配置中添加%ex_data和转换器:

<appender ...>
  ...
  <layout type="log4net.Layout.PatternLayout,log4net">
    <conversionPattern value="%date %d{HH:mm:ss.fff} [%t] %-5p %c %l - %m%n %ex_data"/>
    <converter>
      <name value="ex_data" />
      <type value="YourNamespace.ExceptionDataPatternConverter" />
    </converter>
  </layout>
Run Code Online (Sandbox Code Playgroud)


Ste*_*gli 6

我认为更接近这个问题的log4net方法就是写一个PatternLayoutConverter.这里可以找到一个例子.

在转换方法中,您可以像这样访问您的数据(并按您喜欢的方式编写):

override protected void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
   var data = loggingEvent.ExceptionObject.Data;
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*ger 6

如果定义了多个appender,则可以使用自定义渲染器,而不是为每个布局定义转换器.

网络/的app.config

<log4net>
    ...
    <renderer renderingClass="YourNamespace.ExceptionObjectLogger, YourAssembly" renderedClass="System.Exception" />
    ...
</log4net>
Run Code Online (Sandbox Code Playgroud)

ExceptionObjectLogger

public class ExceptionObjectLogger : IObjectRenderer
{
    public void RenderObject(RendererMap rendererMap, object obj, TextWriter writer)
    {
        var ex = obj as Exception;

        if (ex == null)
        {
            // Shouldn't happen if only configured for the System.Exception type.
            rendererMap.DefaultRenderer.RenderObject(rendererMap, obj, writer);
        }
        else
        {
            rendererMap.DefaultRenderer.RenderObject(rendererMap, obj, writer);

            const int MAX_DEPTH = 10;
            int currentDepth = 0;

            while (ex != null && currentDepth <= MAX_DEPTH)
            {
                this.RenderExceptionData(rendererMap, ex, writer, currentDepth);
                ex = ex.InnerException;

                currentDepth++;
            }
        }
    }

    private void RenderExceptionData(RendererMap rendererMap, Exception ex, TextWriter writer, int depthLevel)
    {
        var dataCount = ex.Data.Count;
        if (dataCount == 0)
        {
            return;
        }

        writer.WriteLine();

        writer.WriteLine($"Exception data on level {depthLevel} ({dataCount} items):");

        var currentElement = 0;
        foreach (DictionaryEntry entry in ex.Data)
        {
            currentElement++;

            writer.Write("[");
            ExceptionObjectLogger.RenderValue(rendererMap, writer, entry.Key);
            writer.Write("]: ");

            ExceptionObjectLogger.RenderValue(rendererMap, writer, entry.Value);

            if (currentElement < dataCount)
            {
                writer.WriteLine();
            }
        }
    }

    private static void RenderValue(RendererMap rendererMap, TextWriter writer, object value)
    {
        if (value is string)
        {
            writer.Write(value);
        }
        else
        {
            IObjectRenderer keyRenderer = rendererMap.Get(value.GetType());
            keyRenderer.RenderObject(rendererMap, value, writer);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我非常喜欢这个想法,但是你的解决方案在渲染内部异常方面存在问题.由于你正在使用`rendererMap.DefaultRenderer.RenderObject(rendererMap,obj,writer);`在循环中,它将使异常堆栈的次数成为堆叠异常的数量.我将建议更改您的实现,以便它能够正常工作. (2认同)