How to Write a Custom Web Event Provider for ASP.NET 2.0

Published: 22 Sep 2006
By: Keyvan Nayyeri

In this article Keyvan shows the process to write a custom web event provider for new Health Monitoring feature in ASP.NET 2.0 and creates a custom XML provider as a sample.

Introduction

Always monitoring a web application has been a common requirement for professional developers and site administrators.   For ASP.NET 1.x there were some solutions that were written by community such as famous ELMAH (Error Logging Modules and Handlers) but now ASP.NET 2.0 has its own mechanism to let developers monitor their applications.  There are some default providers to let you get events via email, log them into SQL Server database or server's Log Events or map them to WMI.

There is also another available option for you to write your own provider.  In this article I show you how to do this by creating an XML provider step by step.

How to Write a Custom Provider

Writing a custom Health Monitoring web event provider is as easy as creating a Class Library project, a class in it and write a class that is inherited from System.Web.Management.BufferedWebEventProvider abstract base class and overriding its methods.

There are four methods in BufferedWebEventProvider abstract base class that you should override:

  • Initialize(string, NameValueCollection): Occurs when your web event provider is being initialized.
  • ProcessEvent(WebBaseEvent): Occurs when an event occurs in your application.  If provider is configured to buffer events, they will be added to buffer otherwise you need to save them.
  • ProcessEventFlush(WebEventBufferFlushInfo): Occurs when provider is being flushed and you need to implement your logic to save all buffered events.
  • Shutdown(): Occurs when provider is being shut down.

User can configure this provider to buffer events so you need to consider this case in your code and try to save buffered events.

Write an XML Provider

Now I show you how to override those methods to write a custom web event provider.  Before anything you need to know that I used a DataSet to save my event data into an XML file.

First I create a class, add necessary references and inherit it from BufferedWebEventProvider abstract base class.

using System;

using System.Collections.Generic;

using System.Text;

using System.Web;

using System.Web.Management;

using System.Collections.Specialized;

using System.Xml;

using System.IO;

using System.Data;

 

 

namespace CustomHealthMonitorProvider

{

    public class XmlProvider : BufferedWebEventProvider

    {

I add some private variables to keep my data such as my XML file path, my provider name and my DataSet to save event data.

#region "Private members"

 

private string providerName = null;

private string filePath = null;

private DataSet data = new DataSet();

 

#endregion

My implementation for Initialize() method tries to set some values for my private members then creates an initial empty XML file with correct structure and saves it by calling CreateInitialFile() private method.

public override void Initialize(string name, NameValueCollection configuration)

{

    base.Initialize(name, configuration);

 

    this.providerName = name;

    this.filePath = @"C:\MyFile.xml";

 

    if (!File.Exists(this.filePath))

        CreateInitialFile();

 

    this.data.ReadXml(this.filePath);

}

private void CreateInitialFile()

{

    DataSet ds = new DataSet(this.providerName);

    DataTable table = new DataTable("Events");

 

    table.Columns.Add("EventID");

    table.Columns.Add("EventTime");

    table.Columns.Add("EventOccurrence");

    table.Columns.Add("Message");

    table.Columns.Add("Details");

 

    ds.Tables.Add(table);

    this.data = ds;

 

    SaveData();

}

Now I override ProcessEvent() and ProcessEventFlush() methods to add events to my DataSet based on user configuration for buffering events then save them into the XML file.

public override void ProcessEvent(WebBaseEvent eventRaised)

{

    if (base.UseBuffering)

    {

        base.ProcessEvent(eventRaised);

    }

    else

    {

        DataRow row = GetRow(eventRaised);

        this.data.Tables[0].Rows.Add(row);

 

        SaveData();

    }

}

 

public override void ProcessEventFlush(WebEventBufferFlushInfo flushInfo)

{

    foreach (WebBaseEvent anEvent in flushInfo.Events)

    {

        DataRow row = GetRow(anEvent);

        this.data.Tables[0].Rows.Add(row);

    }

    SaveData();

}

GetRow() and SaveData() private methods are very simple.  First one creates a DataRow object from the properties of a WebBaseEvent object and second one saves the content of my DataSet into XML file.

private DataRow GetRow(WebBaseEvent eventRaised)

{

    DataRow row = this.data.Tables[0].NewRow();

 

    row["EventID"] = eventRaised.EventID;

    row["EventTime"] = eventRaised.EventTime;

    row["EventOccurrence"] = eventRaised.EventOccurrence;

    row["Message"] = eventRaised.Message;

    row["Details"] = eventRaised.ToString();

 

    return row;

}

 

private void SaveData()

{

    this.data.AcceptChanges();

    this.data.WriteXml(this.filePath);

}

Finally I don't need to override anything for ShutDown() method so simply call it from base class.

public override void Shutdown()

{

    base.Flush();

}

Test

Now that I have an XML web event provider, which can save my ASP.NET 2.0 application events into an XML file.  First I create a simple ASP.NET 2.0 website that has only one page which contains nothing except raising an exception:

protected void Page_Load(object sender, EventArgs e)

{

    throw new ApplicationException

        ("This is an error thrown to be logged in XML file.");

}

Now it's time to configure my application to use my XML web event provider.

<healthMonitoring enabled="true">   

