27

How can I detect when an Exception has been thrown anywhere in my application?

I'm try to auto-magically send myself an email whenever an exception is thrown anywhere in my Java Desktop Application. I figure this way I can be more proactive.

I know I could just explicitly log and notify myself whenever an exception occurs, but I'd have to do it everywhere and I might(more likely will) miss a couple.

Any suggestions?

Allain Lalonde
  • 91,574
  • 70
  • 187
  • 238
  • When working with Java 7 or newer you might want to check answers to http://stackoverflow.com/questions/95767/how-can-i-catch-awt-thread-exceptions-in-java?lq=1 – Suma Jan 09 '15 at 10:36

10 Answers10

37

You probobly don't want to mail on any exception. There are lots of code in the JDK that actaully depend on exceptions to work normally. What I presume you are more inerested in are uncaught exceptions. If you are catching the exceptions you should handle notifications there.

In a desktop app there are two places to worry about this, in the (EDT) and outside of the EDT. Globaly you can register a class implementing java.util.Thread.UncaughtExceptionHandler and register it via java.util.Thread.setDefaultUncaughtExceptionHandler. This will get called if an exception winds down to the bottom of the stack and the thread hasn't had a handler set on the current thread instance on the thread or the ThreadGroup.

The EDT has a different hook for handling exceptions. A system property 'sun.awt.exception.handler' needs to be registerd with the Fully Qualified Class Name of a class with a zero argument constructor. This class needs an instance method handle(Throwable) that does your work. The return type doesn't matter, and since a new instance is created every time, don't count on keeping state.

So if you don't care what thread the exception occurred in a sample may look like this:

class ExceptionHandler implements Thread.UncaughtExceptionHandler {
  public void uncaughtException(Thread t, Throwable e) {
    handle(e);
  }

  public void handle(Throwable throwable) {
    try {
      // insert your e-mail code here
    } catch (Throwable t) {
      // don't let the exception get thrown out, will cause infinite looping!
    }
  }

  public static void registerExceptionHandler() {
    Thread.setDefaultUncaughtExceptionHandler(new ExceptionHandler());
    System.setProperty("sun.awt.exception.handler", ExceptionHandler.class.getName());
  }
}

Add this class into some random package, and then call the registerExceptionHandler method and you should be ready to go.

Paul
  • 10,381
  • 13
  • 48
  • 86
