Most people may prefer Entlib Logging because Exception Handling Application Block (EHAP) which is another cool Entlib part serve integration with Logging as an inbox solution and another thing Entlib Logging has the ability of consuming WCF traces with a proxy trace listener.
But if you don’t want to use Entlib Logging because of some performance issues and prefer using log4net or another tool there is a chance here provided. In this post I will show you how to integrate EHAP with log4net. Directing WCF traces to log4net is beyond this post I will provide it in the next posts.
Now let’s start. EHAP providing us an interface IExceptionHandler and implementing this interface and declaring it in the configuration file you have your own way of handing exceptions. And Entlib configuration system needs this class decorated with ConfigurationElementType attribute like below.
[ConfigurationElementType(typeof(CustomHandlerData))]
This decoration lets Entlib configuration system pass parameters defined in configuration file to your class constructor as a NameValueCollection. IExceptionHandler interface has a single method HandleException that give you the chance to handle exception. At the end of the method you either return same exception or another one you provided.
Here is my implementation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration;
using Microsoft.Practices.EnterpriseLibrary.ExceptionHandling;
using System.Collections.Specialized;
using log4net;
using log4net.Core;
namespace YapBee.Samples.log4netExceptionHandler
{
[ConfigurationElementType(typeof(CustomHandlerData))]
public class log4netExceptionHandler : IExceptionHandler
{
private static readonly ILog log = LogManager.GetLogger(
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public log4netExceptionHandler(NameValueCollection configurationData)
{
string categories = configurationData["logCategories"];
if (string.IsNullOrEmpty(categories))
m_categories = new string[] { "General" };
else
{
m_categories = categories.Split(',');
}
string levelString = configurationData["Level"];
level = log.Logger.Repository.LevelMap[levelString];
}
public Exception HandleException(Exception exception, Guid handlingInstanceId)
{
if (log.Logger.IsEnabledFor(level))
{
LoggingEvent loggingEvent = new LoggingEvent(ThisDeclaringType,
log.Logger.Repository, log.Logger.Name, level, exception.Message, exception);
loggingEvent.Properties["Categories"] = m_categories;
log.Logger.Log(loggingEvent);
}
return exception;
}
private string[] m_categories;
private Level level;
private readonly static Type ThisDeclaringType = typeof(log4netExceptionHandler);
}
}
And here is the configuration
<exceptionHandling>
<exceptionPolicies>
<add name="WCF Exception Shielding">
<exceptionTypes>
<add type="YapBee.Samples.WCFService1.MyInternalSampleException, YapBee.Samples.WCFService1"
postHandlingAction="ThrowNewException" name="MyInternalSampleException">
<exceptionHandlers>
<add logCategories="BusinessFault,UnauthorizedUserOperation" Level="Warn"
type="YapBee.Samples.log4netExceptionHandler.log4netExceptionHandler,
YapBee.Samples.log4netExceptionHandler" name="Custom Handler" />
<add exceptionMessage="" faultContractType="YapBee.Samples.WCFService1.ServiceFault,
YapBee.Samples.WCFService1"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF.FaultContractExceptionHandler,
Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WCF,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=null"
name="DefaultFaultContract Handler">
<mappings>
<add source="{Guid}" name="Id" />
<add source="{Message}" name="MessageText" />
</mappings>
</add>
</exceptionHandlers>
</add>
....
In my sample code I make use of WCF Exception Shielding Policy come with the EHAP so there is a section with the name. This means whenever an exception thrown in service its handled by EHAP. And from the configuration part you can see there is declared an Exception which is a custom exception provided by me and there is two handlers for the exception type one is our log4netExceptionHandler and the other FaultContractExceptionHandler which is come with EHAP. These two handler handles exception consequently.
As you can see from the exceptionHandlers section in xml there is a logCategories parameter and a Level parameter. The other name and type parameters are needed by configuration system in order to initiate handler class. logCategories and Level parameters are passed to constructor of custom handler class. And the constructor logic stores the values to the private fields in order to use in HandleException method later. Lastly in the HandleException method simply initiate a LoggingEvent and call Logger to log it.
You can download sample code from here.
In zip two console applications provided one is wcf service with hosting app the other is client simply calling GetIPAddress and ThrowException methods. When ThrowException method called in wcf app debugger hangs it is not a bug don’t panic, simply press F5 to proceed. You will see exception string on client console and exception log on wcf service console.
Users of log4net know that log4net doesn’t let you categorize logs like Entlib Logging. In discussions around on Entlib Logging or log4net which to prefer I see some says there isn’t categorization available in log4net unlike Entlib Logging and this is a significant functionality they want to see in a logging tool I notice. This is quite true but log4net’s categorization is provided with Levels and Named Loggers. I think there must be a fair requirement for this so I extended log4net for categorization support.
I will provide categorized log4net in my next post.