Coding and Dismantling Stuff

Don't thank me, it's what I do.

About the author

Russell is a .Net developer based in Lancashire in the UK.  His day job is as a C# developer for the UK's largest online white-goods retailer, DRL Limited.

His weekend job entails alternately demolishing and constructing various bits of his home, much to the distress of his fiance Kelly, 3-year-old daughter Amelie, and menagerie of pets.

TextBox

  1. Fix dodgy keywords Google is scraping from my blog
  2. Complete migration of NHaml from Google Code to GitHub
  3. ReTelnet Mock Telnet Server à la Jetty
  4. Learn to use Git
  5. Complete beta release FHEMDotNet
  6. Publish FHEMDotNet on Google Code
  7. Learn NancyFX library
  8. Pull RussPAll/NHaml into NHaml/NHaml
  9. Open Source Blackberry Twitter app
  10. Other stuff

Log4Net Custom Appenders and the ActivateOptions Event

I've been working with Log4Net on and off now for several years. I've used most of the existing appenders, tweaked an appender here or there, but I'd not done any heavy lifting. That was until I started checking out the Gelf4Net appender.

Gelf4Net supports multiple transport mechanisms (currently UDP and AMQP), in the constructor the appender creates up an instance of each transport class and assigns these to two private fields, so that when it comes to appending log events, either can be used.  This isn't ideal, and I believe it's been done because Log4Net doesn't configure the appender (which transport to use) until after the class is constructed. So I've been scratching my head and Googling around to work out a solution, and I've uncovered the ActivateOptions method, which appears to exist exactly for this purpose. Let's see how this works out.

Let's take a look at some code. The following is a simplified version of the code in the existing Gelf4Net appender.

public class Gelf4NetAppender : AppenderSkeleton
{
    private UdpGelfTransport _udpGelfTransport;
    private AmqpGelfTransport _amqpGelfTransport;
    public bool UseUdpTransport { get; set; } // Log4Net populates this

    public Gelf4NetAppender() : base()
    {
        _udpGelfTransport = new UdpGelfTransport();
        _amqpGelfTransport = new AmqpGelfTransport();
    }

    protected override void Append(log4net.Core.LoggingEvent loggingEvent)
    {
        if (UseUdpTransport)
            _udpGelfTransport.Send(loggingEvent);
        else
            _amqpGelfTransport.Send(loggingEvent);
    }
}

See those two private members being assigned in the constructor? And see how the append method only ever uses one of these? It feels wasteful to construct two classes like this, which both inherit from a common base class, when we are only ever going to use one of them. Also what happens if we decide to include a third or fourth transport type?

After my little Googling session, I now suspect that when you use a custom appender with Log4Net, I believe the flow is something like:

  • Log4Net at start-up constructs an instance of each appender in the configuration.
  • After constructing the appender, Log4Net reads any configuration data and assigns public properties on the appender.
  • When the appender has been fully configured, Log4Net in the books of the activate options method.
  • The appender can override this method to perform logic that is dependent on the configuration data.

Taking the above into consideration, the code after my modification now looks like the following:

public class Gelf4NetAppender : AppenderSkeleton
{
    private GelfTransport _gelfTransport;
    public bool UseUdpTransport { get; set; } // Log4Net populates this

    public Gelf4NetAppender() : base()
    { }

    public override void ActivateOptions()
    {
        _transport = UseUdpTransport
            ? (GelfTransport)new UdpGelfTransport()
            : (GelfTransport)new AmqpGelfTransport();
    }

    protected override void Append(log4net.Core.LoggingEvent loggingEvent)
    {
        _gelfTransport.Send(loggingEvent);
    }
}

See how I only have one private member, and that I only construct this member in the newly overridden ActivateOptions method? Incidentally, I'm not surprised the original author of this code was unaware of this method, as I mentioned above I've struggled to find clear documentation. If anyone has any feedback to confirm or correct any of this, comments would be much appreciated.


Categories: Hacking
Permalink | Comments (0)

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading