C# Exception.Message 与 Exception.ToString()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2176707/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Exception.Message vs Exception.ToString()
提问by JL.
I have code that is logging Exception.Message
. However, I read an article which states that it's better to use Exception.ToString()
. With the latter, you retain more crucial information about the error.
我有正在记录的代码Exception.Message
。但是,我阅读了一篇文章,其中指出最好使用Exception.ToString()
. 使用后者,您可以保留有关错误的更多关键信息。
Is this true, and is it safe to go ahead and replace all code logging Exception.Message
?
这是真的,继续替换所有代码日志Exception.Message
是否安全?
I'm also using an XML based layout for log4net. Is it possible that Exception.ToString()
may contain invalid XML characters, which may cause issues?
我还为log4net使用了基于 XML 的布局。是否Exception.ToString()
可能包含无效的 XML 字符,这可能会导致问题?
采纳答案by J?rn Schou-Rode
Exception.Message
contains only the message (doh) associated with the exception. Example:
Exception.Message
仅包含与异常关联的消息 (doh)。例子:
Object reference not set to an instance of an object
你调用的对象是空的
The Exception.ToString()
method will give a much more verbose output, containing the exception type, the message (from before), a stack trace, and all of these things again for nested/inner exceptions. More precisely, the method returns the following:
该Exception.ToString()
方法将提供更详细的输出,包含异常类型、消息(来自之前)、堆栈跟踪以及所有这些再次用于嵌套/内部异常的内容。更准确地说,该方法返回以下内容:
ToString returns a representation of the current exception that is intended to be understood by humans. Where the exception contains culture-sensitive data, the string representation returned by ToString is required to take into account the current system culture. Although there are no exact requirements for the format of the returned string, it should attempt to reflect the value of the object as perceived by the user.
The default implementation of ToString obtains the name of the class that threw the current exception, the message, the result of calling ToString on the inner exception, and the result of calling Environment.StackTrace. If any of these members is a null reference (Nothing in Visual Basic), its value is not included in the returned string.
If there is no error message or if it is an empty string (""), then no error message is returned. The name of the inner exception and the stack trace are returned only if they are not a null reference (Nothing in Visual Basic).
ToString 返回旨在被人类理解的当前异常的表示。如果异常包含区域性敏感数据,则需要 ToString 返回的字符串表示形式将当前系统区域性考虑在内。虽然对返回字符串的格式没有确切的要求,但它应该尝试反映用户感知的对象的值。
ToString 的默认实现获取抛出当前异常的类的名称、消息、对内部异常调用 ToString 的结果以及调用 Environment.StackTrace 的结果。如果这些成员中的任何一个是空引用(在 Visual Basic 中为 Nothing),则其值不包含在返回的字符串中。
如果没有错误消息或者它是一个空字符串 (""),则不会返回任何错误消息。内部异常的名称和堆栈跟踪仅在它们不是空引用(在 Visual Basic 中为 Nothing)时才返回。
回答by Thorsten Dittmar
Well, I'd say it depends what you want to see in the logs, doesn't it? If you're happy with what ex.Message provides, use that. Otherwise, use ex.toString() or even log the stack trace.
好吧,我想说这取决于你想在日志中看到什么,不是吗?如果您对 ex.Message 提供的内容感到满意,请使用它。否则,使用 ex.toString() 甚至记录堆栈跟踪。
回答by Carra
Depends on the information you need. For debugging the stack trace & inner exception are useful:
取决于您需要的信息。对于调试堆栈跟踪和内部异常很有用:
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";
}
回答by Wim Hollebrandse
In addition to what's already been said, don'tuse ToString()
on the exception object for displaying to the user. Just the Message
property should suffice, or a higher level custom message.
除了已经说过的,不要ToString()
在异常对象上使用来显示给用户。仅Message
属性就足够了,或者更高级别的自定义消息。
In terms of logging purposes, definitely use ToString()
on the Exception, not just the Message
property, as in most scenarios, you will be left scratching your head where specifically this exception occurred, and what the call stack was. The stacktrace would have told you all that.
就日志目的而言,一定ToString()
要在 Exception 上使用,而不仅仅是Message
属性,因为在大多数情况下,您会摸不着头脑,特别是在此异常发生的地方,以及调用堆栈是什么。堆栈跟踪会告诉你所有这些。
回答by Christian.K
I'd say @Wim is right. You should use ToString()
for logfiles - assuming a technical audience - and Message
, if at all, to display to the user. One could argue that even that is not suitable for a user, for every exception type and occurance out there (think of ArgumentExceptions, etc.).
我会说@Wim 是对的。您应该使用ToString()
for 日志文件 - 假设是技术受众 - 并且Message
,如果有的话,显示给用户。有人可能会争辩说,即使这样也不适合用户,对于每种异常类型和出现的情况(想想 ArgumentExceptions 等)。
Also, in addition to the StackTrace, ToString()
will include information you will not get otherwise. For example the output of fusion, if enabledto include log messages in exception "messages".
此外,除了 StackTrace 之外,ToString()
还将包括您不会以其他方式获得的信息。例如融合的输出,如果启用以在异常“消息”中包含日志消息。
Some exception types even include additional information (for example from custom properties) in ToString()
, but not in the Message.
某些异常类型甚至在 中包含附加信息(例如来自自定义属性)ToString()
,但不在消息中。
回答by Dillie-O
In terms of the XML format for log4net, you need not worry about ex.ToString() for the logs. Simply pass the exception object itself and log4net does the rest do give you all of the details in its pre-configured XML format. The only thing I run into on occasion is new line formatting, but that's when I'm reading the files raw. Otherwise parsing the XML works great.
对于 log4net 的 XML 格式,您不必担心日志的 ex.ToString()。只需传递异常对象本身,其余的 log4net 会以预先配置的 XML 格式为您提供所有详细信息。我偶尔遇到的唯一问题是换行格式,但那是我读取原始文件的时候。否则解析 XML 效果很好。
回答by Muhammad Rehan Saeed
Converting the WHOLE Exception To a String
将整个异常转换为字符串
Calling Exception.ToString()
gives you more information than just using the Exception.Message
property. However, even this still leaves out lots of information, including:
调用Exception.ToString()
可为您提供更多信息,而不仅仅是使用该Exception.Message
属性。然而,即使这样仍然遗漏了很多信息,包括:
- The
Data
collection property found on all exceptions. - Any other custom properties added to the exception.
Data
在所有异常中找到的集合属性。- 添加到异常中的任何其他自定义属性。
There are times when you want to capture this extra information. The code below handles the above scenarios. It also writes out the properties of the exceptions in a nice order. It's using C# 7 but should be very easy for you to convert to older versions if necessary. See also thisrelated answer.
有时您想要捕获这些额外信息。下面的代码处理上述场景。它还以良好的顺序写出异常的属性。它使用 C# 7,但如果需要,您应该很容易转换为旧版本。另请参阅此相关答案。
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; }
}
Top Tip - Logging Exceptions
重要提示 - 记录异常
Most people will be using this code for logging. Consider using Serilogwith my Serilog.ExceptionsNuGet package which also logs all properties of an exception but does it faster and without reflection in the majority of cases. Serilog is a very advanced logging framework which is all the rage at the time of writing.
大多数人将使用此代码进行日志记录。考虑将Serilog与我的Serilog.ExceptionsNuGet 包一起使用,该包也记录异常的所有属性,但在大多数情况下速度更快且无需反射。Serilog 是一个非常先进的日志框架,在撰写本文时风靡一时。
Top Tip - Human Readable Stack Traces
重要提示 - 人类可读的堆栈跟踪
You can use the Ben.DemystifierNuGet package to get human readable stack traces for your exceptions or the serilog-enrichers-demystifyNuGet package if you are using Serilog.
你可以使用Ben.DemystifierNuGet 包来获取人类可读的异常堆栈跟踪,如果你使用的是 Serilog,则可以使用 serilog -enrichers-demystifyNuGet 包。