- PR -

Webアプリの操作履歴をログファイルに残したい

投稿者投稿内容
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-08-01 16:05
ども、ぼのぼのです。いつも参考にさせて頂いております。

現在、VB.NETでWebアプリを開発しているのですが、掲題のような要望がありまして、実装方法を検討中です。
今のとこ、下記サンプルのような形で考えてます。サンプルなので、ユーザ情報とか、例外処理とか、細かいとこは省略してますが、やりたいことのイメージとしては分かって頂けるかと思います。

コード:


'ログ出力クラス
Imports System.IO
Imports System.Text
Public Class MyLog
Public Shared Sub PutLog _
(ByVal className As String, ByVal methodName As String)

SyncLock GetType(MyLog)
Dim writer As New StreamWriter("C:\Temp\mylog.txt", _
True, Encoding.GetEncoding("Shift-JIS"))
writer.WriteLine( _
Now.ToString("yyyy/MM/dd,HH:mm:ss.fff") & "," & _
className & "," & methodName)
writer.Close()
End SyncLock
End Sub
End Class

'画面
Public Class WebForm1 Inherits System.Web.UI.Page

Private Sub Page_Load _
(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles MyBase.Load

If Not IsPostBack Then
MyLog.PutLog(Me.GetType.ToString, "Page_Load")
'処理
End If
End Sub

Private Sub Button1_Click _
(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button1.Click

MyLog.PutLog(Me.GetType.ToString, "Button1_Click")
'処理
End Sub

Private Sub Button2_Click _
(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles Button2.Click

MyLog.PutLog(Me.GetType.ToString, "Button2_Click")
'処理
End Sub

Private Sub DropDownList1_SelectedIndexChanged _
(ByVal sender As System.Object, ByVal e As System.EventArgs) _
Handles DropDownList1.SelectedIndexChanged

MyLog.PutLog(Me.GetType.ToString, "DropDownList1_SelectedIndexChanged")
'処理
End Sub
End Class

'ログ出力イメージ
2005/08/01,15:42:04.640,ASP.WebForm1_aspx,Page_Load
2005/08/01,15:42:06.031,ASP.WebForm1_aspx,Button1_Click
2005/08/01,15:42:06.640,ASP.WebForm1_aspx,Button2_Click
2005/08/01,15:42:07.625,ASP.WebForm1_aspx,DropDownList1_SelectedIndexChanged


で、やりたいことを実現するだけならここで終わりなんですが、これだと一つ一つのメソッドにコードを書かなきゃならないんで大変です。
そこで、下記のような親クラスを作成して、上記WebForm1はそれを継承するようにすれば、WebForm1の方には何も書かなくて済むのでは、と考えたわけです。

コード:


Public Class BaseForm
Inherits System.Web.UI.Page

Protected Overrides Sub OnLoad(ByVal e As System.EventArgs)
MyLog.PutLog(Me.GetType.ToString, "test")
End Sub
End Class


で、実際に動かしてみると、期待通りログは出力されるんですが、コードをよく見るとわかるように、メソッド名のとこに"test"と決め打ちしています
実際には、ここにPage_LoadとかButton1_Clickとかを出力させたいのですが、やり方が分からなくて行き詰まってしまいました。
イベントハンドラのメソッド名そのものでなくても、判別がつけばなんでも良いのですが…
何か良いアイデアをお持ちでしたら、ご教授ください。よろしくお願い致します m(_ _)m

[ メッセージ編集済み 編集者: ぼのぼの 編集日時 2005-08-01 16:15 ]
nexu
会議室デビュー日: 2005/08/01
投稿数: 2
投稿日時: 2005-08-01 21:03
PutLogメソッドの内部で以下のようにして、
クラス名とメソッド名を取得してみてはいかがでしょうか。

StackTrace st = new StackTrace(true) ;
StackFrame sf = st.GetFrame(2) ; // 2という値は一例です
MethodBase mb = sf.GetMethod() ;
string cName = mb.DeclaringType.FullName ; // クラス名
string mName = mb.ToString() ; // メソッド名
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-08-01 22:00
こんばんわ。nexuさん、回答ありがとうございます。

引用:

nexuさんの書き込み (2005-08-01 21:03) より:
PutLogメソッドの内部で以下のようにして、
クラス名とメソッド名を取得してみてはいかがでしょうか。


残念ながら、StackTraceに残っているのは、これまでに実行されたメソッドです。一方、取得したいのは、これから実行されるはずのメソッドなのです。
.NETのWebアプリのイベントハンドラが、目に見えないとこでどんな処理をやってるかはわかりませんが(わかれば多分私の抱えてる問題は解決するんでしょうが)実際にPage_Loadが実行された後各イベントハンドラに処理が飛ぶわけですから、どこかに判別するための情報が隠れているはずと思い、それを探してたんですが、私の力量では見つけられませんでした。
一番目のサンプルの方法で、やりたいこと自体は実現できるわけで(ただめんどくさいというだけで)、ここにあんま時間を掛け続けることもできないので、今回は諦めてめんどくさい方の方法でいこうかな、と考えております(^^;
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2005-08-02 05:01
引用:

ぼのぼのさんの書き込み (2005-08-01 22:00) より:
残念ながら、StackTraceに残っているのは、これまでに実行されたメソッドです。一方、取得したいのは、これから実行されるはずのメソッドなのです。


 nexuさんのは、PutLog メソッドで、と書いてありますよ?PutLog メソッドで StackFrame を調べれば、PutLog を呼び出した、つまり実行中のメソッドがわかるのではないでしょうか?または、System.Reflection.MethodBase.GetCurrentMethod().Name で、現在実行中のメソッド名が取れます。

引用:

Page_Loadが実行された後各イベントハンドラに処理が飛ぶわけですから、どこかに判別するための情報が隠れているはず


ここの意図が、ちょっとわからなかったのですが、Load イベント中にはわからないでしょう。ポストバックによるイベントが処理されるのは、このあとです(参考)。
 判別するための情報は、たとえば TextChanged であれば、ViewState にある前回の情報と、Post された今回の情報が一致しているか、ですよね。それとも、どのデリゲートが宣言されているか、というところでしょうか。
 これが「今回のリクエストで実行されるであろうイベントを列挙する」という意味であれば、それはプログラムから見ると、二度手間なことをしようとしているように思います。
_________________
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-08-02 11:36
う〜ん、ちょっとわかりづらかったでしょうか?すみません。
やりたかったことの意図としては、まとめると以下のようになります。

<最終目的>
ログファイルに操作履歴を残したい。ログ出力イメージは以下のような形で、
実行時刻とクラス名とメソッド名を出したい。
どの画面でどのイベントハンドラが実行されたかが分かれば、クラス名・メソッド名でなくても良い。
2005/08/01,15:42:04.640,ASP.WebForm1_aspx,Page_Load
2005/08/01,15:42:06.031,ASP.WebForm1_aspx,Button1_Click
2005/08/01,15:42:06.640,ASP.WebForm1_aspx,Button2_Click
2005/08/01,15:42:07.625,ASP.WebForm1_aspx,DropDownList1_SelectedIndexChanged

<実装方法その1>
各画面のクラスは全てのメソッド(イベントハンドラ)の冒頭に、ログ出力用のコードを書く。

<実装方法その2>
全ての画面はBaseFormクラスを継承しさえすれば、ログ出力に関しては何も意識しなくて良い。
ログ出力処理は全てBaseFormで行う(目的の形でログが出せればPage_Loadでなくても良い)

現在の状況としては、実装方法その2のやり方がわからなくて、実装方法その1でいこうかな、という状態です。
burton999
ぬし
会議室デビュー日: 2003/10/06
投稿数: 898
お住まい・勤務地: 東京
投稿日時: 2005-08-02 11:55
PreRenderイベントならClickイベントなどの後に呼ばれるので
StackTraceでなんとかなりそうですけど。(試してません。。。)
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-08-02 12:26
試してみました。

コード:
Public Class BaseForm
    Inherits System.Web.UI.Page

    Private Sub Page_PreRender _
        (ByVal sender As Object, ByVal e As System.EventArgs) _
        Handles MyBase.PreRender

        Dim st As New System.Diagnostics.StackTrace(True)
        MyLog.PutLog(Me.GetType.ToString, st.ToString) 'とりあえず全部出してみる
    End Sub
End Class

<<<画面初期表示時>>>
2005/08/02,12:12:57.078,ASP.WebForm2_aspx,
    at sample.BaseForm.Page_PreRender(Object, EventArgs)
    at System.Web.UI.Control.OnPreRender(EventArgs)
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Page.ProcessRequestMain()
    at System.Web.UI.Page.ProcessRequest()
    at System.Web.UI.Page.ProcessRequest(HttpContext)
    at System.Web.CallHandlerExecutionStep.System.Web.HttpApplication
       +IExecutionStep.Execute()
    at System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean&)
    at System.Web.HttpApplication.ResumeSteps(Exception)
    at System.Web.HttpApplication.System.Web.IHttpAsyncHandler
       .BeginProcessRequest(HttpContext, AsyncCallback, Object)
    at System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest)
    at System.Web.HttpRuntime.ProcessRequest(HttpWorkerRequest)
    at System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

<<<Button1クリック>>>
2005/08/02,12:13:27.968,ASP.WebForm2_aspx,
    at sample.BaseForm.Page_PreRender(Object, EventArgs)
    at System.Web.UI.Control.OnPreRender(EventArgs)
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Page.ProcessRequestMain()
    at System.Web.UI.Page.ProcessRequest()
    at System.Web.UI.Page.ProcessRequest(HttpContext)
    at System.Web.CallHandlerExecutionStep.System.Web.HttpApplication
       +IExecutionStep.Execute()
    at System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean&)
    at System.Web.HttpApplication.ResumeSteps(Exception)
    at System.Web.HttpApplication.System.Web.IHttpAsyncHandler
       .BeginProcessRequest(HttpContext, AsyncCallback, Object)
    at System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest)
    at System.Web.HttpRuntime.ProcessRequest(HttpWorkerRequest)
    at System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)

