Exception.Message ve Exception.ToString ()


208

Günlüğe kaydetme kodu var Exception.Message. Ancak, kullanmanın daha iyi olduğunu belirten bir makale okudum Exception.ToString(). İkincisi ile, hata hakkında daha önemli bilgileri tutarsınız.

Bu doğru Exception.Messagemu ve devam etmek ve tüm kod günlüğünü değiştirmek güvenli mi?

Ayrıca log4net için XML tabanlı bir düzen kullanıyorum . O olması mümkün mü Exception.ToString()sorunlarına neden olabilen, geçersiz XML karakterleri içerebilir?


1
Ayrıca , ASP.NET için Hata Günlüğü için kullanımı kolay bir çerçeve olan ELMAH ( code.google.com/p/elmah ) 'a da bakmalısınız .
Ashish Gupta

Yanıtlar:


279

Exception.Messageyalnızca istisna ile ilişkili mesajı (doh) içerir. Misal:

Nesne referansı bir nesnenin örneğine atanmadı

Exception.ToString()Yöntem, iç içe / iç özel durumlar için yine özel türü, (daha önce gelen) mesajı, bir yığın izleme ve bunların tümünü içeren, çıkış ayrıntılı çok daha verecektir. Daha doğrusu, yöntem aşağıdakileri döndürür:

ToString, insanlar tarafından anlaşılması amaçlanan mevcut istisnanın bir temsilini döndürür. Kural dışı durumun kültüre duyarlı veriler içerdiği durumlarda, ToString tarafından döndürülen dize gösteriminin geçerli sistem kültürünü hesaba katması gerekir. Döndürülen dizenin biçimi için kesin bir gereksinim olmamasına rağmen, nesnenin değerini kullanıcı tarafından algılanan şekilde yansıtmaya çalışmalıdır.

ToString'in varsayılan uygulaması geçerli istisnayı atan sınıfın adını, iletiyi, iç istisnada ToString çağrısının sonucunu ve Environment.StackTrace çağrılmasının sonucunu alır. Bu üyelerden herhangi biri null başvuru ise (Visual Basic'te Nothing), değeri döndürülen dizeye dahil edilmez.

Hata mesajı yoksa veya boş bir dize ("") ise, hata mesajı döndürülmez. İç kural dışı durumun adı ve yığın izlemesi, yalnızca bir boş başvuru (Visual Basic'te Nothing) değilse döndürülür.


86
+1 SADECE günlüklerde "Nesne başvurusu bir nesnenin örneğine ayarlanmadı" ifadesini görmek çok acı verici. Gerçekten çaresiz hissediyorsun. :-)
Ashish Gupta

1
Son bölüm için, Exception.Message ile birlikte gelmeyen İstisnalar vardır. Hata işleme bölümünde ne yaptığınıza bağlı olarak, Exception.Message nedeniyle sorun yaşayabilirsiniz.
Coral Doe

50
Aslında ToString () ile aynı şeyi yapan kod yazdığımı görmek çok acı verici.
Preston McCormick

1
@KunalGoel Günlük prod'dan geliyorsa ve girdinin ne olduğunu gösteren bir işaretiniz yoksa, hayır, yalnızca "CLR istisnasını açarak hata ayıklama" yapamazsınız.
jpmc26

1
Dikkat edin, "ToString'in varsayılan uygulaması" ... ("varsayılan" a vurgu). Bu, herkesin bu uygulamayı herhangi bir özel istisna dışında izlediği anlamına gelmez. #learnedTheHardWay
granadaCoder

52

Zaten ne kadar oldu ek olarak, söz konusu değil kullanmak ToString()kullanıcıya görüntülenmesi için istisna nesne üzerinde. Sadece Messageözellik yeterli olmalı veya daha üst düzey bir özel mesaj olmalıdır.

Günlüğe kaydetme amacıyla, çoğu senaryoda olduğu gibi, ToString()sadece Messagemülk üzerinde değil, sadece İstisna üzerinde kullanın , özellikle bu istisnanın oluştuğu ve çağrı yığınının ne olduğu kafanızı çizmeye bırakılacaksınız. Stacktrace size tüm bunları söylerdi.


Günlüklerde ToString () kullanıyorsanız, ToString
Michael Freidgeim

22

BÜTÜN Kural Dışı Durumunu Dizeye Dönüştürme

Arama özelliği Exception.ToString()yalnızca Exception.Messageözelliği kullanmaktan daha fazla bilgi verir . Bununla birlikte, bu hala dahil olmak üzere birçok bilgi bırakıyor:

  1. DataToplama özelliği tüm istisnalar bulunabilir.
  2. İstisnaya eklenen diğer tüm özel özellikler.

Bu ekstra bilgiyi yakalamak istediğiniz zamanlar vardır. Aşağıdaki kod yukarıdaki senaryoları ele almaktadır. Ayrıca istisnaların özelliklerini güzel bir sırayla yazar. C # 7 kullanıyor ancak gerekirse eski sürümlere dönüştürmeniz çok kolay olmalı. Ayrıca bu ilgili cevaba bakınız.

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

En İyi İpucu - Günlük İstisnaları

Çoğu kişi bu kodu oturum açmak için kullanacaktır. Kullanmayı düşünün Serilog benim ile Serilog.Exceptions da bir istisna tüm özelliklerini kaydeder ancak daha hızlı yapar ve çoğu durumda yansıması olmadan Nuget paketinin. Serilog, yazım sırasındaki tüm öfke olan çok gelişmiş bir günlükleme çerçevesidir.

Üst İpucu - İnsan Tarafından Okunabilir Yığın İzleri

İstisnalarınız için insan tarafından okunabilir yığın izleri almak için Ben.Demystifier NuGet paketini veya Serilog kullanıyorsanız serilog-zenginleştiriciler-demystify NuGet paketini kullanabilirsiniz.


9

@ Wim haklı diyebilirim. Günlük ToString()dosyaları için - teknik bir kitle olduğunu varsayarak - ve Messagekullanıcıya gösterilecekse kullanmalısınız. Bu bile bir kullanıcı için uygun değildir, orada her istisna türü ve olayı için (ArgumentExceptions, vb. Düşünün).

Ayrıca, StackTrace'e ek olarak, ToString()başka türlü alamayacağınız bilgileri de içerecektir. Örneğin, füzyon çıkışı, "mesajlar" istisnasına günlük mesajlarını içerecek şekilde etkinleştirilirse .

Bazı özel durum türleri ToString(), İletiye ek bilgi içerir (örneğin özel özelliklerden) , ancak İletiye dahil değildir.


8

İhtiyacınız olan bilgilere bağlıdır. Hata izleme ve iç istisna hata ayıklamak için yararlıdır:

    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
Bu az çok sana ne Exception.ToString()verecek, değil mi?
Jørn Schou-Rode

5
@Matt: StringBuilderBu senaryoda bir örnek oluşturmak iki yeni dize tahsisinden daha pahalı olabilir, burada daha verimli olması oldukça tartışmalıdır. Yinelemelerle uğraştığımız gibi değil. Kurslar için atlar.
Wim Hollebrandse

2
Burada sorun, sadece en dıştaki istisna "InnerException" elde olmasıdır. IOW, InnerException'ın kendisinde bir InnerException kümesi varsa, onu dökmezsiniz (ilk etapta istediğinizi varsayarsak). Gerçekten ToString () ile devam ediyorum.
Christian.K

6
Sadece ex.ToString kullanın. Size tüm detayları verir.
John Saunders

3
@Christian: Derleyici çoklu + s ile aklı başında. Örneğin, "+ işlecinin kullanımı kolaydır ve sezgisel kod sağlar. Bir ifadede birden fazla + işleci kullansanız bile, dize içeriği yalnızca bir kez kopyalanır." . msdn.microsoft.com/en-us/library/ms228504.aspx adresinden
David Eison

3

Log4net için XML biçimi açısından, günlükler için ex.ToString () hakkında endişelenmenize gerek yoktur. İstisna nesnesinin kendisini geçmeniz yeterlidir, geri kalanı size tüm ayrıntıları önceden yapılandırılmış XML biçiminde verir mi? Zaman zaman karşılaştığım tek şey yeni satır biçimlendirmesi, ama işte dosyaları ham okuduğumda. Aksi takdirde XML ayrıştırmak harika çalışır.


0

Günlüklerde ne görmek istediğine bağlı olduğunu söyleyebilirim, değil mi? Ex.Message'ın sağladığı içerikten memnunsanız bunu kullanın. Aksi takdirde, ex.toString () kullanın ve hatta yığın izlemesini günlüğe kaydedin.


6
ex.ToString yığın izini içerir
John Saunders
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.