AddThis Social Bookmark Button

Print

Using log4net

by Nauman Leghari
06/16/2003

Introduction

Logging is an essential tool in every developer's arsenal. It helps the developer to identify problems faster by showing the state of an application at any given point. It is important after deployment, when all that the poor system admins have are the logs that are generated by your application. So it is absolutely necessary to be equipped with a logging framework which is easy to set up, easy to use, and extensible. With this in mind, we will be discussing log4net, an open source logging and tracing framework. The only prerequisite for this article is to know how to program in .NET using C#, although the concepts are applicable to programmers using VB.NET or any other .NET language.

About log4net

log4net, as I said earlier, is an open source project and is the port of the famous log4j project for Java. It is an excellent piece of work, started by a team at www.neoworks.com, but it would not have been possible without the contributions made by the community. log4net provides many advantages over other logging systems, which makes it a perfect choice for use in any type of application, from a simple single-user application to a complex multiple-threaded distributed application using remoting. The complete features list can be viewed here. It can be downloaded from the web site under the Apache license. The latest version at this writing is 1.2.0 beta 7, upon which this article is based. The changes in this release are listed here.

You can see from the feature document that this framework is released for four different platforms. There are separate builds for Microsoft .NET Framework, Microsoft .NET Compact Framework, Mono 0.23, and SSCLI 1.0. There are different levels of support provided with each framework, the details of which are documented here. This version of log4net is provided with NAnt build scripts. To compile the framework, you can execute the build.cmd file from the root directory where you extracted the zipped file. The log4net.sln file in the <log4net-folder>\src directory is the solution file for log4net source, whereas the examples are provided in a separate solution file in <log4net-folder>\examples\net\1.0. The samples are provided in C#, VB.NET, VC++.NET, and even in JScript.NET. Some of the samples have their configuration files in the project's root folder, so in order to run those samples you need to manually move them with project's executable file. The API documentation is provided in the <log4net-folder>\doc\sdk\net directory.

The Structure of log4net

log4net is built using the layered approach, with four main components inside of the framework. These are Logger, Repository, Appender, and Layout.

Logger

The Logger is the main component with which your application interacts. It is also the component that generates the log messages.

Generating a log message is different than actually showing the final output. The output is showed by the Layout component, as we will see later.

The logger provides you with different methods to log any message. You can create multiple loggers inside of your application. Each logger that you instantiate in your class is maintained as a "named entity" inside of the log4net framework. That means that you don't need to pass around the Logger instance between different classes or objects to reuse it. Instead, you can call it with the name anywhere in the application. The loggers maintained inside of the framework follow a certain organization. Currently, the log4net framework uses the hierarchical organization. This hierarchy is similar to the way we define namespaces in .NET. For example, say there are two loggers, defined as a.b.c and a.b. In this case, the logger a.b is said to be the ancestor of the logger a.b.c. Each logger inherits properties from its parent logger. At the top of the hierarchy is the default logger, which is also called the root logger, from which all loggers are inherited. Although this namespace-naming scheme is preferred in most scenarios, you are allowed to name your logger as you would like.

The log4net framework defines an interface, ILog, which is necessary for all loggers to implement. If you want to implement a custom logger, this is the first thing that you should do. There are a few examples in the /extension directory to get you started.

The skeleton of the ILog interface is shown below:


public interface ILog
{
  void Debug(object message);
  void Info(object message);
  void Warn(object message);
  void Error(object message);
  void Fatal(object message);

  // There are overloads for all of the above methods which 
  // supports exceptions. Each overload in that case takes an 
  // addition parameter of type Exception like the one below.
  void Debug(object message, Exception ex);
  
  // ...
  // ...
  // ...

  // The Boolean properties are used to check the Logger's 
  // level (as we'll see Logging Levels in the next section)
  bool isDebugEnabled;
  bool isInfoEnabled;
  
  // other boolean properties for each method
}

From this layer, the framework exposes a class called LogManager, which manages all loggers. It has a GetLogger() method that retrieves the logger for us against the name we provided as a parameter. It will also create the logger for us if it is not already present inside of the framework.

log4net.ILog log = log4net.LogManager.GetLogger("logger-name");

Most often, we define the class type as the parameter to track the name of the class in which we are logging. The name that is passed is prefixed with all of the log messages generated with that logger. The type of class can be passed in by name using the typeof(Classname) method, or it can be retrieved through reflection by the following statement:

System.Reflection.MethodBase.GetCurrentMethod().DeclaringType

Despite the long syntax, the latter is used in the samples for its portability, as you can copy the same statement anywhere to get the class in which it is used.

Logging Levels

As you can see in the ILog interface, there are five different methods for tracing an application. Why do we need all of these different methods? Actually, these five methods operate on different levels of priorities set for the logger. These different levels are defined as constants in the log4net.spi.Level class.

You can use any of the methods in your application, as appropriate. But after using all of those logging statements, you don't want to have all of that code waste CPU cycles in the final version that is deployed. Therefore, the framework provides seven levels and their respective Boolean properties to save a lot of CPU cycles. The value of Level can be one of the following:

Table 1. Different Levels of a Logger

Level

Allow Method Boolean Property

Value

OFF

   

Highest

FATAL

void Fatal(...); bool IsFatalEnabled;  

ERROR

void Error(...); bool IsErrorEnabled;  

WARN

void Warn(...); bool IsWarnEnabled;  

INFO

void Info(...); bool IsInfoEnabled;  

DEBUG

void Debug(...); bool IsDebugEnabled;  

ALL

   

Lowest

In the log4net framework, each logger is assigned a priority level (which is one of the values from the table above) through the configuration settings. If a logger is not assigned a Level, then it will try to inherit the Level value from its ancestor, according the hierarchy.

Also, each method in the ILog interface has a predefined value of its level. As you can see in Table 1, the Info() method of the ILog interface has the INFO level. Similarly, the Error() method has the ERROR level, and so on. When we use any of these methods, the log4net framework checks the method level against the level of the logger. The logging request is said to be enabled if the logger's level is greater than or equal to the level of the logging method.

For example, let's say you create a logger object and set it to the level of INFO. The framework then sets the individual Boolean properties for that logger. The level checking is performed when you call any of the logging methods.


Logger.Info("message");
Logger.Debug("message");
Logger.Warn("message");

For the first method, the level of method Info() is equal to the level set on the logger (INFO), so the request passes through and we get the output, "message."

For the second method, the level of the method Debug() is less than that of the logger (see Table 1). There, the request is disabled or refused and you get no output.

Similarly, you can easily conclude what would have happened in the third line.

There are two special Levels defined in Table 1. One is ALL, which enables all requests, and the other is OFF, which disables all requests.

You can also explicitly check the level of the logger object through the Boolean properties.


if (logger.IsDebugEnabled) 
{
  Logger.Debug("message");
}

Pages: 1, 2, 3

Next Pagearrow