Exception.Message vs Exception.ToString ()


207

Tôi có mã đang đăng nhập Exception.Message. Tuy nhiên, tôi đã đọc một bài báo nói rằng tốt hơn là sử dụngException.ToString() . Với cái sau, bạn giữ lại thông tin quan trọng hơn về lỗi.

Điều này có đúng không, và có an toàn để tiếp tục và thay thế tất cả các bản ghi mã Exception.Messagekhông?

Tôi cũng đang sử dụng bố cục dựa trên XML cho log4net . Có thể Exception.ToString()có chứa các ký tự XML không hợp lệ, có thể gây ra sự cố không?


1
Bạn cũng nên xem ELMAH ( code.google.com/p/elmah ) - Một khung rất dễ sử dụng để ghi nhật ký lỗi cho ASP.NET.
Ashish Gupta

Câu trả lời:


278

Exception.Messagechỉ chứa thông điệp (doh) liên quan đến ngoại lệ. Thí dụ:

Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng

Các Exception.ToString()phương pháp sẽ cho một nhiều hơn nữa tiết ra, có chứa các loại ngoại lệ, thông điệp (từ trước đó), một chồng dấu vết, và tất cả những điều này một lần nữa cho lồng / trường hợp ngoại lệ bên trong. Chính xác hơn, phương thức trả về như sau:

ToString trả về một đại diện của ngoại lệ hiện tại mà con người dự định sẽ hiểu. Trong trường hợp ngoại lệ chứa dữ liệu nhạy cảm về văn hóa, việc biểu diễn chuỗi được trả về bởi ToString là bắt buộc để tính đến văn hóa hệ thống hiện tại. Mặc dù không có yêu cầu chính xác cho định dạng của chuỗi trả về, nhưng nó nên cố gắng phản ánh giá trị của đối tượng theo cảm nhận của người dùng.

Việc triển khai mặc định của ToString có được tên của lớp đã ném ngoại lệ hiện tại, thông báo, kết quả của việc gọi ToString trên ngoại lệ bên trong và kết quả của việc gọi môi trường.StackTrace. Nếu bất kỳ thành viên nào trong số này là tham chiếu null (Không có gì trong Visual Basic), giá trị của nó không được bao gồm trong chuỗi trả về.

Nếu không có thông báo lỗi hoặc nếu đó là một chuỗi trống (""), thì không có thông báo lỗi nào được trả về. Tên của ngoại lệ bên trong và dấu vết ngăn xếp chỉ được trả về nếu chúng không phải là tham chiếu null (Không có gì trong Visual Basic).


85
+1 Rất đau đớn khi thấy CHỈ rằng "Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng" trong nhật ký. Bạn cảm thấy thực sự bất lực. :-)
Ashish Gupta

1
Đối với phần cuối cùng, có những Ngoại lệ không đi kèm với Exception.Message. Trong chức năng của những gì bạn làm trong phần xử lý lỗi, bạn có thể gặp sự cố do Exception.Message.
San hô Doe

49
Thật đau đớn khi thấy rằng tôi đã viết mã về cơ bản thực hiện chính xác điều tương tự mà ToString () làm.
Preston McCormick

1
@KunalGoel Nếu nhật ký đến từ prod và bạn không có dấu hiệu nào cho thấy đầu vào là gì, thì không, bạn không thể "gỡ lỗi bằng cách bật ngoại lệ CLR".
jpmc26

1
Lưu ý, đó là "triển khai mặc định của ToString" ... (nhấn mạnh vào "mặc định") .. điều đó không có nghĩa là mọi người đều tuân theo thực tiễn đó với bất kỳ ngoại lệ tùy chỉnh nào. #learnedTheHardWay
granadaCoder

52

Ngoài những gì đã được nói, không sử dụng ToString()trên đối tượng ngoại lệ để hiển thị cho người dùng. Chỉ cần Messagetài sản là đủ, hoặc một thông điệp tùy chỉnh cấp cao hơn.

Về mục đích ghi nhật ký, chắc chắn sử dụng ToString()trên Ngoại lệ, không chỉ thuộc Messagetính, như trong hầu hết các trường hợp, bạn sẽ phải gãi đầu trong trường hợp cụ thể ngoại lệ này xảy ra và ngăn xếp cuộc gọi là gì. Các stacktrace sẽ nói với bạn tất cả điều đó.


Nếu bạn đang sử dụng ToString () trong nhật ký, hãy đảm bảo không bao gồm thông tin nhạy cảm trong ToString
Michael Freidgeim

22

Chuyển đổi ngoại lệ WHOLE thành chuỗi

Gọi điện Exception.ToString()cho bạn nhiều thông tin hơn là chỉ sử dụng Exception.Messagetài sản. Tuy nhiên, ngay cả điều này vẫn để lại nhiều thông tin, bao gồm:

  1. Các Data tài sản bộ sưu tập được tìm thấy trên tất cả các ngoại lệ.
  2. Bất kỳ thuộc tính tùy chỉnh khác được thêm vào ngoại lệ.

