Exception.Message vs Exception.ToString()
我有loggingException.Message
代码。 不过,我读了一篇文章,指出最好使用Exception.ToString()
。 使用后者,您可以保留有关错误的更多重要信息。
这是真的,是否安全地继续并取代所有代码日志loggingException.Message
?
我也使用基于XML的布局log4net 。 Exception.ToString()
可能包含无效的XML字符,这可能会导致问题吗?
Exception.Message
只包含与exception相关的消息(doh)。 例:
你调用的对象是空的
Exception.ToString()
方法将给出更详细的输出,包含exceptiontypes,消息(从前),堆栈跟踪,以及所有这些事情嵌套/内部exception。 更确切地说,该方法返回以下内容:
ToString返回当前意图被人类理解的exception表示。 如果exception包含文化敏感数据,则ToString返回的string表示需要考虑当前的系统文化。 尽pipe对返回string的格式没有确切的要求,但它应该试图反映用户感知的对象的价值。
ToString的默认实现获取抛出当前exception的类的名称,消息,对内部exception调用ToString的结果以及调用Environment.StackTrace的结果。 如果这些成员中的任何一个是空引用(在Visual Basic中为Nothing),则其值不会包含在返回的string中。
如果没有错误信息或者是空string(“”),则不返回错误信息。 只有内部exception和堆栈跟踪不是空引用(在Visual Basic中为Nothing)时,才会返回内部exception和堆栈跟踪的名称。
除了已经说过的之外, 不要在exception对象上使用ToString()
来显示给用户。 只要Message
属性就足够了,或更高级别的自定义消息。
在日志logging方面,绝对使用Exception的ToString()
,不仅仅是Message
属性,就像在大多数场景中一样,在发生这种exception的时候,你会被抓到头,以及调用堆栈是什么。 堆栈跟踪会告诉你这一切。
取决于你需要的信息。 debugging堆栈跟踪和内部exception是有用的:
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"; }
我会说@Wim是正确的。 您应该使用ToString()
作为日志文件(假定是技术受众),然后使用Message
(如果有的话)显示给用户。 有人可能会争辩说,即使这不适合用户,每个exceptiontypes和发生在那里(想到ArgumentExceptions等)。
此外,除了StackTrace之外, ToString()
还会包含您不会另外获取的信息。 例如融合的输出(如果启用时将日志消息包含在exception“消息”中)。
某些exceptiontypes甚至在ToString()
包含附加信息(例如来自自定义属性ToString()
,但不包含在消息中。
正如@JornSchouRode指出的那样,做一个exception.ToString()
.ToString exception.ToString()
给你更多的信息,而不仅仅是使用exception.Message
属性。 但是,即使这样也遗漏了很多信息,包括:
-
Data
收集属性find所有例外。 - 任何其他自定义属性添加到例外。
有时候你想要捕获这些额外的信息。 以下代码处理上述情况。 它还以很好的顺序写出exception的属性。 它使用的是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; } }
重要提示 – loggingexception
大多数人将使用此代码进行日志logging。 考虑使用Serilog与我的Serilog.Exceptions NuGet软件包,它也logging一个exception的所有属性,但在大多数情况下更快,没有反映。 Serilog是一个非常先进的日志logging框架,在写作的时候风靡一时。
顶端提示 – 人类可读栈跟踪
您可以使用Ben.Demystifier NuGet软件包为您的例外或者serilog-enrichers(如果您使用Serilog的话,可以解开 NuGet软件包)获取人类可读的堆栈跟踪。
就log4net的XML格式而言,您不必担心日志的ex.ToString()。 简单地传递这个exception对象本身,log4net会给你所有的预先configuration的XML格式的细节。 我遇到的唯一的事情是新的行格式,但是当我正在阅读文件的原始。 否则,parsingXML的效果很好。
那么,我想说这取决于你想在日志中看到什么,不是吗? 如果你对ex.Message提供的东西感到满意,那就使用它。 否则,使用ex.toString()或甚至logging堆栈跟踪。