Подтвердить что ты не робот

Exception.Message vs Exception.ToString()

У меня есть код, регистрирующий Exception.Message. Однако я прочитал статью, в которой говорится, что лучше использовать Exception.ToString(). С последним вы сохраняете более важную информацию об ошибке.

Является ли это правдой, и безопасно ли это сделать и заменить все записи журнала Exception.Message?

Я также использую макет на основе XML для log4net. Возможно ли, что Exception.ToString() может содержать недопустимые символы XML, что может вызвать проблемы?

4b9b3361

Ответ 1

Exception.Message содержит только сообщение (doh), связанное с исключением. Пример:

Ссылка на объект не установлена ​​в экземпляр объекта

Метод Exception.ToString() даст гораздо более подробный вывод, содержащий тип исключения, сообщение (из ранее), трассировку стека, и все это снова для вложенных/внутренних исключений. Точнее, метод возвращает следующее:

ToString возвращает представление текущего исключения, предназначенного для понимания людьми. Если исключение содержит чувствительные к культуре данные, представление строки, возвращаемое ToString, должно учитывать текущую культуру системы. Хотя нет точных требований к формату возвращаемой строки, он должен попытаться отразить значение объекта, воспринимаемого пользователем.

Реализация по умолчанию для ToString по умолчанию получает имя класса, которое выбрасывает текущее исключение, сообщение, результат вызова ToString во внутреннем исключении и результат вызова среды .StackTrace. Если какой-либо из этих элементов является пустой ссылкой (Nothing в Visual Basic), его значение не включено в возвращаемую строку.

Если сообщение об ошибке отсутствует или пустая строка (""), то сообщение об ошибке не возвращается. Имя внутреннего исключения и трассировка стека возвращаются только в том случае, если они не являются нулевой ссылкой (Nothing в Visual Basic).

Ответ 2

В дополнение к уже сказанному, не используйте ToString() для объекта исключения для отображения пользователю. Достаточно только свойство Message или пользовательское сообщение более высокого уровня.

В терминах ведения журнала определенно используйте ToString() в Exception, а не только свойство Message, как и в большинстве сценариев, вы останетесь царапаете голову, где конкретно это исключение произошло, и что стек вызовов был, Стек-стопка сказала бы вам все это.

Ответ 3

Преобразование целого исключения в строку

Как указывает @JornSchouRode, выполнение exception.ToString() дает вам больше информации, чем просто использование свойства exception.Message. Однако даже это все еще оставляет много информации, в том числе:

  1. Свойство коллекции Data найдено во всех исключениях.
  2. Любые другие пользовательские свойства, добавленные в исключение.

Есть моменты, когда вы хотите получить эту дополнительную информацию. Код ниже обрабатывает вышеуказанные сценарии. Он также записывает свойства исключений в хорошем порядке. Он использует С# 7, но при необходимости его будет очень легко конвертировать в более старые версии. См. также этот связанный ответ.

public static class ExceptionExtensions
{
    public static string ToDetailedString(this Exception exception) =>
        ToDetailedString(exception, ExceptionOptions.Default);

    public static string ToDetailedString(this Exception exception, ExceptionOptions options)
    {
        if (exception == null)
        {
            throw new ArgumentNullException(nameof(exception));
        } 

        var stringBuilder = new StringBuilder();

        AppendValue(stringBuilder, "Type", exception.GetType().FullName, options);

        foreach (PropertyInfo property in exception
            .GetType()
            .GetProperties()
            .OrderByDescending(x => string.Equals(x.Name, nameof(exception.Message), StringComparison.Ordinal))
            .ThenByDescending(x => string.Equals(x.Name, nameof(exception.Source), StringComparison.Ordinal))
            .ThenBy(x => string.Equals(x.Name, nameof(exception.InnerException), StringComparison.Ordinal))
            .ThenBy(x => string.Equals(x.Name, nameof(AggregateException.InnerExceptions), StringComparison.Ordinal)))
        {
            var value = property.GetValue(exception, null);
            if (value == null && options.OmitNullProperties)
            {
                if (options.OmitNullProperties)
                {
                    continue;
                }
                else
                {
                    value = string.Empty;
                }
            }

            AppendValue(stringBuilder, property.Name, value, options);
        }

        return stringBuilder.ToString().TrimEnd('\r', '\n');
    }

    private static void AppendCollection(
        StringBuilder stringBuilder,
        string propertyName,
        IEnumerable collection,
        ExceptionOptions options)
        {
            stringBuilder.AppendLine($"{options.Indent}{propertyName} =");

            var innerOptions = new ExceptionOptions(options, options.CurrentIndentLevel + 1);

            var i = 0;
            foreach (var item in collection)
            {
                var innerPropertyName = $"[{i}]";

                if (item is Exception)
                {
                    var innerException = (Exception)item;
                    AppendException(
                        stringBuilder,
                        innerPropertyName,
                        innerException,
                        innerOptions);
                }
                else
                {
                    AppendValue(
                        stringBuilder,
                        innerPropertyName,
                        item,
                        innerOptions);
                }

                ++i;
            }
        }

