Exception.Message vs Exception.ToString ()


207

Ho un codice che sta registrando Exception.Message. Tuttavia, ho letto un articolo che afferma che è meglio usare Exception.ToString(). Con quest'ultimo, conservi informazioni più cruciali sull'errore.

È vero ed è sicuro andare avanti e sostituire tutta la registrazione del codice Exception.Message?

Sto anche usando un layout basato su XML per log4net . È possibile che Exception.ToString()contenga caratteri XML non validi che possono causare problemi?


1
Dovresti anche consultare ELMAH ( code.google.com/p/elmah ) - Un framework molto facile da usare per la registrazione degli errori per ASP.NET.
Ashish Gupta,

Risposte:


278

Exception.Messagecontiene solo il messaggio (doh) associato all'eccezione. Esempio:

Il riferimento non impostato su un'istanza di un oggetto

Il Exception.ToString()metodo fornirà un output molto più dettagliato, contenente il tipo di eccezione, il messaggio (da prima), una traccia dello stack e tutte queste cose di nuovo per eccezioni nidificate / interne. Più precisamente, il metodo restituisce quanto segue:

ToString restituisce una rappresentazione dell'eccezione corrente che deve essere compresa dagli umani. Laddove l'eccezione contenga dati sensibili alla cultura, la rappresentazione di stringa restituita da ToString è tenuta a tenere conto dell'attuale cultura del sistema. Sebbene non vi siano requisiti esatti per il formato della stringa restituita, dovrebbe tentare di riflettere il valore dell'oggetto come percepito dall'utente.

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 è riferimento null (Nothing in Visual Basic), 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 riferimenti null (Nothing in Visual Basic).


86
+1 È molto doloroso vedere SOLO che "Riferimento oggetto non impostato su un'istanza di un oggetto" nei registri. Ti senti davvero impotente. :-)
Ashish Gupta,

1
Per l'ultima parte, ci sono eccezioni che non vengono fornite con Exception.Message. In funzione di ciò che si fa nella parte di gestione degli errori, è possibile che si verifichino problemi a causa di Exception.Message.
Coral Doe,

50
È molto doloroso vedere che ho scritto codice che essenzialmente fa esattamente la stessa cosa che fa ToString ().
Preston McCormick,

1
@KunalGoel Se il registro proviene da prod e non hai indicazioni su quale sia l'input, allora no, non puoi semplicemente "eseguire il debug attivando l'eccezione CLR".
jpmc26,

1
Nota, è "l'implementazione predefinita di ToString" ... (enfasi su "impostazione predefinita") .. non significa che tutti abbiano seguito quella pratica con eccezioni personalizzate. #learnedTheHardWay
granadaCoder

52

Oltre a quanto già detto, non utilizzare ToString()l'oggetto dell'eccezione per la visualizzazione all'utente. Solo la Messageproprietà dovrebbe essere sufficiente o un messaggio personalizzato di livello superiore.

In termini di registrazione, utilizzare sicuramente ToString()l'eccezione, non solo la Messageproprietà, come nella maggior parte degli scenari, rimarrai a grattarti la testa dove si è verificata specificamente questa eccezione e quale era lo stack di chiamate. Lo stacktrace ti avrebbe detto tutto questo.


Se si utilizza ToString () nei registri, assicurarsi di non includere informazioni sensibili in ToString
Michael Freidgeim

22

Conversione dell'INTERA eccezione a una stringa

Chiamare Exception.ToString()ti dà più informazioni che semplicemente usando la Exception.Messageproprietà. Tuttavia, anche questo lascia ancora molte informazioni, tra cui:

  1. Il Data proprietà della raccolta è stata trovata su tutte le eccezioni.
  2. Qualsiasi altra proprietà personalizzata aggiunta all'eccezione.

Ci sono momenti in cui desideri acquisire queste informazioni extra. Il codice seguente gestisce gli scenari precedenti. Scrive anche le proprietà delle eccezioni in un bell'ordine. Sta usando C # 7 ma dovrebbe essere molto facile per te convertire in versioni precedenti, se necessario. Vedi anche questa risposta correlata.

