[C#] Exception.Message와 Exception.ToString ()

로깅하는 코드가 있습니다 Exception.Message. 그러나 사용하는 것이 더 낫다는 기사를 읽었습니다 Exception.ToString(). 후자는 오류에 대한보다 중요한 정보를 유지합니다.

이것이 사실 Exception.Message입니까? 계속 진행하고 모든 코드 로깅을 바꾸는 것이 안전 합니까?

log4net에 XML 기반 레이아웃을 사용하고 있습니다. 그것은 가능성이 Exception.ToString()문제가 발생할 수있는 잘못된 XML 문자를 포함 할 수 있습니다?



답변

Exception.Message예외와 관련된 메시지 (doh) 만 포함합니다. 예:

객체 참조가 객체의 인스턴스로 설정되지 않았습니다

Exception.ToString()메소드는 예외 유형, 메시지 (이전부터), 스택 추적 및 중첩 / 내부 예외에 대한 이러한 모든 사항을 포함하는 훨씬 더 자세한 출력을 제공합니다. 보다 정확하게는이 메서드는 다음을 반환합니다.

ToString은 사람이 이해하려고하는 현재 예외의 표현을 반환합니다. 예외에 문화권 구분 데이터가 포함 된 경우 현재 시스템 문화권을 고려하려면 ToString에서 반환 한 문자열 표현이 필요합니다. 반환 된 문자열의 형식에 대한 정확한 요구 사항은 없지만 사용자가 인식 한대로 개체의 값을 반영해야합니다.

ToString의 기본 구현은 현재 예외를 발생시킨 클래스 이름, 메시지, 내부 예외에서 ToString을 호출 한 결과 및 Environment.StackTrace를 호출 한 결과를 얻습니다. 이러한 멤버 중 하나가 null 참조 (Visual Basic의 경우 Nothing)이면 해당 값이 반환 된 문자열에 포함되지 않습니다.

오류 메시지가 없거나 빈 문자열 ( “”)이면 오류 메시지가 반환되지 않습니다. 내부 예외 이름과 스택 추적은 Null 참조가 아닌 경우에만 반환됩니다 (Visual Basic의 경우 Nothing).


답변

이미 말한 것 외에도 예외 객체를 사용 하여 사용자에게 표시 하지 마십시오ToString() . 그냥 Message속성은 충분, 또는 높은 수준의 사용자 정의 메시지해야한다.

로깅의 관점에서 볼 때, 대부분의 시나리오에서와 같이 속성 ToString()뿐만 아니라 예외에 Message대해서도 확실하게 사용 하면이 예외가 발생한 위치와 호출 스택이 무엇인지 머리를 긁을 수 있습니다. 스택 트레이스가 모든 것을 말해 줄 것입니다.


답변

WHOLE 예외를 문자열로 변환

전화 Exception.ToString()Exception.Message속성을 사용하는 것보다 더 많은 정보를 제공합니다 . 그러나 이것조차도 여전히 다음을 포함한 많은 정보를 남깁니다.

  1. Data컬렉션 속성은 모든 예외를 발견했다.
  2. 다른 사용자 정의 특성이 예외에 추가되었습니다.

이 추가 정보를 캡처하려는 경우가 있습니다. 아래 코드는 위의 시나리오를 처리합니다. 또한 예외의 속성을 좋은 순서로 작성합니다. 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; }
}

맨 위 팁-로깅 예외

대부분의 사람들은이 코드를 로깅에 사용합니다. Serilog 와 함께 Serilog 를 사용하는 것을 고려하십시오. 예제 NuGet 패키지는 예외의 모든 속성을 기록하지만 대부분의 경우에 반영하지 않고 더 빠르게 수행합니다. Serilog는 작성 당시의 모든 분노 인 매우 고급 로깅 ​​프레임 워크입니다.

맨 위 팁-사람이 읽을 수있는 스택 추적

Serilog 를 사용하는 경우 Ben.Demystifier NuGet 패키지를 사용 하여 예외에 대한 사람이 읽을 수있는 스택 추적 또는 serilog-enrichers-demystify NuGet 패키지를 얻을 수 있습니다.


답변

@Wim이 옳습니다. ToString()기술적 대상이 있다고 가정 Message하고 사용자에게 표시하려면 로그 파일에 사용해야 합니다 . 사용자는 모든 예외 유형과 그 밖의 발생에 대해 사용자에게 적합하지 않다고 주장 할 수 있습니다 (ArgumentExceptions 등을 고려하십시오).

또한 StackTrace 외에도 다른 방법으로는 ToString()얻을 수없는 정보가 포함됩니다. 예를 들어, 예외 “메시지”에 로그 메시지를 포함 할 수있는 경우 융합 출력 .

일부 예외 유형에는 ToString()메시지에 포함되지 않은 추가 정보 (예 : 사용자 지정 속성)가 포함되어 있습니다.


답변

필요한 정보에 따라 다릅니다. 스택 추적 및 내부 예외를 디버깅하는 데 유용합니다.

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


답변

log4net의 XML 형식과 관련하여 로그의 ex.ToString ()에 대해 걱정할 필요가 없습니다. 예외 객체 자체를 전달하면 log4net은 미리 구성된 XML 형식으로 모든 세부 정보를 제공합니다. 가끔 내가 만날 수있는 것은 줄 바꿈 형식이지만 파일을 원시로 읽을 때입니다. 그렇지 않으면 XML을 구문 분석하는 것이 좋습니다.


답변

글쎄, 나는 그것이 당신이 로그에서보고 싶은 것에 달려 있다고 말하지 않습니까? ex.Message가 제공하는 것에 만족한다면 그것을 사용하십시오. 그렇지 않으면 ex.toString ()을 사용하거나 스택 추적을 기록하십시오.