Exception.Message बनाम Exception.ToString ()


207

मेरे पास कोड है जो लॉगिंग है Exception.Message। हालांकि, मैंने एक लेख पढ़ा जिसमें कहा गया है कि इसका उपयोग करना बेहतर है Exception.ToString()। उत्तरार्द्ध के साथ, आप त्रुटि के बारे में अधिक महत्वपूर्ण जानकारी रखते हैं।

क्या यह सच है, और क्या आगे जाकर सभी कोड लॉगिंग को बदलना सुरक्षित है Exception.Message?

मैं log4net के लिए XML आधारित लेआउट का भी उपयोग कर रहा हूं । क्या यह संभव है कि Exception.ToString()इसमें अमान्य XML वर्ण हो सकते हैं, जिससे समस्याएँ हो सकती हैं?


1
आपको ELMAH ( code.google.com/p/elmah ) पर भी देखना चाहिए - ASP.NET के लिए त्रुटि लॉगिंग के लिए एक बहुत ही आसान उपयोग है।
आशीष गुप्ता

जवाबों:


278

Exception.Messageकेवल संदेश (doh) अपवाद के साथ जुड़ा हुआ है। उदाहरण:

वस्तु का संदर्भ वस्तु की आवृत्ति अनुसार सेट नहीं। है

Exception.ToString()विधि उत्पादन वर्बोज़ एक और अधिक देने के लिए, नेस्ट / भीतरी अपवाद के लिए फिर से अपवाद प्रकार, संदेश (पहले से), एक स्टैक ट्रेस, और इन बातों के सभी युक्त होगा। अधिक सटीक रूप से, विधि निम्नलिखित लौटाती है:

ToString वर्तमान अपवाद का प्रतिनिधित्व करता है जिसका उद्देश्य मनुष्यों द्वारा समझा जाना है। जहां अपवाद में संस्कृति के प्रति संवेदनशील डेटा होता है, वर्तमान प्रणाली संस्कृति को ध्यान में रखने के लिए ToString द्वारा लौटाए गए स्ट्रिंग प्रतिनिधित्व की आवश्यकता होती है। यद्यपि लौटे स्ट्रिंग के प्रारूप के लिए कोई सटीक आवश्यकताएं नहीं हैं, लेकिन उपयोगकर्ता द्वारा बताई गई वस्तु के मूल्य को प्रतिबिंबित करने का प्रयास करना चाहिए।

ToString का डिफ़ॉल्ट कार्यान्वयन उस वर्ग का नाम प्राप्त करता है जिसने वर्तमान अपवाद को फेंक दिया, संदेश, आंतरिक अपवाद पर ToString को कॉल करने का परिणाम, और कॉल करने का परिणाम Environment.StackTrace। यदि इन सदस्यों में से कोई एक अशक्त संदर्भ है (विजुअल बेसिक में कुछ भी नहीं), तो इसका मान लौटे स्ट्रिंग में शामिल नहीं है।

यदि कोई त्रुटि संदेश नहीं है या यदि यह एक रिक्त स्ट्रिंग ("") है, तो कोई त्रुटि संदेश वापस नहीं आता है। आंतरिक अपवाद और स्टैक ट्रेस का नाम केवल तभी दिया जाता है जब वे एक शून्य संदर्भ नहीं होते हैं (विजुअल बेसिक में कुछ भी नहीं)।


85
+1 केवल यह देखने के लिए बहुत दर्दनाक है कि लॉग में "ऑब्जेक्ट का संदर्भ ऑब्जेक्ट पर सेट नहीं है"। आप वास्तव में असहाय महसूस करते हैं। :-)
आशीष गुप्ता

1
पिछले भाग के लिए, अपवाद हैं जो अपवाद के साथ नहीं आते हैं। त्रुटि से निपटने वाले हिस्से में आप क्या करते हैं, इसके कार्य में, आपको अपवाद के कारण समस्याएँ हो सकती हैं।
कोरल डोए

