.NET TIPS

[ASP.NET]アプリケーション共通のロギングを行うには?(Global.asax編)

山田 祥寛
2004/02/27

 アプリケーションに対するアクセス状況を常時ロギング(ログの収集)し、把握することは、いろいろな意味で重要だ。アプリケーション・ルート配下のそれぞれのコンテンツに対するアクセス比率を把握すれば、人気のあるページ/ないページの傾向が分かるので、コンテンツやメニューの配置などの改善に役立つだろう。例えばREFERERヘッダを監視していれば、自分のサイトがどのようなページからリンクされているかを知ることができるので、おのずからアクセスしてくるユーザー層を推定することができる。ユーザー・エージェントなどの情報は、ページ・デザインを最適化する際の有効な材料の1つでもあるだろう。

 アプリケーションのロギングの重要性は、このようなサイト改善の観点からばかりではない。常時、アクセスを監視することで不正なアクセスに対しても早期の発見が可能になり、特定のIPアドレスからのアクセスを制限するなどの対処法も特定しやすい。そもそも、アクセス・ログを常時記録しているということ自体が、クラッカーに対する抑止効果にもなるものだ。

 本稿では、このロギング機能をASP.NETアプリケーション全体に対して、一元的に実装する方法を紹介する。IISにも標準のロギング機能が実装されているが、あらかじめ定められた項目の範囲内でテキストファイルに記録しているのみであり、当然のことながら、独自の項目を追加したり、ログの出力先を(例えば)データベース・サーバに切り替えたりといったことはできない。これらを柔軟に選択したいというケースでは、やはり自前のロギング機能を実装するのが手っ取り早いだろう。

 いうまでもなく、ロギングはアプリケーション配下の不特定多数の全ファイルに対して行うべきものだ。このような性質のロジックをページ個々に実装するのは保守性の面ではお勧めできない。もちろん、ロギングの部分だけをビジネス・コンポーネント化してもよいが、いずれにせよ「ビジネス・ロジックを明示的に呼び出さなければならない」という意味では、根本的な解決とはならない。

 そこで登場するのが、Global.asaxである。これはアプリケーション・ルートの直下に配置するファイルで、主にアプリケーション共通の処理を記述したり、リソース(オブジェクト)を確保したりする用途に用いる。

 Global.asaxは旧来のASP 3.0ではGlobal.asaと呼ばれていたアプリケーション管理用のファイルであるが、ASP.NETでは大幅に機能が改善されている。ASP 3.0のGlobal.asaはアプリケーション・セッションの開始/終了を監視するだけのものであったが、Global.asaxではリクエストの開始・終了にかかわるステップ(イベント)を、実に12もの段階で監視し、個別のタイミングで特定の処理を介することができるようになった(もちろん既存の機能もそのまま利用することができる)。これらのイベントを利用することで、ASP.NETアプリケーション開発者は、リクエスト処理に際して、独自の認証やセッション管理、暗号化、変換、ロギングなどの処理を規定することができる。

 次の表はGlobal.asaxで発生する12のイベントのそれぞれが発生した場合に呼び出されるイベント・ハンドラをまとめたものだ。

イベント・ハンドラ 呼び出されるタイミング
Application_OnBeginRequest リクエスト処理を開始する前に発生
Application_OnAuthenticateRequest 認証の直前に発生
Application_OnAuthorizeRequest 認証が完了したタイミングで発生
Application_OnResolveRequestCache リクエストをキャッシングするタイミングで発生
Application_OnAcquireRequestState セッション状態などを取得するタイミングで発生
Application_OnPreRequestHandlerExecute ページの実行を開始する直前に発生
Application_OnPostRequestHandlerExecute ページの実行を完了した直後に発生
Application_OnReleaseRequestState すべての処理を完了したタイミングで発生
Application_OnUpdateRequestCache 出力キャッシュを更新したタイミングで発生
Application_OnEndRequest すべてのリクエスト処理が完了したタイミングで発生
Application_OnPreSendRequestHeaders ヘッダをクライアントに送信する直前に発生
Application_OnPreSendRequestContent コンテンツをクライアントに送信する直前に発生
Global.asaxが対応しているイベント・ハンドラの一覧
イベント・ハンドラはこの表の順番で呼び出される。ただし、Application_OnPreSendRequestHeadersメソッドやApplication_OnPreSendRequestContentメソッドはバッファ処理(HTTP応答バッファリング)が有効かどうかによって呼び出されるタイミングが異なるので注意すること。バッファ処理が有効である場合には、上記表の順番で発生するが、バッファ処理が無効である場合には最初のページ出力が開始される任意のタイミングで呼び出される。なお、それぞれのイベント・ハンドラの名前から「Application_On」を取り除いた部分がGlobal.asaxで発生するイベントの名前である。Global.asaxではイベント名に「Application_On」あるいは「Application_」を付けたイベント・ハンドラが事前に定義されており、イベントの発生時に呼び出される。

 もちろん、これらイベントを常にすべて利用する必要はない。本稿では、リクエスト処理を開始する直前のタイミングで発生するApplication_OnBeginRequestイベント・ハンドラを利用して、アプリケーション共通のロギング処理を実装してみることにしよう。