<<<DropDownList1選択変更>>>
2005/08/02,12:13:29.578,ASP.WebForm2_aspx,
    at sample.BaseForm.Page_PreRender(Object, EventArgs)
    at System.Web.UI.Control.OnPreRender(EventArgs)
    at System.Web.UI.Control.PreRenderRecursiveInternal()
    at System.Web.UI.Page.ProcessRequestMain()
    at System.Web.UI.Page.ProcessRequest()
    at System.Web.UI.Page.ProcessRequest(HttpContext)
    at System.Web.CallHandlerExecutionStep.System.Web.HttpApplication
       +IExecutionStep.Execute()
    at System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean&)
    at System.Web.HttpApplication.ResumeSteps(Exception)
    at System.Web.HttpApplication.System.Web.IHttpAsyncHandler
       .BeginProcessRequest(HttpContext, AsyncCallback, Object)
    at System.Web.HttpRuntime.ProcessRequestInternal(HttpWorkerRequest)
    at System.Web.HttpRuntime.ProcessRequest(HttpWorkerRequest)
    at System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)


StackTraceには残っていないっぽいです(´・ω・`)

上記とは別の話ですが、Page_Loadでも、DropDownListのSelectedIndexChangedとか、TextBoxのTextChangedとか、HtmlButtonのServerClickとか、HTML上で__doPostBackという自動生成されたJavaScript関数によってポストバックされる場合は、Request("__EVENTTARGET")でIDがとれるみたいです。でも、WebControlのButtonみたいなsubmitボタンでポストバックされるものに関してはこの方法じゃ取れないんですよね…
ぼのぼの
ぬし
会議室デビュー日: 2004/09/16
投稿数: 544
投稿日時: 2005-08-02 12:48
引用:

Jittaさんの書き込み (2005-08-02 05:01) より:
 判別するための情報は、たとえば TextChanged であれば、ViewState にある前回の情報と、Post された今回の情報が一致しているか、ですよね。それとも、どのデリゲートが宣言されているか、というところでしょうか。
 これが「今回のリクエストで実行されるであろうイベントを列挙する」という意味であれば、それはプログラムから見ると、二度手間なことをしようとしているように思います。


重要かつ基本的なことを理解してませんでした。AutoPostBackをFalseにすると、1回のポストバックで複数のイベントハンドラが実行されるんですね。こういうのも考慮しなければいけなかったのか…
だとすると、上の<実装方法その1>が一番簡単かつ確実なんですね。時刻もリアルですし。

ちなみに、ボタンのクリックとか、AutoPostBack=Trueのアイテムとか、あくまで「ポストバックのきっかけになった操作のみを記録したい」という場合は、判別方法ってあるんでしょうか?あったとして、採用になるかは別問題ですけれども。。。

スキルアップ/キャリアアップ(JOB@IT)