Monday, 23 May 2011

Integrate Sitecore.Logging with Castle Windsor

A couple of months ago I started using Dependency Injection pattern in my projects more consciously and extensively. Initially it was not easy and comfortable, however I found it very profitable and useful. I picked Windsor as a Inversion of Control container. In the same time Sitecore had come along as a new Web CMS platform in Cognifide range. As a natural consequence those two got married in my projects.

The issue
The first problem I encountered was a logging issue. I'm using the Logging Facility so that I don't need reference to log4net library, using ILogger from Castle.Core.Logging namespace. Using this facility has a big advantage - Windsor resolves current logger context automatically for maintained classes, so that you don't have to worry about it. If you would like to obtain current logger directly from container you can resolve ILoggerFactory, which creates a logger instance for you. However, this is not a case here. The case was that while everything seemed to be fine, no logs appeared, moreover Windsor always returned a default logger.

After some investigation I finally found the reason - apparently Sitecore guys took whole log4net project as is, changed it according to their requirements (please note that there are some additional configuration features in Sitecore log4net logging configuration section) and finally embraced it as a Sitecore.Logging library. This causes that Windsor is unable to create any logger instance, because it uses full class name to resolve types (note that there is no log4net library anymore, you have Sitecore.Logging instead).

The solution is simple and pretty straightforward. First you should read great Michael Edward's post where he explains why logging doesn't work by default. However I would like to go one step ahead and keep my custom functionality "Sitecore free". Therefore I would like to keep using ILogger and by that be free from additional dependency like log4net or Sitecore.Logging. To achieve my goal I had to register my own facility just like in Michael's post, then I had to write my own custom ILoggerFactory and ILogger implementations. Both are very simple , for example ILoggerFactory has four methods and one of them is
public ILogger Create(string name)
return new SitecoreLogger(log4net.LogManager.GetLogger(name));
where SitecoreLogger is ILogger implementation. Please note that I pass internal logger in constructor so that I can leverage on it in my implementation. In fact, it is only a wrapper for native logger. ILogger has a number of various methods and each of them is similar to written below:
public void Error(string message, Exception exception)
InnerLogger.Error(message, exception);
To use it you have to add a custom facility, the simplest way is doing that from code, where you simply indicate that logger should be created by a custom factory:
container.AddFacility(p => p.LogUsing<SitecoreLoggerfactory>().WithAppConfig());
where container is a IWindsorContainer instance, and log4net configuration is in web.config file. You can register it like Michael proposed in his post from configuration file as well.
Solution above allows me to use logging in a loosely coupled way, and keep my custom functionalities as intended - clean and free from unnecessary dependencies. This code is fully open source so feel free to download and use it or adjust it in your project.