JL.*_*JL. 192 .net c# exception-handling exception
我有记录的代码Exception.Message.但是,我读了一篇文章,说明最好使用它Exception.ToString().使用后者,您可以保留有关错误的更重要信息.
这是真的,继续更换所有代码记录Exception.Message是否安全?
我也在为log4net使用基于XML的布局.是否Exception.ToString()可能包含无效的XML字符,这可能会导致问题?
Jør*_*ode 258
Exception.Message仅包含与异常关联的消息(doh).例:
你调用的对象是空的
该Exception.ToString()方法将提供更详细的输出,包含异常类型,消息(来自之前),堆栈跟踪以及嵌套/内部异常的所有这些内容.更准确地说,该方法返回以下内容:
ToString返回人类应该理解的当前异常的表示.如果异常包含区分文化的数据,则ToString返回的字符串表示形式需要考虑当前的系统区域性.尽管对返回的字符串的格式没有确切的要求,但它应该尝试反映用户所感知的对象的值.
ToString的默认实现获取抛出当前异常的类的名称,消息,在内部异常上调用ToString的结果,以及调用Environment.StackTrace的结果.如果这些成员中的任何一个是空引用(在Visual Basic中为Nothing),则其值不包含在返回的字符串中.
如果没有错误消息或者它是空字符串(""),则不返回任何错误消息.仅当内部异常和堆栈跟踪不是空引用时才返回它们的名称(在Visual Basic中为Nothing).
Wim*_*dse 49
除了已经说过的内容之外,不要ToString()在异常对象上使用以显示给用户.只是Message属性应该足够,或更高级别的自定义消息.
在日志记录方面,绝对使用ToString()异常,而不仅仅是Message属性,就像在大多数情况下一样,你会在特别发生这种异常的地方留下头脑,以及调用堆栈是什么.堆栈跟踪会告诉你所有这些.
Muh*_*eed 20
正如@JornSchouRode所指出的那样,做一个Exception.ToString()为您提供的信息不仅仅是使用该Exception.Message属性.但是,即便如此,仍然会留下大量信息,包括:
Data在所有异常中找到的集合属性.有时您想要捕获这些额外信息.下面的代码处理上述场景.它还以良好的顺序写出异常的属性.它使用的是C#6.0,但如果需要,您应该很容易转换为旧版本.又见此相关的答案.
public static class ExceptionExtensions
{
public static string ToDetailedString(this Exception exception) =>
ToDetailedString(exception, ExceptionOptions.Default);
public static string ToDetailedString(this Exception exception, ExceptionOptions options)
{
if (exception == null)
{
throw new ArgumentNullException(nameof(exception));
}
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; }
}
Run Code Online (Sandbox Code Playgroud)
大多数人都会使用此代码进行日志记录.考虑将Serilog与我的Serilog.Exceptions NuGet包一起使用,它还记录了异常的所有属性,但在大多数情况下更快,没有反射.Serilog是一个非常先进的日志框架,在撰写本文时风靡一时.
您可以使用Ben.Demystifier NuGet包为您的异常获取人类可读的堆栈跟踪,或者如果您使用Serilog,则可以使用serilog -enrichers-demystify NuGet包.
取决于您需要的信息.对于调试,堆栈跟踪和内部异常非常有用:
string message =
"Exception type " + ex.GetType() + Environment.NewLine +
"Exception message: " + ex.Message + Environment.NewLine +
"Stack trace: " + ex.StackTrace + Environment.NewLine;
if (ex.InnerException != null)
{
message += "---BEGIN InnerException--- " + Environment.NewLine +
"Exception type " + ex.InnerException.GetType() + Environment.NewLine +
"Exception message: " + ex.InnerException.Message + Environment.NewLine +
"Stack trace: " + ex.InnerException.StackTrace + Environment.NewLine +
"---END Inner Exception";
}
Run Code Online (Sandbox Code Playgroud)