    private static void AppendException(
        StringBuilder stringBuilder,
        string propertyName,
        Exception exception,
        ExceptionOptions options)
    {
        var innerExceptionString = ToDetailedString(
            exception, 
            new ExceptionOptions(options, options.CurrentIndentLevel + 1));

        stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
        stringBuilder.AppendLine(innerExceptionString);
    }

    private static string IndentString(string value, ExceptionOptions options)
    {
        return value.Replace(Environment.NewLine, Environment.NewLine + options.Indent);
    }

    private static void AppendValue(
        StringBuilder stringBuilder,
        string propertyName,
        object value,
        ExceptionOptions options)
    {
        if (value is DictionaryEntry)
        {
            DictionaryEntry dictionaryEntry = (DictionaryEntry)value;
            stringBuilder.AppendLine($"{options.Indent}{propertyName} = {dictionaryEntry.Key} : {dictionaryEntry.Value}");
        }
        else if (value is Exception)
        {
            var innerException = (Exception)value;
            AppendException(
                stringBuilder,
                propertyName,
                innerException,
                options);
        }
        else if (value is IEnumerable && !(value is string))
        {
            var collection = (IEnumerable)value;
            if (collection.GetEnumerator().MoveNext())
            {
                AppendCollection(
                    stringBuilder,
                    propertyName,
                    collection,
                    options);
            }
        }
        else
        {
            stringBuilder.AppendLine($"{options.Indent}{propertyName} = {value}");
        }
    }
}

public struct ExceptionOptions
{
    public static readonly ExceptionOptions Default = new ExceptionOptions()
    {
        CurrentIndentLevel = 0,
        IndentSpaces = 4,
        OmitNullProperties = true
    };

    internal ExceptionOptions(ExceptionOptions options, int currentIndent)
    {
        this.CurrentIndentLevel = currentIndent;
        this.IndentSpaces = options.IndentSpaces;
        this.OmitNullProperties = options.OmitNullProperties;
    }

    internal string Indent { get { return new string(' ', this.IndentSpaces * this.CurrentIndentLevel); } }

    internal int CurrentIndentLevel { get; set; }

    public int IndentSpaces { get; set; }

    public bool OmitNullProperties { get; set; }
}

Главный совет - регистрация исключений

Большинство людей будут использовать этот код для регистрации. Попробуйте использовать Serilog с моим пакетом Serilog.Exceptions NuGet, который также регистрирует все свойства исключения, но в большинстве случаев делает это быстрее и без отражения. Serilog - это очень продвинутый каркас для ведения журналов, который сейчас в моде.

Главный совет - следы стека, читаемые человеком

Вы можете использовать пакет Ben.Demystifier NuGet для получения удобочитаемых трассировок стека для ваших исключений или пакет serilog-enrichers-demystify NuGet, если вы используете Serilog.

Ответ 4

Я бы сказал, что @Wim прав. Вы должны использовать ToString() для логфайлов - при условии, что у вас есть техническая аудитория, и Message, если это вообще возможно, для отображения пользователю. Можно утверждать, что даже это не подходит для пользователя, для каждого типа исключения и наличия там (подумайте об аргументах ArgumentExceptions и т.д.).

Кроме того, в дополнение к StackTrace, ToString() будет включать в себя информацию, которую вы не получите иначе. Например, вывод слияния, если включить, включить сообщения журнала в "сообщения" исключений.

Некоторые типы исключений даже включают дополнительную информацию (например, из пользовательских свойств) в ToString(), но не в сообщении.

Ответ 5

Зависит от необходимой информации. Для отладки трассировки стека и внутреннего исключения полезны:

    string message =
        "Exception type " + ex.GetType() + Environment.NewLine +
        "Exception message: " + ex.Message + Environment.NewLine +
        "Stack trace: " + ex.StackTrace + Environment.NewLine;
    if (ex.InnerException != null)
    {
        message += "---BEGIN InnerException--- " + Environment.NewLine +
                   "Exception type " + ex.InnerException.GetType() + Environment.NewLine +
                   "Exception message: " + ex.InnerException.Message + Environment.NewLine +
                   "Stack trace: " + ex.InnerException.StackTrace + Environment.NewLine +
                   "---END Inner Exception";
    }

Ответ 6

В терминах формата XML для log4net вам не нужно беспокоиться о ex.ToString() для журналов. Просто передайте сам объект исключения, а log4net сделает все, что вам нужно, в вашем предварительно настроенном формате XML. Единственное, с чем я сталкиваюсь, это новое форматирование строк, но это когда я читаю файлы raw. В противном случае анализ XML отлично работает.

Ответ 7

Хорошо, я бы сказал, что это зависит от того, что вы хотите видеть в журналах, не так ли? Если вы довольны тем, что предоставляет ex.Message, используйте это. В противном случае используйте ex.toString() или даже зарегистрируйте трассировку стека.