วิธีที่เหมาะสมในการแสดง InnerException แบบเต็มคืออะไร


155

เป็นวิธีที่เหมาะสมในการแสดงเต็มของฉันInnerExceptionคืออะไร

ฉันพบว่า InnerExceptions บางอย่างของฉันมีอีกอันหนึ่งInnerExceptionและมันก็ค่อนข้างลึก

จะInnerException.ToString()ทำงานให้ฉันหรือฉันต้องวนซ้ำInnerExceptionsและสร้างStringด้วยStringBuilder?


ทำไมคุณต้องแสดงข้อยกเว้นภายใน?
Akram Shahda

26
@Akram เพราะส่วนใหญ่เวลาเป็นข้อยกเว้นภายในที่น่าสนใจ ตัวอย่างหนึ่งคือ XmlSerializer ซึ่งเพิ่งโยน InvalidOperationException เมื่อใดก็ตามที่มีอะไรผิดพลาด สิ่งที่ผิดพลาดอยู่ในข้อยกเว้นภายใน
adrianm

4
@AkramShahda คุณอาจต้องการใช้วิธีนี้ในการบันทึกของคุณหรือไม่
cederlof

โพสต์ที่เกี่ยวข้อง - Exception.Message vs Exception.ToString ()
RBT

คำตอบ:


239

คุณสามารถพิมพ์exception.ToString()- ซึ่งจะรวมถึงข้อความแบบเต็มสำหรับInnerExceptions ที่ซ้อนกันทั้งหมด


18
ซึ่งรวมถึงการโหลดอึอื่น ๆ ด้วยไม่ใช่แค่ข้อความข้อยกเว้นและข้อความข้อยกเว้นภายใน
ᴍᴀᴛᴛʙᴀᴋᴇʀ

เพียงเพื่อความรัดกุมคุณไม่ต้องการ. ToString () จริง ๆ เพียงแค่ใช้ข้อยกเว้นจะทำเช่นเดียวกัน
Alex Stephens

3
@AlexStephens คุณพูดถูก แต่ถ้าคุณมีการคัดเลือกโดยปริยาย "เป็นสตริง" ด้วยเหตุผลบางอย่างเช่นสตริงก่อนหน้า: "bla" + ข้อยกเว้น
oo_dev

1
FYI: มันจะไม่เรียกToStringวิธีการที่กำหนดเองสำหรับข้อยกเว้นภายในตามรายละเอียดในทำไม System.Exception.ToString โทรเสมือน ToString สำหรับข้อยกเว้นภายใน? .
Jeff B

45

เพียงแค่ใช้ exception.ToString()

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

การใช้งานเริ่มต้นของ ToString รับชื่อของคลาสที่โยนข้อยกเว้นปัจจุบันข้อความผลลัพธ์ของการเรียก ToString บนข้อยกเว้นภายในและผลลัพธ์ของการเรียก Environment.StackTrace ถ้าสมาชิกใด ๆ เหล่านี้เป็นโมฆะค่าจะไม่รวมอยู่ในสตริงที่ส่งคืน

หากไม่มีข้อความแสดงข้อผิดพลาดหรือหากเป็นสตริงว่าง ("") แสดงว่าไม่มีข้อความแสดงข้อผิดพลาดปรากฏขึ้น ชื่อของข้อยกเว้นภายในและการติดตามสแต็กจะถูกส่งคืนเฉพาะในกรณีที่ไม่เป็นโมฆะ

exception.ToString () จะเรียก. ToString () บนข้อยกเว้นภายในของข้อยกเว้นนั้นและ ...


45

ฉันมักจะทำสิ่งนี้เพื่อลบเสียงส่วนใหญ่:

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

    Console.WriteLine(realerror.ToString())
}    

แก้ไข:ฉันลืมเกี่ยวกับคำตอบนี้และไม่แปลกใจที่ไม่มีใครชี้ให้เห็นว่าคุณสามารถทำได้

void LogException(Exception error) {
    Console.WriteLine(error.GetBaseException().ToString())
}    

วิธีนี้ซ่อนทุกอย่างยกเว้นข้อยกเว้นภายในที่ลึกที่สุด หากนั่นเป็นสิ่งที่ผิดปกติเช่น "หารด้วยศูนย์" ข้อผิดพลาดมันจะไม่ชัดเจนว่าเกิดขึ้นที่ไหนและนำไปสู่อะไร เห็นได้ชัดว่าการติดตามสแต็คเต็มรูปแบบมักจะเป็น overkill ยุ่ง แต่การอ่านข้อยกเว้นด้านในเป็นสุดโต่งอื่น ๆ คำตอบของผู้ใช้ 3016982 นั้นดีกว่ามาก คุณได้รับข้อความทุกข้อยกเว้นในกองซ้อนโดยไม่มีการติดตามที่น่ารังเกียจ
JamesHoux

1
@JamesHoux คำตอบ "user3016982" ตัวไหน? หาเขาไม่พบที่นี่
maracuja-juice

ผู้ใช้ 3016982 คือ ThomazMoura โปรดดู: stackoverflow.com/users/3016982/thomazmoura
Apfelkuacha

@JamesHoux ข้อยกเว้นด้านในมีสแต็คสแต็คเต็มแสดงว่าข้อผิดพลาดเกิดขึ้นและสิ่งที่นำไปสู่ ไม่เข้าใจว่ามีข้อมูลพิเศษใดบ้างที่คุณได้รับจากการติดตามสแต็กที่ถูกลบ ข้อความข้อยกเว้นเป็นอีกสิ่งหนึ่งและอาจเป็นประโยชน์ในการรวบรวมทั้งหมด
adrianm

