In my GAE/J application, how would I configure the default logger to report errors via email?
The MailHandler included with JavaMail 1.5.3 and later have built in support for Google App Engine. Make sure you are using the most current version of JavaMail.
For GAE/J you can download the logging-mailhandler.jar and include it in your project.
com.sun.mail
logging-mailhandler
1.5.3
system
FILE_PATH_TO/logging-mailhandler.jar
Otherwise, can use the java.net Maven repository or Maven Central and pull the dependency by groupid=com.sun.mail and artifactId=logging-mailhandler.
com.sun.mail
logging-mailhandler
1.5.3
After the dependency is setup, then configure your logging.properties to contain the correct log settings and email envlope. Here is a sample logging.properties file:
# A default java.util.logging configuration.
# (All App Engine logging is through java.util.logging by default).
#
# To use this configuration, copy it into your application's WEB-INF
# folder and add the following to your appengine-web.xml:
#
#
#
#
#
# Set the default logging level for all loggers to INFO
.level = INFO
java.util.logging.MemoryHandler.level=ALL
java.util.logging.MemoryHandler.push=WARNING
#com.sun.mail.util.logging.CompactFormatter.format=%1$tc %2$s%n%4$s: %5$s%6$s%n
#com.sun.mail.util.logging.MailHandler.formatter=com.sun.mail.util.logging.CompactFormatter
com.sun.mail.util.logging.MailHandler.level=WARNING
com.sun.mail.util.logging.MailHandler.mail.from=me@example.com
com.sun.mail.util.logging.MailHandler.mail.to=me@example.com
#com.sun.mail.util.logging.MailHandler.pushLevel=OFF
#com.sun.mail.util.logging.MailHandler.subject=com.sun.mail.util.logging.CollectorFormatter
com.sun.mail.util.logging.MailHandler.verify=limited
Next create code to install the MailHandler because the LogManager won't be able to see the logging-mailhandler.jar.
Here is an example of a ServletContextListener that will install the MailHandler on the root logger.
/**
* Modify web.xml to include
*
* Install MailHandler on root logger.
* PACKAGE_NAME_FOR.MailHandlerConfig
*
*/
import com.sun.mail.util.logging.*;
import static com.google.appengine.api.ThreadManager.backgroundThreadFactory;
import java.util.Arrays;
import static java.util.concurrent.Executors.newScheduledThreadPool;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.MemoryHandler;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class MailHandlerConfig implements ServletContextListener, Runnable {
private static final String LOGGER_NAME = "";
private static final Logger logger = Logger.getLogger(LOGGER_NAME);
private volatile ScheduledExecutorService ses;
private volatile Future> task;
private volatile Handler handler;
@Override
public void contextInitialized(ServletContextEvent sce) {
MailHandler mh = new MailHandler();
mh.setSubject(defaultSubject());
handler = mh;
try {
handler = new MemoryHandler(mh, mh.getCapacity(), mh.getPushLevel());
ses = newScheduledThreadPool(1, backgroundThreadFactory());
task = ses.scheduleAtFixedRate(this, 30L, 30L, TimeUnit.MINUTES);
} catch (RuntimeException | LinkageError re) {
logger.log(Level.WARNING, "Unable to create push thread.", re);
Level lvl = mh.getLevel();
if (lvl.intValue() < mh.getPushLevel().intValue()) {
mh.setPushLevel(lvl);
handler = mh;
logger.log(Level.WARNING, "Forcing push level to {0}.", lvl);
}
}
logger.addHandler(handler);
logger.log(Level.INFO, "Application initialized. {0}",
Arrays.toString(logger.getHandlers()));
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
//Never called under GAE.
try {
Future> f = task;
if (f != null) {
f.cancel(false);
}
} catch (RuntimeException ignore) {
}
try {
ScheduledExecutorService e = this.ses;
if (e != null) {
e.shutdown();
}
} catch (RuntimeException ignore) {
}
try {
Handler h = handler;
if (h != null) {
h.close();
logger.removeHandler(h);
}
} catch (RuntimeException ignore) {
}
run();
}
@Override
public void run() {
for (Handler h : logger.getHandlers()) {
try {
if (h instanceof MemoryHandler) {
((MemoryHandler) h).push();
}
h.flush();
} catch (RuntimeException ignore) {
}
}
}
private static Formatter defaultSubject() {
return new CollectorFormatter(
"{0}{1}{2}{4,choice,-1#|0#|0<... {4,number,integer} more}",
new CompactFormatter("%7$#.160s"),
new SeverityComparator());
}
}