5
votes

I've bumped through a few of these questions laying around various sites and the answers seem to be spun by l4n officianados toward the value of the lightweight wrapper that log4net 'is' (don't you get it?) and similar points of mind-boggling fact.

However it seems that what users are asking for (and this is my question) is how to fit the log4net objet model into the registertype/registerinstance sequence in the fluent config interface.

The goal here wouldn't be to re-wrap l4n, but simply to grab a decent reference that doesnt' require hijacking the fluent flow, as it were.

poor ejemplow, I want to get a reference to a configured instance of ILog from a LogManger.GetLogger method and put it into the fluent flow early enough to inject it into properties of my downstream objects.

So in response to one impatient suggestion, I tried created a normal instance of ILog in the canonical manner:

log4net.Config.XmlConfigurator.Configure();
static readonly log4Net.Ilog Log = LogManager.GetLogger(thatlongassemblyreflectionstuff);

So it would seem trivial (in the real sense of effortless) to now add that reference to the Unity container and get on with my wonderful life.

However, the signature for the RegisterInstance method wants a 'Type' not an interface.

For those who haven't searched through the log4net object model, the afficiananderos are correct: l4n is a 'wrapper' and you can't get a holt of the actual 'type' for the Log thingie.

So now I have to test. And you know what that means, it could take a minute, but will more likely take an hour or four (similarilties like the spellings of 'hour' and 'four' are never coincidental in real life).

However, the following, minus the elided part about the canonical setup, did work:

container
  .RegisterInstance("Logger", Log, new ContainerControlledLifetimeManager())
.RegisterType<IControllerContext, ControllerContext>
(
   "CtlrCtx", 
   new ContainerControlledLifetimeManager(), 
   new InjectionConstructor(new ResolvedParameter<IMessageBuilder>("MsgBldr")),
   new InjectionProperty("Log",new ResolvedParameter<ILog>("Logger"))
);

So the hashcodes for the orginal Log object and the Log object injected into the property of my marvy little Context object are identical.

However...

The injection process required that I expose the Log property of the Context object through its interface, meaning it could no longer be a static object. And whether the log4net ILog object is static seems to be the deciding factor as to whether it is serializable and can be mashalled between assemblies without the dramatic 'the runtime will become unstable' warnings (which are truly meaningful only to Matrix fans).

Discouraged, though not deterred, I used Resharper's nifty 'to property with a backing field' and set the backing field to be static whilst the Interface property stayed non-static. Well it built and the test ran green.

So I even did a rebuild and it worked. So maybe when this bubbles up to the integration tests I'll sneak past the log4net isn't serializable debacle.

So maybe this will help

Thanks

Stato

2
It's not normal practice to have loggers as instance variables - they're normally static (i.e. class) variables and are implemented as singletons anyway. So I don't think you get any benefit by fetching them from the Unity container - why do you need to?Vinay Sajip
'why?' is definitely a reasonable question. The answer has to do with a single point of configuration. using any logger 'everywhere' gets to be a real configuration burden and maintenance is heavy on the 'aint' part when it comes to fun.Stato Machino

2 Answers

5
votes

Would using the InjectionFactory in Unity 2 help? (see this question). Then your configuration code would look something like this:

IUnityContainer container = new UnityContainer();
container.RegisterType<ILog>(new InjectionFactory(factory => LogManager.GetLogger()));

Then you retrieve the logger with the usual call to Resolve():

ILog logger = container.Resolve<ILog>();
logger.Log(Level.Debug, "Hello world");

You might also be able to configure the lifetime of the logger to be ContainerControllerLifetimeManager as well, to make it a singleton instance, but I haven't verified that yet.

0
votes
ILog logger = container.Resolve<ILog>();
logger.Log(Level.Debug, "Hello world");

does indeed work.

However, if you have a property for a logger on a class, and want to inject this logger instance to it, that will not work AFAICT. I guess I may be off target, but I am trying to reuse the logger instance in a new context. This may just be undoable, so I may have to give up on injecting it and just add the line

ILog logger = container.Resolve<ILog>();

to every class, which gives me an outcome that only seems marginally different than instantiating it in every class....

I was hoping that

private ILog Logger {get;set;} 

could just be injected but that doesn't seem to work at all, since all through log4net everything is done via interfaces and the concrete logger is hiding behind the curtain with the Wizard of Oz.