21

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;
    }
}
yvesmancera
  • 2,915
  • 5
  • 24
  • 33
Kenneth Baltrinic
  • 2,941
  • 2
  • 28
  • 45

6 Answers6

15

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

wageoghe
  • 27,390
  • 13
  • 88
  • 116
  • 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 Jul 28 '11 at 13:34
  • 1
    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](http://blog.baltrinic.com/software-development/dotnet/log4net-integration-with-unity-ioc-container) on my blog. – Kenneth Baltrinic Jul 29 '11 at 01:31
  • there is higher abstraction level available `LogCreation : 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](https://github.com/oleksabor/UnityIOCNLog) – oleksa Sep 26 '17 at 08:50
9

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

Rob Levine
  • 40,328
  • 13
  • 85
  • 111
  • 1
    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? – Harindaka Jun 27 '14 at 15:23
  • @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. – crizzwald Jul 10 '14 at 02:53
  • 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 – Harindaka Jul 10 '14 at 12:25
  • hmm - that is odd - they should both do the same. – Rob Levine Jul 10 '14 at 13:55
4

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);
    }
}
Kenneth Baltrinic
  • 2,941
  • 2
  • 28
  • 45
2

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

Musakkhir Sayyed
  • 7,012
  • 13
  • 42
  • 65
fighter68
  • 21
  • 1
2

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.

yvesmancera
  • 2,915
  • 5
  • 24
  • 33
0

You can use the following code to inject Log4Net

log4net.Config.BasicConfigurator.Configure();    

container.RegisterType<ILog>(new InjectionFactory(x => LogManager.GetLogger(typeof(Program))));

typeof(Program) is used since I'm registering in program class. Use can use the class name or this keyword

Then you can inject ILog into the class

 public class MyClass
 {
   private readonly ILog logger;

   public MyClass(ILog logger)
   {
     this.logger = logger;
   }
 }
Sathish
  • 2,029
  • 15
  • 13