  <providers>

    <add

      name="MyXMLWebEventProvider"

      buffer="false"

      bufferMode="Notification"

      type="CustomHealthMonitorProvider.XmlProvider,

      CustomHealthMonitorProvider" />

  </providers>

  <rules>

    <add name="LogAllEvents"

      eventName="All Events"         

      provider="MyXMLWebEventProvider"/>

  </rules>

</healthMonitoring>

By hitting my page I get an error.  After this I can check my XML file to see the events logged there:

<?xml version="1.0" standalone="yes"?>

<MyXMLWebEventProvider>

  <!-- Other events are removed to save space -->

  <Events>

    <EventID>dcbdf3c6-6208-48f3-93e7-c261e4ef86eb</EventID>

    <EventTime>9/18/2006 11:06:55 AM</EventTime>

    <EventOccurrence>1</EventOccurrence>

    <Message>An unhandled exception has occurred.</Message>

    <Details>Event code: 3005

Event message: An unhandled exception has occurred.

Event time: 9/18/2006 11:06:55 AM

Event time (UTC): 9/18/2006 4:06:55 PM

Event ID: dcbdf3c6620848f393e7c261e4ef86eb

Event sequence: 4

Event occurrence: 1

Event detail code: 0

 

Application information:

    Application domain: a34130de-1-128030692127031250

    Trust level: Full

    Application Virtual Path: /HealthMonitoring

    Application Path: C:\Documents and Settings\Keyvan Nayyeri\My Documents\Visual Studio 2005\WebSites\HealthMonitoring\

    Machine name: KEYVANNAYYERI

 

Process information:

    Process ID: 8800

    Process name: WebDev.WebServer.EXE

    Account name: KEYVANNAYYERI\Keyvan Nayyeri

 

Exception information:

    Exception type: System.ApplicationException

    Exception message: This is an error thrown to be logged in XML file.

 

Request information:

    Request URL: http://localhost:3705/HealthMonitoring/Default.aspx

    Request path: /HealthMonitoring/Default.aspx

    User host address: 127.0.0.1

    User: KEYVANNAYYERI\Keyvan Nayyeri

    Is authenticated: True

    Authentication Type: NTLM

    Thread account name: KEYVANNAYYERI\Keyvan Nayyeri

 

Thread information:

    Thread ID: 4

    Thread account name: KEYVANNAYYERI\Keyvan Nayyeri

    Is impersonating: False

    Stack trace:    at _Default.Page_Load(Object sender, EventArgs e)

  at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e)

  at System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e)

  at System.Web.UI.Control.OnLoad(EventArgs e)

  at System.Web.UI.Control.LoadRecursive()

  at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

 

</Details>

  </Events>

</MyXMLWebEventProvider>

You can extend my provider to save more properties for events and write a reporting application to show this data.

Summary

In this article I talked about the process you need to follow to create a custom web event provider for ASP.NET 2.0 Health Monitoring feature.  I showed this by implementing an XML web event provider and describing the process step by step. 

Download

You can download the source code of my XML web event provider.

About Keyvan Nayyeri

Keyvan is a software architect and developer who has a bachelor of science degree in applied mathematics. He was born in Kermanshah, Kurdistan, in 1984.

Keyvan’s main focus is on Microsoft development technologies and their related technologies such as mark-up languages. He’s also experie...

View complete profile

Top Articles in this category

Inversion of Control and Dependency Injection with Castle Windsor Container - Part I
Inversion of Control (IoC) and Dependency Injection (DI) are two related practices in software development which are known to lead to higher testability and maintainability of software products. While some people employ them daily in their work, many others still don't know much about them, mostly because they require in the former a shift in the usual thinking process.

How to Write a Provider Model
In this article Keyvan teaches you how to write your own data provider for .NET applications using the provider model.

Inversion of Control and Dependency Injection with Castle Windsor Container - Part II
Since the previous article didn't show much of the features offered by Castle Windsor Container, I will resume the previous discussion extending the requirements of the former example to show how IoC deals with changes and what is its real potential.

Inversion of Control and Dependency Injection with Castle Windsor Container - Part IV
In this article I introduce the missing core features I didn't tackle before.

Inversion of Control and Dependency Injection with Castle Windsor Container - Part III
In the previous article I showed how to take advantage of some of the features offered by Windsor Container to configure components and supply dependencies. You have seen how to deal with compulsory and optional dependencies, as well as how to inject simple values and component references, either individually or collected into arrays, lists and dictionaries.

Top
 
 
 

Please login to rate or to leave a comment.

Product Spotlight