semtax의 개발 일지

자바 Uncaught Exception 캐치 하기 본문

개발/Java

자바 Uncaught Exception 캐치 하기

semtax 2020. 5. 5. 14:58
반응형

개요

 

이번 포스팅에서는, 자바의 모든 예외(Exception)을 한번애 전부 캐치 할 수 있는 방법에 대해서 포스팅을 하도록 하겠다.

 

 

자바 예외 처리

 

 

자바에서는 보통 아래와 같이 try-catch 문을 이용해서 에러를 핸들링 할 수 있다.

 

public class ExampleClass {
  public static void main(String[ ] args) {
    try {
      int[] numbers = {1, 2, 3};
      System.out.println(numbers[10]);
    } catch (Exception e) {
      System.out.println("Exception is occured!");
    }
  }
}

 

 

또한, throws 를 이용해서 예외를 다른곳으로 전파 할 수도 있다.

 

(보통 해당 클래스를 호출한곳에서 예외를 처리하게 할때 많이 사용한다.)

 

class ThrowsExample
{ 
    static void fun() 
    { 
        try
        { 
            throw new NullPointerException("Null pointer reference error!"); 
        } 
        catch(NullPointerException e) 
        { 
            System.out.println("Caught inside fun()."); 
            throw e; // rethrowing the exception 
        } 
    } 

    public static void main(String args[]) 
    { 
        try
        { 
            fun(); 
        } 
        catch(NullPointerException e) 
        { 
            System.out.println("Caught in main."); 
        } 
    } 
} 

하지만, 아래와 같이 어떠한 예외도 처리하지 않는 경우에는 어떻게 될까?

public class MainClass {
    public static void main(String[] args) {
        int[] arr = new int[10];
        arr[12] = 10;
    }
}

 

 

그떄는 아래와 같은 에러메시지가 발생한다.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 12
    at MainClass.main(MainClass.java:13)

 

 

신기한것은, 우리는 예외처리를 한 것이 추가한게 없는데도 불구하고 자바에서 위와 같이 예외가 발생했을때, 에러메시지들을 찍어주는 역할을 한다는 것이다.

 

 

그렇다면 자바에서는 위와 같이, 사용자가 어떠한 예외도 처리하지 않는 경우에 위와 같이 예외를 어떻게 처리할 수 있는 것일까?

한 번, 해당 방법에 대해서 알아보도록 하자.

 

 

Uncaught Exception Handler

 

 

정답은, 자바의 Thread 클래스에 있는 UncaughtExceptionHandler 라는 핸들러에 의해 기본적으로 등록된 핸들러에서 나머지 에러를 처리하게 된다.

 

 

따로 UncaughtExceptionHandler 를 설정해주지 않은 경우, 자바에서 기본적으로 정의된 UncaughtExceptionHandler 가 실행이 된다.

 

 

또한, 위의 DefaultUncaughtExceptionHandler 를 아래와 같이 직접 정의해서 사용 가능하다.

 

아래와 같이 정의해서 사용 할 수 있다.

 

public class DefaultExceptionHandler implements Thread.UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {

        StackTraceElement[] stackTraceElements = t.getStackTrace();
        StackTraceElement[] exceptionStackTraceElements = e.getStackTrace();

        System.out.println("Thread Name : " + t.getName());
        System.out.println("=========================================");
        for(StackTraceElement element : stackTraceElements) {
            System.out.println("class name : " + element.getClassName());
            System.out.println("file name : " + element.getFileName());
            System.out.println("method name : " + element.getMethodName());
            System.out.println("line number : " + element.getLineNumber());
        }

        System.out.println("=========================================");
        System.out.println("Exception Message : " + e.getMessage());
        System.out.println("Exception Localized Message : " + e.getLocalizedMessage());
        System.out.println("Exception Cause : " + e.getCause());
        for(StackTraceElement element : exceptionStackTraceElements) {
            System.out.println("=========================================");
            System.out.println("exception class name : " + element.getClassName());
            System.out.println("exception file name : " + element.getFileName());
            System.out.println("exception method name : " + element.getMethodName());
            System.out.println("exception line number : " + element.getLineNumber());
            System.out.println("=========================================");
        }
    }
}

 

위의 UncaughtExpcetionHandler 를 통해, 해당 예외가 발생한 스레드의 전체 스택트레이스, 스레드 이름, 예외가 발생한 함수의 스택 트레이스, 에러메시지, 에러가 발생한 클래스이름, 메소드 이름, 파일 이름 등을 얻을 수 있다.

 

 

또한, 자바 디버깅 심볼 데이터가 살아있는 경우, 해당 에러가 발생한 라인 번호도 얻을 수 있다.

(만약 디버깅 심볼 데이터가 없는 경우 Unknown 이라고 뜬다.)

 

 

위에서 정의한 핸들러를 등록하는 방법은 아래와 같다.

 

Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler());

 

 

 

아래와 같이 사용해보도록 하자.

 

public class MainClass {
    public static void main(String[] args) throws InterruptedException{

        Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler());

        AnotherTask task1 = new AnotherTask();
        Thread t1 = new Thread(task1);
        t1.start();

        while(true) {

        }
    }
}

 

 

 

그러면 다음과 같은 메시지를 얻을 수 있다.

 

Thread Name : Thread-0
=========================================
class name : java.lang.Thread
file name : Thread.java
method name : getStackTrace
line number : 1559
class name : DefaultExceptionHandler
file name : DefaultExceptionHandler.java
method name : uncaughtException
line number : 6
class name : java.lang.ThreadGroup
file name : ThreadGroup.java
method name : uncaughtException
line number : 1057
class name : java.lang.ThreadGroup
file name : ThreadGroup.java
method name : uncaughtException
line number : 1052
class name : java.lang.Thread
file name : Thread.java
method name : dispatchUncaughtException
line number : 1959
=========================================
Exception Message : / by zero
Exception Localized Message : / by zero
Exception Cause : null
=========================================
exception class name : AnotherTask
exception file name : AnotherTask.java
exception method name : run
exception line number : 4
=========================================
=========================================
exception class name : java.lang.Thread
exception file name : Thread.java
exception method name : run
exception line number : 748
=========================================

 

 

 

활용

 

 

보통, Sentry와 같은 에러 트래킹 솔루션에서 위와 같은 방법을 사용해서 에러 트래킹을 수행한다.

 

https://github.com/getsentry/sentry-java/blob/714dbaa4761173493a6af938e0cd88d63776e98d/sentry/src/main/java/io/sentry/SentryUncaughtExceptionHandler.java

 

getsentry/sentry-java

A Sentry SDK for Java and other JVM languages. Contribute to getsentry/sentry-java development by creating an account on GitHub.

github.com

 

반응형
Comments