views:

230

answers:

6

Should a logging framework be injected into classes that need them or should every class that needs it just know the framework and use it directly? I am using Log4Net and currently I am injecting a service that wraps that framework into the classes that need to be able to log, knowing logging is probably not going to change and that most pieces need it, is injecting the answer in this sense?

+1  A: 

Injection is more flexible in the long run, since you can easily selectively inject into certain places and disable logging selectively, etc.

Reed Copsey
A: 

I think there is nothing wrong with injecting it. They recommend using static variables merely for performance reasons, but I see little difference between this and injecting a logger at start up.

Zoidberg
+1  A: 

If memory performance is not that much of an issue you might consider AOP, e.g. using PostSharp. That way your classes don't need to know the logger at all. You basically inject IL-code right into your assembly as a postbuild step. PostSharp uses attributes for marking methods, classes and even whole assemblies. There's even a plugin for using log4net for the logging concern.

EDIT: I mentioned memory because with each attribute you create a new object for each join point.

galaktor
+1 for PostSharp, that shit is magical
Chris McCall
+1  A: 

Even though you stated that logging isn't going to change, I suggest to abstract away from a specific logging framework. Injecting a logger instance however isn't needed in all cases.

Take CommonLogging (see this Stackoverflow answer for a short description of what Common.Logging is) for instance: Your class talks directly to the factory (LoggerManager) but you are not bound to a specific logger implementation.

tobsen
A: 

If you are talking about dependency injection (say in the Spring.NET sense) then it doesn't much matter - though you don't get much value from multiple copies of logger instances. Typically, each logger instance returned by a framework with a specific class/name will be a singleton anyway, so the question is whether you want to clutter each instance with a logger reference, rather than a single static reference per class. If you are concerned about changing logger implementations, then log to an interface (CommonLogging, mentioned in another answer, does this, and supports both log4net and Enterprise Library implementations).

If you are talking about injection using AOP, then I'm not sure that beyond a certain point, AOP injection is more flexible. You are pretty much limited to logging entry, exit and exceptions with method-level granularity.AOP Injection is more of a scatter-shot approach, even if you use AOP to selectively inject into parts of your code. Although logging is touted as a cross-cutting concern of the kind which which is ideal for AOP, I've never found AOP-based logging to be much use in practice (YMMV, of course).

Vinay Sajip
A: 

CSharpAtl,

I am curious as to exactly how you are injecting Log4net? See my question/community wiki here.

I also saw your answer to a DI/Log4net question here.

Most logging injection examples that I have seen show a class having a "logger" injected, something like this:

public class Foo
{
  private ILogger logger;

  public Foo(ILogger logger) //Injection via constructor
  {
    //Which "named" logger is this anyway?  Is it Foo's?  If so, how is it Foo's?
    this.logger = logger;
  }

  public DoSomeWork(int xyz)
  {
    logger.Info("xyz = {0}", xyz);
  }
}

With something like Log4net (and NLog, and maybe others), you typically create a logger like this:

public class Foo
{
  private static ILogger logger = 
          LogManager.GetLogger(
              System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

  public Foo()
  {
  }

  public DoSomeWork(int xyz)
  {
    logger.Info("xyz = {0}", xyz);
  }
}

So, how does my class, Foo, get the correct logger if I dependency inject? Also, maybe I am not naming my loggers based strictly on the class. Rather, I might name them based on functional area (like "DatabaseAccess", "AppStartup", "AppShutdown", etc) that might not be derivable from type.

Are you injecting a "Logger" (that would correspond to a named logger), or a "LogManager" (that would correspond to Log4net's LogManager), or are you doing something else? Do you use a configuration capability of a DI platform to associate "named instances" of injected dependencies? I think that Unity supports something like this, although I am not really sure.

Here is how I would imagine injecting a "LogManager" would work:

public class Foo
{
  private static ILogger logger;

  public Foo(ILogManager logManager) //Injection via constructor
  {
    logger = logManager.GetLogger
                    (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

    //Since logger is (should it be?) static, maybe should be like this:

    if (logger = null)
    {
      logManager.GetLogger
                 (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }
  }

  public DoSomeWork(int xyz)
  {
    logger.Info("xyz = {0}", xyz);
  }
}

It seems to me that injecting something like ILogManager (the implementation of which would wrap Log4net's LogManager) would be easier than injectng ILogger, because of the question of how the DI platform would know which ILogger to inject.

I am new to DI and .NET logging platforms (and actually, pretty new to C# and .NET in general). The vast majority of my experience over the last many years is VB6 + VC++ 6.

Thanks for ANY info ...

Begin edit ...

I have looked around on SO some more and have found that you have also asked about AutoFac, so I am guessing that you are using this for your DI. If that is true, then I guess that you are also doing something like this or, apparently even better, like this to resolve your logger (by class type). I guess that answers the question of how, at least in AutoFac, to inject named loggers (such as from Log4Net or NLog).

End edit ...

wageoghe