5

I have been investigating ways to inject the Log4Net ILog into classes using Castle Windsor. In most examples, I see where Castle Windsor can provide a "facilitator" that provides property injection, and injects the ILogger (not ILog). I have found only one example where contructor injection is used, and the facilitator method is not used (see Castle Windsor dependency injection: Use the caller type as a parameter)

In all these examples, it seems Log4Net wants to have a named logger. Most examples refer to the Log4Net static method LogManager.GetLogger(class name here). This makes it a challenge to define the dependancies for CastleWindsor without using reflection, or the facilitator method (can facilitator method be used with ctor injection???). When looking at the question by Ilya Kogan (URL above...), I guess I don't understand why I need, or even want a named logger. Can't I use a logger by the same name everywhere?

For example, can't I just register the logger with hardcoded name of XXX? (It seems to work fine, and in the end, I just want to log - I don't care which logger logged it...) Is there a scope issue? Is there a memory leak issue? Why can't/shouldn't the logger be a singleton?

public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(
                Component.For<log4net.ILog>().UsingFactoryMethod(() => log4net.LogManager.GetLogger("xxx"))
                );
        }

UPDATE:

Upon some research, a hardcoded named logger can be used - such as XXX in my example above, but if the configuration of the logger outputs the logger name to the logfile and if the logger name is dynamically assigned to the same name as the method or class, you automagically get reference to where the logging originated from. Context within the log file can be very helpful.

When specifically addressing ctor injection, there seems to be 5 possible options...

  • Use a singleton and not use named loggers (thus not reported in log file)
  • Use reflection with ctor injection (as seen in Ilya Kogan's example)
  • Use property injection (via facilitators)
  • Use post-sharp AOP IL Injection for logging
  • Use CTOR injection (via facilitators)
Community
  • 1
  • 1
barrypicker
  • 9,740
  • 11
  • 65
  • 79
  • Related: http://stackoverflow.com/questions/9892137/windsor-pulling-transient-objects-from-the-container – Steven Nov 15 '12 at 06:21

2 Answers2

6

Good questions.

In short, log4net wants named loggers because then the names are used for filtering the log output (see the log4net documentation for details). Type name is just a convenient convention as it provides you that extra bit of context and coupled with correct use of namespaces allows you do things like say "log all NHibernate messages to a separate file"

That's also why commonly, if you're not using container for that, you have a single, static logger property/field in your classes.

The ILogger you're referring to is the abstraction in Castle over logging, one of which can be for log4net.

While the LoggingFacility provides out of the box support for providing dependencies on ILogger it is by no means forcing you to do this.

Your ILog registration should be rewritten as follows (I'm writing this from memory so details may be slightly different):

Component.For<log4net.ILog>()
    .UsingFactoryMethod((k, c) => log4net.LogManager.GetLogger(c.RequestedType))
    .LifestyleTransient()

c.RequestedType will give you the type for which you're satisfying the dependency and making it transient will avoid the issue that all types would reuse the single instance of logger named after whatever the first type to request the dependency was.

Krzysztof Kozmic
  • 27,267
  • 12
  • 73
  • 115
  • Hi Krzysztof, thanks for the reply. After posting this, I realize there are two questions in one - 1.) Why use named loggers (answer= log4net config layout definition "log4net.Layout.PatternLayout" can specify %logger to show in log file), and 2.) how to derive the name dynamically such that the name is the same as the class in which it is injected. Using your suggestion seems to always create a named logger - named "log4net.ILog". My example of a hardcoded named logger of XXX is nearly the same result. Is this what you expected, or perhaps I am using your example incorrectly? – barrypicker Nov 14 '12 at 23:21
  • hmm, one of the properties on the argument `c` should give you the parent type of the `ILog`, that's the one I was referring to. – Krzysztof Kozmic Nov 15 '12 at 04:31
  • I tried to get a type of a class AAA that my logger is injected in but I didn't find it. I managed to find only type of class BBB that contains reference on the class AAA. – Ilya Palkin Oct 09 '13 at 16:39
  • 3
    It's actually `c.Handler.ComponentModel.Implementation`, but thanks for pointing in the right direction! – mycroes Jan 06 '17 at 13:35
4

The recommended way to DI an Log4Net Log using Castle Windsor is to use Facilities. One has already been created for Log4Net and its use is demonstrated in this tutorial.

Adam Goss
  • 1,017
  • 3
  • 14
  • 22
  • Yeah, if I read that example correctly, it performs property injection, not ctor injection. Do you know if the facilitator supports ctor injection? – barrypicker Nov 14 '12 at 22:38
  • Last time I checked it will also allow for constructor injection. – Adam Goss Nov 14 '12 at 22:39
  • OK, upon following this tutorial (remove log4net, add in castle.windsor-log4net instead) I now receive an error Can't create component 'XYZ' as it has dependencies to be satisfied. 'XYZ' is waiting for the following dependencies: Service 'log4net.ILog' which was not registered. – barrypicker Nov 14 '12 at 23:05
  • Assuming you have setup your installer the same as the tutorial and you have you config section setup up it should work (assuming you are using your installer to register the Logger). Also chekc that you are using the Castle Core ILogger rather than the Log4Net ILogger. For further help diagnosing missing services you can view (http://docs.castleproject.org/Windsor.Windsor-Tutorial-Part-Nine-Diagnosing-missing-dependency-issues.ashx). I cannot really say more without more detail of your error and configuration. – Adam Goss Nov 14 '12 at 23:16
  • Here's a very basic example in a console application. I just tested this and it works fine. http://pastebin.com/UPd4FJZU – Adam Goss Nov 14 '12 at 23:34
  • Thanks for the example Adam - it helped me see the errors of my ways. Classes that use ctor injection needed to inject ILogger, not ILog. After making that small change, everything else remains the same and ctor injection is working, and log files show named loggers, dynamically named the fully qualified class name (including namespace) in which they were injected. Now my log files have context. – barrypicker Nov 14 '12 at 23:52