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

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

我发现我的一些 InnerException 有另一个 InnerException,这个值很高。

InnerException.ToString()将为我做这项工作还是我需要通过 InnerExceptions循环和建立一个 StringStringBuilder

127285 次浏览

您可以简单地打印 exception.ToString()——它也将包含所有嵌套 InnerException的全文。

exception.ToString()

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

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

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

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

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

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

当你需要完整的细节(所有的消息和堆栈跟踪)和推荐的信息时,@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的情况) ,以打印其中包含的所有 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));

如果你想要所有异常的信息,那么使用 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,我添加了它。

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

我愿意:

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

这个“漂亮打印”所有内部异常,还处理 AggreateExceptions 和 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();
}

我觉得这个更好

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