Edit: This post was linked from Stack Overflow – you might want to check back there for more/better discussion about the topic.
Recently Jeff Attwood wrote about how they are using ELMAH to get more information about the types of errors occurring in Stack Overflow.
Effectively ELMAH is designed as a ‘drop in’ fault capturing system for ASP.NET. It works really well there, and for many situations you can get along just fine without even needing to recompile your application (it does need some editing of the web.config though).
I wanted a way to capture more detail about the faults occurring in our dev and production environments, especially when working with WCF – since a lot of error detail tends to be hidden, or is difficult to reproduce.
Dropping in ELMAH into a WCF application will by default mean you miss the vast majority of errors – WCF swallows the error, and doesn’t let it get back up to ASP.NET.
There’s two ways you can go about fixing this:
Side Note: If you’re not hosting WCF in ASP.NET, then Option 2 may not be directly possible for you without some modification.
#1 – Wrap everything in try/catch blocks (if you didn’t already) and sprinkle this line around everywhere:
Elmah.ErrorSignal.FromCurrentContext().Raise(YourExceptionHere);
#2 Add a HttpHandler, and Decorate your Service(s) with an Error Handling attribute.
I borrowed the ServiceErrorBehaviourAttribute code from somewhere else, and I can’t find the source of it at the moment. Effectively this was so I could manipulate the HTTP Status Codes going back to the client when there was an error. It just so happens that this is a great way of capturing Exceptions and sending them to ELMAH at the same time.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Collections.ObjectModel;
using System.Net;
using System.Web;
using System.IO;
using Elmah;
namespace YourApplication
{
/// <summary>
/// Your handler to actually tell ELMAH about the problem.
/// </summary>
public class HttpErrorHandler : IErrorHandler
{
public bool HandleError(Exception error)
{
return false;
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (error != null ) // Notify ELMAH of the exception.
{
Elmah.ErrorSignal.FromCurrentContext().Raise(error);
}
}
}
/// <summary>
/// So we can decorate Services with the [ServiceErrorBehaviour(typeof(HttpErrorHandler))]
/// ...and errors reported to ELMAH
/// </summary>
public class ServiceErrorBehaviourAttribute : Attribute, IServiceBehavior
{
Type errorHandlerType;
public ServiceErrorBehaviourAttribute(Type errorHandlerType)
{
this.errorHandlerType = errorHandlerType;
}
public void Validate(ServiceDescription description, ServiceHostBase serviceHostBase)
{
}
public void AddBindingParameters(ServiceDescription description, ServiceHostBase serviceHostBase, Collection endpoints, BindingParameterCollection parameters)
{
}
public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase serviceHostBase)
{
IErrorHandler errorHandler;
errorHandler = (IErrorHandler)Activator.CreateInstance(errorHandlerType);
foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
{
ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;
channelDispatcher.ErrorHandlers.Add(errorHandler);
}
}
}
}
Once you’ve added that, then it’s just a matter of decorating your Service like so:
[ServiceContract(Namespace = "http://example.com/api/v1.0/")]
[ServiceErrorBehaviour(typeof(HttpErrorHandler))]
public class MyServiceService
{
// ...
}
…and then making sure ELMAH is added as a reference, and adding it’s entries to your web.config.
Then you’ll be getting a whole stack of errors you otherwise may not have seen.
It’s also possible to log exceptions from higher up the chain (eg Databases, Files, etc) by using the line of code from Option 1.
Issues
Whilst ELMAH is great for capturing information about the request, I havn’t yet found any way to capture the original HTTP Request – this would be the ultimate goal for me.
It’s also not particularly easy to capture additional information (such as database records, or objects in cache, etc) without rolling your own copy of ELMAH.
All in all though – for a few minutes work, it’s one additional way to capture errors that your existing code may not be able to.
Yes, ELMAH even captures errors (in most situations) when your WCF services can’t start up (eg your fubar’ed some attributes).
Hope that helps.
NB: Use this code at your own risk, don’t blame me if it brings down your multi-million-dollar-per-hour application and causes you to go bankrupt.