One of the most common cross cutting infrastructure aspect of enterprise applications is Logging, It can be categorized to two different types:
- Tracing: Usually used to debug application by tracing method calls and sequence of execution.
- Logging: When you want to deliberately log some informative message to the log.
The first type of logging always cluttering application code and introduce a redundant dependency upon logging interface whether this interface is consumed using static Factory, Service Locator, or Dependency Injection Container).
Using dependency injection in constructor will clutter the constructors of almost all application classes where each one will add a constructor parameter for the logger interface.
Using setter injection or service locator or static factory will resolve this issue but we still have logging code in each method beside the problem of hiding dependency.
The best solution to tracing is to use Aspect Oriented Programming to inject tracing code to all required method calls, there are many types of AOP tools you can use one of these tools to inject tracing code in your project.
As the project already use Unity for dependency injection, I used interception to intercept method calls:
public class TraceCallHandler : ICallHandler
{
#region Fields
private readonly IUnityContainer _container;
#endregion
#region Properties
public int Order { get; set; }
#endregion
#region Constructors
/// <summary>
/// Constructor
/// </summary>
/// <param name="container"></param>
public TraceCallHandler(IUnityContainer container)
{
_container = container;
}
#endregion
#region Methods
/// <summary>
/// Invoke method
/// </summary>
///input">method invacation info
///getNext">next handler delegate
/// <returns></returns>
public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)
{
var className = input.MethodBase.DeclaringType.Name;
var methodName = input.MethodBase.Name;
var arguments = GetCommaSeparatedArgumentList(input.Arguments);
var preMethodMessage =
string.Format("{0}.{1}({2})", className, methodName, arguments);
ServiceLocator.Current.GetInstance<ILogger>().
Debug(className, preMethodMessage);
var methodReturn = getNext()(input, getNext);
if (methodReturn.Exception != null)
_container.Resolve().Debug(className,
String.Format("Exception intercepted: {0}",
methodReturn.Exception));
else
{
var postMethodMessage = string.Format("{0}.{1}() -> {2}",
className, methodName,
methodReturn.ReturnValue);
_container.Resolve().Debug(className, postMethodMessage);
}
return methodReturn;
}
This code creates a call handler interceptor to intercept method calls, it print call with its parameter types, values, returned value, and exception if happened.
public class AnyMatchingRule : IMatchingRule
{
public bool Matches(MethodBase member)
{
return true;
}
}
A matching rule is to match the criteria of selecting methods to inject in.
Finally you need to configure Unity to register these extensions and inject it to the given interfaces:
xmlns="http://schemas.microsoft.com/practices/2010/unity">
<sectionExtension type="...InterceptionConfigurationExtension, ...Interception.Configuration" />
<typeAliases>
<typeAlias alias="AnyMatchingRule" type="Extensions.Unity.AnyMatchingRule"/>
<typeAlias alias="TraceCallHandler" type="Extensions.Unity.TraceCallHandler"/>
<typeAlias alias="ILogger" type="Logging.ILogger, Infrastructure.Core"/>
<typeAlias alias="Log4NetLogger" type="Logging.Log4NetLogger, Infrastructure.Core"/>
<typeAlias alias="IMyService" type="IMyService"/>
<typeAlias alias="MyService" type="MyService"/>
</typeAliases>
<container>
<extension type="Interception" />
<register name="Any"
type="IMatchingRule"
mapTo="AnyMatchingRule" />
<register
type="ICallHandler"
mapTo="TraceCallHandler" />
<register
type="ILogger"
mapTo="Log4NetLogger" />
<register
type="IMyService"
mapTo="MyService" >
<interceptor type="InterfaceInterceptor" />
<policyInjection />
</register>
<interception>
<policy name="All">
<matchingRule name="Any" />
<callHandler name="TraceCallHandler" type="TraceCallHandler" />
</policy>
</interception>
</container>
</unity>
Now you can get rid of all calls of logger in all the methods.
About logging extra or custom information you will still need to get the logger, either using DI or Service Locator.
Really nice article, Abed.
I am also very interested to know why you chose Unity for AOP over PostSharp and Spring.Net especially that I see in your project page that you are using NHibernate.
I know PostSharp is much more powerful in the are of AOP, but the point is that I already use Unity for Dependency Injection, so I wanted to take advantage of it as I am configuring dependency mapping configuration already and would add the code for interceptor with no much addition or dependency upon other libraries and configurations.
If I wanted a hard core AOP to inject advanced tracing for any and every class (not only classes resolved using DI) I would use PostSharp!
Pingback: Logging using Decorator | Mohamed Abed