Java : StackWalker (スタックトレース) - API使用例

StackWalker (Java SE 18 & JDK 18) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。


概要

スタック・ウォーカ。

クラス構成

StackWalker は、現在のスレッドのスタックトレース(StackFrame) を取得するためのクラスです。

public class Main {

    public static void main(String[] args) {
        func();
    }

    private static void func() {
        final var foo = new Foo();
        foo.aaa();
    }
}
public class Foo {
    public void aaa() {
        bbb();
    }

    private void bbb() {
        final var sw = StackWalker.getInstance();

        System.out.println("-- forEach --");
        sw.forEach(System.out::println);

        // 結果
        // ↓
        //-- forEach --
        //Foo.bbb(Foo.java:12)
        //Foo.aaa(Foo.java:5)
        //Main.func(Main.java:11)
        //Main.main(Main.java:6)

        System.out.println("-- forEach --");
        sw.forEach(action -> {
            System.out.println(" -- action --");
            System.out.println("  file : " + action.getFileName());
            System.out.println("  line :" + action.getLineNumber());
            System.out.println("  class :" + action.getClassName());
            System.out.println("  method :" + action.getMethodName());
        });

        // 結果
        // ↓
        //-- forEach --
        // -- action --
        //  file : Foo.java
        //  line :22
        //  class :Foo
        //  method :bbb
        // -- action --
        //  file : Foo.java
        //  line :5
        //  class :Foo
        //  method :aaa
        // -- action --
        //  file : Main.java
        //  line :11
        //  class :Main
        //  method :func
        // -- action --
        //  file : Main.java
        //  line :6
        //  class :Main
        //  method :main
    }
}

メソッド

void forEach (Consumer<? super StackWalker.StackFrame> action)

現在のスレッドのStackFrameストリームの各要素に対して、このforEachメソッドを呼び出すメソッドである、スタックの先頭フレームを走査して、指定されたアクションを実行します。

public class Main {

    public static void main(String[] args) {
        func1();
    }

    private static void func1() {
        func2();
    }

    private static void func2() {
        final var sw = StackWalker.getInstance();

        System.out.println("-- forEach --");
        sw.forEach(System.out::println);

        // 結果
        // ↓
        //-- forEach --
        //Main.func2(Main.java:17)
        //Main.func1(Main.java:10)
        //Main.main(Main.java:6)
    }
}

Class<?> getCallerClass ()

getCallerClassを呼び出したメソッドを呼び出した呼び出し元のClassオブジェクトを取得します。

public class Main {

    public static void main(String[] args) {

        final var foo = new Foo();
        foo.func();

        // 結果
        // ↓
        //Foo : getCallerClass = class Main
        //Bar : getCallerClass = class Foo
    }
}
public class Foo {

    public void func() {
        final var sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
        System.out.println("Foo : getCallerClass = " + sw.getCallerClass());

        final var bar = new Bar();
        bar.func();
    }
}
public class Bar {

    public void func() {
        final var sw = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
        System.out.println("Bar : getCallerClass = " + sw.getCallerClass());
    }
}
class A {
    void func() {
        final var sw = StackWalker.getInstance();
        try {
            final var ret = sw.getCallerClass();
        } catch (UnsupportedOperationException e) {
            System.out.println(e);
        }
    }
}

class B {
    void func() {
        new A().func();
    }
}

new B().func();

// 結果
// ↓
//java.lang.UnsupportedOperationException:
//  This stack walker does not have RETAIN_CLASS_REFERENCE access

static StackWalker getInstance ()

StackWalkerインスタンスを返します。

このメソッドの使用例は、forEach(Consumer<? super StackWalker.StackFrame> action) にまとめて記載しました。
そちらのAPI使用例をご参照ください。

static StackWalker getInstance (StackWalker.Option option)

指定されたオプションを持つStackWalkerインスタンスを返します。このインスタンスは、アクセスできるスタック・フレーム情報を指定します。

