Java - Stack trace 출력하는 방법 (Throwable, Exception)

JS · 17 Jan 2020

Java에서 Throwable 객체로 콜스택을 출력할 수 있습니다. 현재 코드 위치까지 어떤 함수들을 거쳐왔는지 보여주는 것을 Stack trace라고 합니다.

Throwable의 다음 메소드들을 이용하여 Trace를 가져오거나 출력할 수 있습니다.

// Throwable.java
public void printStackTrace()
public void printStackTrace(PrintStream s)
public StackTraceElement[] getStackTrace()

Java 코드를 보면 Exception 클래스는 Throwable 클래스를 상속받습니다. 그렇기 때문에 Exception 객체도 Throwable의 메소드를 이용할 수 있습니다.

public class Exception extends Throwable {
  ...
}

Stack trace 출력

보통 try-catch 패턴으로 예외처리를 합니다. 여기서 Exception.printStackTrace()를 호출하면 로그에 Stack trace가 출력됩니다.

try {
    Exception e = new Exception();
    e.initCause(new IOException("No space memory"));
    throw e;
} catch(Exception e) {
    e.printStackTrace();
}

결과

java.lang.Exception
	at example.StackTrace$AAA.ccc(StackTrace.java:21)
	at example.StackTrace$AAA.bbb(StackTrace.java:14)
	at example.StackTrace$AAA.aaa(StackTrace.java:10)
	at example.StackTrace.main(StackTrace.java:47)
Caused by: java.io.IOException: No space memory
	at example.StackTrace$AAA.ccc(StackTrace.java:22)
	... 3 more

Throwable은 printStackTrace(PrintWriter s) 메소드도 지원합니다.

다음과 같이 PrintWriter를 인자로 전달하여 Trace를 PrintWriter에 추가할 수 있습니다.

try {
    Exception e = new Exception();
    e.initCause(new IOException("No space memory"));
    throw e;
} catch(Exception e) {
    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);

    pw.append("+++Start printing trace:\n");
    e.printStackTrace(pw);
    pw.append("---Finish printing trace");
    System.out.println(sw.toString());
}

결과

+++Start printing trace:
java.lang.Exception
	at example.StackTrace$AAA.fff(StackTrace.java:50)
	at example.StackTrace$AAA.bbb(StackTrace.java:22)
	at example.StackTrace$AAA.aaa(StackTrace.java:12)
	at example.StackTrace.main(StackTrace.java:75)
Caused by: java.io.IOException: No space memory
	at example.StackTrace$AAA.fff(StackTrace.java:51)
	... 3 more
---Finish printing trace

Exception 없이 Stack trace 출력

위의 코드들은 Exception이 발생할 때만 사용할 수 있습니다.

그냥 디버깅 목적으로 Trace를 출력하고 싶다면 아래와 같이 Throwable 객체를 생성하고 printStackTrace()를 호출하면 됩니다. throw하는 것이 아니기 때문에 로그만 출력되고 객체는 소멸됩니다.

// print stack trace
(new Throwable()).printStackTrace();

결과

java.lang.Throwable
	at example.StackTrace$AAA.eee(StackTrace.java:39)
	at example.StackTrace$AAA.bbb(StackTrace.java:16)
	at example.StackTrace$AAA.aaa(StackTrace.java:10)
	at example.StackTrace.main(StackTrace.java:47)

Trace를 출력하지 않고 String으로 가져오기

Trace를 로그에 출력하지 않고 String 객체로 가져올 수 있습니다.

아래와 같이 getStackTrace()을 호출하면, StackTraceElement 배열을 리턴합니다. StackTraceElement 객체는 Trace의 정보를 갖고 있으며, Stack이 많이 쌓일 수록 배열 길이도 길어집니다. 이 객체의 아이템들을 String으로 출력할 수 있습니다.

// get stack trace and print
StackTraceElement[] stacks = (new Throwable()).getStackTrace();
for (StackTraceElement element : stacks) {
    System.out.println(element);
}

결과

example.StackTrace$AAA.ddd(StackTrace.java:31)
example.StackTrace$AAA.bbb(StackTrace.java:15)
example.StackTrace$AAA.aaa(StackTrace.java:10)
example.StackTrace.main(StackTrace.java:47)

예제

아래 코드는 위에서 예제로 사용한 코드입니다.

package example;


import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

public class StackTrace {

    static class AAA {
        void aaa() {
            bbb();
        }

        void bbb() {
            ccc();
            sleep(1000);
            ddd();
            sleep(1000);
            eee();
            sleep(1000);
            fff();
        }

        void ccc() {
            try {
                Exception e = new Exception();
                e.initCause(new IOException("No space memory"));
                throw e;
            } catch(Exception e) {
                e.printStackTrace();
            }
        }

        void ddd() {
            // get stack trace and print
            StackTraceElement[] stacks = (new Throwable()).getStackTrace();
            for (StackTraceElement element : stacks) {
                System.out.println(element);
            }
        }

        void eee() {
            // print stack trace
            (new Throwable()).printStackTrace();
        }

        void fff() {
            try {
                Exception e = new Exception();
                e.initCause(new IOException("No space memory"));
                throw e;
            } catch(Exception e) {
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);

                pw.append("+++Start printing trace:\n");
                e.printStackTrace(pw);
                pw.append("---Finish printing trace");
                System.out.println(sw.toString());
            }
        }

        void sleep(long ms) {
            try {
                Thread.sleep(ms);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String args[]) {
        AAA a = new AAA();
        a.aaa();
    }
}

참고