Quelle est la bonne façon d'afficher l'intégralité de InnerException?

155

Quelle est la bonne façon de montrer mon plein InnerException.

J'ai trouvé que certaines de mes InnerExceptions en ont une autre InnerExceptionet que cela est assez profond.

Fera InnerException.ToString()- t -il le travail pour moi ou dois-je parcourir le InnerExceptionset créer un Stringavec StringBuilder?

Willem
la source
Pourquoi avez-vous besoin d'afficher l'exception interne?
Akram Shahda
26
@Akram car la plupart du temps c'est l'exception interne qui est intéressante. Un exemple est le XmlSerializer qui lève simplement une InvalidOperationException chaque fois que quelque chose ne va pas. Ce qui a mal tourné, c'est dans l'exception interne.
adrianm
4
@AkramShahda Eh bien, peut-être voulez-vous utiliser cette méthode dans votre journalisation?
cederlof

Réponses:

239

Vous pouvez simplement imprimer exception.ToString()- cela inclura également le texte intégral de tous les InnerExceptions imbriqués .

Jon
la source
18
Cela inclut également un tas d'autres conneries, pas seulement le message d'exception et les messages d'exception internes
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
juste pour être concis, vous n'avez pas besoin de .ToString (), le simple fait d'utiliser exception fera de même.
Alex Stephens
3
@AlexStephens vous avez raison, mais seulement si vous avez un casting implicite "to string" pour une raison quelconque, comme la chaîne précédente: "bla" + exception
oo_dev
1
FYI: il n'appellera pas de ToStringméthodes personnalisées pour les exceptions internes comme détaillé dans Pourquoi System.Exception.ToString n'appelle-t-il pas virtuel ToString pour les exceptions internes? .
Jeff B le
45

Juste utiliser exception.ToString()

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

L'implémentation par défaut de ToString obtient le nom de la classe qui a levé l'exception actuelle, le message, le résultat de l'appel de ToString sur l'exception interne et le résultat de l'appel de Environment.StackTrace. Si l'un de ces membres est nul, sa valeur n'est pas incluse dans la chaîne renvoyée.

S'il n'y a pas de message d'erreur ou s'il s'agit d'une chaîne vide (""), aucun message d'erreur n'est renvoyé. Le nom de l'exception interne et la trace de pile ne sont renvoyés que s'ils ne sont pas nuls.

exception.ToString () appellera également .ToString () sur l'exception interne de cette exception, et ainsi de suite ...

Rob P.
la source
45

J'aime généralement ceci pour éliminer la plupart du bruit:

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

    Console.WriteLine(realerror.ToString())
}    

Edit: J'ai oublié cette réponse et je suis surpris que personne n'ait souligné que vous pouvez simplement le faire

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    
adrianm
la source
Cette méthode masque tout sauf l'exception intérieure la plus profonde. Si c'était quelque chose de banal comme une erreur «Diviser par zéro», il serait difficile de savoir où cela s'est produit et ce qui y a conduit. De toute évidence, une trace de pile complète est généralement une exagération désordonnée, mais seule la lecture de l'exception interne est l'autre extrême. La réponse de user3016982 est bien meilleure. Vous obtenez chaque message d'exception dans la pile sans la trace désagréable.
JamesHoux
1
@JamesHoux Laquelle est la réponse "user3016982"? Je ne peux pas le trouver ici.
maracuja-juice le
L'utilisateur3016982 est ThomazMoura, voir: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha
@JamesHoux, l'exception interne a un stacktrace complet montrant où l'erreur s'est produite et ce qui l'a conduit. Je ne comprends pas les informations supplémentaires que vous obtenez des traces de pile supprimées. Les messages d'exception sont une autre chose et il peut être utile de tous les collecter.
adrianm
2
Pourquoi n'utilisez-vous pas simplement error.GetBaseException(). Je crois que cela fait la même chose ...
Robba
37

La réponse de @ Jon est la meilleure solution lorsque vous voulez des détails complets (tous les messages et la trace de la pile) et celle recommandée.

Cependant, il peut y avoir des cas où vous ne voulez que les messages internes, et pour ces cas, j'utilise la méthode d'extension suivante:

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

J'utilise souvent cette méthode lorsque j'ai différents écouteurs pour le traçage et la journalisation et que je souhaite avoir des points de vue différents sur eux. De cette façon, je peux avoir un écouteur qui envoie toute l'erreur avec trace de pile par e-mail à l'équipe de développement pour le débogage à l'aide de la .ToString()méthode et un qui écrit un fichier journal avec l'historique de toutes les erreurs qui se sont produites chaque jour sans la trace de la pile avec la .GetFullMessage()méthode.

ThomazMoura
la source
7
FYI Si exest un AggregateException, aucune des exceptions internes ne sera incluse dans cette sortie
kornman00
3
Cela devrait être une méthode .NET stock. Tout le monde devrait l'utiliser.
JamesHoux
9

Pour n'imprimer que la Messagepartie des exceptions profondes, vous pouvez faire quelque chose comme ceci:

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

Cela parcourt de manière récursive toutes les exceptions internes (y compris le cas de AggregateExceptions) pour afficher toutes les Messagepropriétés qu'elles contiennent, délimitées par un saut de ligne.

Par exemple

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

Un agrex externe s'est produit.
Intérieur aggr ex.
Le nombre n'est pas au format correct.
Accès aux fichiers non autorisé.
Pas administrateur.


Vous devrez écouter les autres propriétés d' exception pour plus de détails. Par exemple Dataaura quelques informations. Vous pourriez faire:

foreach (DictionaryEntry kvp in exception.Data)

Pour obtenir toutes les propriétés dérivées (pas sur la Exceptionclasse de base ), vous pouvez faire:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
nawfal
la source
+1, c'est presque exactement la même chose que moi. Envisagez de rechercher une propriété implémentant IEnumerable<Exception>au lieu d'un codage en dur AggregrateExceptionpour gérer d'autres types similaires. Exclure également p.IsSpecialNameet pi.GetIndexParameters().Length != 0éviter les problèmes. Inclure le nom du type d'exception dans la sortie est également une bonne idée
adrianm
@adrianm bon point sur les vérifications d'informations sur les propriétés En ce qui concerne la vérification de la collecte des exceptions, tout dépend de l'endroit où vous voulez tracer la ligne. Bien sûr, cela peut être fait aussi ..
nawfal
4

Je fais:

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

Ce "joli imprime" toutes les exceptions internes et gère également les AggregateExceptions et les cas où InnerException.Message est identique à Message

kofifus
la source
3

Si vous voulez des informations sur toutes les exceptions, utilisez exception.ToString(). Il collectera les données de toutes les exceptions internes.

Si vous ne voulez que l'exception d'origine, utilisez exception.GetBaseException().ToString(). Cela vous donnera la première exception, par exemple l'exception interne la plus profonde ou l'exception actuelle s'il n'y a pas d'exception interne.

Exemple:

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
la source
2

accumulation sur la réponse de nawfal.

lors de l'utilisation de sa réponse, il manquait une variable aggrEx, je l'ai ajoutée.

fichier 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;
        }
    }
}
Shimon Doodkin
la source
J'ai eu une exception parce que:e.StackTrace == null
Andrei Krasutski
1
J'ai mis à jour .Select (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); peut-être que cela aide
Shimon Doodkin