Exception Logging for WCF Services using ELMAH
The solution from my blog post (referenced in the OP) was based on an existing solution we were/are using to alter HTTP Response Codes during an error state.
So, for us it was a one-line change to pass the Exception to ELMAH. If there's a better solution, I'd love to know about it too.
For Posterity/Reference, and potential improvement - here's the code from the current solution.
HttpErrorHandler and ServiceErrorBehaviourAttribute Classes
using System;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 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. { if (System.Web.HttpContext.Current == null) return; 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<ServiceEndpoint> 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); } } }}
Usage Example
Decorate your WCF Services with the ServiceErrorBehaviour Attribute:
[ServiceContract(Namespace = "http://example.com/api/v1.0/")][ServiceErrorBehaviour(typeof(HttpErrorHandler))]public class MyServiceService{ // ...}
When creating a BehaviorExtensionElement it is even possible to activate the behavior using config:
public class ErrorBehaviorExtensionElement : BehaviorExtensionElement{ public override Type BehaviorType { get { return typeof(ServiceErrorBehaviourAttribute); } } protected override object CreateBehavior() { return new ServiceErrorBehaviourAttribute(typeof(HttpErrorHandler)); }}
Config:
<system.serviceModel> <extensions> <behaviorExtensions> <add name="elmah" type="Namespace.ErrorBehaviorExtensionElement, YourAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/> </behaviorExtensions> </extensions> <behaviors> <serviceBehaviors> <behavior> <elmah /> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
That way it is also possible to use ELMAH in combination with RIA services!
This may well be obvious to some people but I just spent quite a while trying to figure out why my HttpContext.Current was null despite following all of Will Hughes' excellent answer. Embarassingly, I realised that this was because my WCF service is activated by a MSMQ message.
I ended up rewriting the ProvideFault()
method:
if (HttpContext.Current == null){ ErrorLog.GetDefault(null).Log(new Error(error));}else{ ErrorSignal.FromCurrentContext().Raise(error);}