Exception.Message vs Exception.ToString ()


207

Saya memiliki kode yang sedang login Exception.Message. Namun, saya membaca sebuah artikel yang menyatakan bahwa lebih baik digunakan Exception.ToString(). Dengan yang terakhir, Anda menyimpan informasi yang lebih penting tentang kesalahan.

Apakah ini benar, dan apakah aman untuk melanjutkan dan mengganti semua pencatatan kode Exception.Message?

Saya juga menggunakan tata letak berbasis XML untuk log4net . Apakah Exception.ToString()mungkin berisi karakter XML yang tidak valid, yang dapat menyebabkan masalah?


1
Anda juga harus melihat ELMAH ( code.google.com/p/elmah ) - Kerangka kerja yang sangat mudah digunakan untuk Error Logging untuk ASP.NET.
Ashish Gupta

Jawaban:


278

Exception.Messagehanya berisi pesan (doh) yang terkait dengan pengecualian. Contoh:

Referensi objek tidak disetel ke instance objek

The Exception.ToString()metode akan memberikan yang jauh lebih verbose output, yang berisi jenis pengecualian, pesan (dari sebelumnya), jejak stack, dan semua hal ini lagi untuk bersarang / pengecualian batin. Lebih tepatnya, metode mengembalikan yang berikut:

ToString mengembalikan representasi pengecualian saat ini yang dimaksudkan untuk dipahami oleh manusia. Di mana pengecualian berisi data sensitif-budaya, representasi string yang dikembalikan oleh ToString diperlukan untuk memperhitungkan budaya sistem saat ini. Meskipun tidak ada persyaratan pasti untuk format string yang dikembalikan, itu harus berusaha untuk mencerminkan nilai objek seperti yang dirasakan oleh pengguna.

Implementasi default dari ToString memperoleh nama kelas yang melemparkan pengecualian saat ini, pesan, hasil dari panggilan ToString pada pengecualian dalam, dan hasil dari memanggil Lingkungan.StackTrace. Jika salah satu dari anggota ini adalah referensi nol (Tidak ada dalam Visual Basic), nilainya tidak termasuk dalam string yang dikembalikan.

Jika tidak ada pesan kesalahan atau jika itu adalah string kosong (""), maka tidak ada pesan kesalahan yang dikembalikan. Nama pengecualian dalam dan jejak tumpukan dikembalikan hanya jika mereka bukan referensi nol (Tidak ada dalam Visual Basic).


86
+1 Sangat menyakitkan melihat HANYA bahwa "Referensi objek tidak disetel ke instance objek" di log. Anda merasa sangat tidak berdaya. :-)
Ashish Gupta

1
Untuk bagian terakhir, ada Pengecualian yang tidak datang dengan Pengecualian. Pesan. Dalam fungsi apa yang Anda lakukan di bagian penanganan kesalahan, Anda bisa mendapatkan masalah karena Exception.Message.
Coral Doe

50
Sangat menyakitkan melihat bahwa saya menulis kode yang pada dasarnya melakukan hal yang persis sama seperti yang dilakukan ToString ().
Preston McCormick

1
@KunalGoel Jika log berasal dari prod dan Anda tidak memiliki indikasi apa inputnya, maka tidak, Anda tidak bisa hanya "men-debug dengan mengaktifkan pengecualian CLR".
jpmc26

1
Catatan, ini adalah "implementasi default ToString" ... (penekanan pada "default") .. itu tidak berarti bahwa setiap orang telah mengikuti praktik itu dengan pengecualian khusus. #learnedTheHardWay
granadaCoder

52

Selain apa yang sudah dikatakan, jangan gunakan ToString()pada objek pengecualian untuk ditampilkan kepada pengguna. Hanya Messageproperti yang cukup, atau pesan khusus tingkat yang lebih tinggi.

Dalam hal tujuan logging, pasti digunakan ToString()pada Pengecualian, bukan hanya Messageproperti, seperti dalam kebanyakan skenario, Anda akan dibiarkan menggaruk-garuk kepala Anda di mana pengecualian ini terjadi, dan apa tumpukan panggilan itu. Stacktrace akan memberitahumu semua itu.


Jika Anda menggunakan ToString () dalam log, pastikan tidak menyertakan info sensitif di ToString
Michael Freidgeim

22

Mengubah Pengecualian SELURUH Menjadi String

