开发者

How to Inject Log4Net ILog implementations using Unity 2.0

开发者 https://www.devze.com 2023-03-23 20:03 出处:网络
Ultimately this has to do with setting up log4Net but generically the problem is not logging specific.

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;
    }
}


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


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


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);
    }
}


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


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.


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;
   }
 }
0

精彩评论

暂无评论...
验证码 换一张
取 消