Search code examples
c#loggingdependency-injectionlog4netsimple-injector

How to use transient logger in singleton classes, Simple Injector


I have a LogManager class which has a type parameter to give to log4net like this:

public class LogManager<T> : ILogManager
{
    private ILog Logger { get; set; }

    public LogManager()
    {
        Logger = LogManager.GetLogger(typeof(T));
    }
}

And I register this logger as conditional:

container.RegisterConditional(typeof(ILogManager),
c => typeof(LogManager<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Transient,
c => true);

It works like it supposed to, I inject the log manager to my controllers' constuctors and use it. But there is a problem. I have a lot of singleton classes that should use the log manager too, but I can't inject it to them because log manager has a shorter life then others, so Simple Injector won't allow me to do it.

The proposed solution is to inject a log factory class to the other singletons and call the factory every time which sounds like a bad idea. I don't want to do _logFactory.GetLogger().Debug("log") every time I want to log something. Or I am missing something about this solution?

The other proposed solution is to make singleton classes transient which doesn't make any sense.

Any suggestions how should I proceed? Is there anything wrong with my design?


Solution

  • You should make the registration singleton:

    container.RegisterConditional(typeof(ILogManager),
        c => typeof(LogManager<>).MakeGenericType(c.Consumer.ImplementationType),
        Lifestyle.Singleton,
        c => true);
    

    Since LogManager<> is a generic type, Simple Injector will create one instance per closed generic version. So LogManager<HomeController> is a different instance than LogManager<UsersController>. This is simply because there is no way to use the same instance (this is impossible in .NET). So each consumer will still have its own LogManager<T> and with that its own log4net ILog implementation. The loggers returned from log4net's LogManager.GetLogger are thread-safe, and that's why its no problem to make your own LogManager<T> singleton.