このメソッドの使用例は、getCallerClass() にまとめて記載しました。
そちらのAPI使用例をご参照ください。

static StackWalker getInstance (Set<StackWalker.Option> options)

指定されたoptionsを持つStackWalkerインスタンスを返します。このインスタンスは、アクセス可能なスタック・フレーム情報を指定します。

import java.lang.reflect.InvocationTargetException;

public class Main {

    public static void main(String[] args) throws NoSuchMethodException,
            InvocationTargetException, IllegalAccessException {

        final var foo = new Foo();

        final var method = Foo.class.getDeclaredMethod("func");
        method.invoke(foo);

        // 結果
        // ↓
        //-- with options --
        //Foo.func(Foo.java:16)
        //java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
        //java.base/java.lang.reflect.Method.invoke(Method.java:577)
        //Main.main(Main.java:13)
        //getCallerClass : class Main
        //
        //-- no options --
        //Foo.func(Foo.java:24)
        //Main.main(Main.java:13)
    }
}
import java.util.Set;

public class Foo {

    public void func() {
        {
            final var options = Set.of(
                    StackWalker.Option.SHOW_REFLECT_FRAMES,
                    StackWalker.Option.RETAIN_CLASS_REFERENCE
            );

            System.out.println("-- with options --");
            final var sw = StackWalker.getInstance(options);
            sw.forEach(System.out::println);

            System.out.println("getCallerClass : " + sw.getCallerClass());
        }

        {
            System.out.println("-- no options --");
            final var sw = StackWalker.getInstance();
            sw.forEach(System.out::println);
        }
    }
}

static StackWalker getInstance (Set<StackWalker.Option> options, int estimateDepth)

指定されたoptionsを持つStackWalkerインスタンスを返します。このインスタンスは、アクセス可能なスタック・フレーム情報を指定します。

関連:getInstance(Set<StackWalker.Option> options)

public class Main {

    public static void main(String[] args) {

        new Foo().func1();

        // 結果
        // ↓
        //-- forEach --
        //Foo.func2(Foo.java:18)
        //Foo.func1(Foo.java:8)
        //Main.main(Main.java:7)
        //getCallerClass : class Foo
    }
}
import java.util.Set;

public class Foo {

    public void func1() {
        func2();
    }

    private void func2() {
        final var options = Set.of(
                StackWalker.Option.RETAIN_CLASS_REFERENCE
        );

        System.out.println("-- forEach --");
        final var sw = StackWalker.getInstance(options, 5);
        sw.forEach(System.out::println);

        System.out.println("getCallerClass : " + sw.getCallerClass());
    }
}

<T> T walk (Function<? super Stream<StackWalker.StackFrame>,? extends T> function)

指定された関数を、現在のスレッドのStackFrameストリームに適用します。この関数は、このwalkメソッドを呼び出すメソッドです。

public class Main {

    public static void main(String[] args) {

        aaa1();

        // 結果
        // ↓
        //-- forEach --
        //Main.bbb2(Main.java:39)
        //Main.bbb1(Main.java:31)
        //Main.aaa2(Main.java:27)
        //Main.aaa1(Main.java:23)
        //Main.main(Main.java:7)
        //-- walk --
        //Main.aaa2(Main.java:27)
        //Main.aaa1(Main.java:23)
    }

    private static void aaa1() {
        aaa2();
    }

    private static void aaa2() {
        bbb1();
    }

    private static void bbb1() {
        bbb2();
    }

    private static void bbb2() {

        final var sw = StackWalker.getInstance();

        System.out.println("-- forEach --");
        sw.forEach(System.out::println);

        System.out.println("-- walk --");
        final var list = sw.walk(
                stream -> stream.filter(
                        stackFrame -> stackFrame.getMethodName().startsWith("aaa")
                ).toList()
        );

        list.forEach(System.out::println);
    }
}

関連記事

ページの先頭へ