Real-time error monitoring in Google app engine

Yesterday, I showed you how your Google app engine application can send a mail whenever an error occurs (an uncaught Exception for example).

Today we are going to see how to monitor your application in real-time using Google talk.

Of course, the Google talk client has a little icon that tells you how many unread emails you have:

But that’s not enough: we want the GAE application to send a “google talk message” each time an error occurs. This can easily be done using the XMPP service.

First, we define a servlet to handle all exceptions:

<servlet>
  <servlet-name>ShowErrorServlet</servlet-name>
  <servlet-class>fr.kubaski.servlet.ShowError</servlet-class>
</servlet>
<servlet-mapping>
  <servlet-name>ShowErrorServlet</servlet-name>
  <url-pattern>/showerror</url-pattern>
</servlet-mapping>
<error-page>
  <exception-type>java.lang.Exception</exception-type>
  <location>/showerror</location>
</error-page>

And here is the code for the servlet:

public class ShowError extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    // note that 't' cannot be null since this servlet is defined as the error handler in web.xml:
    Throwable t = (Throwable) req.getAttribute("javax.servlet.error.exception");
    JID jid = new JID("theadmin@gmail.com");
    String msgBody = "an error occured: " + t.getMessage();
    Message msg = new MessageBuilder()
    .withRecipientJids(jid)
    .withBody(msgBody)
    .build();
    boolean isSent = false;
    XMPPService xmpp = XMPPServiceFactory.getXMPPService();
    if (xmpp.getPresence(jid).isAvailable()) {
      SendResponse status = xmpp.sendMessage(msg);
      isSent = (status.getStatusMap().get(jid) == SendResponse.Status.SUCCESS);
    }
    if (!isSent) {
      // error handling
    }
    getServletContext().getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(req, resp);
  }
}

In the code sample above, “theadmin@gmail.com” is the email of an administrator of the GAE application: replace it with yours !

Now you need to invite your GAE application from the Google talk client (exactly like you would invite a friend using his email). To do this, just invite [APPLICATION_IDENTIFIER]@appspot.com. In my case, my application is http://atestapplication.appspot.com so I need to invite atestapplication@appspot.com:

Now generate an exception in your application so that the error servlet is invoked and you will receive an instant notification:

Laurent KUBASKI

Error monitoring in Google app engine

Checking the GAE app engine logs each day for error messages can be very boring: what if you could receive a mail each time a non-handled Exception occurs in your Google app engine Java application ?

Well, it’s very easy: first define an generic “error handler” servlet in web.xml:

<servlet>
  <servlet-name>ShowErrorServlet</servlet-name>
  <servlet-class>fr.kubaski.servlet.ShowError</servlet-class>
</servlet>

<servlet-mapping>
  <servlet-name>ShowErrorServlet</servlet-name>
  <url-pattern>/showerror</url-pattern>
</servlet-mapping>

<error-page>
  <exception-type>java.lang.Exception</exception-type>
  <location>/showerror</location>
</error-page>

And here is the code for the servlet:

public class ShowError extends HttpServlet {
  private final Log log = LogFactory.getLog(getClass());

  @Override
  protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    Throwable t = (Throwable) req.getAttribute("javax.servlet.error.exception");
    log.error("error", t);
    try {
      Message msg = newMessage("theadmin@gmail.com", t.getMessage(), getDisplayableThrowable(t));
      Transport.send(msg);
    } catch (Exception e) {
      // we just log the error: this is not a big deal if the mail was not sent
      log.error("error", t);
    }
    getServletContext().getRequestDispatcher("/WEB-INF/jsp/error.jsp").forward(req, resp);
  }

  public static String getDisplayableThrowable(Throwable t) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw, true);
    t.printStackTrace(pw);
    String result = sw.toString();
    try {
      sw.close();
    } catch (IOException e) { /* nothing */}
    pw.close();
    return result;
  }

  private Message newMessage(String adminMail, String subject, String body) throws MessagingException {
    Properties props = new Properties();
    Session session = Session.getDefaultInstance(props, null);
    Message msg = new MimeMessage(session);
    // As stated in GAE documentation:
    // "For security purposes, the sender address of a message must be the email address of an administrator for the application"
    msg.setFrom(new InternetAddress(adminMail));
    msg.addRecipient(Message.RecipientType.TO, new InternetAddress(adminMail));
    msg.setSubject(subject);
    msg.setText(body);
    return msg;
  }
}

Note that the mail sending feature does NOT work in “development mode” and that you need to deploy your app in GAE for this to work.

Laurent KUBASKI