log4net

Log All Asp.Net MVC Errors

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(&quot;MyLogger&quot;);

   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.

Catch All WCF Errors

Want to log all uncaught errors in a WCF service? Want to return a nicely formatted fault exception too?

IErrorHandler can help.

I like using this interface as you can apply it to an entire WCF Service with just a few lines of code, making it easy to use against numerous WCF services ensuring that your error handling is reliable. It allows you to log your errors in a consistent manner and also do any bespoke cleaning up of resources that you might want to do. On top of this, your implementation of the ErrorHandler can (and should) exist totally outside of your WCF service, allowing a good separation of concerns

This interface exposes two methods:

1. provide fault: This is where you can provide what fault gets sent to the client. By default, sometimes the error gets wrapped up in a fault exception and sent to the client. Sometimes not. Here is where you can wrap up the exception any way you like and return it to the client in a fault exception.

2. handle error: This is where you prove code to handle the error and commonly where you will log the error, raise system alerts, etc.

For MSDN’s description, see here

Let’s Create our class

public class MyErrorHandler : IErrorHandler
{
		private readonly ILog _logger;

		public ErrorHandler()
			:this(LogManager.GetLogger("myassembly"))
		{}

		public MyErrorHandler(ILog logger)
		{
			_logger = logger;
		}
}

Nothing too exciting here – we’re injecting a logging object, or creating one if not provided. We need to implement that interface though.

Let’s provide a fault.

public void ProvideFault(Exception ex, MessageVersion version, ref Message fault)
{
	if (ex is FaultException)
	{
		return;	//WCF will handle this
	}

	var messageFault = MessageFault.CreateFault(new FaultCode("Service"), new FaultReason(ex.Message), ex, new NetDataContractSerializer());
	fault = Message.CreateMessage(version, messageFault, null);
}

In this example I want to make sure that the client receives the error in the fault every time. If the exception is already of type FaultException, then nothing need be done.
So here we are creating a new Message, which contains the exception message. If we didn’t want the client to know the specifics of the fault we could just create a generic message here instead of the exception message.

Let’s handle that error

public bool HandleError(Exception ex)
{
	_logger.Error(ex.Message, ex);

	if (ex is FaultException)
	{
		return false;	//WCF will handle this
	}

	return true;
}

The first line logs the error. Then as you probably noticed this method needs to return a bool. This is so that the dispatcher knows if the error was handled or not. You want to return false in the event the error is a FaultException, as WCF will automatically handle this. Otherwise we need to tell it that we’ve handled it (true).

Wiring it into the WCF service

There are a few ways to achieve this. My preferred method is to take the least painful way and make our ErrorHandler class implement IContractBehaviour and apply it as an attribute to the service’s DataContract. Taking a step back what we need to do is add our ErrorHandler class to the collection of ChannelDispatchers.ErrorHandlers, so our WCF service will use our custom ErrorHandler.

This is how we do it.  First add the interface to our class.

public class MyErrorHandler : IErrorHandler, IContractBehavior
{
...

Now implement the interface, adding our implementation of IErrorHandler to the collection of ErrorHandlers.

public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
{
	dispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(this);
}

public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{}

public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{}

public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{}

Now the final part of the wiring up is to make MyErrorhandler an attribute

public class MyErrorHandler : Attribute, IErrorHandler, IContractBehavior

and apply it to our WCF Service interface

 [ServiceContract]
 [MyErrorHandler]
 public interface IMyService
    {
      ... my interface methods

That’s it. There is an even cleaner way of wiring up the MyErrorHandler class – detail in my next blog to come.