Jaki jest właściwy sposób wyświetlania pełnego wyjątku InnerException?


155

Jaki jest właściwy sposób, aby pokazać moje pełne InnerException.

Okazało się, że niektóre z moich wyjątków InnerExceptions mają inny, InnerExceptionktóry jest dość głęboki.

Czy InnerException.ToString()wykona pracę za mnie, czy muszę przejść przez pętlę InnerExceptionsi zbudować Stringz StringBuilder?


Dlaczego musisz pokazać wewnętrzny wyjątek?
Akram Shahda

26
@Akram, ponieważ przez większość czasu interesujący jest wewnętrzny wyjątek. Jednym z przykładów jest XmlSerializer, który po prostu zgłasza InvalidOperationException, gdy coś pójdzie nie tak. To, co poszło nie tak, dotyczy wewnętrznego wyjątku.
adrianm

4
@AkramShahda A może chcesz użyć tej metody w swoim logowaniu?
cederlof

Odpowiedzi:


239

Możesz po prostu wydrukować exception.ToString()- będzie to również zawierać pełny tekst dla wszystkich zagnieżdżonych plików InnerException.


18
Obejmuje to
również mnóstwo

tylko dla zwięzłości w rzeczywistości nie potrzebujesz .ToString (), samo użycie wyjątku zrobi to samo.
Alex Stephens

3
@AlexStephens masz rację, ale tylko wtedy, gdy z jakiegoś powodu masz niejawne rzutowanie „na łańcuch”, na przykład poprzedzający ciąg: „bla” + wyjątek
oo_dev.

1
FYI: nie wywoła niestandardowych ToStringmetod dla wyjątków wewnętrznych, jak opisano szczegółowo w Dlaczego System.Exception.ToString nie wywołuje wirtualnego ToString dla wyjątków wewnętrznych? .
Jeff B

45

Po prostu użyj exception.ToString()

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

Domyślna implementacja ToString uzyskuje nazwę klasy, która zgłosiła bieżący wyjątek, komunikat, wynik wywołania ToString w przypadku wyjątku wewnętrznego oraz wynik wywołania Environment.StackTrace. Jeśli którykolwiek z tych elementów członkowskich ma wartość null, jego wartość nie jest uwzględniana w zwracanym ciągu.

Jeśli nie ma komunikatu o błędzie lub jeśli jest to pusty ciąg („”), nie jest zwracany żaden komunikat o błędzie. Nazwa wewnętrznego wyjątku i ślad stosu są zwracane tylko wtedy, gdy nie są puste.

wyjątek.ToString () wywoła również funkcję .ToString () na wyjątku wewnętrznym tego wyjątku i tak dalej ...


45

Zwykle robię to, aby usunąć większość szumów:

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

    Console.WriteLine(realerror.ToString())
}    

Edycja: zapomniałem o tej odpowiedzi i jestem zaskoczony, że nikt nie wskazał, że możesz to zrobić

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    

Ta metoda ukrywa wszystko z wyjątkiem najgłębszego wewnętrznego wyjątku. Gdyby to było coś przyziemnego, jak błąd „Podziel przez zero”, nie będzie jasne, gdzie to nastąpiło i co do niego doprowadziło. Oczywiście śledzenie pełnego stosu jest zwykle niechlujną przesadą, ale tylko odczytanie wewnętrznego wyjątku jest drugą skrajnością. odpowiedź user3016982 jest znacznie lepsza. Otrzymujesz każdy komunikat o wyjątku na stosie bez nieprzyjemnych śladów.
JamesHoux

1
@JamesHoux Która z nich jest odpowiedzią „user3016982”? Nie mogę go tu znaleźć.
sok z marakui

Użytkownik3016982 to ThomazMoura, patrz: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha

@JamesHoux, wyjątek wewnętrzny ma pełną ścieżkę stosu pokazującą, gdzie wystąpił błąd i co do niego doprowadziło. Nie rozumiem, jakie dodatkowe informacje uzyskasz z usuniętych śladów stosu. Komunikaty o wyjątkach to kolejna rzecz i może być przydatne zebranie ich wszystkich.
adrianm

2
Dlaczego po prostu nie użyjesz error.GetBaseException(). Myślę, że to samo ...
Robba

37

Odpowiedź @ Jona jest najlepszym rozwiązaniem, jeśli chcesz uzyskać pełne szczegóły (wszystkie wiadomości i ślad stosu) i zalecane.

Mogą się jednak zdarzyć sytuacje, w których potrzebujesz tylko komunikatów wewnętrznych, w takich przypadkach używam następującej metody rozszerzenia:

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

Często używam tej metody, gdy mam różnych słuchaczy do śledzenia i rejestrowania i chcę mieć różne poglądy na ich temat. W ten sposób mogę mieć jednego odbiornika, który wysyła cały błąd ze śladem stosu pocztą e-mail do zespołu programistów w celu debugowania przy użyciu .ToString()metody, oraz jednego, który zapisuje plik dziennika z historią wszystkich błędów, które zdarzały się każdego dnia bez śladu stosu z .GetFullMessage()metodą.


7
Do Twojej wiadomości Jeśli exjest a AggregateException, żaden z wewnętrznych wyjątków nie zostanie uwzględniony w tym wyniku
kornman00

3
Powinna to być standardowa metoda .NET. Każdy powinien tego używać.
JamesHoux

9

Aby całkiem wydrukować tylko Messageczęść głębokich wyjątków, możesz zrobić coś takiego:

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

To rekursywnie przechodzi przez wszystkie wewnętrzne wyjątki (w tym przypadek AggregateExceptions), aby wypisać wszystkie Messagezawarte w nich właściwości, rozdzielone podziałem wiersza.

Na przykład

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());

Wystąpiła zewnętrzna aggr ex.
Wewnętrzny aggr ex.
Numer ma nieprawidłowy format.
Nieautoryzowany dostęp do plików.
Nie administrator.


Aby uzyskać więcej informacji, należy posłuchać innych właściwości wyjątku . Na przykład Databędzie miał jakieś informacje. Mógłbyś:

foreach (DictionaryEntry kvp in exception.Data)

Aby uzyskać wszystkie właściwości pochodne (nie w Exceptionklasie bazowej ), możesz zrobić:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));

+1, to jest prawie dokładnie to samo co ja. Zastanów się nad poszukaniem właściwości implementującej IEnumerable<Exception>zamiast twardego kodu AggregrateExceptiondo obsługi innych podobnych typów. Wykluczaj p.IsSpecialNamei pi.GetIndexParameters().Length != 0unikaj problemów.
Dołączenie

@adrianm dobra uwaga na temat sprawdzania informacji o nieruchomościach. Jeśli chodzi o sprawdzanie kolekcji wyjątków, chodzi o to, gdzie chcesz narysować linię. Jasne, że to też da się zrobić ...
nawfal

4

Ja robię:

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

To „ładnie wypisuje” wszystkie wewnętrzne wyjątki, a także obsługuje AggregateExceptions i przypadki, w których InnerException.Message jest takie samo jak Message


3

Jeśli chcesz uzyskać informacje o wszystkich wyjątkach, użyj exception.ToString(). Będzie zbierać dane ze wszystkich wyjątków wewnętrznych.

Jeśli chcesz tylko oryginalny wyjątek, użyj exception.GetBaseException().ToString(). W ten sposób otrzymasz pierwszy wyjątek, np. Najgłębszy wyjątek wewnętrzny lub bieżący wyjątek, jeśli nie ma wyjątku wewnętrznego.

Przykład:

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

narastanie odpowiedzi nawfala.

używając jego odpowiedzi, brakowało zmiennej aggrEx, dodałem ją.

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

Miałem wyjątek, ponieważ:e.StackTrace == null
Andrei Krasutski

1
Zaktualizowałem .Select (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); może to pomoże
Shimon Doodkin
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.