Introduction

I’m really interested in using Atif Aziz’s ELMAH (Error Logging Modules and Handlers for ASP.NET) in my next project. Up to now I’ve used the perennial favourite Log4Net, or rolled my own error logging implementation. Currently, all our web errors get emailed to our support team, and… immediately deleted. No one likes those emails!

My production environment has fairly stringent architectural and security requirements. Our web servers are load balanced, and typically run multiple web applications. I’d like to store all the error logs in a single repository, and be able to identify which server, and application logged which error. All possible with ELMAH.

From a security perspective, I’m not allowed to store anything on my web server of a potentially sensitive nature, and this includes error logs.

Logically, the production architecture looks like this:

  • Database Server — hosting SQL Server 2005
  • Application Server — hosting various applications, with functionality exposed as services, and able to talk to the database server.
  • Web Server — hosting public-facing websites and web applications, and able to interact with services exposed by the application server.

Each of these servers is suitably fire-walled.

Out of the box ELMAH provides a SqlErrorLog provider backed by SQL Server. However, that database needs to be accessible from the web server, which is not allowed in my scenario. I need a ServiceErrorLog provider to use instead. This is my attempt at creating one.

Creating a Skeleton Provider

I started by creating a new web project, using the existing SqlErrorLog provider to perform the logging. It’s all well documented on-line, and in very little time I had a working application logging to a new SQL Server database (all hosted on the one machine for now).

I added a new Class Library (MyCompany.ElmahExt) project to the solution containing my web application, and created a skeleton implementation of my new ErrorLog provider:

namespace MyCompany.ElmahExt
{
    using System;
    using System.Collections;
    using Elmah;
 
    public class ServiceErrorLog : ErrorLog
    {
        public ServiceErrorLog(IDictionary config)
        {
        }
 
        public override string Name
        {
            get { return "Custom WebService Error Log"; }
        }
 
        public override string Log(Error error)
        {
            throw new NotImplementedException("Please implement me");
        }
 
        public override int GetErrors(int pageIndex, int pageSize, IList errorEntryList)
        {
            throw new NotImplementedException("Please implement me");
        }
 
        public override ErrorLogEntry GetError(string id)
        {
            throw new NotImplementedException("Please implement me");
        }
    }
}

My provider inherits Elmah.ErrorLog. There are three abstract methods that I need to provide implementations for:

  • Log — logs a new error
  • GetErrors — gets a list of errors
  • GetError — gets a specific error

For the moment, we’ll just throw errors from these methods, but later I’ll have them call the relevant services.

I also need to provide a constructor that takes an IDictionary argument (ELMAH’s class factory will expect it).

Finally, I’ve overridden the Name property, and assigned my provider a unique name. Although not strictly necessary, I took a good look at the source code for the existing providers and decided this was best practice.

Configuring and Testing

I added a project reference for my new MyCompany.ElmahExt component to my web application project.

And finally, I changed the web config so that the new provider gets used.

<elmah>
  <security allowRemoteAccess="0" />
  <errorLog type="MyCompany.ElmahExt.ServiceErrorLog, MyCompany.ElmahExt" />
</elmah>

Fire up the project in debug, and then navigate to the elmah.axd page and the code breaks in my implementation of the GetErrors method, which is just what I want for now.

In part 2, I’ll have a look at creating a web service to wrap database access.