SlowDownSpamEvaluator

From EggeWiki

Occasionally something will go wrong in a program an log4j will send a mountain of emails. Here's a simple filtering class that can be used to prevent too many emails from being sent at once.

<geshi lang="java5"> import java.util.LinkedList;

import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.TriggeringEventEvaluator; /**

* This class is a filter for log4j to send at most four emails per hour.
* @author brianegge
*/

public class SlowDownSpamEvaluator implements TriggeringEventEvaluator {

private static final int MAX_MESSAGES_PER_HOUR = 4; private final LinkedList queue = new LinkedList(); private final TriggeringEventEvaluator parent;

public SlowDownSpamEvaluator() { parent = null; }

public SlowDownSpamEvaluator(TriggeringEventEvaluator parent) { this.parent = parent; }

/** * At most, {@value #MAX_MESSAGES_PER_HOUR} messages per hour will be triggered. * @return Is this the triggering event? */ public boolean isTriggeringEvent(LoggingEvent event) { long oneHourAgo = getTime() - 1000L * 60 * 60; synchronized (queue) { while(queue.size() > 0 && ((Long)queue.getLast()).longValue() <= oneHourAgo) queue.removeLast(); if (queue.size() >= MAX_MESSAGES_PER_HOUR) { // we've sent more than four messages in the past hour return false; } queue.addFirst(new Long(getTime())); } if (parent != null) return parent.isTriggeringEvent(event); return true; }

/** * subclasses can override this method for testing. <a href="http://joda-time.sourceforge.net/"> * Joda Time</a> provides good testing capabilities, * but I don't want to add an external dependency. * * @return System.currentTimeMillis() */ protected long getTime() { return System.currentTimeMillis(); } } </geshi>

And of course the test case. <geshi lang="java5"> import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.TriggeringEventEvaluator;

import junit.framework.TestCase;

public class SlowDownSpamEvaluatorUnitTest extends TestCase implements TriggeringEventEvaluator {

int parent; long offset;

public void testIsTriggeringEvent() { TriggeringEventEvaluator evaluator = new SlowDownSpamEvaluator() { protected long getTime() { return super.getTime() + offset; } }; assertTrue(evaluator.isTriggeringEvent(null)); assertTrue(evaluator.isTriggeringEvent(null)); assertTrue(evaluator.isTriggeringEvent(null)); assertTrue(evaluator.isTriggeringEvent(null)); assertFalse(evaluator.isTriggeringEvent(null)); offset += 1000L * 60 * 60; // One hour assertTrue(evaluator.isTriggeringEvent(null)); }

public void testParent() throws Exception { TriggeringEventEvaluator evaluator = new SlowDownSpamEvaluator(this); assertEquals(0, parent); assertFalse(evaluator.isTriggeringEvent(null)); assertEquals(1, parent); }

// we could use EasyMock, but since this is a small project, I'll create the stub in-line public boolean isTriggeringEvent(LoggingEvent event) { parent++; return false; }

} </geshi>