How to Add Tracing to IIS 7.0 Managed Modules

by Saad Ladki

Introduction

IIS 7.0 and above features the ability to plug-in and use custom developed managed modules to accomplish many different tasks and use those modules for all sorts of content. However, the question arises: what happens if the module has problems or difficulties? Previously, you used System.Diagnostics to trace the event when problems occurred. Unfortunately, those traces are completely separate from IIS traces.

Not so with IIS 7.0 and above. Now you can add traces to the module code using System.Diagnostics.TraceSource (new to .Net 2.0). You have the ability to route those traces into the IIS tracing infrastructure so that they are available to modules that consume traces--for example, Failed Request Tracing.

The IIS Team encourages instrumenting code using basic patterns such as:

  • START & STOP events around key activities within your code
  • WARNING & ERROR events for unexpected occurrences that might cause the request to fail (such as failed authentication)
  • INFORMATIONAL & VERBOSE events for help with diagnostics, such as module configuration being used

Tasks illustrated in this walkthrough include:

  • Adding tracing to the module using System.Diagnostics.TraceSource
  • Configuring failed request tracing to capture these traces
  • Generating the failure condition and viewing the resulting trace

Prerequisites

Follow the steps below before performing the tasks in this article.

Step 1: Installing IIS

First, IIS must be installed. Check to see if IIS is installed by browsing to http://localhost/. If IIS is installed, you see the "under construction" page. If IIS is not installed, refer to Installing IIS for instructions. Make sure to install the following IIS components:

  • ASP (under World Wide Web Services => Application Development Features => ASP)
  • ASP.Net (under World Wide Web Services => Application Development Features => ASP.Net)
  • Tracing (under World Wide Web Services => Health & Diagnostics => Tracing)

Step 2: Log in as Administrator

Login as is the administrator account or in the Administrators group.

Note

Being in the Administrators group does not grant you complete administrator privileges by default. You must run many applications as Administrator. Right-click the application icon and choose "Run as Administrator".

Step 3: Make a Backup

Make a backup of the configuration before executing the tasks in this article. Run the following:

  1. Click the Start button -> All Programs -> Accessories -> (r-click)Command Prompt -> Run as Administrator
    Screenshot of the Windows Start menu with a focus on the Run as administrator option in the right-click drop-down menu.

  2. Execute the following command in that command prompt:

    %windir%\system32\inetsrv\appcmd add backup
    

Step 4 : Creating Sample Content

  1. Delete everything in the c:\inetpub\wwwroot folder.
  2. From the Administrator command prompt started above, paste the attached code into a file in inetpub\wwwroot called test.htm:
<h2>HOWTO: Adding tracing to modules </h2>
This is a sample page served by the static file handler (native code).

Creating & Tracing a Simple Module

In this section, you create a sample module to instrument.

Step 1: Create a Simple Module

Using the Administrator command prompt, copy and paste the following code into a file called IIS_MOD_REQDATA.cs:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace IIS_MOD_REQDATA
{
    public class IIS_MOD_REQDATA : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.EndRequest += (new EventHandler(this.Application_EndRequest));
        }

        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            // start writing out the request data

            context.Response.Write("<hr>");
            context.Response.Write("<b><font size=2 color=green>REQUEST HEADERS</font></b><br>");
            context.Response.Write("<font size=2>");
            context.Response.Write("METHOD : " + context.Request.HttpMethod + "<br>");
            context.Response.Write("URL : " + context.Request.Url + "<br>");
            context.Response.Write("QUERYSTRING : " + context.Request.QueryString + "<br>");
            context.Response.Write("</font><br>");

            // now response data

            context.Response.Write("<b><font size=2 color=blue>RESPONSE HEADERS</font></b><br>");
            context.Response.Write("<font size=2>");
            context.Response.Write("STATUS CODE : " + context.Response.StatusCode.ToString() + "." + context.Response.SubStatusCode.ToString() + "<br>");
            context.Response.Write("CONTENT TYPE : " + context.Response.ContentType.ToString() + "<br>");
            context.Response.Write("EXPIRES : " + context.Response.Expires.ToString() + "<br>");
            context.Response.Write("</font><br>");

            // set cache policy on response so it's not cached.

            context.Response.DisableKernelCache();
        }

        public void Dispose()
        {
        }
    }
}

