Why have named logger in Log4Net?
Asked Answered
O

2

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)
Ostrom answered 14/11, 2012 at 22:28 Comment(1)
Related: #9892637Grossman
H
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.

Halbeib answered 14/11, 2012 at 22:31 Comment(6)
Yeah, if I read that example correctly, it performs property injection, not ctor injection. Do you know if the facilitator supports ctor injection?Ostrom
Last time I checked it will also allow for constructor injection.Halbeib
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.Ostrom
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 (docs.castleproject.org/…). I cannot really say more without more detail of your error and configuration.Halbeib
Here's a very basic example in a console application. I just tested this and it works fine. pastebin.com/UPd4FJZUHalbeib
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.Ostrom
T
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.

Tildy answered 14/11, 2012 at 22:41 Comment(4)
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?Ostrom
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.Tildy
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.Sibeal
It's actually c.Handler.ComponentModel.Implementation, but thanks for pointing in the right direction!Alliaceous
H
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.

Halbeib answered 14/11, 2012 at 22:31 Comment(6)
Yeah, if I read that example correctly, it performs property injection, not ctor injection. Do you know if the facilitator supports ctor injection?Ostrom
Last time I checked it will also allow for constructor injection.Halbeib
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.Ostrom
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 (docs.castleproject.org/…). I cannot really say more without more detail of your error and configuration.Halbeib
Here's a very basic example in a console application. I just tested this and it works fine. pastebin.com/UPd4FJZUHalbeib
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.Ostrom

© 2022 - 2024 — McMap. All rights reserved.