Який правильний спосіб відобразити повний InnerException?


155

Який правильний спосіб показати мою повноту InnerException.

Я виявив, що деякі з моїх InnerExceptions мають інше, InnerExceptionі це йде досить глибоко.

Буде чи InnerException.ToString()робити цю роботу для мене або мені потрібно , щоб Переберія InnerExceptionsі побудувати вгору Stringз StringBuilder?


Чому потрібно показати внутрішній виняток ??
Акрам Шагда

26
@Akram, тому що більшість часу це цікавий внутрішній виняток. Одним із прикладів є XmlSerializer, який просто кидає InvalidOperationException, коли щось піде не так. Те, що пішло не так, є внутрішнім винятком.
Адріанм

4
@AkramShahda Ну, можливо, ви хочете скористатися цим методом у своєму журналі?
cederlof

Повідомлення, пов’язані з цим - Exception.Message vs Exception.ToString ()
RBT

Відповіді:


239

Ви можете просто надрукувати exception.ToString()- що також міститиме повний текст для всіх вкладених InnerExceptions.


18
Сюди входить також багато інших лайнів, а не лише повідомлення про виключення та повідомлення про внутрішні виключення
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ

тільки для лаконічності вам насправді не потрібен .ToString (), лише використовуючи виняток, буде зроблено те саме.
Алекс Стівенс

3
@AlexStephens ви маєте рацію, але тільки якщо ви з якихось причин неявно перекладаєте "на рядок", як-от попередня рядок: "bla" + виняток
oo_dev

1
FYI: він не називатиме власні ToStringметоди внутрішніх винятків, як детально описано у Чому System.Exception.ToString не викликає віртуальний ToString для внутрішніх винятків? .
Джефф Б

45

Просто використовуйте exception.ToString()

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

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

Якщо немає повідомлення про помилку або якщо це порожній рядок (""), повідомлення про помилку не повертається. Ім’я внутрішнього винятку та слід стека повертаються лише у тому випадку, якщо вони не є нульовими.

виключення.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())
}    

Цей метод приховує все, крім найглибшого внутрішнього винятку. Якби це було щось мирське, як помилка "Розділити нуль", було б незрозуміло, де це сталося і до чого це призвело. Очевидно, що повний слід стека - це зазвичай безладний надмір, але інша крайність є лише читанням внутрішнього винятку. відповідь user3016982 набагато краща. Ви отримуєте кожне повідомлення про виняток у стеку без неприємного сліду.
JamesHoux

1
@JamesHoux Який відповідь "user3016982"? Не можу його знайти тут.
маракуя-сік

User3016982 є 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()метод.


7
FYI Якщо 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;
        }
    }
}

Це рекурсивно проходить через усі внутрішні винятки (включаючи випадки AggregateExceptions), щоб надрукувати всю 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

@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 те саме, що і повідомлення


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
}

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 (): "")); можливо, це допомагає
Шимон Додкін
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.