Log4Net is a cool, stable, fully featured, highly configurable, highly customizable and open source logging framework for .Net.
One of its powerful features is the ability to write logs to multiple targets, by using the notation of Appenders.
An Appender is a Log4Net component that handles log messages; It receives a log message each time a new message is logged and it 'handles' the message. E.g a simple file appender will write log messages to a local file.
Although there are a lot of Log4Net Appenders included in the Log4net framework out of the box, occasionally we will have to implement specific behaviors to fully satisfies our needs. During a project I was working on, I had to use a failover mechanism for logging; where the app would to start logging to a remote server, and failback to a local file on the file system if that remote server wasn't reachable anymore.
Fortunately, we can implement our own custom Appenders and use them with Log4Net.
The Appender had to start logging to a remote service, and failback to logging to a local file after the first failed attempt to log to that service.
Implementing the Appender
To create a custom Appender we have to implement the IAppender interface. Although easy to implement, Log4Net makes it even simpler by providing the AppenderSkeleton abstract class, which implements IAppender and adds common functionalities on top of it.
The FailoverAppender above accepts two appenders; a primary appender and a failover appender.
By default it will propagate Log events only to the primary appender, but in case an exception is thrown from the�primary appender during event logging , it will stop sending log events to that appender and instead it starts propagating log events only to the failover appender.
I've used AppenderSkeleton to reference both the primary and the failover appenders in order to utilize a functionality in the AppenderSkeleton class - in this case the ability to handle errors (a.k.a Exceptions) that were thrown during an appender attempt to log an event.
We can do so by assigning the ErrorHandler property defined in AppenderSkeleton an object.
I use the LogToFailOverAppender flag to determine whether we are in 'normal' mode or in 'FailOver' mode.
The actual logging logic exists in the overridden 'Append' method:
If the LogToFailOverAppender flag is active, it logs events using the failover appender, as it means an exception has been thrown already. Otherwise, it logs events using the primary appender, and it will activate the failover mode, if an exception is thrown during that time.
The following are the IErrorHandlers that I defined and used
Testing the Appender
I've created a config file you can use to test the appender. These are the important bits:
In this post I'll discuss the parts that are relevant to the appender. You can find the full config file here. The rest of the config file are a regular Log4Net configurations, which you can read more about here and here.
Log4Net has a feature that give us an ability to instantiate and assign values to public properties of appenders in the config file using XML. I'm using this feature to instantiate and assign values to both the PrimaryAppender and the FailOverAppender properties.
In this section I'm instantiating the PrimaryAppender:
The type attribute's value is the fully qualified name of the appender's class.
For our example, I've created the ExceptionThrowerAppender appender for testing purposes. It can be configured to throw exceptions once per a certain amount of log events.
In a similar manner, in the following XML I've instantiated and configured the FailOverApppender to be a regular RollingFileAppender
I used the following code to create log events:
I started the program in debug mode and placed a breakpoint inside the 'Append' method:
Notice how OzCode's Predict the Future feature marks the if-statement with an X and with a red background, telling us that the condition is evaluated to false. That means an exception wasn't thrown yet from the primary appender.
In order to make figuring out the loggingEvent message value easier, I've used OzCode's Magic Glance feature to view the necessary information in every LoggingEvent object.
By continuing the program, the primary appender will handle the logging event, and it will throw an exception
After that exception is propagated by the ErrorHandler, it will be handled by the catch-clause, which activates the FailOverAppender mode (notice how the�log event is sent to the FailOverAppender as well) which would send future logging events only to the FailOverAppender
This time the if-statement is marked by a green 'V' . This tell us that the condition is evaluated to be true and that it will execute the if-statement body (sends the logging-event to the failover appender).
You can view and download the code by visiting this GitHub Repository.
Log4Net is a well-known logging framework for .Net. The framework comes with a list of out-of-the-box Appenders that we can use in our programs.
Although these Appenders are usually sufficient for most of us, sometimes you'll need Appenders that are more customized for your needs.
We saw how we can use Log4Net's extensibility features to implement our own Log4Net custom Appenders. In this example, we have created a Fail-over mechanism for Appenders that we can use to change the active Appender when it is failing to append log messages.
Log4Net is highly extensible and it has many more extensibility features that I encourage you to explore.