Exception mailing

A couple of weeks ago our Scrum team was thinking about exception handling. We don’t use checked exceptions, since they are the embodiment of evil. So everything is translated into runtime exceptions whenever possible. But this is where the problems start: What do you do with the uncatched runtime exceptions?

Taking it one step further

Most projects will decide to just write the exceptions to the console, maybe to a log file. In some cases there is specialized software which will analyze log files, detect stacktraces and act on them. We decided to create a fast specialized solution. We want to have a mailbox where all the runtime exceptions end up, including stacktrace, system properties, program properties and even a screenshot.

Catching the uncaught exception

In Java you can set a exception handlers on Threads. You can set a handler on a Thread, but it is also possible to set a default exception handler. It is very easy:

public class CustomUncaughtExceptionHandler implements UncaughtExceptionHandler {

    public CustomUncaughtExceptionHandler() {
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(final Thread t, final Throwable throwable) {
        //do something here!    
    }
}

If an exception occurs, the uncaughtException method will be called. Then you can do anything you want with the throwable. As said before, we decided it is best for us that the application phones home and sends us an email with as much information as we can get.

What to send?

Our application is launched by JNLP and will run on several clients. We don’t have complete contol over these clients, so we want to include as much information as possible to solve possible bugs in the application. The more information we have about the runtime, the easier it might be to find the problem. We grab all (relevant) system properties and program properties we can get. Including usernames, machine details.

Creating a screenshot

In Java there are actually two methods of creating a screenshot. The first method is the easiest, using the Java Robot:

Robot robot = new Robot();
 
BufferedImage screenShot = robot.createScreenCapture(new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()));

Very easy, but this can contain a LOT of information about the moment the exception occurs. It may provide valuable information. But we encountered a major security problem with the above method. Java will create an actual screenshot, the size of your application. But it doesn’t know if your application is currently the active window..! When we did tests we saw screenshots with Skype messages in screen and browsers being active… We can’t invade the lives of our users and send this information over the mail!

So I decided to search for something less invasive. The main frame of our application has a paint method, and most of the time this method can still be called if we have an exception. It is possible to create our own Graphics object and let the main frame paint itself into memory. So eventually I settled on the code below:

private BufferedImage createScreenshot() {
    try {
        // Try to repaint the main frame of our application:
        int w = this.screenshotComponent.getWidth();
        int h = this.screenshotComponent.getHeight();
        BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        this.screenshotComponent.paintAll(image.getGraphics());
        // We can't use Robot, it might capture a bit too much information
        return image;
    } catch (Exception e) {
        // Screen can't be captured, don't throw exception, else we'll end up in a loop :-)
        return null;
    }
}

Mailing the exception

The next step in our solution is sending an email with all the information. This is done using Java Mail, an API to send emails. More about sending emails from Java here. In our case we create a pretty HTML email with all relevant information and three attachements:

  • Text file with all system properties
  • Text file with the complete exception
  • The image/screenshot

To create the email it is easy to create MimeBodyParts which contain the text, the only part a bit more complicated it to add the screenshot inside the email, this can be done using:

public MimeBodyPart createImagePart(final BufferedImage screenshot)
		throws MessagingException {
    
	if (screenshot == null) {
		return null;
	}
    
	MimeBodyPart imagePart = new MimeBodyPart();
	ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
	try {
		ImageIO.write(screenshot, "jpg", outputStream);
	} catch (IOException ioException) {
		// Impossible to get the screenshot, return null and continue without it
		return null;
	}
    
	DataSource source = new ByteArrayDataSource(outputStream.toByteArray(), "image/jpeg");
	imagePart.setDataHandler(new DataHandler(source));
	imagePart.setFileName("screenshot.jpg");
	imagePart.setContentID("<screenshot>");
	return imagePart;
}

The only non-obvious line in the code above is setting the ContentID, this can be used to refer to the image-data in case of an HTML email. In the main email content I added the following line:

Screenshot: <img src="/images/cid:screenshot" alt="Screenshot of the moment the exception occured." />

The src=”cid:screenshot” will cause the email to show the screenshot embedded inside the email. This makes the exception-email look very pretty.

Even more in the future…?

With our current implementation we’ll be able to detect exceptions instantly (on every new email). Also we’ll have more information then just a stacktrace, we have a screenshot and some parameters/system properties which can help us re-create the situation.

To improve this even more I’m planning on adding a simple bounded FIFO queue. This queue will contain a list of actions performed by the user. These user-actions can be added automatically by using aspect oriented programming. For example annotating all the service methods. Everytime a service method is called we can add it to the bounded queue, which has a maximum of N elements, for example 20. If an exception occurs we print our the current queue, we can read the last N actions leading up to the exception. This too can be very helpful in re-creating the situation.

What more information could be useful in an debug email like this? How do you guys solve exception handling?