Exception.Message vs Exception.ToString () - PullRequest
       11

Exception.Message vs Exception.ToString ()

182 голосов
/ 01 февраля 2010

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

Это правда, и безопасно ли идти дальше и заменять все записи кода Exception.Message?

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

Ответы [ 7 ]

245 голосов
/ 01 февраля 2010

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

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

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

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

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

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

48 голосов
/ 01 февраля 2010

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

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

16 голосов
/ 30 ноября 2015

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

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

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

public static class ExceptionExtensions
{
    public static string ToDetailedString(this Exception exception)
    {
        if (exception == null)
        {
            throw new ArgumentNullException(nameof(exception));
        }

        return ToDetailedString(exception, ExceptionOptions.Default);
    }

    public static string ToDetailedString(this Exception exception, ExceptionOptions options)
    {
        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.

9 голосов
/ 01 февраля 2010

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

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

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

8 голосов
/ 01 февраля 2010

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

    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";
    }
3 голосов
/ 01 февраля 2010

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

0 голосов
/ 01 февраля 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...