スタックトレースから呼び出し元の情報を取得する
javaでプログラムを組んでいると、メソッドがどこから呼び出されたのかを知りたい時があります。
そんな時は、スタックトレース情報を取得して呼び出し元情報を取得することが出来ます。
スタックトレース情報を取得するにはThrowable#getStackTrace()
を利用します。
StackTraceElement[] ste = new Throwable().getStackTrace();
for (int i = 0; i < ste.length; i++) {
System.out.println(ste[i].getClassName()); // クラス名を取得
System.out.println(ste[i].getMethodName()); // メソッド名を取得
System.out.println(ste[i].getFileName()); // ファイル名を取得
System.out.println(ste[i].getLineNumber()); // 行番号を取得(nativeメソッドの場合は取得できない)
System.out.println(ste[i].isNativeMethod()); // nativeメソッドか判定する。
System.out.println(ste[i]); // スタックトレースの情報を整形して表示
}
Throwable#getStackTrace()
の代わりにThread#getStackTrace()
を使うこともできます。
StackTraceElement[] ste = Thread.currentThread().getStackTrace();
ただし、2つの方法で取得したスタックトレース情報には若干の違いがあります。
サンプルプログラムを実行して確認してみます。
package sample;
public class Main1 {
public static void main(String[] args) {
SampleClass1 sc = new SampleClass1();
sc.method1();
}
}
class SampleClass1 {
public void method1() {
method2();
}
public void method2() {
System.out.println("Throwable#getStackTrace()を利用してスタックトレース情報を取得");
StackTraceElement[] ste = new Throwable().getStackTrace();
for (int i = 0; i < ste.length; i++) {
System.out.println(ste[i]);
}
System.out.println("\nThread#getStackTrace()を利用してスタックトレース情報を取得");
ste = Thread.currentThread().getStackTrace();
for (int i = 0; i < ste.length; i++) {
System.out.println(ste[i]);
}
}
}
サンプルプログラムを実行した結果は以下のようになります。
Throwable#getStackTrace()を利用してスタックトレース情報を取得
sample.SampleClass1.method2(Main1.java:18)
sample.SampleClass1.method1(Main1.java:13)
sample.Main1.main(Main1.java:7)
Thread#getStackTrace()を利用してスタックトレース情報を取得
java.lang.Thread.getStackTrace(Thread.java:1556)
sample.SampleClass1.method2(Main1.java:24)
sample.SampleClass1.method1(Main1.java:13)
sample.Main1.main(Main1.java:7)
Thread#getStackTrace()
を利用した場合、スタックトレース情報が1行多くなります。
大した違いではありませんのでどちらを利用してもよいと思います。
スタックトレース情報を取得する際の注意点
取得できるスタックトレース情報には上限があります。
つまり、取得したStackTraceElement[]
に全てのスタックトレース情報が含まれているとは限りません。
以下のサンプルプログラムを実行して確認してみましょう。
package sample;
public class Main2 {
public static void main(String[] args) {
SampleClass2 sc = new SampleClass2();
sc.printStackTrace(200);
sc.printStackTrace(500);
sc.printStackTrace(1000);
sc.printStackTrace(2000);
sc.printStackTrace(3000);
}
}
class SampleClass2 {
public void printStackTrace(int stackCount) {
if (stackCount > 2) {
printStackTrace(stackCount - 1);
} else {
StackTraceElement[] ste = new Throwable().getStackTrace();
System.out.println("ste.length=" + ste.length);
}
}
}
実行結果はJVMによって変わりますが、私の環境では以下のように出力されます。
ste.length=200
ste.length=500
ste.length=1000
ste.length=1024
ste.length=1024
取得できるスタックトレース情報の上限数が1024
となっていることが分かります。
つまり、呼び出し元を大元まで遡れるとは限らないということになります。