Is there a way to configure the SMTPAppender
in LogBack to meet the following criteria?
One simple solution is to log those errors to a file and have a script on your server/machine that reads the file once a day and sends an email.
If you want to use an appender, it seems to me that you would need to roll your own as I don't think the standard SMTPAppender
enables you to send emails once a day:
SMTPAppender
sendBuffer
method that is in SMTPAppenderBase
so that it simply adds the log message to a collectionScheduledExecutorService
to your appender that runs a sendEmail
method once a daysendEmail
method would synchronize on this
for thread safety, check if the collection is empty, send an email with all the errors and clear the collectionA basic implementation could look like the class below (I have not tested it - I'm using Java 8 syntax but you can replace it by anonymous classes if required). Note that I only keep the event that caused the exception, you may also want to keep the content of the CyclicBuffer in the sendBuffer method and/or add some error separators between errors in the sendEmail method. This can become quite complex and is to be fine-tuned depending on your requirements.
public class ScheduledSMTPAppender extends SMTPAppender {
private final ThreadFactory tf = r -> {
Thread t = new Thread(r, "ScheduledSMTPAppender Thread");
t.setDaemon(true); //make daemon or it will prevent your program to exit
return t;
};
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, tf);
private final List<ILoggingEvent> events = new ArrayList<> ();
private int maxMessages = 10;
public ScheduledSMTPAppender() { super(); }
public ScheduledSMTPAppender(EventEvaluator<ILoggingEvent> eventEvaluator) { super(eventEvaluator); }
@Override public void start() {
super.start();
scheduler.scheduleAtFixedRate(this::sendEmail, 1, 1, TimeUnit.DAYS);
}
@Override protected void sendBuffer(CyclicBuffer<ILoggingEvent> cb, ILoggingEvent lastEventObject) {
events.add(lastEventObject);
if (events.size() > maxMessages) sendEmail();
}
//needs to be synchronized for thread safety
private synchronized void sendEmail() {
try {
if (events.isEmpty()) return;
ILoggingEvent lastEvent = events.get(events.size() - 1);
events.remove(events.size() - 1);
CyclicBuffer<ILoggingEvent> cb;
if (events.isEmpty()) {
cb = new CyclicBuffer<>(1);
} else {
cb = new CyclicBuffer<>(events.size());
for (ILoggingEvent e : events) cb.add(e);
}
super.sendBuffer(cb, lastEvent);
events.clear();
} catch (Exception e) {
//Important to have a catch all here or the scheduled task will die
addError("Error occurred while sending e-mail notification.", e);
}
}
//this allows to make "maxMessages" a parameter of your appender
public int getMaxMessages() { return maxMessages; }
public void setMaxMessages(int maxMessages) { this.maxMessages = maxMessages; }
}
Your logback configuration file then looks like:
<appender name="Email" class="your.package.ScheduledSMTPAppender">
<smtpHost>SERVER</smtpHost>
<smtpPort>PORT</smtpPort>
<asynchronousSending>false</asynchronousSending>
<from>SENDER</from>
<to>RECIPIENT</to>
<subject>SUBJECT</subject>
<maxMessages>10</maxMessages>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{HH:mm:ss.SSS} %-55(%X{user} %level [%thread] %logger{20}) - %msg%n</pattern>
</layout>
</appender>
To go further, you could add parameters, such as the time of the day when this is sent, the number of emails per day etc.