Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using LogManager.GetLogger with Unity

Given this class:

class Foo
{
    readonly ILog log;

    public Foo(ILog log)
    {
        this.log = log;
    } 

    ...
}

I'd like to configure Unity to inject ILog. That's easy:

container.RegisterInstance<ILog>(LogManager.GetLogger(typeof(XYZ)));

But I'd like to make Unity call LogManager.GetLogger with the type of the parent type being resolved.

This is close:

container.RegisterType<ILog>(new InjectionFactory((c, t, s) => LogManager.GetLogger(t)));

But t in this case is the type being resolved (ILog), not the type that the object is being resolved for (Foo).

I know I can do this:

container.RegisterType<Foo>(new InjectionFactory(c => new Foo(LogManager.GetLogger(typeof(Foo)));

But I don't want to have to add that crazy declaration every time I register an object.

I know this can be done in Autofac, and I know the Real Answer is not to use Unity in the first place, but can this be done? :)

like image 533
Paul Stovell Avatar asked Dec 20 '11 14:12

Paul Stovell


2 Answers

Unity might not give you all the goodies some of the other containers offer but I have yet to find a feature you can't easily add.

var container = new UnityContainer();
container.AddNewExtension<TrackingExtension>();
container.RegisterType<ILog>(
  new InjectionFactory((ctr, type, name) =>
    {
      var tracker = ctr.Resolve<ITracker>();
      var parentType = tracker.CurrentBuildNode.Parent.BuildKey.Type;
      return LogManager.GetLogger(parentType);
    }));
var sut = container.Resolve<UsesLog>();
Assert.AreEqual(typeof(UsesLog), sut.Log.Type);

You can find the source code for the TrackingExtension here. Its located in the TecX.Unity project folder.

like image 165
Sebastian Weber Avatar answered Sep 28 '22 08:09

Sebastian Weber


If you want a DI container to return you a logger based on the class’ type information, then put the type information into the public interface so the DI container can see it. It removes the need for any container specific override features and then it won’t matter if you are using Unity or AutoFac.

Someone that knows the log4net object model well might be able to give you a more efficient implementation, but try something like this:

using System;
using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace UnityLoging
{
    public interface ILog<T> : log4net.ILog
    { }

    public class MyLogger<T> : log4net.Core.LogImpl, ILog<T>
    {
        public MyLogger() : base(log4net.LogManager.GetLogger(typeof(T).Name).Logger)
        { }
    }

    public class ClassToLog
    {
        private readonly log4net.ILog log;

        public ClassToLog(ILog<ClassToLog> log)
        {
            this.log = log;
        }

        public void LogMe()
        {
            log.Debug("Got here");
        }
    }

    [TestClass]
    public class TestClass
    {
        [TestMethod]
        public void GenericLogRegistrationTest()
        {
            log4net.Config.XmlConfigurator.Configure();
            IUnityContainer container = new UnityContainer();
            container.RegisterType(typeof(ILog<>), typeof(MyLogger<>));

            ClassToLog c = container.Resolve<ClassToLog>();
            c.LogMe();

            log4net.LogManager.Shutdown();
        }
    }
}
like image 35
ErnieL Avatar answered Sep 28 '22 08:09

ErnieL