Как правильно отобразить полное InnerException?

155

Какой правильный способ показать мое полное InnerException.

Я обнаружил, что некоторые из моих InnerExceptions имеют другое, InnerExceptionи это происходит довольно глубоко.

Будет InnerException.ToString()ли работа для меня или мне нужно пройти через InnerExceptionsи создать Stringс StringBuilder?

Willem
источник
Зачем вам нужно показывать внутреннее исключение ??
Акрам Шахда
26
@Akram, потому что большую часть времени это интересное внутреннее исключение. Одним из примеров является XmlSerializer, который просто генерирует исключение InvalidOperationException, когда что-то идет не так. Что пошло не так во внутреннем исключении.
Адриан
4
@AkramShahda Ну, может быть, вы хотите использовать этот метод в своем журнале?
cederlof
Связанный пост - Exception.Message против Exception.ToString ()
RBT

Ответы:

239

Вы можете просто напечатать exception.ToString()- это также будет включать в себя полный текст для всех вложенных InnerExceptionэлементов.

Джон
источник
18
Это включает в себя множество других дерьма, а не только сообщение об исключении и внутренние сообщения об исключении
ʙᴀᴋᴇʀ ʙᴀᴋᴇʀ
просто для краткости вам на самом деле не нужен .ToString (), просто использование исключения сделает то же самое.
Алекс Стивенс
3
@AlexStephens вы правы, но только если у вас есть неявное приведение «к строке» по какой-то причине, как предыдущая строка: «bla» + исключение
oo_dev
1
К вашему сведению: он не будет вызывать пользовательские ToStringметоды для внутренних исключений, как описано в разделе Почему System.Exception.ToString не вызывает виртуальный ToString для внутренних исключений? ,
Джефф Б
45

Просто используйте exception.ToString()

http://msdn.microsoft.com/en-us/library/system.exception.tostring.aspx

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

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

exception.ToString () также вызовет .ToString () для внутреннего исключения этого исключения и так далее ...

Роб П.
источник
45

Я обычно делаю так, чтобы удалить большую часть шума:

void LogException(Exception error) {
    Exception realerror = error;
    while (realerror.InnerException != null)
        realerror = realerror.InnerException;

    Console.WriteLine(realerror.ToString())
}    

Редактировать: я забыл об этом ответе и удивлен, что никто не указал, что вы можете просто сделать

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    
adrianm
источник
Этот метод скрывает все, кроме самого глубокого внутреннего исключения. Если бы это было чем-то обыденным, как ошибка «Разделить на ноль», было бы неясно, где это произошло и что к этому привело. Очевидно, что трассировка с полным стеком обычно является беспорядочным излишним, но другой крайностью является только чтение внутреннего исключения. Ответ user3016982 гораздо лучше. Вы получаете каждое сообщение об исключении в стеке без неприятного следа.
JamesHoux
1
@JamesHoux Какой ответ "user3016982"? Не могу найти его здесь.
сок маракуйи
Пользователь3016982 - это ThomazMoura, см .: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha
@JamesHoux, внутреннее исключение имеет полную трассировку стека, показывающую, где произошла ошибка и что привело к ней. Не понимаю, какую дополнительную информацию вы получите от удаленных следов стека. Сообщения об исключениях - другое дело, и может быть полезно собрать их все.
Адриан
2
Почему бы тебе просто не использовать error.GetBaseException(). Я считаю, что это делает то же самое ...
Робба
37

@ Ответ Джона - лучшее решение, когда вам нужна полная детализация (все сообщения и трассировка стека) и рекомендуемое.

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

public static class ExceptionExtensions
{
    public static string GetFullMessage(this Exception ex)
    {
        return ex.InnerException == null 
             ? ex.Message 
             : ex.Message + " --> " + ex.InnerException.GetFullMessage();
    }
}

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

ThomazMoura
источник
7
К вашему сведению, если exесть AggregateException, ни одно из внутренних исключений не будет включено в этот вывод
kornman00
3
Это должен быть стандартный метод .NET. Каждый должен использовать это.
JamesHoux
9

Чтобы распечатать только Messageчасть глубоких исключений, вы можете сделать что-то вроде этого:

public static string ToFormattedString(this Exception exception)
{
    IEnumerable<string> messages = exception
        .GetAllExceptions()
        .Where(e => !String.IsNullOrWhiteSpace(e.Message))
        .Select(e => e.Message.Trim());
    string flattened = String.Join(Environment.NewLine, messages); // <-- the separator here
    return flattened;
}

