Exception Logging for WCF Services Using ELMAH

We use the excellent ELMAH to handle unhandled exceptions in an ASP.NET 3.5 web application. This works very well for the whole site, except for WCF services that are used using REST functions. When an exception occurs in operation methods that are not handled by application code, WCF handles it in various ways depending on service contracts and configuration parameters. This means that the exception does not trigger the ASP.NET HttpApplication.Error event, which is used by ELMAH . Two solutions that I know of concern this:

  • Wrap all method calls in try {} catch (Exception ex) {Elmah.ErrorSignal.FromCurrentContext (). Raise (ex); quit; } to explicitly call Elmah in the catch block.
  • Use the IErrorHandler as described in the Will Hughes' blog post Make WCF and ELMAH pleasant together to cancel the ELMAH call to a separate ErrorHandler.

The first option is extremely simple, but not quite DRY . The second option requires that you decorate a custom attribute every day after implementing the attribute and ErrorHandler. I did this based on Will , but I want to verify that this is the correct approach before posting the code.

Is there a better way that I skipped?

The MSDN documentation for IErrorHandler says that the HandleError method is a place to log, but ELMAH accesses HttpContext.Current. ApplicationInstance , which is null in this method, although HttpContext.Current is available. Making an Elmah call in the ProvideFault method is a workaround since ApplicationInstance is installed, but this does not match the intent described in the API documentation. Am I missing something here? The documentation states that you should not rely on the HandleError method called in the workflow, and therefore ApplicationInstance has a null value in this area.

+59
logging exception wcf elmah
May 22 '09 at 12:11 a.m.
source share
6 answers

The solution from my blog post (mentioned in the OP) was based on an existing solution that we used to change the HTTP response codes during the error state.

So, for us it was a one-line change to pass the exception to ELMAH. If there is a better solution, I would also like to know about it.

For Posterity / Reference and potential improvement - here is the code from the current solution.

classes HttpErrorHandler and ServiceErrorBehaviourAttribute

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 { // ... } 
+86
May 25 '09 at 12:11
source share

When creating the BehaviorExtensionElement element, it is 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> 

Thus, you can also use ELMAH in combination with RIA services!

+9
Jun 17 2018-11-11T00:
source share

I did this based on Will's work, but I want to verify that this is the correct approach before publishing the code.

I think this is a great approach (quest for this publication!). I don't think Will or you missed anything here. Implementing IErrorHandler is the preferred way of catching all possible server-side exceptions that would otherwise lead to a communication channel failure (tear down) and thus this is a natural place to connect to some logs such as ELMAH.

Mark

+2
May 22 '09 at 5:25
source share

This may be obvious to some people, but I just spent quite a bit of time trying to figure out why my HttpContext.Current was null, despite the fact that it responded perfectly to all of Will Hughes's reviews. Vaguely, I realized that this is because my WCF service is being activated by an 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); } 
+2
Dec 05 '13 at 16:47
source share

I was unable to get the suggested answer using the WCF data service. I have connected a behavior attribute, etc., but still have not received any errors. Instead, I included the following in the service implementation:

 protected override void HandleException(HandleExceptionArgs args) { Elmah.ErrorSignal.FromCurrentContext().Raise(args.Exception); base.HandleException(args); } 
+1
Dec 05 '13 at 19:40
source share

I did not try to do this explicitly with REST elements and I did not use ELMAH myself, but another option worth paying attention to is to connect to WCF using IDispatchMessageInspector instead of IErrorHandler.

0
May 22 '09 at 2:11
source share



All Articles