トップ回答者
例外発生元の情報を取得したい

質問
-
質問はタイトルの通りです。
■クラスA Private Sub A_search() ・ ・ Dim RecCount As Integer = B.GetRecCount() ←★★★ ・ ・ End Sub ■クラスB Private Function GetRecCount() As Integer SQLServerに接続しレコード件数を取得 End Function ■★★★で例外発生 Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) Server.Transfer("c_error.aspx") End Sub ■c_error.aspx.vb Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not IsPostBack Then ●●● End If End Sub
●●●で下記情報をログとして出力したいです。
・例外発生元メソッド名(上記の場合:GetRecCount)
・例外発生元クラス名(上記の場合:B)
・例外発生行番号
・原因
下記①②も試してみましたが取得できませんでした。
① Dim Proc As StackFrame Dim History As New StackTrace(True) Dim K As Integer Dim ClassName As String '呼び出し履歴上のクラス名 Dim ProcName As String '呼び出し履歴上のプロシージャ名 Dim SourceFileName As String Dim LineNumber As Integer For K = 0 To History.FrameCount - 1 'スタックフレームを取得 Proc = History.GetFrame(K) 'この履歴のクラス名、プロシージャ名等を取得 ClassName = Proc.GetMethod.ReflectedType.Name ProcName = Proc.GetMethod.Name SourceFileName = Proc.GetFileName LineNumber = Proc.GetFileLineNumber Next K ② Dim Err As Exception = Server.GetLastError().InnerException
方法をご存じの方がいらっしゃいましたら、是非教えてください。
宜しくお願い致します。
回答
-
その情報を何に利用したいですか?発生したメソッドのメソッド名だけだとあまり意味がないように思えます。
例えば、例外の発生元が.NET Framework内部のメソッドだったり、回り回った結果たまたまその例外だったりしたらどうしますか?
System.String.SubString で NullReferenceが発生したという情報よりも、SubStringがどういった経路でよばれているかがわかったほうがいいんじゃないですか?
大元の質問についてですが、なんらかの例外が発生した際に、最後にその原因となるメソッドを呼び出したユーザーコードのメソッドと行番号だけを取得したいということですが、そういったメソッドは無いと思います。もしやりたいなら、頑張ってスタックトレースを解析する、、、のかな?
あと、行番号はPDBが生成されていない場合は取得できないことにも注意したほうがいいです。
- 回答としてマーク hana0101 2012年3月5日 0:31
-
Application_Error の中で Server.GetLastError().InnerException から例外を取り出せるはずですので、
この例外をもとに StackTrace オブジェクトを構築すれば、色々と取り出せるのではないかと思います。
例えば、
Dim Err As Exception = Server.GetLastError().InnerException Dim ErrMethod As System.Reflection.MethodInfo = New StackTrace(Err).GetFrame(0).GetMethod()
とすると、ErrMethod 変数を介してご期待の情報を持ってこれそうに思います。
# ご期待の情報が有用な情報であるかは別として。- 回答としてマーク hana0101 2012年3月5日 0:31
-
かるあさんへのレスには、
> ②の方法だと、
> クラスA・A_search・○行目
> クラスB・GetRecCount・○行目
> という最初の目的であったものも取得できるとありますが、私へのレスには、
> 今回の目的のクラスB、GetRecCountは取得できませんでした。
とあります。矛盾してないでしょうか?
何にせよ、以下のページにある ExceptionUtility の情報があれば
よさそうに思えますが、どうでしょうか?エラー ハンドラーの完全なコード例
http://msdn.microsoft.com/ja-jp/library/bb397417.aspx- 回答としてマーク hana0101 2012年3月5日 0:31
-
質問はタイトルの通りです。
え?スタック トレース調べれば良いじゃない。
そうではなく、本文では「なぜタイトルの様な質問をしたいか」と言うことを説明して頂けないでしょうか。
今回のケースなら、次のようになるかと思います。質問の背景は、次の通りです。
ASP.NET で、例外が発生した場合に、全ての例外を error.aspx で表示し、情報を集約しようとしています。【重要な情報1:質問の背景】この目的のため、次のようなコードを書きました。【重要な情報2:実際に動作させたコード】■クラスB 例外が発生するクラス Private Function GetRecCount() As Integer SQLServerに接続しレコード件数を取得中に例外が発生する場合を想定【重要な情報3:質問している現象が発生する条件】 End Function ■ Global.asax Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) Server.Transfer("c_error.aspx") End Sub ■c_error.aspx.vb 例外情報を表示するページ Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not IsPostBack Then ここに書くべきコードについて【重要な情報4:質問の主題】 End If End Sub
c_error.aspx.vb にて、次の情報をログ出力させたいと考えています。
- 例外発生元メソッド名(上記の場合:GetRecCount)
- 例外発生元クラス名(上記の場合:B)
- 例外発生行番号
- 原因
やったこととして、次のようなコードを試しましたが、希望する情報を得られませんでした。【重要な情報5:実行したこと】
① Dim Proc As StackFrame Dim History As New StackTrace(True) Dim K As Integer Dim ClassName As String '呼び出し履歴上のクラス名 Dim ProcName As String '呼び出し履歴上のプロシージャ名 Dim SourceFileName As String Dim LineNumber As Integer For K = 0 To History.FrameCount - 1 'スタックフレームを取得 Proc = History.GetFrame(K) 'この履歴のクラス名、プロシージャ名等を取得 ClassName = Proc.GetMethod.ReflectedType.Name ProcName = Proc.GetMethod.Name SourceFileName = Proc.GetFileName LineNumber = Proc.GetFileLineNumber Next K ② Dim Err As Exception = Server.GetLastError().InnerException
この様にしたところ、①では期待するクラスB、GetRecCount は表示されませんでした。②では余計な情報、クラス B、GetRecCount メソッド以外の情報も出てしまいます。【重要な情報6:実行したことに対して期待したこと】【重要な情報7:実行して得られた結果】
「どういう目的があって質問に至った」というのは、重要な情報です。皆さん、何らかの調査をして、この方向で行こうと決めて、その途中で行き詰まって質問に至っています。ところが、話を聞いていると、進めている方向が間違っている事があります。この場合、目的を聞き出すまでの時間が無駄になります。(回答者は困りません。でも、質問者さんは、早く解決したいのでは?)
例えば、今回、c_error.aspx の画面は、“誰に”見せることを目的としているのでしょうか。これが“エンド ユーザー”であれば、この様な機構は不要…というか、実装してはいけないかもしれません。.NET では、特に ASP.NET プロジェクトでは、例外メッセージをエンド ユーザーに見せることは、控えるように言われています。例外メッセージは、開発者向けのメッセージです。つまり、想定される例外発生状況は、開発中にすべて塞ぎ、実行時には本当に例外的な状況でのみ、例外が発生するようにします。(参照:とあるコンサルタントのつぶやき「.NET の例外処理 Part. 4」このため、例外情報はむやみにエンドユーザに見せてはいけません。特に、そこそこ知識を持った開発者が例外情報を読むと、アプリケーション内部構造に関する情報が漏えいすることになり、セキュリティ上のリスクにつながります。)
今回は「SQLServer に接続しレコード件数を取得」ということですが、この作業の中でどんな例外が発生するでしょうか。ここでエンド ユーザーに通達しなければならないのは、「接続できなかった」ということぐらいではないでしょうか。
では何故接続できなかったのか。「DB サーバーが起動していない」「Web サーバーから DB サーバーまでのネットワーク回線が物理的に切れている」などが想定されます。エンド ユーザーに見せるのは、こういった、エンド ユーザーが何とかできる問題と、それに対する解決方法です。エンド ユーザーから開発元に送信するログ ファイルに保存するということであれば、Application_Error メソッドで行うと良いでしょう。ここなら、ひとつ上が発生したメソッドではないでしょうか。そして、今回の質問では、ここでセッション変数なりにしまってしまうという手もあります。
なお、①の方法では、Server.Transfer をしたときにコンテキストが変わっています。その為に以前のコンテキストが取れません。(参照:「HttpServerUtility.Transer メソッド(String)」Transfer は End を呼び出します。これは完了時に ThreadAbortException 例外をスローします。)Redirect では一旦クライアントに制御が返り、もう一度リクエストが発行されます。Transfer はサーバー内でコンテキストの変更を行います。
②の方法では、Exception が取れるなら、そこに StackTrace が入っているので、その StackTrace を①のようにして取り出せば良いのではないかと思います。もっとも、何番目に必要な情報があるのか、わかりませんが(試さずに言いますが、InnerException でなくても良いのでは?)。Jitta@わんくま同盟
- 回答としてマーク hana0101 2012年3月5日 0:31
すべての返信
-
その情報を何に利用したいですか?発生したメソッドのメソッド名だけだとあまり意味がないように思えます。
例えば、例外の発生元が.NET Framework内部のメソッドだったり、回り回った結果たまたまその例外だったりしたらどうしますか?
System.String.SubString で NullReferenceが発生したという情報よりも、SubStringがどういった経路でよばれているかがわかったほうがいいんじゃないですか?
大元の質問についてですが、なんらかの例外が発生した際に、最後にその原因となるメソッドを呼び出したユーザーコードのメソッドと行番号だけを取得したいということですが、そういったメソッドは無いと思います。もしやりたいなら、頑張ってスタックトレースを解析する、、、のかな?
あと、行番号はPDBが生成されていない場合は取得できないことにも注意したほうがいいです。
- 回答としてマーク hana0101 2012年3月5日 0:31
-
その情報を何に利用したいですか?発生したメソッドのメソッド名だけだとあまり意味がないように思えます。
例えば、例外の発生元が.NET Framework内部のメソッドだったり、回り回った結果たまたまその例外だったりしたらどうしますか?
System.String.SubString で NullReferenceが発生したという情報よりも、SubStringがどういった経路でよばれているかがわかったほうがいいんじゃないですか?
大元の質問についてですが、なんらかの例外が発生した際に、最後にその原因となるメソッドを呼び出したユーザーコードのメソッドと行番号だけを取得したいということですが、そういったメソッドは無いと思います。もしやりたいなら、頑張ってスタックトレースを解析する、、、のかな?
あと、行番号はPDBが生成されていない場合は取得できないことにも注意したほうがいいです。
かるあ様、ありがとうございます。
確かにそうですね。どのような流れで例外が起きたのか、それをログとして出力した方が良さそうですね。
②の方法だと、
クラスA・A_search・○行目
クラスB・GetRecCount・○行目
という最初の目的であったものも取得できるので、その他の情報も含めてログ出力した方がいいかもです。
-
Application_Error の中で Server.GetLastError().InnerException から例外を取り出せるはずですので、
この例外をもとに StackTrace オブジェクトを構築すれば、色々と取り出せるのではないかと思います。
例えば、
Dim Err As Exception = Server.GetLastError().InnerException Dim ErrMethod As System.Reflection.MethodInfo = New StackTrace(Err).GetFrame(0).GetMethod()
とすると、ErrMethod 変数を介してご期待の情報を持ってこれそうに思います。
# ご期待の情報が有用な情報であるかは別として。- 回答としてマーク hana0101 2012年3月5日 0:31
-
かるあさんへのレスには、
> ②の方法だと、
> クラスA・A_search・○行目
> クラスB・GetRecCount・○行目
> という最初の目的であったものも取得できるとありますが、私へのレスには、
> 今回の目的のクラスB、GetRecCountは取得できませんでした。
とあります。矛盾してないでしょうか?
何にせよ、以下のページにある ExceptionUtility の情報があれば
よさそうに思えますが、どうでしょうか?エラー ハンドラーの完全なコード例
http://msdn.microsoft.com/ja-jp/library/bb397417.aspx- 回答としてマーク hana0101 2012年3月5日 0:31
-
質問はタイトルの通りです。
え?スタック トレース調べれば良いじゃない。
そうではなく、本文では「なぜタイトルの様な質問をしたいか」と言うことを説明して頂けないでしょうか。
今回のケースなら、次のようになるかと思います。質問の背景は、次の通りです。
ASP.NET で、例外が発生した場合に、全ての例外を error.aspx で表示し、情報を集約しようとしています。【重要な情報1:質問の背景】この目的のため、次のようなコードを書きました。【重要な情報2:実際に動作させたコード】■クラスB 例外が発生するクラス Private Function GetRecCount() As Integer SQLServerに接続しレコード件数を取得中に例外が発生する場合を想定【重要な情報3:質問している現象が発生する条件】 End Function ■ Global.asax Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs) Server.Transfer("c_error.aspx") End Sub ■c_error.aspx.vb 例外情報を表示するページ Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load If Not IsPostBack Then ここに書くべきコードについて【重要な情報4:質問の主題】 End If End Sub
c_error.aspx.vb にて、次の情報をログ出力させたいと考えています。
- 例外発生元メソッド名(上記の場合:GetRecCount)
- 例外発生元クラス名(上記の場合:B)
- 例外発生行番号
- 原因
やったこととして、次のようなコードを試しましたが、希望する情報を得られませんでした。【重要な情報5:実行したこと】
① Dim Proc As StackFrame Dim History As New StackTrace(True) Dim K As Integer Dim ClassName As String '呼び出し履歴上のクラス名 Dim ProcName As String '呼び出し履歴上のプロシージャ名 Dim SourceFileName As String Dim LineNumber As Integer For K = 0 To History.FrameCount - 1 'スタックフレームを取得 Proc = History.GetFrame(K) 'この履歴のクラス名、プロシージャ名等を取得 ClassName = Proc.GetMethod.ReflectedType.Name ProcName = Proc.GetMethod.Name SourceFileName = Proc.GetFileName LineNumber = Proc.GetFileLineNumber Next K ② Dim Err As Exception = Server.GetLastError().InnerException
この様にしたところ、①では期待するクラスB、GetRecCount は表示されませんでした。②では余計な情報、クラス B、GetRecCount メソッド以外の情報も出てしまいます。【重要な情報6:実行したことに対して期待したこと】【重要な情報7:実行して得られた結果】
「どういう目的があって質問に至った」というのは、重要な情報です。皆さん、何らかの調査をして、この方向で行こうと決めて、その途中で行き詰まって質問に至っています。ところが、話を聞いていると、進めている方向が間違っている事があります。この場合、目的を聞き出すまでの時間が無駄になります。(回答者は困りません。でも、質問者さんは、早く解決したいのでは?)
例えば、今回、c_error.aspx の画面は、“誰に”見せることを目的としているのでしょうか。これが“エンド ユーザー”であれば、この様な機構は不要…というか、実装してはいけないかもしれません。.NET では、特に ASP.NET プロジェクトでは、例外メッセージをエンド ユーザーに見せることは、控えるように言われています。例外メッセージは、開発者向けのメッセージです。つまり、想定される例外発生状況は、開発中にすべて塞ぎ、実行時には本当に例外的な状況でのみ、例外が発生するようにします。(参照:とあるコンサルタントのつぶやき「.NET の例外処理 Part. 4」このため、例外情報はむやみにエンドユーザに見せてはいけません。特に、そこそこ知識を持った開発者が例外情報を読むと、アプリケーション内部構造に関する情報が漏えいすることになり、セキュリティ上のリスクにつながります。)
今回は「SQLServer に接続しレコード件数を取得」ということですが、この作業の中でどんな例外が発生するでしょうか。ここでエンド ユーザーに通達しなければならないのは、「接続できなかった」ということぐらいではないでしょうか。
では何故接続できなかったのか。「DB サーバーが起動していない」「Web サーバーから DB サーバーまでのネットワーク回線が物理的に切れている」などが想定されます。エンド ユーザーに見せるのは、こういった、エンド ユーザーが何とかできる問題と、それに対する解決方法です。エンド ユーザーから開発元に送信するログ ファイルに保存するということであれば、Application_Error メソッドで行うと良いでしょう。ここなら、ひとつ上が発生したメソッドではないでしょうか。そして、今回の質問では、ここでセッション変数なりにしまってしまうという手もあります。
なお、①の方法では、Server.Transfer をしたときにコンテキストが変わっています。その為に以前のコンテキストが取れません。(参照:「HttpServerUtility.Transer メソッド(String)」Transfer は End を呼び出します。これは完了時に ThreadAbortException 例外をスローします。)Redirect では一旦クライアントに制御が返り、もう一度リクエストが発行されます。Transfer はサーバー内でコンテキストの変更を行います。
②の方法では、Exception が取れるなら、そこに StackTrace が入っているので、その StackTrace を①のようにして取り出せば良いのではないかと思います。もっとも、何番目に必要な情報があるのか、わかりませんが(試さずに言いますが、InnerException でなくても良いのでは?)。Jitta@わんくま同盟
- 回答としてマーク hana0101 2012年3月5日 0:31