Writing a custom log4net appender - WcfAppender

18.April.08 – 11:49

log4net is a great logging library that comes with a couple of nice Appenders (these are the parts that actually log the stuff to a specific medium). It even has some appenders that can send the log data over the wire:

  • NetSendAppender
  • UdpAppender
  • RemotingAppender
  • SmtpAppender
  • TelnetAppender

But these appenders just did not fit my needs. E.g. when you have to bypass a firewall/proxy (probably even with authentication). Or when you want your log-data suppliers to authenticate them (in case that you don’t want anyone to log).

That’s why i wrote a custom log4net appender : WcfAppender. This appender just sends the logdata to a remote service where it is saved to a database. If you have a couple of installations out there in the wild and want to get informed about Errors in your Application, this might be very helpful. (you probably won’t send debug output to your service, but later more on that )

To implement your own log4net appender you have to either implement log4net.Appender.IAppender or inherit from log4net.Appender.AppenderSkeleton (recommended).We override the following 3 Methods from AppenderSkeleton:

public void ActivateOptions();

protected void Append(log4net.Core.LoggingEvent loggingEvent);

protected void OnClose();

log4net cares about filling public Properties with the values provided in your log4net config. Therefore the config for our appender will look like this:


 
 <param name=”QueueSize” value=”3″/>
 
 <param name=”FlushLevel” value=”ERROR”/>
 
 <param name=”SecurityMode” value=”None”/>
 <param name=”UseDefaultWebProxy” value=”false”/>
 
 <param name=”Username” value=”myUsername”/>
 
 <param name=”Password” value=”myPassword”/>
 
 <param name=”RemoteAddress” value=”http://localhost:8731/WcfAppenderService/”/>

We have to pass those parameters to the appender via the log4net config instead of the normally used system.servicemodel config stuff because libraries (i.e. DLLs) cannot have their own config files and you probably don’t want your application to include System.ServiceModel stuff. The Wcf client gets configured in code with the parameters supplied in this log4net config.
Because WCF communication involves some overhead, we don’t want the appender to send all loggingevents immediately when they arrive. Therefore we just add the logging event to a local queue and flush it if the queue reaches a given size. (or we get an event with a log level above the treshold we supplied in configuration).
E.g. we get a lot of warnings, but as they are not that important we save them locally till we reach the maximum queue size or till we get an error or fatal message. In that case we flush the queue immediately to our wcf service.
As IAppender.Append() is called in a synchronous way we don’t want any lengthy operations in there. Thats why i called the FlushQueue() call of the wcf appender asynchronously with a System.Action delegate.

We also have to flush the queue in the overridden OnClose() method so that we won’t loose logging events when our application closes.On the Service-Side we create a little database that we access via LinqToSql:

The Application table is there to support different Applications (for test purposes this is just set to the root-namespace of the app using the wcf appender).
The service offers the following methods:

  [OperationContract(Name="AppendLoggingEvent",IsOneWay=true)]
  void Append(LoggingEventContainer loggingEvent);

  [OperationContract(Name = "AppendLoggingEvents",IsOneWay=true)]
  void Append(List<LoggingEventContainer> loggingEvents);

We have to define a unique operation name because you cannot use overloaded methods with webservice-operations the normal way. And as we don’t need the service to return information we can mark the operations as OneWay calls.

To use the appender in your project, just add a reference to WcfAppender.dll, add the appender to your log4net configuration and add it to your log4net root:

 
  
   <root>
    <level value=”WARN”/>
    <appender-ref ref=”wcfappender”/>
   </root>
  
 

The sample solution is attached below, but be careful. This appender is maybe not suitable for production use, so use it at your own risk.

Download: WcfAppender.zip

  1. 4 Responses to “Writing a custom log4net appender - WcfAppender”

  2. Wow, thanks for this, it’s just what I’m after.

    By Andy Smith on Jul 23, 2008

  3. Good Stuff,

    What is the ActivateOptions method for? I know it is included in the IOptionHandler Interface. I want to create a custom MSBuildLog4NetAppender and I need to get the context of my MSBuild task into the Appender. If I create a class that inherits from AppenderSkeleton and pass the logging events through to the TaskLoggingHelper in MSBuild all works fine.

    But when I new this up with configuration, I get back a log4net ILog which I can’t set the instance of my TaskLoggingHelper with. How can I pass this context across?

    By Gavin Stevens on Dec 23, 2008

  4. well hard to say without more information.maybe you could provide me with a code sample or something else.

    By jkersch on Dec 24, 2008

  5. Great example. I was able to spring off of this. Quick question though - dev mgmt doesn’t want devs to have to add a reference in their VS2K8 projects for my appender. It’s in the app.config as needed for log4net but the normal log statements don’t cause my appender to be called unless I also include my appender as a reference in the app’s project. What am I missing?

    By Tim E on Jun 6, 2009

Post a Comment

Comment spam protected by SpamBam