How to create an Elastic APM Java plugin for Neo4J?

We are trying to create an Elastic APM Java plugin that supports Neo4J's Bolt protocol.
However, we are new to Java, and have been trying to create one by asking questions on ChatGPT, etc., but with no success.
If you know of any useful websites or examples, please let me know.

The sources we are currently referring to are below:

We are writing code like below, but it seems that we are not able to hook the session properly.
Please let me know if there is anything wrong or missing.

package com.mycompany.apm;

import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import co.elastic.apm.agent.sdk.ElasticApmInstrumentation;
import co.elastic.apm.api.ElasticApm;
import co.elastic.apm.api.Span;

import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import net.bytebuddy.matcher.ElementMatchers;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import java.util.Arrays;
import java.util.Collection;

public class Neo4jPlugin extends ElasticApmInstrumentation {

    @Override
    public ElementMatcher<? super TypeDescription> getTypeMatcher() {
        return named("org.neo4j.driver.Session");
    }

    @Override
    public ElementMatcher<? super MethodDescription> getMethodMatcher() {
        return named("run").and( takesArguments( 1 ))
                .and( takesArgument( 0, named("java.lang.String")));
    }

    @Override
    public String getAdviceClassName() {
        // アドバイスクラスの名前を返す
        return "com.mycompany.apm.Neo4jPlugin$RunAdvice";
    }

    @Override
    public Collection<String> getInstrumentationGroupNames() {
        return Arrays.asList("neo4j", "db");
    }

    public static class RunAdvice {
        @Advice.OnMethodEnter(suppress = Throwable.class)
        public static void onEnter(@Advice.Origin("#t.#m") String method,
                                   @Advice.Argument(0) String query,
                                   @Advice.Local("span") co.elastic.apm.api.Span span) {
            span = ElasticApm.currentTransaction().startSpan("db", "neo4j", "query");
            span.setName("Neo4j Query");
            span.setLabel("query", query);
        }

        @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
        public static void onExit(@Advice.Local("span") Span span,
                                  @Advice.Thrown Throwable thrown) {
            if (span != null) {
                if (thrown != null) {
                    span.captureException(thrown);
                }
                span.end();
            }
        }
    }
}

With your help, we are now able to get some Neo4J metrics.
However, there is still a problem.
The metrics we get are not traced correctly on kibana.
While a normal trace would allow kibana to graphically show us class and method migrations, the trace we get only shows us the Neo4J access itself.
Moreover, the metric that Elasticsearch is supposed to retrieve for us is also missing.
Any insights would be appreciated.

Where we have run the following setup,
java -javaagent:/opt/elastic-apm/elastic-apm-agent-1.43.1-SNAPSHOT.jar -Delastic.apm.plugins_dir=/opt/elastic-apm/plugins -Delastic.apm.server_urls="My Endpoint URL" -Delastic.apm.secret_token=My Token -Delastic.apm.service_name=Neo4j-linux-test -Delastic.apm.application_packages=com.example.App -Delastic.apm.enable_instrumentations=all -jar ./target/neo4j-example-1.0-SNAPSHOT.jar

The following error occurs:
2024-06-27 10:04:56,292 [main] WARN co.elastic.apm.agent.bci.bytebuddy.InstallationListenerImpl - Byte Buddy warmup ended without transforming at least one class. The agent may not work as expected.
Can you figure out what caused it and what to do about it?

WE DID IT!
We have successfully placed the span under the transaction.
The key point was in currentTransaction().

But we also hit a new wall.
We tried to place the actively obtained stacktrace in span.stacktrace, but could not find a way to place it.

Does anyone know how to do this?