I've found logging to be one of the most effective debugging and troubleshooting techniques in my engineering bag, but I'm surprised at how infrequently other people seem to use it.  The arguments orbit around the notion that adding any form of logging won't be that helpful, or that it will slow the application to a crawl.  My experience with application logging has been very positive, and quite frankly I would be a lot less effective without it.

This post is the first in a series aimed at making you comfortable and confident in using log4net as an application logging layer in your .NET applications.  Seriously, it'll make your life a lot easier.

About log4net

My logging layer of choice for .NET is log4net from the Apache Software Foundation.  It's open source, well-documented, extensible, and comes ripe with features right off the vine.  There are alternative logging platforms available if open source isn't an option for you, such as the Microsoft Logging Application Block or, if you're willing to fork out some cash, LucidLog.Net; I can't comment on them directly as I have no experience with either - I've heard good things, but I've also heard that log4net is more feature-rich that both of these.

Note: This series of posts references log4net version 1.2.10.

Getting log4net

log4net is available here as a zip archive. 

The archive contains a ton of stuff; for posterity, here are some hilites of what you'll find in there:

  • \bin houses the log4net binaries.  The distribution contains versions of log4net built for specific platforms of .NET, including Microsoft .NET, Mono, Rotor, and the Compact Framework.
  • \doc contains the log4net documentation.  Open index.html to start your perusing. 
  • \examples contains several small, digestible sample logging applications in various languages and platforms.
  • \src holds the log4net source code.
  • \tests contains unit tests for the log4net source code.

A Quick Example

  1. Open Visual Studio and create a new Console application project.
  2. Add to the project a reference to the \bin\net\2.0\release\log4net.dll assembly in the log4net distribution.
  3. Modify your Main() method like so:
using System;
namespace Tutorial1_GettingStarted
    class Program
        static void Main( string[] args )
            log4net.ILog log = log4net.LogManager.GetLogger( typeof( Program ) );           

            log.Debug( "Hello World!" );
            log.Info( "I'm a simple log4net tutorial." );
            log.Warn( "... better be careful ..." );
            log.Error( "ruh-roh: an error occurred" );
            log.Fatal( "OMG we're dooooooomed!" );
            Console.ReadLine();  // so you can read the output


Before running the application, take a close look at the code.  On line 8, the log4net subsystem is configured in the most basic way possible.  Using the BasicConfigurator will cause log4net to output log entries to the console using a default layout (more on layouts in a moment).  log4net configuration is a very broad topic and can get rather deep, but it can be this shallow and narrow too. 

Line 9 requests a logger from the LogManager object.  Logger objects implement the ILog interface, which is what your application will use to instrument itself and percolate log entries.

Lines 11-15 use the logger to log a few statements with various severity levels.  log4net defines 5 such levels:

  • Debug: fine-grained statements concerning program state, typically used for debugging;
  • Info: informational statements concerning program state, representing program events or behavior tracking;
  • Warn: statements that describe potentially harmful events or states in the program;
  • Error: statements that describe non-fatal errors in the application; this level is used quite often for logging handled exceptions;
  • Fatal: statements representing the most severe of error conditions, assumedly resulting in program termination.

Compile and run the application, and you'll see output to the console similar to the following:

93 [10] DEBUG Tutorial1_GettingStarted.Program (null) - Hello World!
156 [10] INFO Tutorial1_GettingStarted.Program (null) - I'm a simple log4net tutorial.
156 [10] WARN Tutorial1_GettingStarted.Program (null) - ... better be careful ...
156 [10] ERROR Tutorial1_GettingStarted.Program (null) - ruh-roh: an error occurred
156 [10] FATAL Tutorial1_GettingStarted.Program (null) - OMG we're dooooooomed!


Note that each log statement is accompanied by a bunch of data.  I mentioned that the BasicConfigurator uses a default layout; layouts determine how log4net formats a log statement.  In this case, the BasicConfigurator is using a layout that includes stuff like the identifier of the thread from which the log entry was made, the level of the log entry, etc.

Coming Up

That's it for now - just a quick survey of the logging landscape to get you started.  Next time I'd like to talk more about log4net configuration, specifically how to use the app.config file to make log4net ignore log entries below a certain severity level.