Java - Handling exceptions globally with UncaughtExceptionHandler

[Updated: May 21, 2017, Created: May 20, 2017]

Since Java 5, we can set a default exception handler which will be called for all unhandled exceptions. This handler can be set by using following static method of java.lang.Thread:

public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

We have to provide an implementation of the interface Thread.UncaughtExceptionHandler, which has only one method:

  @FunctionalInterface
  public interface UncaughtExceptionHandler {
       void uncaughtException(Thread t, Throwable e);
  }

Examples

The following simple example shows how to use this feature:

public class Example1 {

    public static void main(String[] args) throws Exception {
        Thread.setDefaultUncaughtExceptionHandler(new MyHandler());

        throw new Exception("A test exception");
    }

    private static final class MyHandler implements Thread.UncaughtExceptionHandler {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            System.out.printf("exception caught:" + e);
        }
    }
}

Output

exception caught:java.lang.Exception: A test exception

Customizing stacktrace

This feature can be used in a lot of ways. Following example shows how to use it to show only root cause of the exception.

In this example the source exception is wrapped multiple times. First, we are not going to use a customized UncaughtExceptionHandler to see the difference:

public class Example2 {

    public static void main(String[] args) throws Exception {
        method1();
    }

    static void method1() throws Exception {
        try {
            method2();
        } catch (Exception e) {
            throw new Exception("rethrowing from method1()", e);
        }
    }

    private static void method2() throws Exception {
        try {
            method3();
        } catch (Exception e) {
            throw new Exception("rethrowing from method2()", e);
        }
    }

    private static void method3() throws Exception {
        try {
            test4();
        } catch (Exception e) {
            throw new Exception("rethrowing from method3()", e);
        }
    }

    private static void test4() throws Exception {
        throw new Exception("root exception from method4()");
    }
}

Output

Exception in thread "main" java.lang.Exception: rethrowing from method1()
at com.logicbig.example.Example2.method1(Example2.java:13)
at com.logicbig.example.Example2.main(Example2.java:6)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.Exception: rethrowing from method2()
at com.logicbig.example.Example2.method2(Example2.java:21)
at com.logicbig.example.Example2.method1(Example2.java:11)
... 6 more
Caused by: java.lang.Exception: rethrowing from method3()
at com.logicbig.example.Example2.method3(Example2.java:29)
at com.logicbig.example.Example2.method2(Example2.java:19)
... 7 more
Caused by: java.lang.Exception: root exception from method4()
at com.logicbig.example.Example2.test4(Example2.java:34)
at com.logicbig.example.Example2.method3(Example2.java:27)
... 8 more

Now let's use a UncaughtExceptionHandler to show only root cause stacktrace:

public class Example3 {

    public static void main(String[] args) throws Exception {
        Thread.setDefaultUncaughtExceptionHandler(new MyHandler());
        Example2.method1();
    }

    private static final class MyHandler implements Thread.UncaughtExceptionHandler {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            Throwable rootCause = ExceptionUtils.getRootCause(e);
            rootCause.printStackTrace(System.err);
        }
    }
}

Output

java.lang.Exception: root exception from method4()
at com.logicbig.example.Example2.test4(Example2.java:34)
at com.logicbig.example.Example2.method3(Example2.java:27)
at com.logicbig.example.Example2.method2(Example2.java:19)
at com.logicbig.example.Example2.method1(Example2.java:11)
at com.logicbig.example.Example3.main(Example3.java:9)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

Note that, in above example we used ExceptionUtils of apache-commons (dependency included in project browser below).

When it doesn't work?

  • If another handler is set (by using static method Thread.setUncaughtExceptionHandler) in other part of the application after us.
  • If exception is handled.
  • In a servlet container (like tomcat) where exception is handled and redirected to a servlet etc to show a web-based error message. There we need to use error mapping per servlet specification instead.

Example Project

Dependencies and Technologies Used :

  • commons-lang3 3.5: Apache Commons Lang, a package of Java utility classes for the classes that are in java.lang's hierarchy, or are considered to be so standard as to justify existence in java.lang.
  • JDK 1.8
  • Maven 3.3.9

Uncaught Exception Handler Example Select All Download
  • java-uncaught-exception-handler
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also