Loggers
A log message in your code using Log4Net all begins with the Logger class. The Logger is the portal into your code. While all of the five main objects can be configured from code, most often you will only need to use the Logger class. Because the LogManager class implements the ILog interface, there is a well-defined strong contract between Log4Net and your code. The ILog interface simply implements the following methods: Debug, Info, Warn, Error, and Fatal. The first set of methods corresponds to the available Levels covered in the next portion of the chapter. All of this makes logging in your program very simple, as shown in Listing 8.1.
Listing 8.1. A Simple Log4NET Log Message
Well, if using a Logger to log is so easy, then how is a Logger instantiated? A Logger is created one of two different ways. The first way to obtain a Logger is by passing in a string to associate with the Logger (see Listing 8.2).
_logger.Error("This is an error message.", exception);
Listing 8.2. Creating a Logger
The second way to obtain a Logger is to associate the Logger to the type of the class, as in Listing 8.3. This way, once a Logger is instantiated, you are essentially linking that instance of the Logger with that class.
Logger _logger = LogManager.GetLogger("MyLogger");
Listing 8.3. The Recommended Way of Getting a Logger
Whatever messages are logged will be flagged as coming from this class. The reason for the functionality differences in Listing 8.2 and Listing 8.3 is found in Logger Hierarchy and inheritance.
Logger _logger = LogManager.GetLogger(typeof(namespace.class));
Hierarchy
Loggers are by default hierarchical and based on a named hierarchy. This allows for powerful inheritance of Loggers and ease of use from multiple assemblies. Named hierarchy should be familiar to .NET developers because it is used in namespaces and fully qualified class names in .NET development. Listing 8.4 is an example of Loggers that are related in a parent-child relationship.
Listing 8.4. Hierarchical Loggers
If foo.bar is not assigned a Logging Level, it will inherit it from the parent Logger foo. Also, any log messages for foo.bar will be forwarded to any Appenders in foo.bar as well as up the hierarchy to foo's Appenders. Consequently, there is a Logger that exists as the base or root for all Loggers. This Logger always exists and cannot be retrieved by name. Therefore, you must use the getRootLogger static method of the Logger class to retrieve it. This functionality is due to the fact that every child Logger has a reference to its parent, but the parent Logger knows nothing about the child. Fortunately, this allows for child Loggers to be created before parent Logger, and the hierarchy will all work out in the end.This inheritance and hierarchy functionality can be turned off via the additivity method on the Logger:
Parent Logger LogManager.GetLogger(typeof(foo));
Child Logger LogManager.GetLogger(typeof(foo.bar));
_logger.setAdditivity(false);
Alternatively, this functionality can be achieved by using the following code under the Logger section of the Log4Net config file:
<additivity value="false" />
Looking at this hierarchy from a different angle, Listing 8.5 shows two Loggers that refer to the exact same instance of a Logger object.
Listing 8.5. Identical Logger References
This greatly reduces the amount of code needed to use Log4Net by eliminating the need to pass around a reference to a particular instance of the object throughout all the code. This can be useful when using multiple assemblies and should be very useful for Partial Types in .NET 2.0.
Logger a = LogManager.GetLogger(typeof(foo));
Logger b = LogManager.GetLogger(typeof(foo));
LogManager
The LogManager class works with the Hierarchy class to manage all the Loggers for a given instance of Log4Net. LogManager allows for greater runtime control, as seen in Table 8.1.