log4net FallbackAppender – under the hood

Some Background

I recently put together log4net contrib project up on google code the only item currently up there (though hopefully there should be more stuff from the community up there) is a custom appender I wrote to address the need for having an appender that is clever enough to fallback to other appenders if one fails, this originally came from a forum post of someone wondering how to do it and after some investigation I was that NLog has this built-in.

Investigating how to add it

So my first step was to grab the log4net source and see how I could integrate this appender in, one thing I really didn’t want to do was to change the log4net code and instead allow the appender to be plugged in like you would do normally.

First step was to see how to create an appender that holds other appenders and stumbled across IAppenderAttachable and then the ForwardingAppender which implements it, this interface is what allows you to user the following xml:


      
      

And the configuration makes sure that the appender is populated with the referenced appenders. Looking at the behaviour of the existing ForwardingAppender it gave me a lot of what I was after so I inherited from this.

Hitting the main problem

Now I had the FallbackAppender which got most of it’s behaviour from the ForwardingAppender the next step was to setup the following logic:

  1. Get first appender in the collection
  2. Try and append to it
  3. If succeeded exit
  4. Otherwise try and append to the next appender in the collection
  5. Rinse repeat until one succeeds or all are exhausted

On the face of it this seems to be quite simple, loop the appenders and put a try…catch around the call to Append then have some logic in the catch to use the next appender and repeat, something like this:

while (apppenderQueue.Count > 0)   
{   
    try   
    {   
        var appender = appenderQueue.Dequeue();   
        appender.Append(loggingEvent);   
        break;   
    }   
    catch (Exception thrownException)  
    {  
        // log to internal log about failure 
    }  
}

When I tried this and tested against log4net it didn’t work only the first appender was being appended to if I setup the config incorrectly to simulate an issue.

After some digging the issue became obvious the appenders don’t let exceptions bubble up through the stack and instead log to an IErrorHandler that they get via a property from the AppenderSkeleton object they inherit from and with this being the only way to track errors from appenders I had to then impose a limitation.

Adding the AppenderSkeleton limitation to go forward

With the ErrorHandler property coming from AppenderSkeleton being the only way to discover errors I came to the conclusion that I needed to hook into this property luckily the property can be set, this also imposed the limitation that any of the appenders being referenced had to inherit from AppenderSkeleton in the inheritance tree at some point in order to get access to the ErrorHandler property.

Putting in the hooks

The next step I did was to create my own IErrorHandler that simply recorded if an error occurred, and at appender activation I replace each of the referenced appenders ErrorHandler with that one, now the code can see if an error has been raised for a particular appender and move onto the next one.

Summary

I guess this post demonstrates that even if a third party component doesn’t necessarily provide you with an API that makes extending behaviour easy in this example if log4net appenders let the exception bubble up or the Append method returned a bool whether it succeeded or not, there are still tricks we can use to try and achieve it.

Advertisements

Getting Started with log4net-altconf

log4net-altconf

After working out some of the minor issues with log4net-altconf and dropping a load of code up there on google code I thought it would be a good idea to put together a Getting Started post, to get you up and running, so here goes:

Running from Source

  1. You will firstly need to get the source code onto your machine, this can be done via an SVN client I use TortoiseSVN, the source can be found here http://code.google.com/p/log4net-altconf/source/checkout Built using visual studio 2008
  2. Once you have the source on your machine open up the solution in vs2008 and perform a build.
  3. There is a unit testing project provided, before trying to run the unit tests you should go through instructions located here http://code.google.com/p/log4net-altconf/wiki/TestDslsInNUnit Uses NUnit 2.4.7

Running from Binary

  1. Either download the binary release from here http://code.google.com/p/log4net-altconf/downloads/list or grab the source using step 1 from Running from Source then run nant against the folder with default.build in it requires nant 0.86. The binary files will be put in a build folder.

Adding to Project

You can add a reference to log4net-altconf via a project reference if you want to work from source or by just adding a binary reference to the log4net.altconf assembly.

Configuration Setup

If you have used log4net in the past you should be used to the idea of setting up the configuration of log4net at application startup, this will generally be one of the following:

  1. Application_Start in Global.asax for web projects
  2. Inside Main function for windows / console based projects.

The configuration for log4net-altconf follows the same method only instead of XmlConfigurator you use DslConfigurator, this object has property BaseDirectory and a method Configure, you would use them as follows:

DslConfigurator.BaseDirectory = // path to config file here
DslConfigurator.Configure("logging.boo");

This tells log4net-altconf the path to look for the config file and then the name of the file is passed to the Configure method.

Unlike XmlConfigurator which has a ConfigureAndWatch method to keep track of file changes, DslConfigurator will implicitly keep track of changes made to the file and will reload the config if changes are made, not sure why you would not want this.

Creating a Configuration File

This is the next stage which is were you configure the settings log4net should use, there is nothing special about the configuration file it is simply at text file, generally I have tended to use .boo as the file extension however this is not compulsory.

The config file is generally split into 3 areas from top to bottom:

  1. The top area is where I generally like to set the global options for log4net i.e. internal debugging. I also use it for declaring any variables I would like to re-use further on.
  2. The next area is where your appenders are setup and configured.
  3. After that is where the actual logs should be placed.

Let’s take a look at an example configuration:

# globals and variables
debug = true

# appenders section
add_appender @MyAppender, ConsoleAppender:
    Target = "Console.Error"

# logs section
log_for 'root':
    with_appenders:
        @MyAppender
    at_level ERROR

This should look fairly intuitive, you can find more in depth details about the syntax here http://code.google.com/p/log4net-altconf/wiki/DslKeywordsIdentifiers. Using these details you should be able to put together a configuration file that can be used.

Your now ready to try out logging, you do this the same way you would normally by using the LogManager method GetLogger() I generally stick to using the type of the caller as the argument so it would look like this LogManager.GetLogger(this.GetType()), once you have the returned ILog you can call the methods on it like you would normally such as log.Debug(“foo”).

If you aren’t receiving output to your appenders when you think you should turn internal debugging on by using debug = true in the config file, this should help track any problems.

There is one other point to make if you planning on using this in a web project and this would also affect using the standard XML configuration for log4net in that if you have your configuration file inside you web folder it can be requested and its content brought back to the user, not good! In order to restrict this please read this http://code.google.com/p/log4net-altconf/wiki/RestrictWebFileAccess

Easier log4net configuration is here

I have just released a version of a new project I have been working on after being inspired by Ayende’s ‘Building Domain Specific Languages in Boo‘ e-book, the project is aimed at making log4net configuration easier by using a DSL instead of XML it’s called log4net-altconf and is hosted on google code you can grab the source using a SVN client, I would recommend TortoiseSVN.

It’s early days so some of the syntax will need to be ironed out and there will no doubt be other things that need to be looked at, hopefully someone with better knowledge with this type of thing can give some pointers 🙂

I could probably have had this done a few days back however I was wrestling with NUnit’s addin’s in order to have a set of test DSL’s to test the main DSL that set me back quite a bit!

If you have any comments please feel free to post them here.