Apa cara yang tepat untuk menampilkan InnerException lengkap?

155

Apa cara yang tepat untuk menunjukkan penuh saya InnerException.

Saya menemukan bahwa beberapa InnerExceptions saya memiliki yang lain InnerExceptiondan itu berjalan cukup dalam.

Apakah akan InnerException.ToString()melakukan pekerjaan untuk saya atau apakah saya harus mengulang InnerExceptionsdan membangun Stringdengan StringBuilder?

Willem
sumber
Mengapa Anda perlu menunjukkan pengecualian batin ??
Akram Shahda
26
@ Akram karena sebagian besar waktu itu adalah pengecualian batin yang menarik. Salah satu contoh adalah XmlSerializer yang hanya melempar InvalidOperationException setiap kali terjadi kesalahan. Apa yang salah adalah pengecualian batiniah.
adrianm
4
@ AkramShahda Yah, mungkin Anda ingin menggunakan metode ini di pencatatan Anda?
cederlof

Jawaban:

239

Anda cukup mencetak exception.ToString()- itu juga akan menyertakan teks lengkap untuk semua InnerExceptions bersarang .

Jon
sumber
18
Ini termasuk banyak omong kosong lainnya juga, bukan hanya pesan pengecualian dan pesan pengecualian dalam
ᴍᴀᴛᴛ ʙᴀᴋᴇʀ
hanya untuk ringkasnya Anda tidak benar-benar membutuhkan .ToString (), hanya menggunakan pengecualian akan melakukan hal yang sama.
Alex Stephens
3
@AlexStephens Anda benar, tetapi hanya jika Anda memiliki casting implisit "ke string" karena beberapa alasan, seperti string sebelumnya: "bla" + pengecualian
oo_dev
1
FYI: tidak akan memanggil ToStringmetode khusus untuk pengecualian dalam seperti yang dijelaskan dalam Mengapa System.Exception.ToString tidak memanggil virtual ToString untuk pengecualian dalam? .
Jeff B
45

Gunakan saja exception.ToString()

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

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 nol, 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 tidak nol.

exception.ToString () juga akan memanggil .ToString () dengan pengecualian dalam pengecualian itu, dan seterusnya ...

Rob P.
sumber
45

Saya biasanya suka ini untuk menghilangkan sebagian besar kebisingan:

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

    Console.WriteLine(realerror.ToString())
}    

Sunting: Saya lupa tentang jawaban ini dan terkejut tidak ada yang menunjukkan bahwa Anda bisa melakukannya

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    
adrianma
sumber
Metode ini menyembunyikan semuanya kecuali untuk pengecualian batin terdalam. Jika itu adalah sesuatu yang biasa seperti kesalahan "Bagi dengan nol", itu akan menjadi tidak jelas di mana itu terjadi dan apa yang menyebabkannya. Jelas, jejak tumpukan penuh biasanya berlebihan berlebihan, tetapi hanya membaca pengecualian dalam adalah ekstrem lainnya. Jawaban pengguna3016982 jauh lebih baik. Anda mendapatkan setiap pesan pengecualian di tumpukan tanpa jejak jahat.
JamesHoux
1
@JamesHoux Yang manakah jawaban "user3016982"? Tidak dapat menemukannya di sini
maracuja-jus
User3016982 adalah ThomazMoura, lihat: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha
@ Jamesames, pengecualian dalam memiliki stacktrace penuh yang menunjukkan di mana kesalahan terjadi dan apa yang menyebabkannya. Tidak mengerti informasi tambahan apa yang Anda dapatkan dari jejak tumpukan yang dihapus. Pesan pengecualian adalah hal lain dan mungkin berguna untuk mengumpulkan semuanya.
adrianm
2
Kenapa tidak Anda gunakan saja error.GetBaseException(). Saya percaya ini melakukan hal yang sama ...
Robba
37

@ Jon menjawab adalah solusi terbaik ketika Anda ingin detail lengkap (semua pesan dan jejak stack) dan yang direkomendasikan.

Namun, mungkin ada kasus ketika Anda hanya ingin pesan dalam, dan untuk kasus ini saya menggunakan metode ekstensi berikut:

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

Saya sering menggunakan metode ini ketika saya memiliki pendengar yang berbeda untuk melacak dan masuk dan ingin memiliki pandangan yang berbeda pada mereka. Dengan cara itu saya dapat memiliki satu pendengar yang mengirimkan seluruh kesalahan dengan jejak stack melalui email ke tim pengembang untuk debug menggunakan .ToString()metode dan yang menulis log pada file dengan sejarah semua kesalahan yang terjadi setiap hari tanpa jejak stack dengan yang .GetFullMessage()metode.

ThomazMoura
sumber
7
FYI Jika exa AggregateException, tidak ada pengecualian dalam yang akan dimasukkan dalam output ini
kornman00
3
Ini harus menjadi metode .NET stock. Semua orang harus menggunakan ini.
JamesHoux
9

Untuk mencetak Messagebagian dari pengecualian yang cukup dalam, Anda dapat melakukan sesuatu seperti ini:

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

Ini secara rekursif melewati semua pengecualian dalam (termasuk kasus AggregateExceptions) untuk mencetak semua Messageproperti yang terkandung di dalamnya, dibatasi oleh jeda baris.

Misalnya

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

Aggr luar terjadi.
Aggr ex batin.
Angka tidak dalam format yang benar.
Akses file tidak sah.
Bukan administrator.


Anda perlu mendengarkan properti Pengecualian lainnya untuk detail lebih lanjut. Misalnya Dataakan memiliki beberapa informasi. Anda bisa melakukannya:

foreach (DictionaryEntry kvp in exception.Data)

Untuk mendapatkan semua properti turunan (bukan pada Exceptionkelas dasar ), Anda bisa melakukan:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));
nawfal
sumber
+1, Ini hampir persis sama dengan yang saya lakukan. Pertimbangkan untuk mencari properti yang menerapkan IEnumerable<Exception>alih-alih pengkodean keras AggregrateExceptionuntuk menangani jenis serupa lainnya. Juga kecualikan p.IsSpecialNamedan pi.GetIndexParameters().Length != 0untuk menghindari masalah. Memasukkan nama tipe pengecualian dalam output juga merupakan ide bagus
adrianm
@adrianm poin bagus tentang cek info properti. Mengenai memeriksa untuk koleksi pengecualian, ini semua tentang di mana Anda ingin menarik garis. Tentu itu bisa dilakukan juga ..
nawfal
4

Saya lakukan:

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

Ini "cetakan cantik" semua pengecualian batin dan juga menangani Agregat Eksepsi dan kasus di mana InnerException. Pesan sama dengan Pesan

kofifus
sumber
3

Jika Anda ingin informasi tentang semua pengecualian, gunakan exception.ToString(). Itu akan mengumpulkan data dari semua pengecualian dalam.

Jika Anda hanya menginginkan pengecualian asli, gunakan exception.GetBaseException().ToString(). Ini akan memberi Anda pengecualian pertama, mis. Pengecualian batin terdalam atau pengecualian saat ini jika tidak ada pengecualian batin.

Contoh:

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
sumber
2

penumpukan pada jawaban nawfal.

ketika menggunakan jawabannya ada variabel aggrEx yang hilang, saya menambahkannya.

mengajukan 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
sumber
Saya memiliki pengecualian karena:e.StackTrace == null
Andrei Krasutski
1
Saya telah memperbarui. Pilih (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = Null? StackTrace.Trim (): "")); mungkin ini membantu
Shimon Doodkin