Exception.Message vs Exception.ToString()

j'ai le code qui enregistre Exception.Message . Cependant, j'ai lu un article qui dit qu'il vaut mieux utiliser Exception.ToString() . Avec ce dernier, vous retenir plus d'informations cruciales sur l'erreur.

est-ce vrai, et est-il sûr d'aller de l'avant et de remplacer tous les logs de code Exception.Message ?

j'utilise aussi une mise en page basée sur XML pour log4net . Est-il possible que Exception.ToString() puisse contenir des caractères XML invalides, qui peut causer des problèmes?

167
demandé sur Jesse 2010-02-01 15:53:14

7 réponses

Exception.Message contient seulement le message (doh) associé à l'exception. Exemple:

référence D'objet non définie à une instance d'un objet

la méthode Exception.ToString() donnera une sortie beaucoup plus verbeuse, contenant le type d'exception, le message (d'avant), une trace de pile, et tout cela à nouveau pour les exceptions imbriquées/internes. Plus précisément, la méthode renvoie ce qui suit:

ToString renvoie une représentation de l'exception actuelle qui est destinée à être comprise par les humains. Lorsque l'exception contient des données sensibles à la culture, la représentation de chaîne de caractères retournée par ToString est nécessaire pour tenir compte de la culture du système actuel. Bien qu'il n'y ait pas d'exigences précises pour le format de la chaîne retournée, elle devrait essayer de refléter la valeur de l'objet tel qu'il est perçu par l'utilisateur.

l'implémentation par défaut de ToString obtient le nom de la classe qui a jeté l'exception actuelle, le message, le résultat d'un appel à ToString sur l'exception interne, et le résultat d'un appel à L'environnement.StackTrace. Si l'un de ces membres est une référence nulle (rien dans Visual Basic), sa valeur n'est pas incluse dans la chaîne retournée.

S'il n'y a pas de message d'erreur ou s'il s'agit d'une chaîne vide ( "" ), alors aucune erreur le message est retourné. Le nom de l'exception interne et la trace de la pile ne sont retournés que s'ils ne sont pas une référence nulle (rien dans Visual Basic).

222
répondu Jørn Schou-Rode 2010-02-01 12:55:46

en plus de ce qui a déjà été dit, ne pas utiliser ToString() sur l'objet d'exception pour l'affichage à l'utilisateur. Juste la propriété Message devrait suffire, ou un message personnalisé de niveau supérieur.

Dans les conditions de l'exploitation forestière, certainement utiliser ToString() sur l'Exception, et pas seulement la Message bien, comme dans la plupart des scénarios, vous serez à gauche rayer votre tête où précisément cette exception s'est produite, et ce que l' appel de la pile. Le stactrace vous aurait dit tout ça.

45
répondu Wim Hollebrandse 2010-02-01 13:10:18

comme le souligne @JornSchouRode, faire un exception.ToString() vous donne plus d'informations que de simplement utiliser la propriété exception.Message . Toutefois, cela laisse encore beaucoup d'informations, y compris:

  1. le bien de collection Data se trouve sur toutes les exceptions.
  2. toute autre propriété personnalisée ajoutée à l'exception.

il y a des moments où vous voulez capturer ces informations supplémentaires. Le le code ci-dessous traite les scénarios ci-dessus. Il écrit également les propriétés des exceptions dans un ordre agréable. Il utilise C# 6.0 mais devrait être très facile pour vous de passer à des versions plus anciennes si nécessaire. Voir aussi ce réponse similaire.

public static class ExceptionExtensions
{
    public static string ToDetailedString(this Exception exception)
    {
        if (exception == null)
        {
            throw new ArgumentNullException(nameof(exception));
        }

        return ToDetailedString(exception, ExceptionOptions.Default);
    }

    public static string ToDetailedString(this Exception exception, ExceptionOptions options)
    {
        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; }
}

Tip Top D'Enregistrement Des Exceptions

la plupart des gens utiliseront ce code pour la journalisation. Envisager d'utiliser Serilog avec mon Serilog.Exceptions paquet NuGet qui enregistre aussi toutes les propriétés d'une exception mais le fait plus rapidement et sans réflexion dans la majorité des cas. Serilog est un cadre de journalisation très avancé qui fait fureur au moment de l'écriture.

Tip Top - Lisible Par Les Traces De Pile

vous pouvez utiliser le Ben.Démystifier paquet NuGet pour obtenir des traces de pile lisibles par l'homme pour vos exceptions ou le serilog-enrichers-demystify paquet NuGet si vous utilisez Serilog.

12
répondu Muhammad Rehan Saeed 2017-11-14 09:25:40

je dirais que @Wim a raison. Vous devez utiliser ToString() pour les logfiles - en supposant un public technique - et Message , si à tous, pour afficher à l'utilisateur. On pourrait arguer que même cela n'est pas approprié pour un utilisateur, pour chaque type d'exception et occurance (pensez à ArgumentExceptions, etc.).

aussi, en plus de la chaîne de StackTrace, ToString() inclura des informations que vous n'obtiendrez pas autrement. Par exemple, la production de fusion, si activé pour inclure les messages de log dans l'exception"messages".

certains types d'exceptions incluent même des informations supplémentaires (par exemple des propriétés personnalisées) dans ToString() , mais pas dans le Message.

9
répondu Christian.K 2013-05-08 09:46:22

dépend des informations dont vous avez besoin. Pour déboguer la pile trace et l'exception interne sont utiles:

    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";
    }
8
répondu Carra 2010-02-01 12:59:03

en ce qui concerne le format XML pour log4net, vous n'avez pas à vous soucier de ex.ToString () pour les logs. Il suffit de passer l'objet exception lui-même et log4net ne le reste ne vous donne tous les détails dans son format XML préconfiguré. La seule chose que je rencontre à l'occasion est le formatage de nouvelles lignes, mais c'est quand je lis les fichiers bruts. Sinon, l'analyse XML fonctionne très bien.

3
répondu Dillie-O 2010-02-01 15:07:21

Eh bien, je dirais que ça dépend de ce que vous voulez voir dans les journaux, n'est-ce pas? Si tu es content de ton ex.Message, utilisez-le. Sinon, utilisez le ex.toString() ou même enregistrer la trace de la pile.

1
répondu Thorsten Dittmar 2010-02-01 12:56:37