1

最近、例外から可能な限り多くの詳細を取得するという問題が発生しました。理由?さて、出荷された製品の問題を解決する必要がある場合、通常はログだけがあります。

明らかに

Exception.ToString()

かなりうまく機能しますが、FaultExceptionを処理し、カスタム例外がどのような驚きをもたらすかを誰が知っているかはあまり役に立ちません。

では、まともなレベルのパラノイアで例外の詳細を取得するための最良の方法は何ですか?

4

2 に答える 2

1

私は周りを見回して、トピックについてグーグル検索しました。驚くべきことに、これに関する議論はそれほど多くありません。とにかく真髄をここに合成してみました。

口で言うだけなら簡単です。コードを見せて。

  1. ここに、例外をスローするサンプル コードがあります。

    protected void TestExceptionDetails()
    {
        try
        {
            int zero = 0;
    
            try
            {
                int z = zero / zero;
            }
            catch (Exception e)
            {
                var applicationException = new ApplicationException("rethrow", e);
                // put some hint why exception occured
                applicationException.Data.Add("divider_value", zero);
                throw applicationException;
            }
        }
        catch (Exception e)
        {
            var extendedexceptionDetails = GetExtendedexceptionDetails(e);
            log.ErrorFormat("Detailed:{0}", extendedexceptionDetails);
        }
    }
    
  2. GetExtendedExceptionDetails メソッドは次のとおりです。

    /// <summary>
    /// This utility method can be used for retrieving extra details from exception objects.
    /// </summary>
    /// <param name="e">Exception.</param>
    /// <param name="indent">Optional parameter. String used for text indent.</param>
    /// <returns>String with as much details was possible to get from exception.</returns>
    public static string GetExtendedexceptionDetails(object e, string indent = null)
    {
        // we want to be robust when dealing with errors logging
        try
        {
            var sb = new StringBuilder(indent);
            // it's good to know the type of exception
            sb.AppendLine("Type: " + e.GetType().FullName);
            // fetch instance level properties that we can read
            var props = e.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.CanRead);
    
            foreach (PropertyInfo p in props)
            {
                try
                {
                    var v = p.GetValue(e, null);
    
                    // in case of Fault contracts we'd like to know what Detail contains
                    if (e is FaultException && p.Name == "Detail")
                    {
                        sb.AppendLine(string.Format("{0}{1}:", indent, p.Name));
                        sb.AppendLine(GetExtendedexceptionDetails(v, "  " + indent));// recursive call
                    }
                    // Usually this is InnerException
                    else if (v is Exception)
                    {
                        sb.AppendLine(string.Format("{0}{1}:", indent, p.Name));
                        sb.AppendLine(GetExtendedexceptionDetails(v as Exception, "  " + indent));// recursive call
                    }
                    // some other property
                    else
                    {
                        sb.AppendLine(string.Format("{0}{1}: '{2}'", indent, p.Name, v));
    
                        // Usually this is Data property
                        if (v is IDictionary)
                        {
                            var d = v as IDictionary;
                            sb.AppendLine(string.Format("{0}{1}={2}", " " + indent, "count", d.Count));
                            foreach (DictionaryEntry kvp in d)
                            {
                                sb.AppendLine(string.Format("{0}[{1}]:[{2}]", " " + indent, kvp.Key, kvp.Value));
                            }
                        }
                    }
                }
                catch (Exception exception)
                {
                    //swallow or log
                }
            }
    
            //remove redundant CR+LF in the end of buffer
            sb.Length = sb.Length - 2;
            return sb.ToString();
        }
        catch (Exception exception)
        {
            //log or swallow here
            return string.Empty;
        }
    }
    

ご覧のとおり、Reflection を使用してインスタンス プロパティを取得し、その値を取得します。コストがかかることはわかっていますが、具体的な例外が公開する可能性のあるプロパティについてはよくわかりません。また、アプリケーションでエラーが頻繁に発生してパフォーマンスが低下しないことを願っています。

では、実際に得たものを見てみましょう。

これは Exception.ToString が返すものです:

System.ApplicationException: rethrow ---> System.DivideByZeroException: Attempted to divide by zero.
   at NET4.TestClasses.Other.TestExceptionDetails() in c:\tmp\prj\NET4\TestClasses\Other.cs:line 1116
   --- End of inner exception stack trace ---
   at NET4.TestClasses.Other.TestExceptionDetails() in c:\tmp\prj\NET4\TestClasses\Other.cs:line 1123

これにより、新しいメソッドが返されます。

Type: System.ApplicationException
Message: 'rethrow'
Data: 'System.Collections.ListDictionaryInternal'
 count=1
 [divider_value]:[0]
InnerException:
  Type: System.DivideByZeroException
  Message: 'Attempted to divide by zero.'
  Data: 'System.Collections.ListDictionaryInternal'
   count=0
  InnerException: ''
  TargetSite: 'Void TestExceptionDetails()'
  StackTrace: '   at NET4.TestClasses.Other.TestExceptionDetails() in c:\tmp\prj\NET4\TestClasses\Other.cs:line 1116'
  HelpLink: ''
  Source: 'NET4'
TargetSite: 'Void TestExceptionDetails()'
StackTrace: '   at NET4.TestClasses.Other.TestExceptionDetails() in c:\tmp\prj\NET4\TestClasses\Other.cs:line 1123'
HelpLink: ''
Source: 'NET4'

ロギングにはlog4netを使用し、パフォーマンスのオーバーヘッドを削減するためにILog.IsErrorEnabledプロパティがあります。拡張例外処理を呼び出す前にチェックするだけです。

于 2012-10-10T19:52:22.597 に答える
1

例外からのログ情報は役立つ場合がありますが、トランザクション ログのアプローチにより、より多くの情報が得られる場合があります。

アイデアは、できるだけ多くのログ メッセージをトレースすることですが、それらをログ ファイルに書き出すのではなく、メモリ内のキューに蓄積することです。ユーザーがコミットした操作が正常に完了した場合は、トレース メッセージのコレクションを破棄するだけです。ただし、例外がスローされた場合は、実行中に蓄積した例外とトレース メッセージの両方をログに記録できます。

その利点は明らかです。何か問題が発生したという情報を入手できるだけでなく、システム内で発生したプロセスによって例外が発生したこともわかります。

于 2012-10-10T21:03:56.923 に答える