Step 2: Adding Tracing to the Managed Module

In order to add tracing to your module and route its trace events into IIS, use System.Diagnostics.Trace source. Add the following line under the using statements:

using System.Diagnostics;

You must create a TraceSource within the code – notice the definition of the traceSource within the declaration of the IIS_MOD_REQDATA module:

public class IIS_MOD_REQDATA : IHttpModule
{
    TraceSource tsStatus;

The tsStatus member is initialized during the IHttpModule's Init() method:

public void Init(HttpApplication application)    
{    
    application.EndRequest += (new EventHandler(this.Application_EndRequest));
    // setup traceSource
    tsStatus = new TraceSource("tsStatus");    
}

The name of the TraceSource ("tsStatus") is important, as this name is later referenced in the web.config file later. The module is now setup to emit events if needed.

To add a new trace event, use tsStatus.TraceEvent(<type>, 0, <somestring>) to write out events. Add the recommended Start & End events to the Application_EndRequest() method:

private void Application_EndRequest(Object source, EventArgs e)        
{    
    tsStatus.TraceEvent(TraceEventType.Start, 0, "[REQDATA MODULE] START EndRequest");

    // other code

    tsStatus.TraceEvent(TraceEventType.Stop, 0, "[REQDATA MODULE] END EndRequest");    
}

Notice the different <type>'s – the types supported, among others, include:

  • TraceEventType.Start
  • TraceEventType.Stop
  • TraceEventType.Error
  • TraceEventType.Warning
  • TraceEventType.Information
  • TraceEventType.Verbose

For completeness, the entire source for the module (including the trace events), can be copied from here:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Diagnostics;

namespace IIS_MOD_REQDATA
{
    public class IIS_MOD_REQDATA : IHttpModule
    {

        TraceSource tsStatus;

        public void Init(HttpApplication application)
        {
            application.EndRequest += (new EventHandler(this.Application_EndRequest));

            // TRACING

            tsStatus = new TraceSource("tsStatus");
        }

        private void Application_EndRequest(Object source, EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;

            tsStatus.TraceEvent(TraceEventType.Start, 0, "[REQDATA MODULE] START EndRequest");

            // start writing out the request data

            context.Response.Write("<hr>");
            context.Response.Write("<b><font size=2 color=green>REQUEST HEADERS</font></b><br>");
            context.Response.Write("<font size=2>");
            context.Response.Write("METHOD : " + context.Request.HttpMethod + "<br>");
            context.Response.Write("URL : " + context.Request.Url + "<br>");
            context.Response.Write("QUERYSTRING : " + context.Request.QueryString + "<br>");
            context.Response.Write("</font><br>");

            tsStatus.TraceEvent(TraceEventType.Verbose, 0, "[REQDATA MODULE] done with Req Data, moving onto Response");

            // now response data

            context.Response.Write("<b><font size=2 color=blue>RESPONSE HEADERS</font></b><br>");
            context.Response.Write("<font size=2>");
            context.Response.Write("STATUS CODE : " + context.Response.StatusCode.ToString() + "." + context.Response.SubStatusCode.ToString() + "<br>");
            context.Response.Write("CONTENT TYPE : " + context.Response.ContentType.ToString() + "<br>");
            context.Response.Write("EXPIRES : " + context.Response.Expires.ToString() + "<br>");
            context.Response.Write("</font><br>");

            if (context.Response.StatusCode > 399)
            {
                tsStatus.TraceEvent(TraceEventType.Warning, 0, "[REQDATA MODULE] error status code detected");
            }

            tsStatus.TraceEvent(TraceEventType.Verbose, 0, "[REQDATA MODULE] done with Response Data");

            // set cache policy on response so it's not cached.

            context.Response.DisableKernelCache();
            tsStatus.TraceEvent(TraceEventType.Verbose, 0, "[REQDATA MODULE] cache setting is (" + context.Response.Cache.ToString() + ")");

            tsStatus.TraceEvent(TraceEventType.Stop, 0, "[REQDATA MODULE] STOP - EndRequest");
        }