Có những lúc bạn muốn nắm bắt thông tin thêm này. Mã dưới đây xử lý các tình huống trên. Nó cũng viết ra các thuộc tính của các ngoại lệ theo một thứ tự tốt đẹp. Đó là sử dụng C # 7 nhưng sẽ rất dễ dàng để bạn chuyển đổi sang các phiên bản cũ hơn nếu cần thiết. Xem thêm câu trả lời liên quan này .

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; }
}

Mẹo hàng đầu - Ghi nhật ký ngoại lệ

Hầu hết mọi người sẽ sử dụng mã này để đăng nhập. Cân nhắc sử dụng Serilog với gói NuGet Serilog.Exceptions của tôi , nó cũng ghi lại tất cả các thuộc tính của một ngoại lệ nhưng nó nhanh hơn và không có sự phản chiếu trong phần lớn các trường hợp. Serilog là một khung đăng nhập rất tiên tiến, đó là tất cả các cơn thịnh nộ tại thời điểm viết.

Mẹo hàng đầu - Dấu vết ngăn xếp có thể đọc được của con người

Bạn có thể sử dụng Ben.Demystifier gói NuGet để có được đống dấu vết có thể đọc được con người cho trường hợp ngoại lệ của bạn hoặc serilog-enrichers-sáng tỏ gói NuGet nếu bạn đang sử dụng Serilog.


9

Tôi muốn nói @Wim đúng. Bạn nên sử dụng ToString()cho logfiles - giả sử đối tượng kỹ thuật - vàMessage , nếu có, để hiển thị cho người dùng. Người ta có thể lập luận rằng thậm chí điều đó không phù hợp với người dùng, cho mọi loại ngoại lệ và sự xuất hiện ngoài kia (nghĩ về ArgumentExceptions, v.v.).

Ngoài ra, ngoài StackTrace, ToString()sẽ bao gồm thông tin bạn sẽ không nhận được bằng cách khác. Ví dụ đầu ra của phản ứng tổng hợp, nếu kích hoạt để bao gồm các thông điệp tường trình trong "thông điệp" ngoại lệ.

Một số loại ngoại lệ thậm chí bao gồm thông tin bổ sung (ví dụ từ các thuộc tính tùy chỉnh) trong ToString(), nhưng không có trong Thông báo.


8

Phụ thuộc vào thông tin bạn cần. Để gỡ lỗi theo dõi ngăn xếp & ngoại lệ bên trong rất hữu ích:

    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";
    }

12
Đây là nhiều hay ít những gì Exception.ToString()sẽ cung cấp cho bạn, phải không?
Jørn Schou-Rode

5
@Matt: Xây dựng một thể hiện StringBuildertrong kịch bản này có thể tốn kém hơn hai phân bổ chuỗi mới, điều gây tranh cãi là nó sẽ hiệu quả hơn ở đây. Nó không giống như chúng ta đang đối phó với các lần lặp. Ngựa cho các khóa học.
Wim Hollebrandse

2
Vấn đề ở đây là, bạn sẽ chỉ nhận được "InternalException" của ngoại lệ ngoài cùng. IOW, nếu bản thân InternalException có bộ InternalException, bạn sẽ không bỏ nó (giả sử rằng bạn muốn ở vị trí đầu tiên). Tôi thực sự gắn bó với ToString ().
Christian.K

6
Chỉ cần sử dụng ex.ToString. Nó giúp bạn có tất cả các chi tiết.
John Saunders

3
@Christian: Trình biên dịch lành mạnh với nhiều + s. Xem ví dụ "Toán tử + dễ sử dụng và tạo mã trực quan. Ngay cả khi bạn sử dụng nhiều toán tử + trong một câu lệnh, nội dung chuỗi chỉ được sao chép một lần." từ msdn.microsoft.com/en-us/l Library / ms228504.aspx
David Eison

3

Về định dạng XML cho log4net, bạn không cần phải lo lắng về ex.ToString () cho các bản ghi. Chỉ cần truyền chính đối tượng ngoại lệ và log4net, phần còn lại sẽ cung cấp cho bạn tất cả các chi tiết theo định dạng XML được định cấu hình trước. Điều duy nhất tôi tình cờ gặp phải là định dạng dòng mới, nhưng đó là khi tôi đọc các tệp thô. Nếu không, phân tích cú pháp XML hoạt động tuyệt vời.


0

Chà, tôi muốn nói nó phụ thuộc vào những gì bạn muốn thấy trong nhật ký, phải không? Nếu bạn hài lòng với những gì ex.Message cung cấp, hãy sử dụng nó. Nếu không, sử dụng ex.toString () hoặc thậm chí ghi nhật ký theo dõi ngăn xếp.


5
ex.ToString bao gồm theo dõi ngăn xếp
John Saunders
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.