WCF error-handling strategies (part 1): IServiceBehavior and IErrorHandler

Probably the biggest of all cross-cutting concerns is error-handling and logging. Unfortunately, WCF doesn’t seem to have a silver bullet for capturing, logging and suppressing exceptions thrown in the service layer. Ignoring Windsor interceptors for the moment (which deserve a separate post of their own) you really only have two options: put a try-catch block inside all of your service operations – which becomes very tedious very quickly –  or let your services throw exceptions and catch them in the code which consumes them. Initially I went for option one but it’s too prone to human error. If you or another developer forgets to put a try-catch block inside one of your service operations it could potentially bring down a client app. Or at the very least, error information will be lost making it harder to identify the problem.

As I found out, it’s actually quite difficult to stop WCF services from throwing exceptions. So like it or not, the better option is to actually let your service throw errors back to the client application, which should be ready to deal with them. At least this approach of ‘expect the worst case scenario’ means there won’t be any nasty unpredictable situations or crashes on your production servers if errors do occur, because you should have already catered for them.

The standard FaultException
When a System.Exception is raised in the service layer and no matching catch block is found, the ServiceModel converts it to a System.ServiceModel.FaultException before re-throwing it. This is problematic for two reasons. Firstly, it doesn’t give you an opportunity to log the error before it’s thrown (the client may be a third party so you need to maintain your own error log) and secondly, the standard conversion to a FaultException is next to useless because the original error information is discarded.

Here is a standard FaultException message which doesn’t tell you anything about the error that actually caused it:

System.ServiceModel.FaultException: The server was unable to process the request due to an internal error. For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework 3.0 SDK documentation and inspect the server trace logs.

On the subject of what this exception message recommends, it’s OK to enable the IncludeExceptionDetailInFaults attribute in the config during development, but you should never do this on a production server. So that doesn’t solve the problem.

The Solution
The solution to getting useful server-side logging and not losing error information during conversion to a FaultException is to implement the IServiceBehavior and IErrorHandler interfaces. IServiceBehavior allows you to implement a service behaviour attribute which takes a custom error handler parameter which implements IErrorHandler. Whenever an unhandled error occurs in a service decorated with the service behaviour attribute, two methods on the custom error handler are called automatically: ProvideFault which allows you to construct a FaultException (rather than let ServiceModel do it), and HandleError which is intended for logging. The ProvideFault method is called first and HandlerError is called asynchronously on a separate thread, so lengthy logging operations don’t block the service request thread from sending a FaultException back to the client immediately.

Here is a standard implementation of IServiceBehavior which is completely reusable and should be put in an infrastructure assembly so that it can be shared between different projects/solutions. As you can see, it instantiates the specified error handler type using Activator.CreateInstance, adds it to the channel dispatcher’s error handlers collection and that’s basically it.

And here is an abstract base class implemention of IErrorHandler which again is completely reusable and should go in the same infrastructure assembly. The most important thing here is that the original exception information is added to the fault message rather than being discarded.  If third parties call your service, you will probably want to return a more cut-down version of the original exception which doesn’t include security-sensitive things like the exception stack trace.

Now all you have to do is implement CustomErrorHandler with a specialised error handler for your service. How and where you log error information to (file, event log, database, logging service, etc.) is up to you. And remember this is called asynchronously so don’t be too concerned if logging isn’t instantaneous – it won’t delay the FaultException being thrown.  And the class is non-static and stateless so there are no multi-threading issues to worry about as long as you keep it that way.

The final step is to decorate services with the ErrorBehaviour attribute and the type of your specialised error handler.

All unhandled exceptions will now be caught and processed by your error handler, before being re-thrown to the consumer of the service. This means you will always be able to (a) log any errors, and (b) control what error information is, or is not sent back in the FaultException message.

See the follow up post on throwing specific fault exceptions here.

Advertisements

About Phil Munro

I have been developing commercial desktop and distributed web applications with Microsoft technologies since 1997.
This entry was posted in C#, WCF. Bookmark the permalink.