shemnon
  • 5,318
  • 4
  • 28
  • 37
  • 2
    Why do you need last line System.setProperty("sun.awt.exception.handler", ExceptionHandler.class.getName()); It's working without this line. And sun.awt could be specific to sun's implementation I guess. – feiroox Dec 14 '09 at 06:07
  • 1
    In case the Exception is thrown in side the Event Dispatch Thread. The Swing/AWT EDT has their own Exception catcher at the root of their thread and this is the hook into it. – shemnon Dec 15 '09 at 17:59
  • 3
    Are you sure about the infinite looping part? The documentation of UncaughtExceptionHandler.uncaughtException(...) states: Any exception thrown by this method will be ignored by the Java Virtual Machine. (see: file:///usr/lib/jvm/java-6-sun/docs/api/java/lang/Thread.UncaughtExceptionHandler.html) – jilles de wit Oct 15 '10 at 14:48
  • Who reads javadocs? So it won't cause infinite looping, but this makes it wholly expressive in the code. And prior to 1.5 you need to do this in the ThreadGroup, which made no such promises. So the code and thinking is legacy. – shemnon Oct 19 '10 at 15:25
  • I couldn't get the EDT method suggested here to work, using a 1.6 JVM. Instead, I followed the advice at [this website](http://ruben42.wordpress.com/2009/03/30/catching-all-runtime-exceptions-in-swing/), which works perfectly for catching EDT exceptions. This coupled with the `registerExceptionHandler` approach catches all runtime exceptions in my Swing application. – Duncan Jones Mar 28 '12 at 08:47
5

The new debugging hooks in Java 1.5 let you do this. It enables e.g. "break on any exception" in debuggers.

Here's the specific Javadoc you need.

Jason Cohen
  • 81,399
  • 26
  • 107
  • 114
4

Check out Thread.UncaughtExceptionHandler. You can set it per thread or a default one for the entire VM.

This would at least help you catch the ones you miss.

Justin Rudd
  • 5,334
  • 4
  • 22
  • 16
1

If you're using a web framework such as Spring then you can delegate in your web.xml to a page and then use the controller to send the email. For example:

In web.xml:

<error-page>
  <error-code>500</error-code>
  <location>/error/500.htm</location>
</error-page>

Then define /error/500.htm as a controller. You can access the exception from the parameter javax.servlet.error.exception:

Exception exception = (Exception) request.getAttribute("javax.servlet.error.exception");

If you're just running a regular Java program, then I would imagine you're stuck with public static void main(String[] args) { try { ... } catch (Exception e) {} }

Mat Mannion
  • 3,315
  • 2
  • 30
  • 31
  • That example applies to any "WebApp" and has nothing to do with Spring or web frameworks. It also only applies to uncaught exceptions which bubble up to the base servlet class, thus causing the http status 500 -- which may (or may not) be what is wanted. – Cheekysoft Sep 16 '08 at 18:51
  • Ah, thanks - I only have experience with Spring so I didn't want to speak out of turn :) – Mat Mannion Sep 17 '08 at 17:56
1

If you are using java 1.3/1.4, Thread.UncaughtExceptionHandler is not available. In this case you can use a solution based on AOP to trigger some code when an exception is thrown. Spring and/or aspectJ might be helpful.

Alexandre Victoor
  • 3,104
  • 2
  • 27
  • 27
1

In my current project I faced the similar requirement regarding the errors detection. For this purpose I have applied the following approach: I use log4j for logging across my app, and everywhere, where the exception is caught I do the standard thing: log.error("Error's description goes here", e);, where e is the Exception being thrown (see log4j documentation for details regarding the initialization of the "log"). In order to detect the error, I use my own Appender, which extends the log4j AppenderSkeleton class:

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LoggingEvent;

public class ErrorsDetectingAppender extends AppenderSkeleton {

    private static boolean errorsOccured = false;

    public static boolean errorsOccured() {
        return errorsOccured;
    }

    public ErrorsDetectingAppender() {
        super();
    }

    @Override
    public void close() {
        // TODO Auto-generated method stub
    }

    @Override
    public boolean requiresLayout() {
        return false;
    }

    @Override
    protected void append(LoggingEvent event) {
        if (event.getLevel().toString().toLowerCase().equals("error")) {
            System.out.println("-----------------Errors detected");
            this.errorsOccured = true;
        }
    }
}

The log4j configuration file has to just contain a definition of the new appender and its attachement to the selected logger (root in my case):

log4j.rootLogger = OTHER_APPENDERS, ED
log4j.appender.ED=com.your.package.ErrorsDetectingAppender

You can either call the errorsOccured() method of the ErrorsDetectingAppender at some significant point in your programs's execution flow or react immidiately by adding functionality to the if block in the append() method. This approach is consistent with the semantics: things that you consider errors and log them as such, are detected. If you will later consider selected errors not so important, you just change the logging level to log.warn() and report will not be sent.

Alex Fedulov
  • 1,442
  • 17
  • 26
0

There is simply no good reason to be informed of every thrown exception. I guess you are assuming that a thrown exception indicates a "problem" that your "need" to know about. But this is wrong. If an exception is thrown, caught and handled, all is well. The only thing you need to be worried about is an exception that is thrown but not handled (not caught). But you can do that in a try...catch clause yourself.

Raedwald
  • 46,613
  • 43
  • 151
  • 237
0

In this case I think your best bet might be to write a custom classloader to handle all classloading in your application, and whenever an exception class is requested you return a class that wraps the requested exception class. This wrapper calls through to the wrapped exception but also logs the exception event.

toluju
  • 4,097
  • 2
  • 23
  • 27
0

I assume you don't mean any Exception but rather any uncaught Exception.

If this is the case this article on the Sun Website has some ideas. You need to wrap your top level method in a try-catch block and also do some extra work to handle other Threads.

David Webb
  • 190,537
  • 57
  • 313
  • 299
0

Sending an email may not be possible if you are getting a runtime exception like OutOfMemoryError or StackOverflow. Most likely you will have to spawn another process and catch any exceptions thrown by it (with the various techniques mentioned above).

Nik
  • 255
  • 2
  • 3
  • 12
  • It's worth noting the difference between Errors and Exceptions. Both are Throwable, but Errors aren't Exceptions as they are siblings. – Cheekysoft Sep 16 '08 at 18:54