Qual è il modo corretto di visualizzare la InnerException completa?


155

Qual è il modo corretto di mostrare il mio pieno InnerException.

Ho scoperto che alcune delle mie InnerExceptions ne hanno un'altra InnerExceptione che vanno molto in profondità.

Farà InnerException.ToString()il lavoro per me o devo passare in rassegna il InnerExceptionse creare un Stringcon StringBuilder?


Perché è necessario mostrare l'eccezione interna ??
Akram Shahda,

26
@Akram perché il più delle volte è l'eccezione interiore che è interessante. Un esempio è XmlSerializer che genera una InvalidOperationException ogni volta che qualcosa va storto. Ciò che è andato storto è l'eccezione interiore.
Adrianm,

4
@AkramShahda Bene, forse vuoi usare questo metodo nella tua registrazione?
cederlof,

Risposte:


239

Puoi semplicemente stampare exception.ToString()- che includerà anche il testo completo per tutti i messaggi nidificati InnerException.


18
Ciò include anche un sacco di altre schifezze, non solo il messaggio di eccezione e i messaggi di eccezione interni
ʙᴀᴋᴇʀ ʙᴀᴋᴇʀ

solo per sintonia non hai davvero bisogno di .ToString (), solo usare l'eccezione farà lo stesso.
Alex Stephens,

3
@AlexStephens hai ragione, ma solo se hai un cast implicito "stringa" per qualche motivo, come la stringa precedente: "bla" + eccezione
oo_dev

1
Cordiali saluti: non chiamerà un ToStringmetodo personalizzato per le eccezioni interne come dettagliato in Perché System.Exception.ToString non chiama ToString virtuale per le eccezioni interne? .
Jeff B,

45

Basta usare exception.ToString()

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

L'implementazione predefinita di ToString ottiene il nome della classe che ha generato l'eccezione corrente, il messaggio, il risultato della chiamata a ToString sull'eccezione interna e il risultato della chiamata di Environment.StackTrace. Se uno di questi membri è nullo, il suo valore non è incluso nella stringa restituita.

Se non è presente alcun messaggio di errore o se si tratta di una stringa vuota (""), non viene restituito alcun messaggio di errore. Il nome dell'eccezione interna e la traccia dello stack vengono restituiti solo se non sono nulli.

exception.ToString () chiamerà anche .ToString () sull'eccezione interna di quell'eccezione, e così via ...


45

Di solito mi piace in questo modo per rimuovere la maggior parte del rumore:

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

    Console.WriteLine(realerror.ToString())
}    

Modifica: ho dimenticato questa risposta e sono sorpreso che nessuno abbia sottolineato che puoi semplicemente farlo

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

Questo metodo nasconde tutto tranne l'eccezione interiore più profonda. Se fosse qualcosa di banale come un errore "Dividi per zero", non sarebbe chiaro dove si sia verificato e che cosa lo abbia portato. Ovviamente, una traccia dello stack completo di solito è un disordine eccessivo, ma solo la lettura dell'eccezione interna è l'altro estremo. La risposta di user3016982 è di gran lunga migliore. Ricevi tutti i messaggi di eccezione nello stack senza la brutta traccia.
JamesHoux,

1
@JamesHoux Quale è la risposta "user3016982"? Non riesco a trovarlo qui.
succo di maracuja,

L'user3016982 è ThomazMoura, si veda: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha

@JamesHoux, l'eccezione interna ha uno stacktrace completo che mostra dove si è verificato l'errore e cosa lo ha portato. Non capisci quali informazioni extra ottieni dalle tracce dello stack rimosse. I messaggi di eccezione sono un'altra cosa e potrebbe essere utile raccoglierli tutti.
Adrianm,

2
Perché non usi semplicemente error.GetBaseException(). Credo che questo faccia lo stesso ...
Robba,

37

@La risposta di Jon è la soluzione migliore quando vuoi tutti i dettagli (tutti i messaggi e la traccia dello stack) e quella consigliata.

Tuttavia, potrebbero esserci casi in cui desideri solo i messaggi interni e per questi casi utilizzo il seguente metodo di estensione:

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

Uso spesso questo metodo quando ho ascoltatori diversi per tracciare e registrare e voglio avere visioni diverse su di essi. In questo modo posso avere un ascoltatore che invia l'intero errore con la traccia dello stack via e-mail al team di sviluppo per il debug utilizzando il .ToString()metodo e uno che scrive un file di accesso con la cronologia di tutti gli errori che si sono verificati ogni giorno senza la traccia dello stack con il .GetFullMessage()metodo.


7
FYI If exis a AggregateException, nessuna delle eccezioni interne verrà inclusa in questo output
kornman00

3
Questo dovrebbe essere un metodo .NET di serie. Tutti dovrebbero usarlo.
JamesHoux,

9

Per stampare piuttosto solo la Messageparte delle eccezioni profonde, potresti fare qualcosa del genere:

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

Ciò ricorre in modo ricorsivo a tutte le eccezioni interne (incluso il caso di AggregateExceptions) per stampare tutte le Messageproprietà in esse contenute, delimitate dall'interruzione di riga.

Per esempio

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

Si è verificato un errore esterno.
Inner aggr ex.
Il numero non è nel formato corretto.
Accesso ai file non autorizzato.
Non amministratore.


Sarà necessario ascoltare altre proprietà delle eccezioni per maggiori dettagli. Per esempio Dataavrà alcune informazioni. Potresti fare:

foreach (DictionaryEntry kvp in exception.Data)

Per ottenere tutte le proprietà derivate (non sulla Exceptionclasse base ), puoi fare:

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

+1, questa è quasi esattamente la stessa cosa che faccio. Considerare di cercare un'implementazione della proprietà IEnumerable<Exception>anziché la codifica hardware AggregrateExceptionper gestire altri tipi simili. Escludere anche p.IsSpecialNameed pi.GetIndexParameters().Length != 0evitare problemi. Includere anche il nome del tipo di eccezione nell'output è una buona idea
adrianm,

@adrianm buon punto sui controlli delle informazioni sulla proprietà. Per quanto riguarda il controllo della raccolta di eccezioni, si tratta di dove si desidera tracciare la linea. Certo che si può fare anche ..
nawfal

4

Lo voglio:

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

Questo "stampa" tutte le eccezioni interne e gestisce anche AggregateExceptions e casi in cui InnerException.Message è uguale a Message


3

Se desideri informazioni su tutte le eccezioni, utilizza exception.ToString(). Raccoglierà dati da tutte le eccezioni interne.

Se si desidera solo l'eccezione originale, utilizzare exception.GetBaseException().ToString(). In questo modo otterrai la prima eccezione, ad esempio l'eccezione interna più profonda o l'eccezione corrente se non esiste un'eccezione interna.

Esempio:

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

accumulo sulla risposta di nawfal.

quando ho usato la sua risposta c'era una variabile aggrEx mancante, l'ho aggiunta.

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

Ho fatto un'eccezione perché:e.StackTrace == null
Andrei Krasutski il

1
Ho aggiornato .Select (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); forse questo aiuta
Shimon Doodkin il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.