A better SMTPAppender

SMTPAppender for log4j is a type of appender which sends emails via an SMTP server. It’s very useful for applications released in production where you’d definitely need to know of all application errors logged. Of course every caring developer should look at the server logs every now and then, but if you’ve got hundreds of them (applications) then it becomes a full time job in itself.

Sometimes a fresh release of a high traffic website may produce hundreds or thousands of ERROR level log events. Many times this may be something minor which is being logged deep inside your code. Until the bug is fixed and a new release is deployed, your inbox and the mail server may suffer heavily.

What follows is an extension of SMTPAppender which limits the amount of emails sent in a specified period of time. It features sensible defaults which of course can be configured externally via the log4j configuration file.

package com.cherouvim;

import org.apache.log4j.Logger;
import org.apache.log4j.net.SMTPAppender;

public class LimitedSMTPAppender extends SMTPAppender {

    private int limit = 10;           // max at 10 mails ...
    private int cycleSeconds = 3600;  // ... per hour

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public void setCycleSeconds(int cycleSeconds) {
        this.cycleSeconds = cycleSeconds;
    }

    private int lastVisited;
    private long lastCycle;

    protected boolean checkEntryConditions() {
        final long now = System.currentTimeMillis();
        final long thisCycle =  now - (now % (1000L*cycleSeconds));
        if (lastCycle!=thisCycle) {
            lastCycle = thisCycle;
            lastVisited = 0;
        }
        lastVisited++;
        return super.checkEntryConditions() && lastVisited<=limit;
    }

}

The configuration would look something like this:

log4j.appender.???=com.cherouvim.LimitedSMTPAppender
log4j.appender.???.limit=3
log4j.appender.???.cycleSeconds=60
log4j.appender.???.BufferSize=25
log4j.appender.???.SMTPHost=${mail.smtp.host}
log4j.appender.???.From=${mail-sender}
log4j.appender.???.To=${sysadmin.email}
log4j.appender.???.Subject=An error occured
log4j.appender.???.layout=org.apache.log4j.PatternLayout
log4j.appender.???.layout.ConversionPattern=%d{ISO8601} %-5p (%F:%L) - %m%n
log4j.appender.???.threshold=ERROR

The above configuration will limit the mail dispatch to only 3 emails per minute. Any further errors in that minute will not be emailed. The limit and cycleSeconds setting lines can be omitted and the defaults will be applied.

Happy logging!

4 Responses to “A better SMTPAppender”

  1. ifi Says:

    Neat post. I’m struggling with the same problem. I did something like you did but ran into the following problem.

    Example: Limit it to say 3 emails every 10 minutes. Let’s say you get all 3 emails in the first few seconds. You fix the errors. Then you get another error at minute 9. But your quota is up, so it doesn’t send an email. Then the app stops logging b/c there is nothing to log for now. You will never be notified of the error from minute 9, right?

    I think it’s better to use a timer to check if an errors have been cached and then send an email. What do you think?

  2. cherouvim Says:

    Yes that is correct. My implementation is very simple and may ignore important errors after the limit per minutes is up. Possible improvements could include caching all errors and sending them in one mail or comparing them somehow (pattern matching) to make sure that it does not ignore different errors even when the threshold has been exceeded.

    Of course, if the production server starts sending errors you ought to SSH and view the log4j logs yourself :)

  3. Ellie Perrett Says:

    Informativer Artikel, vielen Dank.

  4. Franklin Dattein Says:

    Exactly what I was looking for. Simple and effective.