        public void Dispose()
        {
        }
    }
}

Step 3: Compiling Our Traced Module

Now to compile the module & deploy it. In the Administrator command prompt, run the following command:

%systemroot%\Microsoft.NET\Framework\v2.0.50727\csc.exe /target:library /out:IIS_MOD_REQDATA.dll /debug /d:TRACE /R:System.Web.dll IIS_MOD_REQDATA.cs

Screenshot of the exclamation code dash consoleMain command in Notepad.

Note

If you are running this on a 64bit system, compile using the 64bit c# compiler in %windir%\microsoft.net\framework64\v2.0.50727\csc.exe

Note the use of the /debug & /d:TRACE switches. You must use these switches to compile the trace events into the binary. Failing to compile with these switches means that the module will not have any trace events in it.

Having compiled the module, deploy the module and run an initial test to see if the module works before capturing its traces.

Adding Modules to the Site's Configuration

This section includes adding and testing the module.

Step 1 : Adding the Module to the Site's Configuration

  1. Using the same Administrator command prompt, make a directory called \inetpub\wwwroot\bin, then copy IIS_MOD_REQDATA.dll over to that directory.
  2. Enable the module. From the administrator command prompt, type start inetmgr to bring up the IIS administration UI.
  3. Under the Connections pane, expand the local machine name, then Sites, and click on Default Web Site.
  4. Under IIS in the center pane, double-click on Modules:
    Screenshot of the Default Web Site Home screen with the Modules option being highlighted.
  5. You see a large list of modules configured for use by this site. On the right side of the UI under Actions, click Add Managed Module:
    Screenshot of the Actions pane with a focus on the Add Managed Module option.
  6. In the window that displays, name the managed module IIS_MOD_REQDATA and the type of the module is IIS_MOD_REQDATA.IIS_MOD_REQDATA(select this from the dropdown list box):
    Screenshot of the Add Managed Module dialog box, showing the Name and Type fields.
  7. Click OK. The newly traced module is now configured for the web site's use.
    Screenshot of the Modules screen, showing the newly traced module.

Step 2 : Testing Our Module

Test the module by opening Internet Explorer and browsing to http://localhost/test.htm. You see the following window:

Screenshot of a webpage reading here is a sample page that is served by static file handler.

The "REQUEST HEADERS" & "RESPONSE HEADERS" content came from our module, indicating that is works.

Routing Events to IIS

This task hooks the Module's TraceSource up to IIS tracing so that its events are emitted through IIS and then configure Failure Request Tracing to capture these trace events.

Step 1 : Enabling the Module TraceSource & Routing Its Events into IIS

The module is updated to include trace events. Configure System.Diagnostics and IIS to capture these trace events and route them into IIS's Failed Request Tracing module. Do this by configuring the <system.diagnostics> section in the web.config file to setup the TraceSource & route its events accordingly.

  1. Using your Administrator command prompt, navigate to c:\inetpub\wwwroot, and use notepad to edit your web.config file.

  2. There are 3 parts of the configuration that must be completed to get the events emitted by the module to be routed to IIS's tracing infrastructure:

    • Defining the IIS event traceListener as a shared Listener
    • Defining a switch to enable all the events
    • Defining the trace source, attaching the switch we defined as well as defining the trace listener for our source.
  3. IIS ships a new System.Diagnostics.TraceListener used to route TraceSource events into IIS's Trace infrastructure. This provider must also be defined in your web.config file.

  4. Define the traceSource by its name (tsStatus) and wire it up to the DefaultSwitch & IisTraceListener.

  5. Copy and paste this <system.Diagnostics> section into your web.config file (after your <system.webServer> section).

    <system.diagnostics>
        <sharedListeners>
          <add name="IisTraceListener" type="System.Web.IisTraceListener, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
        </sharedListeners>
    
        <switches>
          <add name="DefaultSwitch" value="All" />
        </switches>
    
        <sources>
          <source name="tsStatus" switchName="DefaultSwitch">
            <listeners>
              <add name="IisTraceListener" type="System.Web.IisTraceListener, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
            </listeners>
          </source>
        </sources>
    </system.diagnostics>
    

The tsStatus traceSource is wired up to the IIS7TraceListener, which will emit events into IIS's tracing infrastructure. Continue to the next step to wire those events up to the Failed Request Tracing provider.

Step 2 : Enabling Failed Request Tracing to Capture Module Trace Events

When these diagnostics events are emitted into IIS's tracing infrastructure, they are mapped to the ASP.net Provider & the Module flag on that provider. Their verbosity depends on the TraceEventType used. To configure Failed Request Tracing to pick these up:

  1. From your Administrator command prompt, type start inetmgr. In the Connections panel, expand the machine name, then Sites folder, then click on the Default Web Site. To the right under the Actions pane, click on Failed Request Tracing… link under Configure:
    Screenshot of the Manage Web Site section of the Actions pane, with the Failed Request Tracing option being highlighted.

  2. In the next dialog box, configure the following:
    Screenshot of the Edit Web Site Failed Request Tracing Settings dialog box.

  3. Check the Enable check box. Keep the defaults for the other settings. Click OK to continue.

  4. Now that we have verified that Failed Request Tracing Logging is enabled, we must configure the failure definitions. Back in the IIS Manager, under IIS, double-click on Failed Request Tracing Rules
    Screenshot of the Default Web Site Home screen with the Failed Request Tracing Rules option being highlighted.

  5. In the Actions pane, click Add…. This launches the Add Failed Request Tracing Rule wizard.

  6. On the Specify Content to Trace page, select the All Content (*) option for what to trace. Click Next.
    Screenshot of the Add Failed Request Tracing Rule page with a focus on the Next option.

  7. In the Define Trace Conditions screen, check the Status Codes check box & enter "200" as the status code to trace.
    Screenshot of the Define Trace Conditions screen with the Status codes field being checked.

  8. Click Next. The Select Trace Providers page appears. Select the ASPNET check box and the Module & Page check boxes under "Areas". Under Verbosity, select Verbose.
    Screenshot of the Select Trace Providers screen with the ASPNET Provider option being highlighted.

    Note

    Due to a bug in Server Beta 3 builds, module traces can only be captured if both Module and Page areas are selected. Post Server Beta 3, only Module is required to collect these events.

  9. Click Finish. You see the following definition for the Default Web Site:
    Screenshot of the Failed Request Tracing Rules screen.

Testing and Viewing the Results

In this task, we generate the failed request and view the resulting trace log. Remember, we configured IIS to capture trace logs for http://localhost/* requests that fail with a 200. To verify that it worked:

  1. Open a new Internet Explorer window. Type in the address http://localhost/test.htm. Again, you see the following:
    Screenshot of the sample webpage in an Internet Explorer window.
  2. To verify our module has generated traces that have been captured, use an Administrator-elevated Internet Explorer window and hit CTRL-O, then navigate to c:\inetpub\logs\FailedReqLogFiles\W3SVC1. In the dropdown list box that reads HTML Files, select All Files.
  3. Select the most recent fr######.xml file (today's date) and click Open. You see the events:
    Screenshot of the Request Diagnostics screen, showing the Custom Module Traces tab.

Summary

You finished adding a new trace event to a managed module, compiled and deployed that module, configured System.Diagnostics to route its events to IIS's tracing infrastructure, and finally, configured IIS's Failed Request Tracing feature to capture its events. You can now easily add new events to the module, then recompile the module & deploy it to the \bin directory. Using Failed Request Tracing, you see the results of the events.

Remember that using System.Diagnostics.TraceSource allows you to still use your module and its trace events down level, provided you hook it up to a different trace Listener.