Ведение журнала Exception.Data с использованием Log4Net - PullRequest
17 голосов
/ 04 августа 2011

Мы только начинаем работать с Log4Net (и хотим, чтобы мы сделали это раньше).Хотя мы можем видеть внутренние исключения и т. Д., Единственное, что, по-видимому, отсутствует в выходных данных при регистрации исключения, - это любая информация о ключе / значении, содержащаяся в «Exception.Data».Есть ли в любом случае мы можем сделать это "из коробки"?Если нет, то, как мы на самом деле только начинаем, где следует искать способ реализовать эту функцию?

В качестве примера, пожалуйста, посмотрите самый основной псевдокод ниже.Мы не хотим загрязнять сообщение об исключении контекстной информацией только о том, в чем заключалась проблема (мы, вероятно, потеряли бы больше информации в данных, что помогло бы исследовать реальную проблему).Но сейчас все, что мы видим в наших журналах, это тип исключения, сообщение, любая трассировка стека, но не исключение «данные».Это означает, что в наших журналах мы теряем идентификатор клиента и т. Д. Как мы можем легко получить эту информацию в наших журналах (без необходимости кодировать ее вручную при каждом исключении).

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;
}

Ответы [ 5 ]

19 голосов
/ 17 октября 2011

Следуя примеру Стефана:

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]);
                }
            }

        }
    }   
}

И в вашей конфигурации добавьте% 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>

6 голосов
/ 19 февраля 2015

Если у вас определено несколько приложений, вы можете использовать пользовательский рендер, а не определять конвертер для каждого макета.

Web / app.config

<log4net>
    ...
    <renderer renderingClass="YourNamespace.ExceptionObjectLogger, YourAssembly" renderedClass="System.Exception" />
    ...
</log4net>

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);
        }
    }
}
5 голосов
/ 05 августа 2011

Я думаю, что более логичный способ решения этой проблемы - написать PatternLayoutConverter. Пример можно найти здесь .

В методе конвертации вы можете получить доступ к своим данным следующим образом (и записать их так, как вам нравится):

override protected void Convert(TextWriter writer, LoggingEvent loggingEvent)
{
   var data = loggingEvent.ExceptionObject.Data;
}
0 голосов
/ 04 августа 2011

Я думаю, у Массимилиано правильная идея, но я бы немного изменил его решение.

Если вы планируете поместить все свои дополнительные данные в словарь Data в исключение, я бы изменил его метод расширения на следующий:

public static class ExLog4Net
{
    public static void Error(this ILog log, Exception ex)
    {
        StringBuilder formattedError = new StringBuilder();
        formattedError.AppendFormat("Exception: {0}\r\n", ex.ToString());

        foreach (DictionaryEntry de in ex.Data)
            formattedError.AppendFormat("{0}: {1}\r\n", de.Key, de.Value);

        log.Error(formattedError.ToString());
    }
 }

Затем вы добавите это расширение метода в библиотеку, которую будете использовать во всех своих приложениях. Если у вас его нет, вам придется добавить это в каждый проект.

0 голосов
/ 04 августа 2011

Вы можете создать метод расширения для своего регистратора для регистрации идентификатора клиента: вам не следует добавлять важную информацию к исключению

Вы можете абстрагировать понятие «Дополнительная информация для регистрации» и создать интерфейс с методом, который возвращает дополнительную информацию, которую вы хотите зарегистрировать

 public interface IDataLogger
    {
        string GetAdditionalInfo();
    }

    public class UserDataLogger: IDataLogger
    {
        public string GetAdditionalInfo()
        {
            return "UserName";
        }
    }

    public class MoreDataLogger : IDataLogger
    {
        public string GetAdditionalInfo()
        {
            return "something";
        }
    }

Вы можете создать разные «Регистраторы данных» и, возможно, объединить их вместе

тогда вы можете создать универсальный метод расширения, который получит тип регистратора

public static class ExLog4Net
    {
        public static void Error<T>(this ILog log, Exception ex) where T:IDataLogger,new()
        {
            var dataLogger=new T();
            log.Error(ex.ToString() + " " + dataLogger.GetAdditionalInfo());
        }

    }

Вы сможете сделать следующее:

      try
        {

        }
        catch (Exception ex)
        {
            logger.Error<UserDataLogger>(ex);
            logger.Error<MoreDataLogger>(ex);
            throw;
        }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...