Sunday, August 07, 2005 2:40 AM bart

A simple request logger in ASP.NET

Introduction

I just deployed a self-written request logger HTTP Module to my blogs website to have a better picture about the activity on my blog over here. Through this post, I'd like to share information with you on how to create such a logger HTTP Module.

 

What's an HTTP Module?

ASP.NET processes requests through a so-called HTTP request pipeline. A common example of such a request is browsing to an .aspx page. First of all, the request reaches the IIS machine on a some website (based on the IP and possibly a host header). In there, an ISAPI called aspnet_isapi.dll is configured for the .aspx extension, therefore sending the request to that ISAPI dll for further processing. In here, the HTTP pipeline of ASP.NET comes into play. Based on the configuration of the web server and the web application, the request is sent further to the right party for further processing. In the case of an .aspx page, the System.Web.UI.PageHandlerFactory class takes over the request for further processing.

This "further processing" is based on two principles:

  • HTTP Modules
  • HTTP Handlers

An HTTP Module can be seen as a filter (and is somewhat equivalent to an ISAPI filter). All requests pass through the configured modules before further processing is done. Samples of modules include caching, authentication, authorization modules and also a logging module as I'll show you later on.

HTTP Handlers are the equivalent of an ISAPI extension and are responsible to handle certain requests. Examples include the processing logic for .aspx, .asmx, .cs, ... files. By writing a custom HTTP Handler you can extend the ASP.NET runtime with your own logic to process certain requests. An simple example is a handler for .jpg images that supports image resizing as I explained earlier in an article of mine (http://www.microsoft.com/belux/nl/msdn/community/columns/desmet/httphandler.mspx).

In the machine.config file (%windir%\Microsoft.NET\Framework\v1.1.4322\CONFIG\machine.config) you'll find a section and that contains preconfigured HTTP Handlers and HTTP Modules, as shown below:

    <httpHandlers>
      <add verb="*" path="*.vjsproj" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.java" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.jsl" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler" />
      <add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
      <add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory" />
      <add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="false" />
      <add verb="*" path="*.rem" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false" />
      <add verb="*" path="*.soap" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false" />
      <add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.ascx" type="System.Web.HttpForbiddenHandler" />
      <add verb="GET,HEAD" path="*.dll.config" type="System.Web.StaticFileHandler" />
      <add verb="GET,HEAD" path="*.exe.config" type="System.Web.StaticFileHandler" />
      <add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.csproj" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.vb" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.vbproj" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.webinfo" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.asp" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.licx" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.resx" type="System.Web.HttpForbiddenHandler" />
      <add verb="*" path="*.resources" type="System.Web.HttpForbiddenHandler" />
      <add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler" />
      <add verb="*" path="*" type="System.Web.HttpMethodNotAllowedHandler" />
    </httpHandlers>
    <httpModules>
      <add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
      <add name="Session" type="System.Web.SessionState.SessionStateModule" />
      <add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
      <add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
      <add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
      <add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
      <add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
      <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
    </httpModules>

Take a close look at these and try to understand what the role of each handler/module is. On a non-production box, feel free to play with these defaults.

 

Developing a custom HTTP Module

Let's take a look at the development of our logging module, step by step:

  1. Start Visual Studio .NET 2003 and create a new Class Library project in C# and give it the name WebLogger.
  2. Right click the References node in the Solution Explorer and choose Add Reference. Go to the .NET tab and select System.Web.dll (version 1.0.5000.0) and click OK.
  3. Now go to Class1.cs and change the code as follows:
using System;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Web;

namespace BdsSoft
{
   public class WebLogger : IHttpModule
   {
      //
      //Holds the connection string to the logging database
      //
      private string dsn;

      ///
      /// Default constructor. Initializes the logger with configuration read from web.config.
      ///

      public WebLogger()
      {
         //
         //Grab the connection string from in web.config
         //
         dsn = ConfigurationSettings.AppSettings["dsn"];
      }

      #region IHttpModule Members

      ///
      /// Initialization routine for the module.
      ///

      /// Context object for the current web application
      public void Init(HttpApplication context)
      {
         //
         //We're only interested in incoming requests for logging purposes
         //
         context.BeginRequest += new EventHandler(context_BeginRequest);
      }

      ///
      /// Implementation of IDisposable.
      ///

      public void Dispose()
      {
      }

      #endregion

      ///
      /// Event handler for the beginning of a web request.
      ///

      /// HttpApplication object
      /// Event arguments
      private void context_BeginRequest(object sender, EventArgs e)
      {
         //
         //Get access to the request context
         //
         HttpApplication context = sender as HttpApplication;
         
         //
         //Connect to the database
         //
         using (SqlConnection conn = new SqlConnection(dsn))
         {
            //
            //Stored procedure to insert an entry in our web log
            //
            SqlCommand cmd = new SqlCommand("InsertLogEntry", conn);
            cmd.CommandType = CommandType.StoredProcedure;

            //
            //Parameterization of the SqlCommand
            //
            cmd.Parameters.Add("@agent", SqlDbType.NVarChar, 100);
            cmd.Parameters.Add("@rawUrl", SqlDbType.NVarChar, 1000);
            cmd.Parameters.Add("@userHostAddress", SqlDbType.NVarChar, 15);
            cmd.Parameters.Add("@userHostName", SqlDbType.NVarChar, 1000);
            cmd.Parameters.Add("@httpMethod", SqlDbType.NVarChar, 10);

            //
            //Query the stuff we need and set the parameters
            //
            cmd.Parameters["@agent"].Value = context.Request.UserAgent;
            cmd.Parameters["@rawUrl"].Value = context.Request.RawUrl;
            cmd.Parameters["@userHostAddress"].Value = context.Request.UserHostAddress;
            cmd.Parameters["@userHostName"].Value = Dns.Resolve(context.Request.UserHostAddress).HostName; //this can cause a significant slowdown because of DNS reverse lookup queries!!!
            cmd.Parameters["@httpMethod"].Value = context.Request.HttpMethod;

            //
            //Open connection and execute the stored procedure
            //
            conn.Open();
            cmd.ExecuteNonQuery();
         }
      }
   }
}

As you can see, the HTTP Module is nothing more than an implementation of the System.Web.IHttpModule interface. In the Init method, we register an event handler for the BeginRequest event of the HttpApplication which is passed to use by the ASP.NET runtime through the context parameter. The rest of the code is pretty straightforward and just plain vanilla ADO.NET magic. Build the project by hitting CTRL-SHIFT-B.

Now, for the database:

  1. Open SQL Server 2000 Query Analyzer.
  2. Connect to the database where you want to put the logs; make sure you have administrative privileges over that database in order to add a table and a stored procedure.
  3. Execute the following script:

create table RequestLog
(
[ID] INT IDENTITY(1,1) PRIMARY KEY,
[agent] nvarchar(100) NOT NULL,
[rawUrl] nvarchar(1000) NOT NULL,
[userHostAddress] nvarchar(15) NOT NULL,
[userHostName] nvarchar(1000) NOT NULL,
[httpMethod] nvarchar(10) NOT NULL,
[dateTime] datetime DEFAULT GetDate()
)
go

create proc InsertLogEntry
@agent nvarchar(100),
@rawUrl nvarchar(1000),
@userHostAddress nvarchar(15),
@userHostName nvarchar(1000),
@httpMethod nvarchar(10)
as
insert into RequestLog (agent, rawUrl, userHostAddress, userHostName, httpMethod) values (@agent, @rawUrl, @userHostAddress, @userHostName, @httpMethod)
go

Very straightforward, isn't it?

 

Deployment

Time to deploy. Connect to the webserver by means of FrontPage Server Extensions or FTP or file share or ... and open up the folder that contains the web application. Do the following:

  1. Copy the WebLogger.dll file from the bin\Debug folder of your project (that's the location where the module was compiled to) to the bin folder on the server.
  2. Edit the web.config file in the root folder of the web application as follows:

<configuration>
 
<appSettings>
    <!--Web logger -->  
    <add key="dsn" value = "server=localhost;uid=Blogs_BartDeSmet;pwd=8BhWx9CB73;database=Blogs_BartDeSmet" />
  </appSettings>

  <system.web>
   
<httpModules>
      <add name="RequestLogger" type= "BdsSoft.WebLogger, WebLogger" />
    </httpModules>

  </system.web>
</configuration>

Of course, maintain all the other stuff needed in your web.config file. The code displayed above just indicates the changes needed, including the registration of the HTTP Module and the configuration of it through the dsn-setting under , which needs to be adapted to reflect your configuration (user name, password and database name). Once you have saved the web.config file, the logging should start. While writing this post (took me about 10 minutes or so), I got already 239 rows in my event log table. Analysis can start! In particular I'm interested to see which kind of user agents are visiting my blog. Currently I have:

  • Abilon
  • Bloglines/2.0 (http://www.bloglines.com; 29 subscribers)
  • BlogPulse (ISSpider-3.0)
  • FeedDemon/1.5 (http://www.bradsoft.com/; Microsoft Windows XP)
  • Feed-Directory/0.1 (+http://www.feed-directory.com/bot.html)
  • http://www.feedtagger.com/rss-fetcher.php
  • JetBrains Omea Pro 2.0 Release Candidate 2 (http://www.jetbrains.com/omea/)
  • JetBrains Omea Pro 2.0 Release Candidate 4 (http://www.jetbrains.com/omea/)
  • JetBrains Omea Reader 1.0.4 (http://www.jetbrains.com/omea_reader/)
  • Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0) Fetch API Request
  • Mozilla/4.0 (compatible; MSIE 5.5; Windows NT 5.0; .NET CLR 1.0.3705)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows 98)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows 98; .NET CLR 1.1.4322)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50215)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50215; .NET CLR 1.1.4322)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50215)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; (R1 1.3); .NET CLR 1.1.4322)
  • Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.40607)
  • Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.1; Maxthon; .NET CLR 1.1.4322)
  • Mozilla/5.0 (compatible; Yahoo! Slurp; http://help.yahoo.com/help/us/ysearch/slurp)
  • Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.10) Gecko/20050716 Thunderbird/1.0.6
  • Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.7.10) Gecko/20050716 Firefox/1.0.6
  • NewsGator/2.0 (http://www.newsgator.com; Microsoft Windows NT 5.1.2600.0; .NET CLR 1.1.4322.2032)
  • NewsGatorOnline/2.0 (http://www.newsgator.com; 1 subscribers)
  • NewsGatorOnline/2.0 (http://www.newsgator.com; 24 subscribers)
  • RssBandit/1.3.0.26 (.NET CLR 1.1.4322.2032; WinNT 5.1.2600.0; http://www.rssbandit.org)
  • RssBandit/1.3.0.29 (.NET CLR 1.1.4322.2032; WinNT 5.1.2600.0; http://www.rssbandit.org)
  • RssReader/1.0.88.0 (http://www.rssreader.com) Microsoft Windows NT 5.2.3790.0
  • SharpReader/0.9.5.1 (.NET CLR 1.1.4322.2032; WinNT 5.1.2600.0)
  • SharpReader/0.9.5.1 (.NET CLR 1.1.4322.2300; WinNT 5.2.3790.0)
  • Waggr_Fetcher)
  • YahooSeeker-Testing/v3.9 (compatible; Mozilla 4.0; MSIE 5.5; http://search.yahoo.com/)

Pretty cool. Hope you enjoy it too!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under: ,

Comments

# re: A simple request logger in ASP.NET

Sunday, August 07, 2005 1:42 PM by bart

Good job as usual!
Thanks for having such a nice blog. :)

# seohaean.net

Friday, September 05, 2014 10:01 PM by seohaean.net

A simple request logger in ASP.NET - B# .NET Blog

# ethan allen country french furniture

Thursday, September 18, 2014 10:43 PM by ethan allen country french furniture

A simple request logger in ASP.NET - B# .NET Blog

# energy review

Saturday, September 27, 2014 2:53 AM by energy review

A simple request logger in ASP.NET - B# .NET Blog

# BagsOK Tote Store

Wednesday, October 01, 2014 1:04 PM by BagsOK Tote Store

A simple request logger in ASP.NET - B# .NET Blog

# grupolr.com.br

Wednesday, October 01, 2014 8:46 PM by grupolr.com.br

A simple request logger in ASP.NET - B# .NET Blog

# adobe photoshop cs5 serial number

Friday, October 03, 2014 2:41 AM by adobe photoshop cs5 serial number

A simple request logger in ASP.NET - B# .NET Blog

# trendy Handbag

Saturday, October 04, 2014 3:25 PM by trendy Handbag

A simple request logger in ASP.NET - B# .NET Blog

# www.ww.isaev.infoww.isaev.info

Wednesday, October 08, 2014 5:25 PM by www.ww.isaev.infoww.isaev.info

A simple request logger in ASP.NET - B# .NET Blog

# deborah dolen

Sunday, October 12, 2014 6:54 AM by deborah dolen

A simple request logger in ASP.NET - B# .NET Blog

# sofás

Sunday, October 12, 2014 9:56 AM by sofás

A simple request logger in ASP.NET - B# .NET Blog

# http://bomonana.co.kr

Wednesday, October 15, 2014 10:44 AM by http://bomonana.co.kr

A simple request logger in ASP.NET - B# .NET Blog

# car garages for sale

Wednesday, October 15, 2014 2:12 PM by car garages for sale

A simple request logger in ASP.NET - B# .NET Blog

# apple watch cases

Thursday, October 16, 2014 12:21 AM by apple watch cases

A simple request logger in ASP.NET - B# .NET Blog

# apple watches

Thursday, October 16, 2014 2:20 AM by apple watches

A simple request logger in ASP.NET - B# .NET Blog

# apple watches

Thursday, October 16, 2014 4:37 AM by apple watches

A simple request logger in ASP.NET - B# .NET Blog

# apple watch screen protectors

Thursday, October 16, 2014 5:02 AM by apple watch screen protectors

A simple request logger in ASP.NET - B# .NET Blog

# Highly recommended Resource site

Thursday, October 16, 2014 1:26 PM by Highly recommended Resource site

A simple request logger in ASP.NET - B# .NET Blog

# p

Thursday, October 16, 2014 8:22 PM by p

A simple request logger in ASP.NET - B# .NET Blog

# ow.ly

Saturday, October 18, 2014 8:34 AM by ow.ly

A simple request logger in ASP.NET - B# .NET Blog

# sleep mask

Saturday, October 18, 2014 3:43 PM by sleep mask

A simple request logger in ASP.NET - B# .NET Blog

# Mx1.Stepnogorsk.org

Wednesday, October 22, 2014 5:41 AM by Mx1.Stepnogorsk.org

A simple request logger in ASP.NET - B# .NET Blog

# just click the up coming article

Wednesday, October 22, 2014 8:13 AM by just click the up coming article

A simple request logger in ASP.NET - B# .NET Blog

# ...

Thursday, October 23, 2014 3:51 AM by ...

A simple request logger in ASP.NET - B# .NET Blog

# embroidery kit

Friday, October 24, 2014 6:10 PM by embroidery kit

A simple request logger in ASP.NET - B# .NET Blog

# pre workout formula

Saturday, October 25, 2014 9:12 AM by pre workout formula

A simple request logger in ASP.NET - B# .NET Blog

# energy investments

Sunday, October 26, 2014 8:08 PM by energy investments

A simple request logger in ASP.NET - B# .NET Blog

# Lisbon cleaning services

Wednesday, October 29, 2014 8:21 AM by Lisbon cleaning services

A simple request logger in ASP.NET - B# .NET Blog

# continue reading this..

Tuesday, November 04, 2014 3:52 AM by continue reading this..

A simple request logger in ASP.NET - B# .NET Blog

# anti aging systems

Tuesday, November 04, 2014 5:14 AM by anti aging systems

A simple request logger in ASP.NET - B# .NET Blog

# simpsons tapped out hack

Tuesday, November 04, 2014 7:43 AM by simpsons tapped out hack

A simple request logger in ASP.NET - B# .NET Blog

# quilting cutting mats

Monday, November 17, 2014 11:11 PM by quilting cutting mats

A simple request logger in ASP.NET - B# .NET Blog