显示完整的InnerException的正确方法是什么?


155

什么是展示我的全部的正确方法InnerException

我发现我的一些InnerExceptions还有另一个InnerException,而且还很深入。

InnerException.ToString()做的工作,我还是我通过需要循环InnerExceptions,建立一个StringStringBuilder


为什么需要显示内部异常?
Akram Shahda

26
@Akram,因为大多数时候它是有趣的内部异常。一个示例是XmlSerializer,它在出现问题时仅引发InvalidOperationException。出问题的是内部异常。
阿德里安

4
@AkramShahda好吧,也许您想在日志记录中使用此方法?
塞德洛夫2012年

Answers:


239

您可以简单地打印exception.ToString()-还将包括所有嵌套InnerExceptions的全文。


18
这也包括其他废话的负载,而不仅仅是异常消息和内部异常消息
2016年

仅出于简洁起见,您实际上不需要.ToString(),仅使用exception即可。
亚历克斯·史蒂芬斯

3
@AlexStephens是正确的,但仅在出于某种原因(例如前面的字符串)“ bla” +异常而隐式将“ to string”强制转换为“ to string”时
oo_dev

1
仅供参考:它不会ToString为内部异常调用自定义方法,如为何System.Exception.ToString不为内部异常调用虚拟ToString所述。
Jeff B

45

只需使用 exception.ToString()

http://msdn.microsoft.com/zh-CN/library/system.exception.tostring.aspx

ToString的默认实现获取引发当前异常的类的名称,消息,在内部异常上调用ToString的结果以及调用Environment.StackTrace的结果。如果这些成员中的任何一个为null,则其值不包含在返回的字符串中。

如果没有错误消息,或者它是一个空字符串(“”),则不会返回任何错误消息。内部异常的名称和堆栈跟踪仅在它们不为null时返回。

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

此方法隐藏除最深层的内部异常以外的所有内容。如果那是一个普通的事情,例如“除以零”错误,则不清楚它发生在哪里以及导致它的原因。显然,完整的堆栈跟踪通常是一个混乱的矫over过正,但是仅读取内部异常是另一个极端。user3016982的答案要好得多。您会在堆栈中获得所有异常消息,而不会产生令人讨厌的跟踪。
JamesHoux

1
@JamesHoux“ user3016982”的答案是哪一个?在这里找不到他。
maracuja-juice

user3016982是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();
    }
}

当我有不同的侦听器进行跟踪和记录并想对它们有不同的视图时,我经常使用此方法。这样,我可以拥有一个侦听器,该侦听器通过该方法将带有堆栈跟踪的整个错误通过电子邮件发送给开发团队,以使用该.ToString()方法进行调试;还有一个侦听器,该日志将记录每天发生的所有错误的历史记录而无需使用堆栈跟踪的日志文件。的.GetFullMessage()方法。


7
仅供参考,如果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前。
数字格式不正确。
未经授权的文件访问。
不是管理员。


您将需要收听其他Exception属性以获取更多详细信息。例如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.IsSpecialNamepi.GetIndexParameters().Length != 0避免出现问题。在输出中包括异常类型名称也是一个好主意
adrianm '18

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

此“漂亮打印”所有内部异常,还处理AggregateException和InnerException.Message与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

建立在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
我已经更新了.Select(e => e.Message.Trim()+“ \ r \ n” +(e.StackTrace!= null?StackTrace.Trim():“”)); 也许有帮助
Shimon Doodkin
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.