How to Inject Log4Net ILog implementations using Unity 2.0
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
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, 2022Comments
-
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 almost 13 yearsYou this the hammer on the head this time. Exactly what I was looking for and much better than my solution. Many thanks.
-
Kenneth Baltrinic almost 13 yearsFor 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 almost 10 yearsI 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 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 almost 10 yearsYes 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 almost 10 yearshmm - that is odd - they should both do the same.
-
oleksa over 6 yearsthere 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