<%@ Application Description="Insider .NET Sample" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script Language="VB" runat="Server">
Sub Application_OnBeginRequest(sender As Object, e As EventArgs)
  Dim objDb As New SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial Catalog=nettips")
  Dim objCom As New SqlCommand("SELECT * FROM accessLog WHERE url=@url AND accessDate=@accessDate",objDb)
  objCom.Parameters.Add("@url",Request.Url.ToString())
  objCom.Parameters.Add("@accessDate",DateTime.Now.ToString("yyyy/MM"))
  objDb.Open()
  Dim objCom2 As SqlCommand
  Dim objDr As SqlDataReader =objCom.ExecuteReader()
  ' リクエストされたURL、アクセス月日でaccessLogテーブルを検索
  If objDr.HasRows Then
    objDr.Close()
    objCom2=New SqlCommand("UPDATE accessLog SET cnt=cnt+1 WHERE url=@url AND accessDate=@accessDate",objDb)
  Else
    objDr.Close()
    objCom2=New SqlCommand("INSERT INTO accessLog(url,accessDate,cnt) VALUES(@url,@accessDate,1)",objDb)
  End If
  ' 冒頭のSELECT命令で既存のレコードが存在する場合にはUPDATE命令を、
  ' 存在しない場合にはINSERT命令を発行する

  objCom2.Parameters.Add("@url",Request.Url.ToString())
  objCom2.Parameters.Add("@accessDate",DateTime.Now.ToString("yyyy/MM"))
  objCom2.ExecuteNonQuery()
  ' リクエスト情報を基にパラメータをセットし、クエリを実行
  objDb.Close()
End Sub
</script>
ロギング処理を実装したGlobal.asax(VB.NET版)
Application_OnBeginRequestイベント・ハンドラを利用して、アプリケーション共通のロギング処理を実装している。
 
<%@ Application Description="Insider .NET Sample" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<script Language="C#" runat="Server">
void Application_OnBeginRequest(Object sender , EventArgs e) {
  SqlConnection objDb=new SqlConnection("Data Source=(local);User ID=sa;Password=sa;Persist Security Info=True;Initial Catalog=nettips");
  SqlCommand objCom=new SqlCommand("SELECT * FROM accessLog WHERE url=@url AND accessDate=@accessDate",objDb);
  objCom.Parameters.Add("@url",Request.Url.ToString());
  objCom.Parameters.Add("@accessDate",DateTime.Now.ToString("yyyy/MM"));
  objDb.Open();
  SqlCommand objCom2;
  SqlDataReader objDr=objCom.ExecuteReader();
  if(objDr.HasRows){
    objDr.Close();
    objCom2=new SqlCommand("UPDATE accessLog SET cnt=cnt+1 WHERE url=@url AND accessDate=@accessDate",objDb);
  }else{
    objDr.Close();
    objCom2=new SqlCommand("INSERT INTO accessLog(url,accessDate,cnt) VALUES(@url,@accessDate,1)",objDb);
  }
  objCom2.Parameters.Add("@url",Request.Url.ToString());
  objCom2.Parameters.Add("@accessDate",DateTime.Now.ToString("yyyy/MM"));
  objCom2.ExecuteNonQuery();
  objDb.Close();
}
</script>
ロギング処理を実装したGlobal.asax(C#版)

 本サンプルを利用するに当たっては、SQL Server(もしくはMSDE)に対して、以下のデザインでログ記録用のデータベース・テーブルを用意しておく必要がある。

フィールド名 データ型 概要
url VARCHAR(100) 対象のURL(主キー)
accessDate VARCHAR(7) YYYY/MM形式のアクセス月日(主キー)
cnt INT アクセス数
accessLogテーブルのフィールド・レイアウト
urlとaccessDateの組み合わせを主キーに設定する。

 以上のGlobal.asaxファイルをアプリケーション・ルートの直下に配置すれば完了だ。アプリケーション配下の任意のファイル(サブフォルダを含む)にアクセスすることで、自動的にアクセス記録がデータベースに記録されていく。本稿では、単純に月ごとのアクセス・カウント数を記録するだけであるが、冒頭で紹介したように、必要に応じてユーザー・エージェントのリンク元URLなどを記録できるように改良を施すこともできるだろう。

記録されたアクセス・ログ情報の表示例

 なお、最後に一点だけ注意してほしい点がある。それはGlobal.asaxが監視の対象とするのは、あくまで拡張子が「.aspx」や「.asmx」など、ASP.NETが処理対象として認識するファイルだけであるということだ。「.html」や「.txt」は処理の対象外なので、注意すること。これらのファイルを含めてロギングを行う方法については、別稿「TIPS:[ASP.NET].htmlや.pdfファイルをフォーム認証やロギングの対象にするには?」にて紹介している。End of Article

カテゴリ:Webフォーム 処理対象:ロギング
関連TIPS:[ASP.NET].htmlや.pdfファイルをフォーム認証やロギングの対象にするには?
 
この記事と関連性の高い別の.NET TIPS
[ASP.NET]アプリケーション共通のロギングを行うには?(HTTPモジュール編)
[ASP.NET]アプリケーション内で発生したエラー情報をロギングするには?
[ASP.NET AJAX]非同期通信時に発生した例外情報をロギングするには?
[ASP.NET].htmlや.pdfファイルをフォーム認証やロギングの対象にするには?
[ASP.NET]途中ページへの直接アクセスを防ぐには?
このリストは、(株)デジタルアドバンテージが開発した
自動関連記事探索システム Jigsaw(ジグソー) により自動抽出したものです。
generated by

「.NET TIPS」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間