这个全面的答案处理了以下内容:
- 在所有异常中找到的
Data
集合属性(被接受的答案没有做到这一点)。
- 任何其他自定义属性添加到该异常中。
- 递归写出
InnerException
(被接受的答案没有做到这一点)。
- 写出包含在
AggregateException
中的异常集合。
它还以更好的顺序写出异常的属性。它使用C# 6.0,但如果需要,很容易转换为较旧的版本。
public static class ExceptionExtensions
{
public static string ToDetailedString(this Exception exception)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
return ToDetailedString(exception, ExceptionOptions.Default);
}
public static string ToDetailedString(this Exception exception, ExceptionOptions options)
{
var stringBuilder = new StringBuilder();
AppendValue(stringBuilder, "Type", exception.GetType().FullName, options);
foreach (PropertyInfo property in exception
.GetType()
.GetProperties()
.OrderByDescending(x => string.Equals(x.Name, nameof(exception.Message), StringComparison.Ordinal))
.ThenByDescending(x => string.Equals(x.Name, nameof(exception.Source), StringComparison.Ordinal))
.ThenBy(x => string.Equals(x.Name, nameof(exception.InnerException), StringComparison.Ordinal))
.ThenBy(x => string.Equals(x.Name, nameof(AggregateException.InnerExceptions), StringComparison.Ordinal)))
{
var value = property.GetValue(exception, null);
if (value == null && options.OmitNullProperties)
{
if (options.OmitNullProperties)
{
continue;
}
else
{
value = string.Empty;
}
}
AppendValue(stringBuilder, property.Name, value, options);
}
return stringBuilder.ToString().TrimEnd('\r', '\n');
}
private static void AppendCollection(
StringBuilder stringBuilder,
string propertyName,
IEnumerable collection,
ExceptionOptions options)
{
stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
var innerOptions = new ExceptionOptions(options, options.CurrentIndentLevel + 1);
var i = 0;
foreach (var item in collection)
{
var innerPropertyName = $"[{i}]";
if (item is Exception)
{
var innerException = (Exception)item;
AppendException(
stringBuilder,
innerPropertyName,
innerException,
innerOptions);
}
else
{
AppendValue(
stringBuilder,
innerPropertyName,
item,
innerOptions);
}
++i;
}
}
private static void AppendException(
StringBuilder stringBuilder,
string propertyName,
Exception exception,
ExceptionOptions options)
{
var innerExceptionString = ToDetailedString(
exception,
new ExceptionOptions(options, options.CurrentIndentLevel + 1));
stringBuilder.AppendLine($"{options.Indent}{propertyName} =");
stringBuilder.AppendLine(innerExceptionString);
}
private static string IndentString(string value, ExceptionOptions options)
{
return value.Replace(Environment.NewLine, Environment.NewLine + options.Indent);
}
private static void AppendValue(
StringBuilder stringBuilder,
string propertyName,
object value,
ExceptionOptions options)
{
if (value is DictionaryEntry)
{
DictionaryEntry dictionaryEntry = (DictionaryEntry)value;
stringBuilder.AppendLine($"{options.Indent}{propertyName} = {dictionaryEntry.Key} : {dictionaryEntry.Value}");
}
else if (value is Exception)
{
var innerException = (Exception)value;
AppendException(
stringBuilder,
propertyName,
innerException,
options);
}
else if (value is IEnumerable && !(value is string))
{
var collection = (IEnumerable)value;
if (collection.GetEnumerator().MoveNext())
{
AppendCollection(
stringBuilder,
propertyName,
collection,
options);
}
}
else
{
stringBuilder.AppendLine($"{options.Indent}{propertyName} = {value}");
}
}
}
public struct ExceptionOptions
{
public static readonly ExceptionOptions Default = new ExceptionOptions()
{
CurrentIndentLevel = 0,
IndentSpaces = 4,
OmitNullProperties = true
};
internal ExceptionOptions(ExceptionOptions options, int currentIndent)
{
this.CurrentIndentLevel = currentIndent;
this.IndentSpaces = options.IndentSpaces;
this.OmitNullProperties = options.OmitNullProperties;
}
internal string Indent { get { return new string(' ', this.IndentSpaces * this.CurrentIndentLevel); } }
internal int CurrentIndentLevel { get; set; }
public int IndentSpaces { get; set; }
public bool OmitNullProperties { get; set; }
}
顶级提示 - 记录异常
大多数人会使用这段代码进行日志记录。考虑使用Serilog和我的Serilog.Exceptions NuGet包,它还能更快地记录异常的所有属性,而且在大多数情况下不需要反射。Serilog是一个非常先进的日志框架,在编写时十分流行。
顶级提示 - 人类可读的堆栈跟踪
您可以使用Ben.Demystifier NuGet包获取异常的人类可读的堆栈跟踪,或者使用serilog-enrichers-demystify NuGet包(如果您正在使用Serilog)。如果您正在使用.NET Core 2.1,则此功能已内置。
ExceptionToString
函数,它具有微软自己的ExceptionToString
在将字符串复制到剪贴板时所具有的所有有用功能。 - Ian Boyd-2146232060
和异常消息中报告的0x80131904
是相同的数字。因此,这些信息并没有丢失,只是以不同的方式表示。(在这种情况下,十六进制表示法对于大多数目的来说更加“正确”.) - Daniel PrydenHRESULT
的带符号十进制表示形式。 - Ian Boyd