There are multiple ways to do error handling in WCF as listed by Pedram Rezaei Blog.
The default way that WCF allows errors
message to display is by setting IncludeExceptionDetailInFaults Property to true in web.config or on the service attribute but this is only recommended when you need to debug you development code not in your shipped/release code.
In Web.config file
In the web.config file of the WCF service the includeExceptionDetailFaults attribute set it to true. With this action every endpoint associated to WCF service will send managed exception information.
<system.serviceModel>
<services>
<service name="ZeytinSoft.WCF.OliveService"
behaviorConfiguration="OliveDebugServiceConfiguration">
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="OliveDebugServiceConfiguration">
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<system.serviceModel> <services> <service name="ZeytinSoft.WCF.OliveService" behaviorConfiguration="OliveDebugServiceConfiguration"> </service> </services> <behaviors> <serviceBehaviors> <behavior name="OliveDebugServiceConfiguration"> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> |
In Attribute
Another way is setting the IncludeExceptionDatailInFaults property to true using the ServiceBehaviorAttribute.
[ServiceBehavior(IncludeExceptionDetailInFaults=true)]
public class OliveService{ }
1 2 |
[ServiceBehavior(IncludeExceptionDetailInFaults=true)] public class OliveService{ } |
That is fine and dandy but it is definitely not recommended for production server, you don’t want an stack trace to show up when someone is viewing it on the webpage for this service.
The IErrorHandler interface
The basic form of error logging in WCF is to use the IErrorHandler interface, which enables developers to customize the default exception reporting and propagation, and provides for a hook for custom logging.
public interface IErrorHandler
{
bool HandleError(Exception error);
void ProvideFault(Exception error,MessageVersion version,ref Message fault);
}
1 2 3 4 5 |
public interface IErrorHandler { bool HandleError(Exception error); void ProvideFault(Exception error,MessageVersion version,ref Message fault); } |
Another thing to note is we need to implement the IServiceBehavior also since installing our own custom implementation of IErrorHandler requires adding it to the desired dispatcher. Since we need to treat the extensions as custom service behaviors in order for it to work.
public interface IServiceBehavior
{
void AddBindingParameters(ServiceDescription description,
ServiceHostBase host,
Collection <ServiceEndpoint> endpoints,
BindingParameterCollection parameters);
void ApplyDispatchBehavior(ServiceDescription description,
ServiceHostBase host);
void Validate(ServiceDescription description,ServiceHostBase host);
}
1 2 3 4 5 6 7 8 9 10 11 12 |
public interface IServiceBehavior { void AddBindingParameters(ServiceDescription description, ServiceHostBase host, Collection <ServiceEndpoint> endpoints, BindingParameterCollection parameters); void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase host); void Validate(ServiceDescription description,ServiceHostBase host); } |
Rather than just calling Log4Net I create a class called Logger which decouples log4net so that one can use any logging framework by using a dependency injection framework. (Code not listed)
Back to building our own ErrorHandler, ServiceBehavior with Attribute, the code is listed below
[AttributeUsage(AttributeTargets.Class)]
public class OliveErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior, IErrorHandler
{
protected Type ServiceType { get; set; }
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
//Dont do anything
}
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection <ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
//dont do anything
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
ServiceType = serviceDescription.ServiceType;
foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
{
dispatcher.ErrorHandlers.Add(this);
}
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
fault = null; //Suppress any faults in contract
}
public bool HandleError(Exception error)
{
Logger.LogException(error); //Calls log4net under the cover
return false;
}
}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
[AttributeUsage(AttributeTargets.Class)] public class OliveErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior, IErrorHandler { protected Type ServiceType { get; set; } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { //Dont do anything } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection <ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { //dont do anything } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { ServiceType = serviceDescription.ServiceType; foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers) { dispatcher.ErrorHandlers.Add(this); } } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { fault = null; //Suppress any faults in contract } public bool HandleError(Exception error) { Logger.LogException(error); //Calls log4net under the cover return false; } } |
Now one can just add an attribute to the WCF service code to log our error message to log4net or whatever logging framework that you may be using.
[ServiceContract]
[OliveErrorHandlerBehavior]
public class OliveService{ }
1 2 3 |
[ServiceContract] [OliveErrorHandlerBehavior] public class OliveService{ } |
There is also another way by using the web.config and adding the error logging to all the services listed by Stever B in his blog, but I find that does not give me the flexibility that I wanted, I may want to log some but not others etc.