public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
{
    yield return exception;

    if (exception is AggregateException aggrEx)
    {
        foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
        {
            yield return innerEx;
        }
    }
    else if (exception.InnerException != null)
    {
        foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
        {
            yield return innerEx;
        }
    }
}

Это рекурсивно проходит через все внутренние исключения (включая случай AggregateException s), чтобы напечатать все Messageсодержащиеся в них свойства, разделенные переводом строки.

Например

var outerAggrEx = new AggregateException(
    "Outer aggr ex occurred.",
    new AggregateException("Inner aggr ex.", new FormatException("Number isn't in correct format.")),
    new IOException("Unauthorized file access.", new SecurityException("Not administrator.")));
Console.WriteLine(outerAggrEx.ToFormattedString());

Произошло внешнее скопление.
Внутренний агр. Отл.
Номер не в правильном формате.
Несанкционированный доступ к файлам.
Не администратор.


Вам нужно будет прослушать другие свойства исключения для получения более подробной информации. Например, Dataбудет иметь некоторую информацию. Вы могли бы сделать:

foreach (DictionaryEntry kvp in exception.Data)

Чтобы получить все производные свойства (не для базового Exceptionкласса), вы можете сделать:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
Навфал
источник
+1, это почти то же самое, что и я. Подумайте о поиске свойства, реализующего IEnumerable<Exception>вместо жесткого кодирования AggregrateExceptionдля обработки других подобных типов. Также исключить p.IsSpecialNameи pi.GetIndexParameters().Length != 0избежать проблем. Включение имени типа исключения в выходные данные также является хорошей идеей
Адриан
@adrianm хорошая точка зрения о проверке информации о недвижимости. Что касается проверки на наличие исключений, все зависит от того, где вы хотите нарисовать линию. Конечно, это тоже можно сделать ..
nawfal
4

Я делаю:

namespace System {
  public static class ExtensionMethods {
    public static string FullMessage(this Exception ex) {
      if (ex is AggregateException aex) return aex.InnerExceptions.Aggregate("[ ", (total, next) => $"{total}[{next.FullMessage()}] ") + "]";
      var msg = ex.Message.Replace(", see inner exception.", "").Trim();
      var innerMsg = ex.InnerException?.FullMessage();
      if (innerMsg is object && innerMsg!=msg) msg = $"{msg} [ {innerMsg} ]";
      return msg;
    }
  }
}

Это «печатает» все внутренние исключения, а также обрабатывает AggregateExceptions и случаи, когда InnerException.Message совпадает с Message

kofifus
источник
3

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

Если вы хотите только оригинальное исключение, используйте exception.GetBaseException().ToString(). Это даст вам первое исключение, например, самое глубокое внутреннее исключение или текущее исключение, если нет внутреннего исключения.

Пример:

try {
    Exception ex1 = new Exception( "Original" );
    Exception ex2 = new Exception( "Second", ex1 );
    Exception ex3 = new Exception( "Third", ex2 );
    throw ex3;
} catch( Exception ex ) {
    // ex => ex3
    Exception baseEx = ex.GetBaseException(); // => ex1
}
dkostas
источник
2

наращивание по ответу nawfal.

при использовании его ответа отсутствовала переменная aggrEx, я ее добавил.

файл ExceptionExtenstions.class:

// example usage:
// try{ ... } catch(Exception e) { MessageBox.Show(e.ToFormattedString()); }

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace YourNamespace
{
    public static class ExceptionExtensions
    {

        public static IEnumerable<Exception> GetAllExceptions(this Exception exception)
        {
            yield return exception;

            if (exception is AggregateException )
            {
                var aggrEx = exception as AggregateException;
                foreach (Exception innerEx in aggrEx.InnerExceptions.SelectMany(e => e.GetAllExceptions()))
                {
                    yield return innerEx;
                }
            }
            else if (exception.InnerException != null)
            {
                foreach (Exception innerEx in exception.InnerException.GetAllExceptions())
                {
                    yield return innerEx;
                }
            }
        }


        public static string ToFormattedString(this Exception exception)
        {
            IEnumerable<string> messages = exception
                .GetAllExceptions()
                .Where(e => !String.IsNullOrWhiteSpace(e.Message))
                .Select(exceptionPart => exceptionPart.Message.Trim() + "\r\n" + (exceptionPart.StackTrace!=null? exceptionPart.StackTrace.Trim():"") );
            string flattened = String.Join("\r\n\r\n", messages); // <-- the separator here
            return flattened;
        }
    }
}
Шимон Дудкин
источник
У меня было исключение, потому что:e.StackTrace == null
Андрей Красуцкий
1
Я обновил .Select (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); может быть, это поможет
Шимон Дудкин