49
यह देखना बहुत दर्दनाक है कि मैंने कोड लिखा था जो अनिवार्य रूप से वही काम करता है जो ToString () करता है।
प्रेस्टन मैककॉर्मिक

1
@KunalGoel यदि लॉग ठेस से आता है और आपके पास कोई संकेत नहीं है कि इनपुट क्या था, तो नहीं, आप "CLR अपवाद को चालू करके" केवल डीबग नहीं कर सकते।
jpmc26

1
ध्यान दें, यह "ToString का डिफ़ॉल्ट कार्यान्वयन" है ... ("डिफ़ॉल्ट" पर जोर दिया गया) .. इसका मतलब यह नहीं है कि सभी ने किसी भी कस्टम अपवाद के साथ उस अभ्यास का पालन किया है। # लार्नेटेडहार्डवे
ग्रेनाकोडर

52

पहले से ही कहा गया है इसके अलावा, उपयोगकर्ता को प्रदर्शित करने के लिए अपवाद ऑब्जेक्ट पर उपयोग ToString() करें। बस Messageसंपत्ति पर्याप्त होनी चाहिए, या उच्च स्तर कस्टम संदेश।

लॉगिंग उद्देश्यों के संदर्भ में, निश्चित रूप ToString()से अपवाद पर उपयोग करें , न केवल Messageसंपत्ति, जैसा कि अधिकांश परिदृश्यों में, आपको अपना सिर खरोंच करना छोड़ दिया जाएगा जहां विशेष रूप से यह अपवाद हुआ था, और कॉल स्टैक क्या था। स्टैकट्रेस ने आपको वह सब बताया होगा।


यदि आप लॉग में ToString () का उपयोग कर रहे हैं, तो सुनिश्चित करें कि ToString में संवेदनशील जानकारी शामिल न करें
माइकल फ्रीजिम

22

स्ट्रिंग अपवाद को एक स्ट्रिंग में परिवर्तित करना

कॉलिंग Exception.ToString()आपको Exception.Messageसंपत्ति का उपयोग करने की तुलना में अधिक जानकारी देता है। हालाँकि, यह अभी भी बहुत सारी जानकारी को छोड़ देता है, जिसमें शामिल हैं:

  1. Dataसंग्रह संपत्ति सभी अपवादों पर पाया।
  2. किसी भी अन्य कस्टम गुण अपवाद में जोड़े गए।

ऐसे समय होते हैं जब आप इस अतिरिक्त जानकारी को कैप्चर करना चाहते हैं। नीचे दिए गए कोड उपरोक्त परिदृश्यों को संभालते हैं। यह एक अच्छा क्रम में अपवादों के गुणों को भी लिखता है। यह C # 7 का उपयोग कर रहा है, लेकिन यदि आवश्यक हो तो पुराने संस्करणों में बदलना आपके लिए बहुत आसान होना चाहिए। इससे संबंधित उत्तर भी देखें ।

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

शीर्ष टिप - लॉगिंग अपवाद

अधिकांश लोग लॉगिंग के लिए इस कोड का उपयोग करेंगे। मेरे Serilog.Exception NuGet पैकेज के साथ Serilog का उपयोग करने पर विचार करें , जो अपवाद के सभी गुणों को भी लॉग करता है, लेकिन अधिकांश मामलों में यह तेजी से और प्रतिबिंब के बिना करता है। सेरिलॉग एक बहुत ही उन्नत लॉगिंग फ्रेमवर्क है जो लेखन के समय सभी गुस्से में है।

शीर्ष टिप - मानव पठनीय स्टैक निशान

यदि आप Serilog का उपयोग कर रहे हैं तो आप अपने अपवादों या Serilog-richers-demystify NuGet पैकेज के लिए मानव पठनीय स्टैक निशान पाने के लिए Ben.Demystifier NuGet पैकेज का उपयोग कर सकते हैं ।


9