2
error.GetBaseException()ทำไมคุณไม่เพียงแค่ใช้ ฉันเชื่อว่าสิ่งนี้จะทำเช่นเดียวกัน ...
Robba

37

@ คำตอบของ Jon เป็นคำตอบที่ดีที่สุดเมื่อคุณต้องการรายละเอียดทั้งหมด (ข้อความทั้งหมดและการติดตามสแต็ก) และคำแนะนำที่แนะนำ

อย่างไรก็ตามอาจมีบางกรณีเมื่อคุณต้องการข้อความภายในและสำหรับกรณีเหล่านี้ฉันใช้วิธีการต่อไปนี้:

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

ฉันมักจะใช้วิธีนี้เมื่อฉันมีผู้ฟังที่แตกต่างกันสำหรับการติดตามและการบันทึกและต้องการมีมุมมองที่แตกต่างกัน ด้วยวิธีนี้ฉันสามารถมีผู้ฟังหนึ่งคนซึ่งส่งข้อผิดพลาดทั้งหมดด้วยการติดตามสแต็กทางอีเมลไปยังทีม dev สำหรับการดีบักโดยใช้.ToString()วิธีการและหนึ่งที่เขียนล็อกไฟล์โดยมีประวัติข้อผิดพลาดทั้งหมดที่เกิดขึ้นในแต่ละวัน.GetFullMessage()วิธี


7
FYI ถ้าexเป็น a จะAggregateExceptionไม่มีข้อยกเว้นภายในอยู่ในเอาต์พุตนี้
kornman00

3
นี่ควรเป็นวิธีการของ. NET ทุกคนควรใช้สิ่งนี้
JamesHoux

9

หากต้องการพิมพ์สวยMessageๆ ยกเว้นข้อลึกคุณสามารถทำสิ่งนี้:

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

การทำซ้ำนี้จะทำผ่านข้อยกเว้นภายในทั้งหมด (รวมถึงกรณีของAggregateExceptions) เพื่อพิมพ์Messageคุณสมบัติทั้งหมดที่มีอยู่ในนั้นคั่นด้วยการแบ่งบรรทัด

เช่น

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 นอก
Inner aggr เช่น
หมายเลขไม่อยู่ในรูปแบบที่ถูกต้อง
การเข้าถึงไฟล์โดยไม่ได้รับอนุญาต
ไม่ใช่ผู้ดูแลระบบ


คุณจะต้องฟังคุณสมบัติข้อยกเว้นอื่น ๆเพื่อดูรายละเอียดเพิ่มเติม สำหรับเช่นDataจะมีข้อมูลบางอย่าง คุณสามารถทำได้:

foreach (DictionaryEntry kvp in exception.Data)

ในการรับคุณสมบัติที่ได้รับทั้งหมด (ไม่ใช่Exceptionคลาสพื้นฐาน) คุณสามารถทำได้:

exception
    .GetType()
    .GetProperties()
    .Where(p => p.CanRead)
    .Where(p => p.GetMethod.GetBaseDefinition().DeclaringType != typeof(Exception));

+1, นี่เป็นสิ่งเดียวกับที่ฉันทำ อย่ามองหาการใช้งานคุณสมบัติIEnumerable<Exception>แทนการเข้ารหัสฮาร์ดAggregrateExceptionเพื่อจัดการกับชนิดที่คล้ายกันอื่น ๆ ยังแยกp.IsSpecialNameและpi.GetIndexParameters().Length != 0เพื่อหลีกเลี่ยงปัญหา การรวมชื่อประเภทข้อยกเว้นในผลลัพธ์ก็เป็นความคิดที่ดีด้วย
adrianm

@adrianm จุดที่ดีเกี่ยวกับการตรวจสอบข้อมูลสถานที่ให้บริการ เกี่ยวกับการตรวจสอบการรวบรวมข้อยกเว้นทุกอย่างเกี่ยวกับตำแหน่งที่คุณต้องการวาดเส้น แน่นอนว่าสามารถทำได้เช่นกัน ..
nawfal

4

ฉันทำ:

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

"ภาพสวย" พิมพ์ข้อยกเว้นภายในทั้งหมดและจัดการ AggregateExceptions และกรณีที่ InnerException.Message เหมือนกับข้อความ


3

exception.ToString()หากท่านต้องการข้อมูลเกี่ยวกับข้อยกเว้นทั้งหมดแล้วใช้ มันจะรวบรวมข้อมูลจากข้อยกเว้นภายในทั้งหมด

exception.GetBaseException().ToString()หากคุณต้องการเพียงข้อยกเว้นเดิมแล้วใช้ สิ่งนี้จะทำให้คุณได้รับข้อยกเว้นแรกเช่นข้อยกเว้นภายในที่ลึกที่สุดหรือข้อยกเว้นปัจจุบันหากไม่มีข้อยกเว้นภายใน

ตัวอย่าง:

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
}

2

buildup ตามคำตอบของ nawfal

เมื่อใช้คำตอบของเขามีตัวแปร aggrEx ที่ขาดหายไปฉันเพิ่มเข้าไป

ไฟล์ 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;
        }
    }
}

ฉันมีข้อยกเว้นเพราะ:e.StackTrace == null
Andrei Krasutski

1
ฉันได้อัปเดตแล้วเลือก (e => e.Message.Trim () + "\ r \ n" + (e.StackTrace! = null? StackTrace.Trim (): "")); บางทีนี่อาจช่วยได้
Shimon Doodkin
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.