public static class ExceptionExtensions
{
    public static string ToDetailedString(this Exception exception) =>
        ToDetailedString(exception, ExceptionOptions.Default);

    public static string ToDetailedString(this Exception exception, ExceptionOptions options)
    {
        if (exception == null)
        {
            throw new ArgumentNullException(nameof(exception));
        } 

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

Suggerimento: eccezioni alla registrazione

La maggior parte delle persone utilizzerà questo codice per la registrazione. Prendi in considerazione l'utilizzo di Serilog con il mio pacchetto NuGet Serilog.Exceptions che registra anche tutte le proprietà di un'eccezione ma lo fa più velocemente e senza riflettere nella maggior parte dei casi. Serilog è un framework di registrazione molto avanzato che è di gran moda al momento della scrittura.

Suggerimento: tracce di stack leggibili dall'uomo

È possibile utilizzare il pacchetto NuGet Ben.Demystifier per ottenere tracce di stack leggibili dall'uomo per le proprie eccezioni o il pacchetto NuGet serilog-arricchitori-demistificare se si utilizza Serilog.


9

Direi che @Wim ha ragione. Si dovrebbe usare ToString()per i file di registro - assumendo un pubblico tecnico - eMessage , se non del tutto, per mostrarli all'utente. Si potrebbe sostenere che anche questo non è adatto per un utente, per ogni tipo di eccezione e occorrenza là fuori (si pensi ad ArgumentExceptions, ecc.).

Inoltre, oltre a StackTrace, ToString()includerà informazioni che non otterrai altrimenti. Ad esempio l'output di fusione, se abilitato per includere i messaggi di registro in "messaggi" di eccezione.

Alcuni tipi di eccezione includono anche informazioni aggiuntive (ad esempio da proprietà personalizzate) in ToString(), ma non nel messaggio.


8

Dipende dalle informazioni di cui hai bisogno. Per il debug la traccia dello stack e l'eccezione interna sono utili:

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

12
Questo è più o meno ciò che Exception.ToString()ti darà, giusto?
Jørn Schou-Rode,

5
@Matt: la creazione di un'istanza StringBuilderin questo scenario potrebbe essere più costosa di due nuove allocazioni di stringhe, è altamente discutibile sarebbe più efficiente qui. Non è che abbiamo a che fare con iterazioni. Cavalli per i corsi.
Wim Hollebrandse,

2
Il problema qui è che otterrai solo "InnerException" dell'eccezione più esterna. IOW, se InnerException in sé ha un set InnerException, non lo scaricherai (supponendo che lo desideri in primo luogo). Starei davvero con ToString ().
Christian.K,

6
Basta usare ex.ToString. Ti dà tutti i dettagli.
John Saunders,

3
@Christian: il compilatore è sano con più + s. Vedere ad esempio "L'operatore + è facile da usare e rende il codice intuitivo. Anche se si utilizzano più operatori + in un'istruzione, il contenuto della stringa viene copiato una sola volta." da msdn.microsoft.com/en-us/library/ms228504.aspx
David Eison

3

In termini di formato XML per log4net, non è necessario preoccuparsi di ex.ToString () per i registri. Basta passare l'oggetto di eccezione stesso e log4net fa il resto fornendo tutti i dettagli nel suo formato XML preconfigurato. L'unica cosa che mi capita di incontrare in alcune occasioni è la nuova formattazione di riga, ma è quando sto leggendo i file grezzi. Altrimenti l'analisi dell'XML funziona alla grande.


0

Bene, direi che dipende da cosa vuoi vedere nei registri, no? Se sei soddisfatto di ciò che offre Ex.Message, usalo. In caso contrario, utilizzare ex.toString () o persino registrare la traccia dello stack.


6
ad esempio ToString include la traccia dello stack
John Saunders,
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.