मैं कहता हूं कि @Wim सही है। आपको ToString()लॉगफ़ाइल्स के लिए उपयोग करना चाहिए - एक तकनीकी दर्शक मानकर - और Message, यदि बिल्कुल, तो उपयोगकर्ता को प्रदर्शित करने के लिए। कोई यह तर्क दे सकता है कि यहां तक ​​कि एक उपयोगकर्ता के लिए उपयुक्त नहीं है, हर अपवाद प्रकार के लिए और वहां से बाहर निकलना (तर्क के बारे में सोचना, आदि)।

इसके अलावा, स्टैकट्रेस के अलावा, ऐसी ToString()जानकारी शामिल होगी जो आपको अन्यथा नहीं मिलेगी। उदाहरण के लिए संलयन के आउटपुट, यदि लॉग संदेश को "संदेश" में शामिल करने के लिए सक्षम किया गया है।

कुछ अपवाद प्रकारों में अतिरिक्त जानकारी भी शामिल है (उदाहरण के लिए कस्टम गुणों से) ToString(), लेकिन संदेश में नहीं।


8

आपको आवश्यक जानकारी पर निर्भर करता है। स्टैक ट्रेसिंग और आंतरिक अपवाद डीबगिंग के लिए उपयोगी हैं:

    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
यह कमोबेश वही है जो Exception.ToString()आपको देगा, है ना?
जॉर्न शॉ-रोड 13

5
@ मैट: StringBuilderइस परिदृश्य में एक उदाहरण का निर्माण दो नए स्ट्रिंग आवंटन की तुलना में अधिक महंगा हो सकता है, यह अत्यधिक बहस योग्य है यह यहां अधिक कुशल होगा। ऐसा नहीं है कि हम पुनरावृत्तियों से निपट रहे हैं। मैदान के लिए घोड़े।
१३

2
यहाँ समस्या यह है, कि आपको केवल सबसे बाहरी अपवाद का "इनर एक्सेप्शन" मिलेगा। आईओडब्ल्यू, अगर इनरसेप्शन में ही इनरसेप्शन सेट है, तो आप इसे डंप नहीं करेंगे (यह मानते हुए कि आप पहले स्थान पर चाहते हैं)। मैं वास्तव में ToString () के साथ रहना चाहूंगा।
क्रिश्चियन.के।

6
बस ex.ToString का उपयोग करें। यह आपको सारी जानकारी देता है।
जॉन Saunders

3
@ क्रिश्चियन: कंपाइलर मल्टीपल + s के साथ है। उदाहरण के लिए देखें "सहज ऑपरेटर का उपयोग करना आसान है और सहज कोड के लिए बनाता है। भले ही आप एक बयान में कई + ऑपरेटरों का उपयोग करते हैं, स्ट्रिंग सामग्री केवल एक बार कॉपी की जाती है।" से msdn.microsoft.com/en-us/library/ms228504.aspx
डेविड Eison

3

Log4net के लिए XML प्रारूप के संदर्भ में, आपको लॉग के लिए ex.ToString () के बारे में चिंता करने की आवश्यकता नहीं है। बस अपवाद ऑब्जेक्ट को स्वयं पास करें और लॉग 4नेट करता है, बाकी आपको इसके पूर्व-कॉन्फ़िगर XML प्रारूप में सभी विवरण देता है। केवल एक चीज जो मैं इस अवसर पर चलाता हूं, वह है नई लाइन फॉर्मेटिंग, लेकिन ऐसा तब है जब मैं फाइलों को कच्चा पढ़ रहा हूं। अन्यथा XML को पार्स करना बहुत अच्छा काम करता है।


0

खैर, मैं कहूंगा कि यह निर्भर करता है कि आप लॉग में क्या देखना चाहते हैं, है न? यदि आप इस बात से खुश हैं कि क्या प्रदान करता है। अन्यथा, ex.toString () का उपयोग करें या स्टैक ट्रेस भी लॉग करें।


5
ex.ToString में स्टैक ट्रेस शामिल हैं
जॉन सॉन्डर्स
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.