Exception.Message vs Exception.ToString()

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).

  • +1在日志中仅查看"对象引用未设置为对象的实例"非常痛苦.你真的很无奈.:-) (78认同)
  • 看到我编写的代码基本上与ToString()完全相同,这是非常痛苦的. (45认同)
  • 请注意,它是“ToString 的默认实现”...(强调“默认”)...这并不意味着每个人都遵循了任何自定义例外的做法。#learnedTheHardWay (2认同)

Wim*_*dse 49

除了已经说过的内容之外,不要ToString()在异常对象上使用以显示给用户.只是Message属性应该足够,或更高级别的自定义消息.

在日志记录方面,绝对使用ToString()异常,而不仅仅是Message属性,就像在大多数情况下一样,你会在特别发生这种异常的地方留下头脑,以及调用堆栈是什么.堆栈跟踪会告诉你所有这些.

  • 如果您在日志中使用 ToString(),请确保 ToString 中不包含敏感信息 (2认同)

Muh*_*eed 20

正如@JornSchouRode所指出的那样,做一个Exception.ToString()为您提供的信息不仅仅是使用该Exception.Message属性.但是,即便如此,仍然会留下大量信息,包括:

  1. Data在所有异常中找到的集合属性.
  2. 添加到例外的任何其他自定义属性.

有时您想要捕获这些额外信息.下面的代码处理上述场景.它还以良好的顺序写出异常的属性.它使用的是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包.


Chr*_*n.K 9

我会说@Wim是对的.您应该使用ToString()日志文件 - 假设技术受众 - 并且Message如果有的话,显示给用户.有人可能会争辩说,即使这样也不适合用户,因为每个异常类型和出现(想想ArgumentExceptions等).

此外,除了StackTrace之外,ToString()还将包含您不会获得的信息.例如,Fusion的输出,如果允许在异常"消息"中包括日志消息.

某些异常类型甚至包含其他信息(例如来自自定义属性)ToString(),但不包含在Message中.


Car*_*rra 8

取决于您需要的信息.对于调试,堆栈跟踪和内部异常非常有用:

    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)

  • 这或多或少是`Exception.ToString()`会给你的,对吧? (11认同)
  • 使用stringbuilder (5认同)
  • @Matt:在这个场景中构造一个`StringBuilder`的实例可能比两个新的字符串分配更昂贵,这是非常有争议的,它在这里会更有效率.这不像我们正在处理迭代.马匹课程. (5认同)
  • 只需使用ex.ToString.它可以为您提供所有细节. (5认同)
  • @Christian:编译器是多个+ s的理智.例如,参见"+运算符易于使用并生成直观的代码.即使在一个语句中使用多个+运算符,字符串内容也只复制一次." 来自http://msdn.microsoft.com/en-us/library/ms228504.aspx (3认同)
  • 这里的问题是,你只能获得最外层异常的"InnerException".IOW,如果InnerException本身有一个InnerException集,你就不会转储它(假设你想要首先).我真的坚持使用ToString(). (2认同)