Want to follow this site? Here's the RSS feed.

Fundamentals of Themelia - Error Processors

Thursday, September 4, 2008

This documentation has been updated for Themelia Framework 2.0 Beta 5.

Regardless of what type of web site you are building, you absolutely must have some way to have the web site tell you that it's having problems.  If the web site isn't important enough to keep up, I can't see how it's important enough to put up in the first place.  Thus, ASP.NET developers tap into the beauty of the HttpApplication object's Error event.  By handling this event, we get immediate notification whenever an unhandled exception is thrown anywhere on the web site. For personal web sites, this is awesome, but corporate web sites, it could keep you from being fired. 

When I mention the Error event to junior developers (this is one of the things which, to me, defines a junior developer), they immediately think I'm talking about Global.asax.  Yes, it's related to Global.asax , but that's not where we're going to do this.  This isn't ASP.  This is ASP.NET.  Just because the letters of the file name contain "Global.asa", that doesn't mean it's the same thing  This is the powerful and professional beast known as ASP.NET and as such we should apply the best tried and true elegant design pattern and best practices wherever possible.  In this case, this means not putting all kinds of stuff into your Global.asax file.

Aside from the magical Application_Start method and its buddies, which must be in Global.asax, this file is really just a connection to the web site's HttpApplication instance.  Furthermore, since we are in an object-oriented world, we can inherit from that and keep our implementation away from the file entirely.  Thus the average Global.asax file should look like this:

<%@ Application Language="C#" Inherits="MySite.Web.HttpApplication" %>

That's it.  Well, unless you need the Application_Start method (or its buddies), but for the most part that's it.  Don't go handling events in there.  You will have your own HttpApplication class with error handling in another place. Here's an example:

namespace MySite.Web
{
    public class HttpApplication : System.Web.HttpApplication
    {
        //- @Init -//
        public override void Init()
        {
            this.Error += new EventHandler(OnError);
            //+
            base.Init();
        }

        //- $OnError -//
        private void OnError(Object sender, EventArgs e)
        {
            HttpApplication ha = sender as HttpApplication;
            if (ha != null)
            {
                System.Web.HttpContext context = ha.Context;
                Themelia.Tracing.ReportFacade.Send(context.Error);
            }
        }
    }
}

This example traps each unhandled exception and sends it to the default exception notification contact in Themelia.  All exception details including all inner exceptions will be included in the e-mail.

This is primarily the ASP.NET world before Themelia 2.0 (well, the ReportFacade is Themelia 2.0!): you trap an error and send it, save it, or whatever.

Error Processors

In the real world, sometimes you don't need to be notified of every error on every part of your web site.  Sometimes you only need to know about a certain sensitive portion.  The rest, perhaps, can be saved into a database or queued up for sending once a day.  On the other hand, some parts of a web site should not just notify you of an exception, it should also be logged... in triplicate.  Thus Themelia 2.0 provides error processors which handle error within the boundaries of a web domain.

If you recall, a web domain is a Themelia 2.0 concept that bring the idea of an AppDomain to the web.  What happens in a web domain, stays in the web domain.  Thus, if you register an error processor in a web domain, you will only see error about that particular web domain.

Configuration of error processors is incredibly simply: just register one in a web domain.  Period!

Here's the actual configuration, which replaces everything we did with Global.asax and the custom HttpApplication file:

<themelia.web>
  <webDomains>
    <errorProcessors>
      <add type="EmailSendingErrorProcessor" />
    </errorProcessors>
  </webDomains>
</themelia.web>

In this sample, EmailSendingErrorProcessor is an error processor provided by Themelia and is, obviously, registered in Themelia's processor factory to allow it's usage outside of declaring it's full type (Themelia.Web.Routing.EmailSendingErrorProcessor, Themelia.Web).

If you would like to create your own custom error processor, just create a class which inherits from Themelia.Web.Routing.ErrorProcessorBase.  This abstract class requires that you implement one method:

void OnErrorProcessorExecute(System.Web.HttpContext context, params Object[] parameterArray)

In this method you can do whatever you want.  Here's a sample custom error processor:

public class SavingErrorProcessor : Themelia.Web.Routing.ErrorProcessorBase
{
    //- @OnErrorProcessorExecute -//
    public override void OnErrorProcessorExecute(System.Web.HttpContext context, params object[] parameterArray)
    {
        Exception ex = context.Error;
        if (ex != null && !Http.UrlPartArray.Contains("problem"))
        {
            String problemGuid = Web.ExceptionSaver.Save(ex);
            Http.Redirect("/problem/" + problemGuid);
        }
        else
        {
            try
            {
                Themelia.Reporting.ReportFacade.Send("Unknown exception occurred");
            }
            catch
            {
                //+ just in case the sample web.config isn't configured for mail
            }
        }
    }
}

In this sample (as seen in the Themelia "Basic" sample), when an exception is thrown that is not caught by anyone, this error processor will catch it and check if we are already on the problem page.  If we aren't, then save the exception in the database and send the reference to the appropriate page to allow the user to enter in a description of what he or she was doing when the exception was raised.  If the exception was thrown on that page... oops, then just send us an alert so we know to get on it right away.

Error Processors in WebDomain Inheritance

In the following example, we have three web domains each with its own configuration:

<themelia.web>
  <webDomains>
    <add>
      <processors>
        <add type="Sample.Web.Routing.SavingErrorProcessor, Sample.Web" />
      </processors>
    </add>
    <add name="sales" path="sales">
      <processors>
        <add type="EmailSendingErrorProcessor" />
      </processors>
    </add>
    <add name="finance" path="finance" basedOn="sales">
      <processors>
        <add type="Sample.Web.Routing.DatabaseSavingErrorProcessor, Sample.Web" />
      </processors>
    </add>
  </webDomains>
</themelia.web>

When the root web domain has an error, the exception is handled as we previously explained.  If the sales web domain has a problem, send an alert immediately.  Finally, notice that the finance web domain is based on the sales web domain.  Thus, when an error is raised in the finance web domain, an email is sent immediately and the error is saved to the database (assuming thats what the DatabaseSavingErrorProcessor does!) To be clear, this does NOT mean that sales errors will go to the finance web domain.  Web domain inheritance just copies configuration and maintains the protective boundaries.  Thus each web domain will see only its' own exceptions.

Exception Filters

In addition to the flexibility you receive by having error processors run at the web domain level, you may also set filters as to what exception a error processor will handle.  For example, perhaps you only want to save exceptions, email only on a SqlException, but have someone paged on all OutOfMemory exceptions.

Configuring this feature couldn't possibly be simple: you just register exceptions as initialization parameters.  Here's an example configuration:

<themelia.web>
  <webDomains>
    <add name="sales" path="sales">
      <processors>
        <add type="EmailSendingErrorProcessor">
          <parameters>
            <add value="OutOfMemoryException" />
          </parameters>
        </add>
      </processors>
    </add>
  </webDomains>
</themelia.web>

In this example, only exceptions of OutOfMemoryException will be emailed and then only for the sales web domain.

Object Factories

Notice that we aren't specifying the assembly nor are we using the fully qualified type name for the exception.  This is due to Themelia's object factory concept, which work just like handler factories and processor factories.

When using an object factory for exceptions, you create a class which inherits from the Themelia.Web.Routing.ObjectFactoryBase class and override the CreateObject method.  This method accepts a string and returns an object (of type System.Object-- the root of all objects).  Then you want to be sure to return an object of type System.Type.

By way of example, below is a snippet of Themelia's internal object factory for exceptions:

internal class ExceptionTypeObjectFactory : ObjectFactoryBase
{
    //- Type List -//
    #region Type List
    internal static Type _Argument = typeof(System.ArgumentException);
    #endregion

    //- @CreateObject -//
    public override Object CreateObject(String text)
    {
        Type ex = null;
        switch (text)
        {
            case "argumentexception":
                return _Argument;
        }
        //+
        return ex;
    }
}

You may use this example as a template for your own custom exception type object factories.

To register a custom object factory, simply register it like any other Themelia factory:

<themelia.web>
  <webDomains>
    <add>
      <factories>
        <add type="ABCCorp.ExceptionTypeObjectFactory, ABCCorp" />
      </factories>
    </add>
  </webDomains>
</themelia.web>

Lastly, what types are already aliased for use in your own error processors?  Themelia's internal object factory for exceptions actually aliases all types in the mscorlib, System, System.Data, System.Web, and System.Xml assemblies.

{$Footer$}}

Links

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.5 License.

Powered by the Minima Blog Engine 3.1 and Squid Micro-Blogging Library.

Built on Themelia Framework 2.0

Developed using NetFXHarmonics DevServer 1.1.

(all of which are NetFXHarmonics products created by David Betz)

Mini-icons are part of the Silk Icons set of icons at famfamfam.com