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

本文关键字:方法 是什么 InnerException 显示 | 更新日期: 2023-09-27 17:55:17

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

我发现我的一些 InnerExceptions 还有另一个InnerException而且进展得很深。

InnerException.ToString()会为我完成这项工作,还是我需要循环InnerExceptions并与StringBuilder建立String

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

您可以

简单地打印exception.ToString() - 这也将包括所有嵌套InnerException的全文。

我通常这样做来消除大部分噪音:

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

只需使用exception.ToString()

https://learn.microsoft.com/en-us/dotnet/api/system.exception.tostring#remarks

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

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

例外。ToString() 也会调用 。ToString() 在该异常的内部异常上,依此类推...

当您

需要完整的详细信息(所有消息和堆栈跟踪)和推荐的答案时,@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() 方法进行堆栈跟踪。

要漂亮地打印深度异常的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;
        }
    }
}

这将递归地遍历所有内部异常(包括 AggregateException s 的情况),以打印其中包含的所有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());

发生外部聚合前。
内聚合前
数字格式不正确。
未经授权的文件访问。
不是管理员。


您将需要侦听其他异常属性以获取更多详细信息。例如 Data将提供一些信息。你可以做:

foreach (DictionaryEntry kvp in exception.Data)

要获取所有派生属性(不在基类Exception上),您可以执行以下操作:

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

我愿意:

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 与 Message 相同的情况

如果您使用的是实体框架,exception.ToString()不会提供DbEntityValidationException异常的详细信息。您可能希望使用相同的方法来处理所有异常,例如:

catch (Exception ex)
{
   Log.Error(GetExceptionDetails(ex));
}

其中GetExceptionDetails包含如下内容:

public static string GetExceptionDetails(Exception ex)
{
    var stringBuilder = new StringBuilder();
    while (ex != null)
    {
        switch (ex)
        {
            case DbEntityValidationException dbEx:
                var errorMessages = dbEx.EntityValidationErrors.SelectMany(x => x.ValidationErrors).Select(x => x.ErrorMessage);
                var fullErrorMessage = string.Join("; ", errorMessages);
                var message = string.Concat(ex.Message, " The validation errors are: ", fullErrorMessage);
                stringBuilder.Insert(0, dbEx.StackTrace);
                stringBuilder.Insert(0, message);
                break;
            default:
                stringBuilder.Insert(0, ex.StackTrace);
                stringBuilder.Insert(0, ex.Message);
                break;
        }
        ex = ex.InnerException;
    }
    return stringBuilder.ToString();
}

如果需要有关所有异常的信息,请使用 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
}
建立在

Nawfal的答案上。

当使用他的答案时,缺少一个变量 aggrEx,我添加了它。

文件异常扩展.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;
        }
    }
}
我认为

这个更好

public static string GetCompleteMessage(this Exception error)
    {
        System.Text.StringBuilder builder = new StringBuilder();
        Exception realerror = error;
        builder.AppendLine(error.Message);
        while (realerror.InnerException != null)
        {
            builder.AppendLine(realerror.InnerException.Message);
            realerror = realerror.InnerException;
        }
        return builder.ToString();
    }

此代码生成异常的格式化HTML表示形式:

const string _HTML_TAB = "&nbsp;&nbsp;&nbsp;";
public static string ToHtmlString(this Exception ex, int level = 0)
{
    string message = GetText("Message", ex.Message, level);
    if (ex.InnerException != null && level < 30)
    {
        message += ToHtmlString(ex.InnerException, level + 1);
    }
    else
    {
        message += GetText("StackTrace", ex.StackTrace, level); ;
        message += GetText("Source", ex.Source, level); ;
        message += GetText("TargetSite", ex.TargetSite.ToString(), level);
    }
    return message;
}
private static string GetText(string headline, string text, int level)
{
    var indentText = string.Join(_HTML_TAB, new string[level + 1]);
    var newLine = $"<br />{indentText}{_HTML_TAB}";
    return $"{indentText}<b>{headline}</b>{newLine}"
            + $"{text.Replace(Environment.NewLine, newLine)}<br /><br />";
}