A Logger util class for log4net
In my opinion, log4net is the best logging library available for .NET. It combines simplicity with extensibility and has proven its value in many other languages on a number of platforms through sister projects like log4j, log4Cxx, …
The official log4net FAQ recommends you to create a static reference to an ILogger instance in each class. Although you could state that this is the fastest way to access the logger for your class, this approach has a few drawbacks which make it worth considering a different way of doing things.
I feel that having a static logger instance in each class has the following disadvantages:
- Fragmentation
- every class has to have a static ‘log’ field, usually looking up the calling type through reflection to avoid copy-paste errors when copying the declaration to other classes
- Barrier to start logging
- if I want to log something from a class that does not have a log field yet, I’ll have to look for a class that has one so I can copy the field declaraction (because that’s how lazy I am :))
- Lack of polymorphism
- if something is logged in a method of a base class, I want to know what the instance is that actually triggered the logging. Using the standard static ‘log’ field approach, the name of the logger will be the name of the base class.
To get by this, I have created a static Logger util class which exposes the most commonly used ILogger functionality, but provides a way of passing the calling type so the correct logger will be used. For calling it from instance members it has an object parameter in which you can pass ‘this’, and for calls from static members, a Type parameter is provided.
class FooBar { private void Foo(int a) { if (Logger.IsDebugEnabled(this) { Logger.Debug(this, "a = " + a); Logger.DebugFormat(this, "a = {0}", a); } } private static void Bar(int a) { if (Logger.IsDebugEnabled(typeof(FooBar)) { Logger.Debug(typeof(FooBar), "a = " + a); Logger.DebugFormat(typeof(FooBar), "a = {0}", a); } } }
Of course it is also possible to pass a string to explicitly pass the name of the logger if you are using descriptive functional names for the loggers:
public DataTable ExecuteQuery(string sql) { Logger.Debug("SQL", "Query: {0}", sql); }
The Logger class has these kind of methods for all default log levels, as well as the IsLevelEnabled porperties. As a bonus, each log method has a levelFormat equivalent which accepts a string.Format() template and its parameters.
Some may say that obtaining a reference to the log4net logger on each call to the logging method will have a negative impact on performance, and that using a static logger instance in the class is faster. My experience is that the performance loss in using this static Logger utility class is theoretical and negligeable. The cost for writing the log entry (to a FileAppender for instance) will probably be much greater than looking up the logger instance.
You can get the Logger.cs file from this link (it is a .txt file for viewing inside your browser).
Tuesday 04 Jul 2006 | Guy Mahieu | .net(t) , c#(t) , log4net(t) , logging(t)
Hi,
I am getting error, when I use your code, the functions isdebugEnabled already declared.
could you get back to me , with sample how you are using the above approach.
Jay,
It should be as simple as copy/pasting the contents of this source into a Logger.cs file; you can then use the sample code from the article to test the class. Are you sure you copied the class contents correctly?
Guy
Hello Guy,
I was wondering what is the license for your code? I would like to use it, although I could certainly write it myself, it’s easier to just use your class.
Although it seems to be public domain, I wanted to be sure nevertheless. Of course I will attribute it to you :-)
Thanks!
Jo,
The code is indeed public domain. You can use and/or adapt it any way you want.
Guy
Thanks Guy!
– Jo