views:

421

answers:

2

I've created a custom log4net appender. It descends from log4net.Appender.SmtpAppender which descends from log4net.Appender.BufferingAppenderSkeleton.

I programatically setup the following parameters in its constructor:

this.Lossy = false;  //don't drop any messages
this.BufferSize = 3; //buffer up to 3 messages
this.Threshold = log4net.Core.Level.Error; //append messages of Error or higher
this.Evaluator = new log4net.Core.LevelEvaluator(Level.Off); //don't flush the buffer for any message, regardless of level

I expect this would buffer 3 events of level Error or higher and deliver those events when the buffer is filled. However, I'm finding that the events are not buffered at all; instead, SendBuffer() is called immediately every time an error is logged.

Is there a mistake in my configuration?

Thanks

+1  A: 

What you want to do can be done with the following configuration:

<bufferSize value="3" />
<lossy value="false" />
<filter type="log4net.Filter.LevelRangeFilter">
    <levelMin value="ERROR" />
    <levelMax value="FATAL" />
</filter>

My understanding is that Threshold tells log4net when to flush the buffer (it would also flush if you reach the buffer size). If you want to exclude messages from the log you need to use a filter. In code this works as follows:

this.BufferSize = 3;
this.Lossy = false;

var filter = new log4net.Filter.LevelRangeFilter();
filter.LevelMin = log4net.Core.Level.Error;           
this.AddFilter(filter);

Btw. the buffer is filled and on the 4th error message the messages are sent. So if you want 3 messages only in the email you have to set the buffer size to 2...

Stefan Egli
Hey Stefan. This gives me the same behavior. That is, error messages are delivered immediately, rather than buffered.
Eric
you removed the "Threshold" line in your code?
Stefan Egli
I did remove the Threshold line.
Eric
hmmm, the evaluator line as well? or do you have anything else that might have an influence? I used the code from here: http://stackoverflow.com/questions/2409485/prevent-log4net-to-send-duplicate-issues-via-smtp/2410218#2410218 and just added the above in the constructor. I can see the correct messages by setting the breakpoint in the SendBuffer method (I did not actually send an email, as I do not have an SMTP server around at the moment...)
Stefan Egli
Yes, I also removed the evaluator line. The correct messages are being delivered, they just aren't being buffered. That is, SendBuffer() fires for every message instead of collecting them until the buffer is filled.
Eric
+2  A: 

This line:

this.Evaluator = new log4net.Core.LevelEvaluator(Level.Off);

tells the appender to flush the buffer when a message with level equal or above the evaluator is received. Level.Off will therefore block all events.

Update: after tracing over the log4net source once more I cannot see why buffering is not working for you.

Update2: I was stumped, but again, I found myself forgetting about log4net appenders need for the ActivateOptions to be called after settings have been changed. Until ActivateOptions is called, the internal cyclic buffer is not created and buffering will not happen.

So, making a quick test appender:

    public sealed class MyBufferingAppender: BufferingAppenderSkeleton
    {
        public MyBufferingAppender()
        {
            BufferSize = 3;
            ActivateOptions();
        }

        public readonly List<LoggingEvent> SentEvents = new List<LoggingEvent>();
        protected override void SendBuffer(LoggingEvent[] events)
        {
            SentEvents.AddRange(events);
        }
    }

...and a quick test:

    [Test]
    public void DoAppend_BuffersEvents()
    {
        var appender = new MyBufferingAppender();

        appender.DoAppend(new LoggingEvent(
           new LoggingEventData {Level = Level.Error, Message = "Hello world"}));

        Assert.That(appender.SentEvents, Has.Count(0));
    }

The test passes (on my machine at least :).

Peter Lillevold
My understanding is that it tells the appender to flush when a message exceeding the specified threshold is received. http://logging.apache.org/log4net/release/sdk/log4net.Core.LevelEvaluator.htmlI also understand that Level.Off is the highest threshold.http://logging.apache.org/log4net/release/sdk/log4net.Core.Level.Off.htmlAre either of these incorrect?
Eric
@Eric: see my updated answer.
Peter Lillevold
@Peter: nicely done. That is working for me. Thank you.
Eric