Memanggil Exception.ToString()memberi Anda lebih banyak informasi daripada hanya menggunakan Exception.Messageproperti. Namun, bahkan ini masih menyisakan banyak informasi, termasuk:

  1. The Dataproperti koleksi ditemukan pada semua pengecualian.
  2. Properti kustom lainnya ditambahkan ke pengecualian.

Ada saatnya Anda ingin mengambil informasi tambahan ini. Kode di bawah ini menangani skenario di atas. Itu juga menulis properti pengecualian dalam urutan yang bagus. Ini menggunakan C # 7 tetapi harus sangat mudah bagi Anda untuk mengonversi ke versi yang lebih lama jika perlu. Lihat juga jawaban terkait ini .

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

Kiat Teratas - Pengecualian Pencatatan

Kebanyakan orang akan menggunakan kode ini untuk masuk. Pertimbangkan untuk menggunakan Serilog dengan paket Serilog saya. Eksepsi NuGet yang juga mencatat semua properti pengecualian tetapi melakukannya lebih cepat dan tanpa refleksi di sebagian besar kasus. Serilog adalah kerangka kerja logging yang sangat maju yang semuanya populer pada saat penulisan.

Tip Teratas - Jejak Tumpukan yang Dapat Dibaca Manusia

Anda dapat menggunakan paket Ben.Demystifier NuGet untuk mendapatkan jejak stack yang dapat dibaca manusia untuk pengecualian Anda atau paket NuGet yang diperkaya serilog-demistify jika Anda menggunakan Serilog.


9

Saya akan mengatakan @ Win benar. Anda harus menggunakan ToString()file log - dengan asumsi audiens teknis - danMessage , jika sama sekali, untuk ditampilkan kepada pengguna. Orang bisa berpendapat bahwa itu pun tidak cocok untuk pengguna, untuk setiap jenis pengecualian dan kejadian di luar sana (pikirkan ArgumentExceptions, dll.).

Selain itu, selain StackTrace, ToString()akan termasuk informasi yang tidak akan Anda dapatkan sebaliknya. Misalnya keluaran fusi, jika diaktifkan untuk memasukkan pesan log kecuali "pesan".

Beberapa tipe pengecualian bahkan menyertakan informasi tambahan (misalnya dari properti khusus) di ToString(), tetapi tidak di dalam Pesan.


8

Tergantung pada informasi yang Anda butuhkan. Untuk debugging jejak tumpukan & pengecualian dalam berguna:

    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
Ini kurang lebih apa yang Exception.ToString()akan memberi Anda, kan?
Jørn Schou-Rode

5
@ Matt: Membangun contoh StringBuilderdalam skenario ini mungkin lebih mahal daripada dua alokasi string baru, itu sangat bisa diperdebatkan akan lebih efisien di sini. Ini tidak seperti kita berurusan dengan iterasi. Kuda untuk kursus.
Wim Hollebrandse

2
Masalahnya di sini adalah, bahwa Anda hanya akan mendapatkan "InnerException" pengecualian terluar. TKI, jika InnerException sendiri memiliki set InnerException, Anda tidak akan membuangnya (dengan asumsi Anda ingin melakukannya terlebih dahulu). Saya akan tetap menggunakan ToString ().
Christian.K

6
Cukup gunakan ex.ToString. Ini memberi Anda semua detail.
John Saunders

3
@Christian: Kompilator ini waras dengan banyak + s. Lihat misalnya "Operator + mudah digunakan dan dibuat untuk kode intuitif. Bahkan jika Anda menggunakan beberapa operator + dalam satu pernyataan, konten string disalin hanya sekali." dari msdn.microsoft.com/en-us/library/ms228504.aspx
David Eison

3

Dalam hal format XML untuk log4net, Anda tidak perlu khawatir tentang ex.ToString () untuk log. Cukup lewati objek pengecualian itu sendiri dan log4net tidak memberi Anda semua detail dalam format XML pra-konfigurasi. Satu-satunya hal yang saya alami pada kesempatan adalah pemformatan baris baru, tetapi saat itulah saya membaca file mentah. Kalau tidak, parsing XML berfungsi dengan baik.


0

Yah, saya akan mengatakan itu tergantung apa yang ingin Anda lihat di log, bukan? Jika Anda puas dengan apa yang diberikan oleh ex.Message, gunakan itu. Jika tidak, gunakan ex.toString () atau bahkan catat jejak stack.


6
ex.ToString termasuk jejak tumpukan
John Saunders
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.