How to Inject Log4Net ILog implementations using Unity 2.0

29,940

Solution 1

I don't know if this what you are looking for, but I saw it a few months ago and was reminded of it when I saw your question. I have not used Unity, so I can't really compare what you have posted with what is at the link. Hopefully it will be useful to you:

http://davidkeaveny.blogspot.com/2011/03/unity-and-log4net.html

Solution 2

I've been trying to achieve the same result of being able to insert correctly configured ILog instances into a dependency using constructor injection with Unity.

In the end, I wrote my own "log4net" unity extension to do exactly this (in part inspired by a blog post that another answerer, Kenneth Baltrinic, wrote).

This allows you to register the extension once with Unity:

var container = new UnityContainer();
container.AddNewExtension<Log4NetExtension>();

and then have the correct ILog logger instance passed in:

public class MyClass
{
    private readonly ILog logger;

    public MyClass(ILog logger)
    {
        this.logger = logger;
    }
}

The extension can be found here:

https://github.com/roblevine/UnityLoggingExtensions

More info here: http://blog.roblevine.co.uk/net/using-log4net-with-unity/

EDIT this is now available as a NuGet package

Solution 3

After hours of digging around in the Unity source code, I came up with the following solution. However, I would prefer to find a way to set the appropriate dependency resolver based on the type being resolved rather than overriding the default constructor selector policy. For one, because I previously overrode the default constructor selector for other purposes. For another, this solution only handles dependencies that are injected via constructor. For full coverage one would have to override the default property and method selectors as well I presume. For myself, I only need constructors.

class Logger_IOC_Tests
{
    [Test] 
    public void Logger_should_be_initialized_with_the_type_of_the_object_that_is_using_it()
    {
        var container = new UnityContainer();
        container.AddNewExtension<LoggingExtension>();
        container.RegisterType<LoggerUser>(new ContainerControlledLifetimeManager());
        var user = container.Resolve<LoggerUser>();

        Assert.True(user.Logger.GetUserType() == user.GetType());
    }
}

class LoggingExtension : UnityContainerExtension
{
    protected override void Initialize()
    {
        Context.Policies.SetDefault(typeof(IConstructorSelectorPolicy), new LoggingConstructorSelectorPolicy()); 
    }
}

public class LoggingConstructorSelectorPolicy : DefaultUnityConstructorSelectorPolicy
{
    protected override IDependencyResolverPolicy CreateResolver(ParameterInfo parameter)
    {
        return parameter.ParameterType == typeof(ILogger) 
                   ? new LoggerResolverPolicy(parameter.Member.DeclaringType) 
                   : base.CreateResolver(parameter);
    }
}

class LoggerResolverPolicy : IDependencyResolverPolicy
{
    private readonly Type _dependantType;

    public LoggerResolverPolicy(Type dependantType)
    {
        _dependantType = dependantType;
    }

    public object Resolve(IBuilderContext context)
    {
        return new Logger(_dependantType);
    }
}

Solution 4

With Unity 5 and above, you can now use Unity's own Log4Net extension from https://github.com/unitycontainer/log4net.

All you have to do is install the Nuget and add the extension to your container:

container.AddNewExtension<Log4NetExtension>();

And it will work automatically with any classes that use ILog as a dependency.

Solution 5

The above extension works well but more configuration information is needed for MVC5 users. Here are the steps using unity.

Add the following line to the top of your startup.cs class above the namespace.

[assembly: log4net.Config.XmlConfigurator(ConfigFile ="Web.config", Watch = true)]

In your global.asax application_startup method add the following information:

log4net.Config.XmlConfigurator.Configure(new FileInfo(Server.MapPath("~/Web.config")));

The rest of the configuration to the unity container should be as is:

container.AddNewExtension<Log4NetExtension>();

Ensure you have an appender added to your web.config. That should be about it to get this working correctly. Good Luck

Share:
29,940
Kenneth Baltrinic
Author by

Kenneth Baltrinic

Hi, I am Kenneth Baltrinic, a polyglot developer, a generalist and student of agile and lean methodologies. I have been programming since age eight and professionally since 1993. Currently I am a Senior Consultant at Berico Technologies in Reston, Virginia. For a longer introduction, see my blog. (http://blog.baltrinic.com/general/introductions) You can also find me on LinkedIn. (http://www.linkedin.com/in/kenenthbaltrinic)

Updated on July 09, 2022

Comments

  • Kenneth Baltrinic
    Kenneth Baltrinic almost 2 years

    Ultimately this has to do with setting up log4Net but generically the problem is not logging specific.

    Generically what I am trying to figure out is how to do, in Microsoft Unity 2.0, something equivalent to what one gets with the Castle.Facilities.Logging.LoggingFacility. Namely the ability to declare a dependency on a logger and have the logger initialized with the Type of the object into which it is being injected.

    In the spirit of a test is worth a thousand words, here is what I need:

    class Logger_IOC_Tests
    {
        //[Test] 
        public void Logger_should_be_initialized_with_the_type_of_the_object_that_is_using_it()
        {
            var container = new UnityContainer();
            /* Configuration Magic probably involiving registering either 
                * a custom IDependencyResolverPolicy or BuilderStrategy
                * goes here...
                */
            container.RegisterType<LoggerUser>(new ContainerControlledLifetimeManager());
    
            var user = container.Resolve<LoggerUser>();
    
            Assert.True(user.Logger.GetUserType() == user.GetType());
        }
    }
    
    interface ILogger
    {
        Type GetUserType();
    }
    
    class Logger : ILogger
    {
        private readonly Type _type;
    
        public Logger(Type type)
        {
            _type = type;
        }
    
        public Type GetUserType()
        {
            return _type;
        }
    }
    
    class LoggerUser
    {
        public readonly ILogger Logger;
    
        public LoggerUser(ILogger logger)
        {
            Logger = logger;
        }
    }
    
  • Kenneth Baltrinic
    Kenneth Baltrinic almost 13 years
    You this the hammer on the head this time. Exactly what I was looking for and much better than my solution. Many thanks.
  • Kenneth Baltrinic
    Kenneth Baltrinic almost 13 years
    For what its worth, I pulled together the code from the forum and david's blog and put it all together in one place here on my blog.
  • Harindaka
    Harindaka almost 10 years
    I couldn't for the life of me get this to read the web.config log4net configuration in my app. I did try adding the [assembly: log4net.Config.XmlConfigurator(Watch = true)] line in AssemblyInfo.cs and Global.asax. what am I doing wrong?
  • crizzwald
    crizzwald almost 10 years
    @Harindaka after you register your types and add the extension, add the line "log4net.Config.XmlConfigurator.Configure();" That will configure based on your config file.
  • Harindaka
    Harindaka almost 10 years
    Yes this is what I did to make it work. But I wonder why the normal [assembly: log4net.Config.XmlConfigurator(Watch = true)] method doesn't work in this case. I feel like I'm forcing log4net by calling log4net.Config.XmlConfigurator.Configure(). :D
  • Rob Levine
    Rob Levine almost 10 years
    hmm - that is odd - they should both do the same.
  • oleksa
    oleksa over 6 years
    there is higher abstraction level available LogCreation<T,U> : UnityContainerExtension where U : ILogFactory, new() in the Unity.codeplex discussion. It decouples LogCreation from the logging framework. So it is possible to use the same approach for NLog e.g. The only one thing is to implement LogFactory for certain logging framework. I've combined source code at github