23

It seems cleaner to declare a logger and call LogManager.GetLogger in a base class so that everyone who is inheriting can use it. However, on log4net site and other blogs like in this blog post it states that it is better to declare one logger per class because:

You can use loggers this way to isolate logging concerns across your objects, and I wholly recommend you do so. This will enable you to throttle and direct log output from individual loggers using log4net's hierarchical configuration mechanism.

Does this mean if I put it in base class, it will make that logger a bottleneck?

If so, are there other solutions or do I just have to create a logger per class?

dev.e.loper
  • 35,446
  • 76
  • 161
  • 247

5 Answers5

26

The post is not specifically telling you to use a different logger in each derived class, but instead a different logger per class type in general. You can, but don't have to use a new logger instance in each derived class.

One way to look at it is that it might be confusing to have two separate loggers instantiated at the same time (because the base one will still exist), especially if you hide the base one using the same logger name. Your base class methods (not overridden) will still reference the base static logger, and overridden ones will use a different one.

Also, the article instantiates the logger like this:

static ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType );

while a slightly simpler way might be to use:

static readonly ILog Log = LogManager.GetLogger(typeof(YourClass));

First of all, it's marked readonly, meaning you won't be able to change the field accidentally, once initialized. And using the type will work the same way as with reflection, but slightly faster (resolved at compile time). Visual Studio will also update the class name automatically if you choose to rename it (which it wouldn't it you used the string overload).

vgru
  • 49,838
  • 16
  • 120
  • 201
  • 1
    You can also use reflection instead of `typeof(YourClass)`. With reflection, you can copy-paste the boilerplate code, and you don't have to face a situation where someone has copy-pasted the logger and forgot to change `typeof(YourClass)`. The call would then look like: `LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);` – Jim Aho Feb 12 '15 at 11:24
5

Common practice is to have one logger per class, NOT base class. This way you can enable/disable logging on a per class basis.

I might also suggest looking at using Common Logging 2.0, http://netcommon.sourceforge.net/.

There are a variety of logging implementations for .NET currently in use, log4net, Enterprise Library Logging, NLog, to name the most popular. The downside of having differerent implementation is that they do not share a common interface and therefore impose a particular logging implementation on the users of your library.

Common.Logging library introduces a simple abstraction to allow you to select a specific logging implementation at runtime. Thus you can defer the decision what particular logging library to use until deployment. Adapters are used for plugging a particular logging system into Common.Logging.

Community
  • 1
  • 1
Richard Schneider
  • 34,944
  • 9
  • 57
  • 73
1

The provided statement does not refer to a bottleneck. Declaring your logger in a base class limits the control you have over the logging in derived classes. If you have classes A and B deriving from the same logger-containing base class, you are stuck with the same logging settings for all logging done in classes A and B.

log4net allows you to configure your loggers based on the class or namespace they are created for, giving you very tight control of what is logged. For example, having class A log at the info level and class B log at the debug level.

Adding a single line of a code to each class you wish to log things in is a very small burden for the flexibility it provides.

roken
  • 3,946
  • 1
  • 19
  • 32
0

I use a static dictionary in the base class, keyed on type name:

private static readonly Dictionary<string, ILog> _loggers = new Dictionary<string, ILog>();

With a method (on the base class) to return a logger:

    private ILog GetLogger(){
        var tn = this.GetType().FullName;
        if (!_loggers.ContainsKey(tn)) {
            _loggers.Add(tn,LogManager.GetLogger(this.GetType()));
        }
        return _loggers[tn];
    }

I also have the logging methods on the base class:

    public void Log(string msg, params object[] args) {
        GetLogger().InfoFormat(msg, args);
    }

I assume that this would allow me to configure logging per type (although I don't do that) and the GetLogger method could be exposed to derived classes so they can do their own logging.

Lee Willis
  • 1,552
  • 9
  • 14
-2

Usually you should have a single logger class, you could use singleton pattern and make sure that you have only one instance of logger for your application.

Edit: Made it thread safe now, thanks for comments.

    public class Logger
    { 
      private static object syncRoot = new Object();
      private static Logger instance=null;
      // private constructor
      private Logger()
      {
      }
      /// <summary>
    /// Gets an instance with default parameters based upon the caller
    /// </summary>
    /// <returns></returns>
    public static Logger GetInstance()
    {
       // make sure you return single instance
       if (instance == null)
        {
           lock (syncRoot) 
           { 
            instance=new Logger();
           } 
        }
        return instance;
    }
   }

Hope this helps

DotNetUser
  • 6,494
  • 1
  • 25
  • 27
  • OP is using log4net. Although a logger class is one of the rare occasions where singleton pattern is useful, using a different logger per class enhances troubleshooting tremendously: you can configure, in great detail, which level of messages to log for each class separately. You can also output messages from different loggers to different targets (text file, event log, e-mail). I would strongly recommend using a library like that. If a logger is instantiated statically (once per type), performance difference is negligible. (not my downvote, however) – vgru Jan 30 '12 at 22:40
  • Your singleton is not thread safe so you could end up with different instances of Logger (in the case of a logger probably not a major deal, but worth noting). Also, for logging you probably wouldn't want this pattern as its fractionally slower (as it has to do an extra null check...again probably not a big deal, but worth noting). – Mike Hanrahan Jan 30 '12 at 22:41
  • 1
    -1 because the code is not thread safe, is not using Log4Net and could be just `public class Logger { public static readonly Logger Instance = new Logger(); }` – Richard Schneider Jan 30 '12 at 22:45
  • I am aware that its not a thread safe but did not mention it here. However i am not aware of log4net. – DotNetUser Jan 30 '12 at 22:47
  • 2
    I know I'm late to the party, but you need to compare `instance` to null again inside the lock for this to be thread-safe, since another thread can jump in after the first if, but before the lock. See [double-checked locking](http://en.wikipedia.org/wiki/Double-checked_locking) for more details. – Gordon Leigh May 11 '13 at 10:36