ASP.NET MVC Magical Error Logging with ELMAH

If you aren’t using ELMAH (Error Logging Modules and Handlers) you should be – http://code.google.com/p/elmah/ It’s the best thing since sliced bread for logging ASP.NET errors with practically zero effort. It basically plugs into the ASP.NET pipeline and logs all unhandled exceptions thrown and HTTP error codes together with all sorts of other useful information such as request url, stacktrace, current user username, cookies and more. You can say it makes a snapshot of the Yellow Screen of Death with extra information. It then gives you a simple web access to the recent errors logged (by default at http//mysite.com/elmah.axd), exposing a RSS feed as well. It’s fully configurable (error filters, multiple backend log storage systems support, etc) and also can be set up to send emails on error.

Some screenshots:

List of Errors using the Web Interface

Error Details in the web interface

You can pull ELMAH through nuget via the “Elmah” package., which will download it and add a reference. The steps to get it all set up for ASP.NET MVC are as follows.

1. Register Elmah configuration sections in the Web.config

    <sectionGroup name="elmah">
      <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
      <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
    </sectionGroup>

2. Add the root level ELMAH section in Web.config to tell it where to store the logs and how. Easiest to set up is to have a directory where it stores xml file for each error, but make sure the IIS process/AppPool has write access to that directory! Also I have added a filter to ignore all HTTP 404 errors. Note that this filter won’t work for ASP.NET MVC and I will come back to that later.

<elmah>
    <security allowRemoteAccess="1" />
    <errorLog type="Elmah.XmlFileErrorLog, Elmah" logPath="~/App_Data/ElmahLogs" />
    <errorFilter>
      <test>
        <equal binding="HttpStatusCode" value="404" type="Int32" />
      </test>
    </errorFilter>
  </elmah>

3. Register ELMAH with the ASP.NET pipeline both in the system.web section (when running locally) and the  system.webServer section when deployed to IIS. Just copy paste the below snippet in both sections. Notice how the default configuration says that ELMAH will be available at mysite.com/elmah.axd . You can change that if you want (in both places!)

    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
    </httpModules>
    <httpHandlers>
      <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
    </httpHandlers>

You are done and elmah will be available at http://mysite.com/elmah.axd , but it won’t work quite right with ASP.NET MVC, because of the way errors are handled there, so there are those additional things that need to be set up:

4. Log exceptions before ASP.NET MVC swallows them if using customErrors or some other code handles and swallows the error. To do this I’ve implemented the below exception filter that logs all handled exceptions. It’s a good example also of how to programatically log errors with ELMAH:

using System;
using System.Web.Mvc;
using Elmah;

namespace Triply.Extensions
{
	public class ElmahHandledErrorLoggerFilter : IExceptionFilter
	{
		public void OnException (ExceptionContext context)
		{
			// Long only handled exceptions, because all other will be caught by ELMAH anyway.
			if (context.ExceptionHandled)
				ErrorSignal.FromCurrentContext().Raise(context.Exception);
		}
	}
}

To register it add it in Global.asax.cs. The ordering is important – add it before the HandleErrorAttribute is registered.

public static void RegisterGlobalFilters (GlobalFilterCollection filters)
{
	filters.Add(new ElmahHandledErrorLoggerFilter());
	filters.Add(new HandleErrorAttribute());
}

5. One final note regarding filtering out HTTP 404 errors. In ASP.NET MVC they are raised as exceptions so if you want to ignore them unfortunately you can’t do that declaratively in the Web.config. Instead you have to add the following elmah hooks to your Global.asax.cs to do it programatically:

		// ELMAH Filtering
		protected void ErrorLog_Filtering (object sender, ExceptionFilterEventArgs e)
		{
			FilterError404(e);
		}

		protected void ErrorMail_Filtering (object sender, ExceptionFilterEventArgs e)
		{
			FilterError404(e);
		}

		// Dismiss 404 errors for ELMAH
		private void FilterError404 (ExceptionFilterEventArgs e)
		{
			if (e.Exception.GetBaseException() is HttpException) {
				HttpException ex = (HttpException)e.Exception.GetBaseException();
				if (ex.GetHttpCode() == 404)
					e.Dismiss();
			}
		}

That’s it. Now you get super cool logging (and remember – RSS feed!)


Be Sociable, Share!
  • Jay R. Wren

    This looks great, but if I snag nuget.exe, can’t I just nuget install elmah and have it do all this for me?

    • http://ivanz.com/ Ivan Zlatev

      If you snap Elmah no if you snap elmah.mvc maybe, but it didn’t work
      very well for me at all and even broke my web.conf. Also it doesn’t do
      everything that I described. So to make sure it works properly do it
      yourself.

  • Anonymous

    What a great post! I dont need to log the error in BaseController.OnException or even overide the HandleErrorAttribute. Best Idea. Work smart for me. Perfect solution, but just one thing. You can state about IIS7 integrated user to use web.config system.webServer-modules instead of httpModules.

  • Brent

    Hi,
    This is a really nice solution, but I’ve noticed OnException is not being called at all if the exception has been handled in a catch statement (at least for me).  Any thoughts?
    Thank you.

    • http://ivanz.com/ Ivan Zlatev

      Well, if you catch and swallow the exception yourself before it reaches the ASP.NET MVC pipeline there is no way for neither ELMAH nor ASP.NET MVC to know about it :). You will have to log it yourself the same way the exception handler does – ErrorSignal.FromCurrentContext().Raise(exception);

      • Brent

        Thanks for the response Ivan.  I had missed the point.  Works *just* fine when you use it for what it’s supposed to be used for.  =)

      • pm – united infoways

        Or alternatively when done, you can simply throw back your exception from inside your catch block to be further logged/emailed in a usual way via ElmahHandledErrorLoggerFilter.

        BTW, nice article. Super useful ! Thanks.

  • http://www.mactonweb.com/ Web design London

     You can state about IIS7 integrated user to use web.config system.webServer-modules instead of httpModules.Great post Thanks for sharing it..

  • brian

    context.ExceptionHandled (per your filter snippet in pt.4 above) does not seem to be valid in my ASP MVC 4 project

    • http://ivanz.com/ Ivan Zlatev

      I am using it in several ASP.NET MVC 4 projects without any issues.