2008-08-26

Adding an appender to log4net in runtime

I was testing logging with log4net today and wanted to create a unit test that verified that the method logged alright.
To verify that the logging worked I wanted to create a log appender (the MemoryAppender) and add it to the log4net configuration to be able to compare the actual logged messages with the expected ones. (The MemoryAppender has the GetEvents() and Clear() methods which works well in a unit test case).

Normally you use a configuration file (xml) to add your appenders but using configuration files in a unit test is not quite the optimal solution. So how could I set it in runtime in my test method. After a bit of testing and reading (I wonder why the testing and reading always seem to be in that order? (for me anyway) Shouldn't you start reading about the theory before you start coding and testing? ;)

I found some tips using the Hierarchy class to access the root level logger and add append the MemoryAppender there.

The important steps where to set the Configured property of the Hierarchy and call the RaiseConfigurationChanged() method to actually utilize the configuration changes.

Here is a complete example.

//First create and configure the appender
MemoryAppender memoryAppender = new MemoryAppender();
memoryAppender.Name = "MyAppender";

//Notify the appender on the configuration changes
memoryAppender.ActivateOptions();

//Get the logger repository hierarchy.
log4net.Repository.Hierarchy.Hierarchy repository =
LogManager.GetRepository() as Hierarchy;

//and add the appender to the root level
//of the logging hierarchy
repository.Root.AddAppender(memoryAppender);

//configure the logging at the root.
repository.Root.Level = Level.All;

//mark repository as configured and
//notify that is has changed.
repository.Configured = true;
repository.RaiseConfigurationChanged(EventArgs.Empty);
Now we have prepared the logging for the root (all log events) and directed it to the memoryAppender.
//Call the method to test (this one creates
//an info level message to the log
//stating the string "GetConfiguration()".
ServerDataFactory.Instance.GetConfiguration();
When we have ran the code that has produced some logging, we add assert statements to check that the code behaved as we expected.
//use the memoryAppender to get the logged events
LoggingEvent[] events = memoryAppender.GetEvents();

//We only expect one log statement on the info level
Assert.AreEqual(1, events.Length,
"Incorrect number of log statements");
LoggingEvent e = events[0];
Assert.AreEqual(Level.Info, e.Level,
"Incorrect logging level");
Assert.AreEqual("GetConfiguration()", e.MessageObject,
"Incorrect log message");
Thats how I did it. Does anyone has another solution that works well?