Add test for hibernate-reactive (#9225)
This commit is contained in:
parent
2f8db1b443
commit
42e2f738cc
|
@ -25,7 +25,7 @@ public class HibernateInstrumentationModule extends InstrumentationModule {
|
|||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
|
||||
return hasClassesNamed(
|
||||
// not present before 6.0
|
||||
"org.hibernate.query.Query");
|
||||
"org.hibernate.query.spi.SqmQuery");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,10 +15,67 @@ dependencies {
|
|||
library("io.vertx:vertx-sql-client:4.0.0")
|
||||
compileOnly("io.vertx:vertx-codegen:4.0.0")
|
||||
|
||||
// for hibernateReactive2Test
|
||||
testInstrumentation(project(":instrumentation:hibernate:hibernate-6.0:javaagent"))
|
||||
|
||||
testLibrary("io.vertx:vertx-pg-client:4.0.0")
|
||||
testLibrary("io.vertx:vertx-codegen:4.0.0")
|
||||
}
|
||||
|
||||
tasks.withType<Test>().configureEach {
|
||||
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
|
||||
val latestDepTest = findProperty("testLatestDeps") as Boolean
|
||||
|
||||
testing {
|
||||
suites {
|
||||
val hibernateReactive1Test by registering(JvmTestSuite::class) {
|
||||
dependencies {
|
||||
implementation("org.testcontainers:testcontainers")
|
||||
if (latestDepTest) {
|
||||
implementation("org.hibernate.reactive:hibernate-reactive-core:1.+")
|
||||
implementation("io.vertx:vertx-pg-client:+")
|
||||
} else {
|
||||
implementation("org.hibernate.reactive:hibernate-reactive-core:1.0.0.Final")
|
||||
implementation("io.vertx:vertx-pg-client:4.1.5")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val hibernateReactive2Test by registering(JvmTestSuite::class) {
|
||||
dependencies {
|
||||
implementation("org.testcontainers:testcontainers")
|
||||
if (latestDepTest) {
|
||||
implementation("org.hibernate.reactive:hibernate-reactive-core:2.+")
|
||||
implementation("io.vertx:vertx-pg-client:+")
|
||||
} else {
|
||||
implementation("org.hibernate.reactive:hibernate-reactive-core:2.0.0.Final")
|
||||
implementation("io.vertx:vertx-pg-client:4.4.2")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
withType<Test>().configureEach {
|
||||
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
|
||||
}
|
||||
named("compileHibernateReactive2TestJava", JavaCompile::class).configure {
|
||||
options.release.set(11)
|
||||
}
|
||||
val testJavaVersion =
|
||||
gradle.startParameter.projectProperties.get("testJavaVersion")?.let(JavaVersion::toVersion)
|
||||
?: JavaVersion.current()
|
||||
if (testJavaVersion.isJava8) {
|
||||
named("hibernateReactive2Test", Test::class).configure {
|
||||
enabled = false
|
||||
}
|
||||
if (latestDepTest) {
|
||||
named("hibernateReactive1Test", Test::class).configure {
|
||||
enabled = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check {
|
||||
dependsOn(testing.suites)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.hibernate.reactive.v1_0;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_NAME;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_OPERATION;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_SQL_TABLE;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_STATEMENT;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_USER;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_NAME;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_PORT;
|
||||
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.Persistence;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
import org.hibernate.reactive.stage.Stage;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.output.Slf4jLogConsumer;
|
||||
|
||||
class HibernateReactiveTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(HibernateReactiveTest.class);
|
||||
|
||||
private static final String USER_DB = "SA";
|
||||
private static final String PW_DB = "password123";
|
||||
private static final String DB = "tempdb";
|
||||
|
||||
@RegisterExtension
|
||||
protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
private static GenericContainer<?> container;
|
||||
private static int port;
|
||||
private static EntityManagerFactory entityManagerFactory;
|
||||
private static Mutiny.SessionFactory mutinySessionFactory;
|
||||
private static Stage.SessionFactory stageSessionFactory;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() {
|
||||
container =
|
||||
new GenericContainer<>("postgres:9.6.8")
|
||||
.withEnv("POSTGRES_USER", USER_DB)
|
||||
.withEnv("POSTGRES_PASSWORD", PW_DB)
|
||||
.withEnv("POSTGRES_DB", DB)
|
||||
.withExposedPorts(5432)
|
||||
.withLogConsumer(new Slf4jLogConsumer(logger))
|
||||
.withStartupTimeout(Duration.ofMinutes(2));
|
||||
container.start();
|
||||
|
||||
port = container.getMappedPort(5432);
|
||||
System.setProperty("db.port", String.valueOf(port));
|
||||
|
||||
entityManagerFactory = Persistence.createEntityManagerFactory("test-pu");
|
||||
|
||||
Value value = new Value("name");
|
||||
value.setId(1L);
|
||||
|
||||
mutinySessionFactory = entityManagerFactory.unwrap(Mutiny.SessionFactory.class);
|
||||
stageSessionFactory = entityManagerFactory.unwrap(Stage.SessionFactory.class);
|
||||
|
||||
mutinySessionFactory
|
||||
.withTransaction((session, tx) -> session.merge(value))
|
||||
.await()
|
||||
.atMost(Duration.ofSeconds(30));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void cleanUp() {
|
||||
if (entityManagerFactory != null) {
|
||||
entityManagerFactory.close();
|
||||
}
|
||||
if (mutinySessionFactory != null) {
|
||||
mutinySessionFactory.close();
|
||||
}
|
||||
if (stageSessionFactory != null) {
|
||||
stageSessionFactory.close();
|
||||
}
|
||||
container.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMutiny() {
|
||||
testing.runWithSpan(
|
||||
"parent",
|
||||
() -> {
|
||||
mutinySessionFactory
|
||||
.withSession(
|
||||
session ->
|
||||
session
|
||||
.find(Value.class, 1L)
|
||||
.invoke(
|
||||
value -> {
|
||||
testing.runWithSpan("callback", () -> {});
|
||||
}))
|
||||
.await()
|
||||
.atMost(Duration.ofSeconds(30));
|
||||
});
|
||||
|
||||
assertTrace();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStage() throws Exception {
|
||||
testing
|
||||
.runWithSpan(
|
||||
"parent",
|
||||
() ->
|
||||
stageSessionFactory
|
||||
.withSession(
|
||||
session ->
|
||||
session
|
||||
.find(Value.class, 1L)
|
||||
.thenAccept(
|
||||
value -> {
|
||||
testing.runWithSpan("callback", () -> {});
|
||||
}))
|
||||
.toCompletableFuture())
|
||||
.get(30, TimeUnit.SECONDS);
|
||||
|
||||
assertTrace();
|
||||
}
|
||||
|
||||
private static void assertTrace() {
|
||||
testing.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL),
|
||||
span ->
|
||||
span.hasName("SELECT tempdb.Value")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(DB_NAME, DB),
|
||||
equalTo(DB_USER, USER_DB),
|
||||
equalTo(
|
||||
DB_STATEMENT,
|
||||
"select value0_.id as id1_0_0_, value0_.name as name2_0_0_ from Value value0_ where value0_.id=$?"),
|
||||
equalTo(DB_OPERATION, "SELECT"),
|
||||
equalTo(DB_SQL_TABLE, "Value"),
|
||||
equalTo(NET_PEER_NAME, "localhost"),
|
||||
equalTo(NET_PEER_PORT, port)),
|
||||
span ->
|
||||
span.hasName("callback")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.hibernate.reactive.v1_0;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table
|
||||
public class Value {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
public Value() {}
|
||||
|
||||
public Value(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String title) {
|
||||
name = title;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
|
||||
version="2.0">
|
||||
|
||||
<persistence-unit name="test-pu">
|
||||
<provider>org.hibernate.reactive.provider.ReactivePersistenceProvider</provider>
|
||||
<class>io.opentelemetry.javaagent.instrumentation.hibernate.reactive.v1_0.Value</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
|
||||
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:${db.port}/tempdb"/>
|
||||
<property name="jakarta.persistence.jdbc.user" value="SA"/>
|
||||
<property name="jakarta.persistence.jdbc.password" value="password123"/>
|
||||
<property name="hibernate.connection.pool_size" value="10"/>
|
||||
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.hibernate.reactive.v2_0;
|
||||
|
||||
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_NAME;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_OPERATION;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_SQL_TABLE;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_STATEMENT;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.DB_USER;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_NAME;
|
||||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NET_PEER_PORT;
|
||||
|
||||
import io.opentelemetry.api.trace.SpanKind;
|
||||
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.persistence.Persistence;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.hibernate.reactive.mutiny.Mutiny;
|
||||
import org.hibernate.reactive.stage.Stage;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.testcontainers.containers.GenericContainer;
|
||||
import org.testcontainers.containers.output.Slf4jLogConsumer;
|
||||
|
||||
class HibernateReactiveTest {
|
||||
private static final Logger logger = LoggerFactory.getLogger(HibernateReactiveTest.class);
|
||||
|
||||
private static final String USER_DB = "SA";
|
||||
private static final String PW_DB = "password123";
|
||||
private static final String DB = "tempdb";
|
||||
|
||||
@RegisterExtension
|
||||
protected static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
|
||||
|
||||
private static GenericContainer<?> container;
|
||||
private static int port;
|
||||
private static EntityManagerFactory entityManagerFactory;
|
||||
private static Mutiny.SessionFactory mutinySessionFactory;
|
||||
private static Stage.SessionFactory stageSessionFactory;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() {
|
||||
container =
|
||||
new GenericContainer<>("postgres:9.6.8")
|
||||
.withEnv("POSTGRES_USER", USER_DB)
|
||||
.withEnv("POSTGRES_PASSWORD", PW_DB)
|
||||
.withEnv("POSTGRES_DB", DB)
|
||||
.withExposedPorts(5432)
|
||||
.withLogConsumer(new Slf4jLogConsumer(logger))
|
||||
.withStartupTimeout(Duration.ofMinutes(2));
|
||||
container.start();
|
||||
|
||||
port = container.getMappedPort(5432);
|
||||
System.setProperty("db.port", String.valueOf(port));
|
||||
|
||||
entityManagerFactory = Persistence.createEntityManagerFactory("test-pu");
|
||||
|
||||
Value value = new Value("name");
|
||||
value.setId(1L);
|
||||
|
||||
mutinySessionFactory = entityManagerFactory.unwrap(Mutiny.SessionFactory.class);
|
||||
stageSessionFactory = entityManagerFactory.unwrap(Stage.SessionFactory.class);
|
||||
|
||||
mutinySessionFactory
|
||||
.withTransaction((session, tx) -> session.merge(value))
|
||||
.await()
|
||||
.atMost(Duration.ofSeconds(30));
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void cleanUp() {
|
||||
if (entityManagerFactory != null) {
|
||||
entityManagerFactory.close();
|
||||
}
|
||||
if (mutinySessionFactory != null) {
|
||||
mutinySessionFactory.close();
|
||||
}
|
||||
if (stageSessionFactory != null) {
|
||||
stageSessionFactory.close();
|
||||
}
|
||||
container.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMutiny() {
|
||||
testing.runWithSpan(
|
||||
"parent",
|
||||
() -> {
|
||||
mutinySessionFactory
|
||||
.withSession(
|
||||
session ->
|
||||
session
|
||||
.find(Value.class, 1L)
|
||||
.invoke(
|
||||
value -> {
|
||||
testing.runWithSpan("callback", () -> {});
|
||||
}))
|
||||
.await()
|
||||
.atMost(Duration.ofSeconds(30));
|
||||
});
|
||||
|
||||
assertTrace();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testStage() throws Exception {
|
||||
testing
|
||||
.runWithSpan(
|
||||
"parent",
|
||||
() ->
|
||||
stageSessionFactory
|
||||
.withSession(
|
||||
session ->
|
||||
session
|
||||
.find(Value.class, 1L)
|
||||
.thenAccept(
|
||||
value -> {
|
||||
testing.runWithSpan("callback", () -> {});
|
||||
}))
|
||||
.toCompletableFuture())
|
||||
.get(30, TimeUnit.SECONDS);
|
||||
|
||||
assertTrace();
|
||||
}
|
||||
|
||||
private static void assertTrace() {
|
||||
testing.waitAndAssertTraces(
|
||||
trace ->
|
||||
trace.hasSpansSatisfyingExactly(
|
||||
span -> span.hasName("parent").hasKind(SpanKind.INTERNAL),
|
||||
span ->
|
||||
span.hasName("SELECT tempdb.Value")
|
||||
.hasKind(SpanKind.CLIENT)
|
||||
.hasParent(trace.getSpan(0))
|
||||
.hasAttributesSatisfyingExactly(
|
||||
equalTo(DB_NAME, DB),
|
||||
equalTo(DB_USER, USER_DB),
|
||||
equalTo(
|
||||
DB_STATEMENT,
|
||||
"select v1_0.id,v1_0.name from Value v1_0 where v1_0.id=$?"),
|
||||
equalTo(DB_OPERATION, "SELECT"),
|
||||
equalTo(DB_SQL_TABLE, "Value"),
|
||||
equalTo(NET_PEER_NAME, "localhost"),
|
||||
equalTo(NET_PEER_PORT, port)),
|
||||
span ->
|
||||
span.hasName("callback")
|
||||
.hasKind(SpanKind.INTERNAL)
|
||||
.hasParent(trace.getSpan(0))));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright The OpenTelemetry Authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package io.opentelemetry.javaagent.instrumentation.hibernate.reactive.v2_0;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table
|
||||
public class Value {
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
|
||||
public Value() {}
|
||||
|
||||
public Value(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Id
|
||||
@GeneratedValue
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String title) {
|
||||
name = title;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
|
||||
version="2.0">
|
||||
|
||||
<persistence-unit name="test-pu">
|
||||
<provider>org.hibernate.reactive.provider.ReactivePersistenceProvider</provider>
|
||||
<class>io.opentelemetry.javaagent.instrumentation.hibernate.reactive.v2_0.Value</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
|
||||
<properties>
|
||||
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver" />
|
||||
<property name="jakarta.persistence.jdbc.url" value="jdbc:postgresql://localhost:${db.port}/tempdb"/>
|
||||
<property name="jakarta.persistence.jdbc.user" value="SA"/>
|
||||
<property name="jakarta.persistence.jdbc.password" value="password123"/>
|
||||
<property name="hibernate.connection.pool_size" value="10"/>
|
||||
<property name="jakarta.persistence.schema-generation.database.action" value="drop-and-create"/>
|
||||
</properties>
|
||||
</persistence-unit>
|
||||
</persistence>
|
|
@ -5,36 +5,53 @@
|
|||
|
||||
package io.opentelemetry.javaagent.instrumentation.vertx.v4_0.sql;
|
||||
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
|
||||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
|
||||
import static io.opentelemetry.javaagent.instrumentation.vertx.v4_0.sql.VertxSqlClientSingletons.setSqlConnectOptions;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
|
||||
|
||||
import io.opentelemetry.instrumentation.api.util.VirtualField;
|
||||
import io.opentelemetry.javaagent.bootstrap.CallDepth;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
|
||||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.sqlclient.Pool;
|
||||
import io.vertx.sqlclient.SqlConnectOptions;
|
||||
import io.vertx.sqlclient.SqlConnection;
|
||||
import net.bytebuddy.asm.Advice;
|
||||
import net.bytebuddy.description.type.TypeDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
public class PoolInstrumentation implements TypeInstrumentation {
|
||||
|
||||
@Override
|
||||
public ElementMatcher<ClassLoader> classLoaderOptimization() {
|
||||
return hasClassesNamed("io.vertx.sqlclient.Pool");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||
return named("io.vertx.sqlclient.Pool");
|
||||
return implementsInterface(named("io.vertx.sqlclient.Pool"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transform(TypeTransformer transformer) {
|
||||
transformer.applyAdviceToMethod(
|
||||
named("pool")
|
||||
.and(isStatic())
|
||||
.and(takesArguments(3))
|
||||
.and(takesArgument(1, named("io.vertx.sqlclient.SqlConnectOptions")))
|
||||
.and(returns(named("io.vertx.sqlclient.Pool"))),
|
||||
PoolInstrumentation.class.getName() + "$PoolAdvice");
|
||||
|
||||
transformer.applyAdviceToMethod(
|
||||
named("getConnection").and(takesNoArguments()).and(returns(named("io.vertx.core.Future"))),
|
||||
PoolInstrumentation.class.getName() + "$GetConnectionAdvice");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -53,12 +70,33 @@ public class PoolInstrumentation implements TypeInstrumentation {
|
|||
}
|
||||
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void onExit(@Advice.Local("otelCallDepth") CallDepth callDepth) {
|
||||
public static void onExit(
|
||||
@Advice.Return Pool pool,
|
||||
@Advice.Argument(1) SqlConnectOptions sqlConnectOptions,
|
||||
@Advice.Local("otelCallDepth") CallDepth callDepth) {
|
||||
if (callDepth.decrementAndGet() > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
VirtualField<Pool, SqlConnectOptions> virtualField =
|
||||
VirtualField.find(Pool.class, SqlConnectOptions.class);
|
||||
virtualField.set(pool, sqlConnectOptions);
|
||||
|
||||
setSqlConnectOptions(null);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static class GetConnectionAdvice {
|
||||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void onExit(
|
||||
@Advice.This Pool pool, @Advice.Return(readOnly = false) Future<SqlConnection> future) {
|
||||
// copy connect options stored on pool to new connection
|
||||
VirtualField<Pool, SqlConnectOptions> virtualField =
|
||||
VirtualField.find(Pool.class, SqlConnectOptions.class);
|
||||
SqlConnectOptions sqlConnectOptions = virtualField.get(pool);
|
||||
|
||||
future = VertxSqlClientSingletons.attachConnectOptions(future, sqlConnectOptions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,6 +42,7 @@ public class SqlClientBaseInstrumentation implements TypeInstrumentation {
|
|||
@Advice.OnMethodExit(suppress = Throwable.class)
|
||||
public static void onExit(@Advice.This SqlClientBase<?> sqlClientBase) {
|
||||
// copy connection options from ThreadLocal to VirtualField
|
||||
// this virtual field is also set in VertxSqlClientSingletons.attachConnectOptions
|
||||
VirtualField<SqlClientBase<?>, SqlConnectOptions> virtualField =
|
||||
VirtualField.find(SqlClientBase.class, SqlConnectOptions.class);
|
||||
virtualField.set(sqlClientBase, getSqlConnectOptions());
|
||||
|
|
|
@ -16,8 +16,12 @@ import io.opentelemetry.instrumentation.api.instrumenter.db.DbClientSpanNameExtr
|
|||
import io.opentelemetry.instrumentation.api.instrumenter.db.SqlClientAttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.instrumenter.network.ServerAttributesExtractor;
|
||||
import io.opentelemetry.instrumentation.api.util.VirtualField;
|
||||
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig;
|
||||
import io.vertx.core.Future;
|
||||
import io.vertx.sqlclient.SqlConnectOptions;
|
||||
import io.vertx.sqlclient.SqlConnection;
|
||||
import io.vertx.sqlclient.impl.SqlClientBase;
|
||||
import java.util.Map;
|
||||
|
||||
public final class VertxSqlClientSingletons {
|
||||
|
@ -74,5 +78,20 @@ public final class VertxSqlClientSingletons {
|
|||
return otelParentContext.makeCurrent();
|
||||
}
|
||||
|
||||
// this virtual field is also used in SqlClientBase instrumentation
|
||||
private static final VirtualField<SqlClientBase<?>, SqlConnectOptions> connectOptionsField =
|
||||
VirtualField.find(SqlClientBase.class, SqlConnectOptions.class);
|
||||
|
||||
public static Future<SqlConnection> attachConnectOptions(
|
||||
Future<SqlConnection> future, SqlConnectOptions connectOptions) {
|
||||
return future.map(
|
||||
sqlConnection -> {
|
||||
if (sqlConnection instanceof SqlClientBase) {
|
||||
connectOptionsField.set((SqlClientBase<?>) sqlConnection, connectOptions);
|
||||
}
|
||||
return sqlConnection;
|
||||
});
|
||||
}
|
||||
|
||||
private VertxSqlClientSingletons() {}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue