Last year I wrote a post, Catch All WCF Errors, showing how you can implement IErrorHandler to log and process all errors in a WCF service. This post is how to achieve this in a Asp.Net MVC web site.
HandleErrorAttribute
If you are using MVC3 or greater, your default project is already setup to use the HandleErrorAttribute.
In the global.asax file, you should see a call to RegisterGlobalFilters. This will call out to a static method for registering global filters for our MVC application. By default it will look like this
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); } }
What does the HandleError filter do?
When registered globally it handles all errors that may occur in your application. It then redirects them to a view called Error, located in the shared views folder. It will only do this if <customErrors> is turned on in the web.config
What doesn’t HandleError do?
- It doesn’t log errors.
- It only handles http errors, of 500.
IExceptionFilter
public interface IExceptionFilter { void OnException(ExceptionContext filterContext); }
This interface exposes just one method, OnException, which is called when ever an unhandled exception is thrown.
All exception filters implement this filter, including the HandleErrorAttribute. By applying the HandleErrorAttribute filter at a global level, all errors will be handled by it. So, in order to extend our application to also log errors, you could either create a class that inherits from the HandleErrorAttribute and override its OnException method to also perform logging, or you could create another filter that implements IExceptionFilter and register it globally. I prefer the latter option, as I think its implementation is cleaner.
Create an Exception Logger
public class ExceptionLoggingFilter : IExceptionFilter { private readonly ILog _logger; public ExceptionLoggingFilter(ILog logger) { _logger = logger; } public virtual void OnException(ExceptionContext filterContext) { _logger.Error(filterContext.Exception); } public interface IExceptionFilter { void OnException(ExceptionContext filterContext); } }
The ExceptionLoggingFilter above, uses log4Net as its logger and very simply will log all exceptions that occur in a MVC web application.
To register it we need to update the RegisterGlobalFilters method, to look like this
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters, ILog logger) { filters.Add(new HandleErrorAttribute()); filters.Add(new ExceptionLoggingFilter(logger)); } }
As you can see we are now adding our ExceptionLoggingFilter to the GlobalFilters collection and passing an instance of the log4Net logger into the RegisterGlobalFilters method.
Finally, our Application_Start method needs a minor update to register log4Net and pass it into the RegisterGlobalFilters method
protected void Application_Start() { XmlConfigurator.Configure(); var logger = LogManager.GetLogger("MyLogger"); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters, logger); ... other registration }
All this wiring up can of course be done neater by using an IoC container of your choice.
What about those 404s?
Yup, they won’t get logged using the above code, as IExceptionFilter is only concerned with errors in the application. In order to log a 404 error you either need to do something ugly, such as write some code in the Application_Error or use a third party tool like Elmah.
Elmah
This is a good, solid solution for a lot of people who just want something that works without having to understand what an IExceptionFilter is.
As it uses HttpModules it can also log/handle HttpError codes other than 500. From the project website:
ELMAH (Error Logging Modules and Handlers) is an application-wide error logging facility that is completely pluggable. It can be dynamically added to a running ASP.NET web application, or even all ASP.NET web applications on a machine, without any need for re-compilation or re-deployment.
Once ELMAH has been dropped into a running web application and configured appropriately, you get the following facilities without changing a single line of your code:
- Logging of nearly all unhandled exceptions.
- A web page to remotely view the entire log of recoded exceptions.
- A web page to remotely view the full details of any one logged exception, including colored stack traces.
- In many cases, you can review the original yellow screen of death that ASP.NET generated for a given exception, even with customErrorsmode turned off.
- An e-mail notification of each error at the time it occurs.
- An RSS feed of the last 15 errors from the log.
Summary
This post was about showing you how to have complete control over logging in your MVC web application by implementing IExceptionFilter.