Logging is one of the most popular areas used as example to illustrate dependecy injection technique. Components need to log their activities, and to remove hard dependency on actual logger implementation, it’s reasonable to define an interface (something like ILog) that is sent to class constructors, thus freeing components from knowledge about logging details.
Unfortunately things are different in practice. And if your application uses several third-party libraries that depend on different logging frameworks, you are often out of luck if you try to harmonize their logging and route its output to common log sinks (or listeners).
Take for example log4net. It’s LogManager looks DI-compliant: it has a method GetLogger that returns an instance of ILog interface:
ILog log = LogManager.GetLogger(“MyLog”);
You would then expect a way to configure LogManager to return a custom logger. Nope! You can’t control that. log4net does not give you a chance to write your own logger.
But isn’t this wish outside log4net purpose? Since ILog interface is not a generic .NET interface but a contract that is defined by log4net, isn’t it fair to assume that log4net will use its own logging engine leaving possibilities to write custom appenders? Well, as long as log4net is the only third-party component that you use, this will be sufficient. Once you add more, this can become a problem.
Our components use Enterprise Library Logging Application Block for its logging purposes. Recently we started using WCF and NHibernate and would like to aggregate all log output. Not an easy task.
With WCF it seems that there is a way to redirect its logging. There is a special log listener called EntLibLoggingProxyTraceListener used as a bridge between WCF log messages and Enteprise Library LAB. But NHibernate uses log4net, and it looks that the only way to route NHibernate logs to our listeners is to write a special appender. The effort to write such appender is not more complex than to write a custom implementation of ILog interface. I just believe that if a framework provide all necessary elements to fully implement dependency injection pattern (interface definition, factory), it should make the last step and make class instantiation configurable, opening for external implementation of the class.