From ffc5608e05dbd487f1d40947dc701a4b98e1043c Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Thu, 26 May 2022 15:10:36 +0300 Subject: [PATCH 1/3] Implement Oracle UCP connection pool metrics --- docs/standalone-library-instrumentation.md | 1 + docs/supported-libraries.md | 1 + .../javaagent/build.gradle.kts | 20 ++++ .../OracleUcpInstrumentationModule.java | 26 +++++ .../oracleucp/OracleUcpSingletons.java | 21 +++++ ...niversalConnectionPoolInstrumentation.java | 61 ++++++++++++ .../OracleUcpInstrumentationTest.java | 29 ++++++ .../oracle-ucp-11.2/library/README.md | 47 ++++++++++ .../oracle-ucp-11.2/library/build.gradle.kts | 10 ++ .../oracleucp/ConnectionPoolMetrics.java | 57 +++++++++++ .../oracleucp/OracleUcpTelemetry.java | 34 +++++++ .../OracleUcpInstrumentationTest.java | 48 ++++++++++ .../oracle-ucp-11.2/testing/build.gradle.kts | 11 +++ .../AbstractOracleUcpInstrumentationTest.java | 94 +++++++++++++++++++ settings.gradle.kts | 3 + testing-common/build.gradle.kts | 3 +- .../testing/junit/db/MockDriver.java | 59 ++++++++++++ 17 files changed, 523 insertions(+), 2 deletions(-) create mode 100644 instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts create mode 100644 instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java create mode 100644 instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpSingletons.java create mode 100644 instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/UniversalConnectionPoolInstrumentation.java create mode 100644 instrumentation/oracle-ucp-11.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationTest.java create mode 100644 instrumentation/oracle-ucp-11.2/library/README.md create mode 100644 instrumentation/oracle-ucp-11.2/library/build.gradle.kts create mode 100644 instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/ConnectionPoolMetrics.java create mode 100644 instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpTelemetry.java create mode 100644 instrumentation/oracle-ucp-11.2/library/src/test/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpInstrumentationTest.java create mode 100644 instrumentation/oracle-ucp-11.2/testing/build.gradle.kts create mode 100644 instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java create mode 100644 testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/MockDriver.java diff --git a/docs/standalone-library-instrumentation.md b/docs/standalone-library-instrumentation.md index 758556f02054..8372a4e67bbb 100644 --- a/docs/standalone-library-instrumentation.md +++ b/docs/standalone-library-instrumentation.md @@ -21,6 +21,7 @@ that can be used if you prefer that over using the Java agent: * [Logback](../instrumentation/logback/logback-appender-1.0/library) * [MongoDB Driver](../instrumentation/mongo/mongo-3.1/library) * [OkHttp](../instrumentation/okhttp/okhttp-3.0/library) +* [Oracle UCP](../instrumentation/oracle-ucp-11.2/library) * [OSHI](../instrumentation/oshi/library) * [Reactor](../instrumentation/reactor/reactor-3.1/library) * [RocketMQ](../instrumentation/rocketmq-client-4.8/library) diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index 8b92cfd52e67..d48e045d9d7e 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -83,6 +83,7 @@ These are the supported libraries and frameworks: | [MongoDB Driver](https://mongodb.github.io/mongo-java-driver/) | 3.1+ | | [Netty](https://github.com/netty/netty) | 3.8+ | | [OkHttp](https://github.com/square/okhttp/) | 2.2+ | +| [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/) | 11.2+ | | [OSHI](https://github.com/oshi/oshi/) | 5.3.1+ | | [Play](https://github.com/playframework/playframework) | 2.4+ (not including 2.8.x yet) | | [Play WS](https://github.com/playframework/play-ws) | 1.0+ | diff --git a/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts b/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts new file mode 100644 index 000000000000..59b952313930 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/javaagent/build.gradle.kts @@ -0,0 +1,20 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("com.oracle.database.jdbc") + module.set("ucp") + versions.set("[,)") + assertInverse.set(true) + } +} + +dependencies { + library("com.oracle.database.jdbc:ucp:11.2.0.4") + + implementation(project(":instrumentation:oracle-ucp-11.2:library")) + + testImplementation(project(":instrumentation:oracle-ucp-11.2:testing")) +} diff --git a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java new file mode 100644 index 000000000000..e1a648adbe04 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java @@ -0,0 +1,26 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.oracleucp; + +import static java.util.Collections.singletonList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; + +@AutoService(InstrumentationModule.class) +public class OracleUcpInstrumentationModule extends InstrumentationModule { + + public OracleUcpInstrumentationModule() { + super("oracle-ucp"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new UniversalConnectionPoolInstrumentation()); + } +} diff --git a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpSingletons.java b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpSingletons.java new file mode 100644 index 000000000000..cb40a6496f01 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpSingletons.java @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.oracleucp; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.oracleucp.OracleUcpTelemetry; + +public final class OracleUcpSingletons { + + private static final OracleUcpTelemetry oracleUcpTelemetry = + OracleUcpTelemetry.create(GlobalOpenTelemetry.get()); + + public static OracleUcpTelemetry telemetry() { + return oracleUcpTelemetry; + } + + private OracleUcpSingletons() {} +} diff --git a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/UniversalConnectionPoolInstrumentation.java b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/UniversalConnectionPoolInstrumentation.java new file mode 100644 index 000000000000..553654051422 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/UniversalConnectionPoolInstrumentation.java @@ -0,0 +1,61 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.oracleucp; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; +import static io.opentelemetry.javaagent.instrumentation.oracleucp.OracleUcpSingletons.telemetry; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import oracle.ucp.UniversalConnectionPool; + +public class UniversalConnectionPoolInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("oracle.ucp.UniversalConnectionPool"); + } + + @Override + public ElementMatcher typeMatcher() { + return implementsInterface(named("oracle.ucp.UniversalConnectionPool")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("start") + .and(takesArguments(0).or(takesArguments(1).and(takesArgument(0, boolean.class)))), + this.getClass().getName() + "$StartAdvice"); + transformer.applyAdviceToMethod( + named("stop").and(takesArguments(0)), this.getClass().getName() + "$StopAdvice"); + } + + @SuppressWarnings("unused") + public static class StartAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.This UniversalConnectionPool connectionPool) { + telemetry().registerMetrics(connectionPool); + } + } + + @SuppressWarnings("unused") + public static class StopAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void onExit(@Advice.This UniversalConnectionPool connectionPool) { + telemetry().unregisterMetrics(connectionPool); + } + } +} diff --git a/instrumentation/oracle-ucp-11.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationTest.java b/instrumentation/oracle-ucp-11.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationTest.java new file mode 100644 index 000000000000..97c37332df83 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationTest.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.oracleucp; + +import io.opentelemetry.instrumentation.oracleucp.AbstractOracleUcpInstrumentationTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import oracle.ucp.jdbc.PoolDataSource; +import org.junit.jupiter.api.extension.RegisterExtension; + +class OracleUcpInstrumentationTest extends AbstractOracleUcpInstrumentationTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @Override + protected void configure(PoolDataSource connectionPool) {} + + @Override + protected void shutdown(PoolDataSource connectionPool) {} +} diff --git a/instrumentation/oracle-ucp-11.2/library/README.md b/instrumentation/oracle-ucp-11.2/library/README.md new file mode 100644 index 000000000000..47fee08c9aa8 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/library/README.md @@ -0,0 +1,47 @@ +# Manual Instrumentation for Oracle UCP + +Provides OpenTelemetry instrumentation for [Oracle UCP](https://docs.oracle.com/database/121/JJUCP/). + +## Quickstart + +### Add these dependencies to your project: + +Replace `OPENTELEMETRY_VERSION` with the latest stable +[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.15.0` + +For Maven, add to your `pom.xml` dependencies: + +```xml + + + + io.opentelemetry.instrumentation + opentelemetry-oracle-ucp-11.2 + OPENTELEMETRY_VERSION + + +``` + +For Gradle, add to your dependencies: + +```groovy +implementation("io.opentelemetry.instrumentation:opentelemetry-oracle-ucp-11.2:OPENTELEMETRY_VERSION") +``` + +### Usage + +The instrumentation library allows registering `UniversalConnectionPool` instances for collecting +OpenTelemetry-based metrics. + +```java +OracleUcpTelemetry oracleUcpTelemetry; + +void configure(OpenTelemetry openTelemetry, UniversalConnectionPool universalConnectionPool) { + oracleUcpTelemetry = OracleUcpTelemetry.create(openTelemetry); + oracleUcpTelemetry.registerMetrics(universalConnectionPool); +} + +void destroy(UniversalConnectionPool universalConnectionPool) { + oracleUcpTelemetry.unregisterMetrics(universalConnectionPool); +} +``` diff --git a/instrumentation/oracle-ucp-11.2/library/build.gradle.kts b/instrumentation/oracle-ucp-11.2/library/build.gradle.kts new file mode 100644 index 000000000000..ba70bd78eb82 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/library/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("otel.library-instrumentation") + id("otel.nullaway-conventions") +} + +dependencies { + library("com.oracle.database.jdbc:ucp:11.2.0.4") + + testImplementation(project(":instrumentation:oracle-ucp-11.2:testing")) +} diff --git a/instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/ConnectionPoolMetrics.java b/instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/ConnectionPoolMetrics.java new file mode 100644 index 000000000000..5f8cf02b1bc2 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/ConnectionPoolMetrics.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.oracleucp; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.instrumentation.api.metrics.db.DbConnectionPoolMetrics; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import oracle.ucp.UniversalConnectionPool; + +final class ConnectionPoolMetrics { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.orcale-ucp-11.2"; + + // a weak map does not make sense here because each Meter holds a reference to the connection pool + // none of the UniversalConnectionPool implementations contain equals()/hashCode(), so it's safe + // to keep them in a plain ConcurrentHashMap + private static final Map> + dataSourceMetrics = new ConcurrentHashMap<>(); + + public static void registerMetrics( + OpenTelemetry openTelemetry, UniversalConnectionPool connectionPool) { + dataSourceMetrics.computeIfAbsent( + connectionPool, (unused) -> createMeters(openTelemetry, connectionPool)); + } + + private static List createMeters( + OpenTelemetry openTelemetry, UniversalConnectionPool connectionPool) { + DbConnectionPoolMetrics metrics = + DbConnectionPoolMetrics.create( + openTelemetry, INSTRUMENTATION_NAME, connectionPool.getName()); + + return Arrays.asList( + metrics.usedConnections(connectionPool::getBorrowedConnectionsCount), + metrics.idleConnections(connectionPool::getAvailableConnectionsCount), + metrics.maxConnections(() -> connectionPool.getStatistics().getPeakConnectionsCount()), + metrics.pendingRequestsForConnection( + () -> connectionPool.getStatistics().getPendingRequestsCount())); + } + + public static void unregisterMetrics(UniversalConnectionPool connectionPool) { + List observableInstruments = + dataSourceMetrics.remove(connectionPool); + if (observableInstruments != null) { + for (ObservableLongUpDownCounter observable : observableInstruments) { + observable.close(); + } + } + } + + private ConnectionPoolMetrics() {} +} diff --git a/instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpTelemetry.java b/instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpTelemetry.java new file mode 100644 index 000000000000..f92430b5c96f --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/library/src/main/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpTelemetry.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.oracleucp; + +import io.opentelemetry.api.OpenTelemetry; +import oracle.ucp.UniversalConnectionPool; + +/** Entrypoint for instrumenting Oracle UCP database connection pools. */ +public final class OracleUcpTelemetry { + + /** Returns a new {@link OracleUcpTelemetry} configured with the given {@link OpenTelemetry}. */ + public static OracleUcpTelemetry create(OpenTelemetry openTelemetry) { + return new OracleUcpTelemetry(openTelemetry); + } + + private final OpenTelemetry openTelemetry; + + private OracleUcpTelemetry(OpenTelemetry openTelemetry) { + this.openTelemetry = openTelemetry; + } + + /** Start collecting metrics for given connection pool. */ + public void registerMetrics(UniversalConnectionPool universalConnectionPool) { + ConnectionPoolMetrics.registerMetrics(openTelemetry, universalConnectionPool); + } + + /** Stop collecting metrics for given connection pool. */ + public void unregisterMetrics(UniversalConnectionPool universalConnectionPool) { + ConnectionPoolMetrics.unregisterMetrics(universalConnectionPool); + } +} diff --git a/instrumentation/oracle-ucp-11.2/library/src/test/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpInstrumentationTest.java b/instrumentation/oracle-ucp-11.2/library/src/test/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpInstrumentationTest.java new file mode 100644 index 000000000000..4fc0301d5d22 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/library/src/test/java/io/opentelemetry/instrumentation/oracleucp/OracleUcpInstrumentationTest.java @@ -0,0 +1,48 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.oracleucp; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import oracle.ucp.UniversalConnectionPool; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.PoolDataSource; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class OracleUcpInstrumentationTest extends AbstractOracleUcpInstrumentationTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + private static OracleUcpTelemetry telemetry; + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + @BeforeAll + static void setup() { + telemetry = OracleUcpTelemetry.create(testing.getOpenTelemetry()); + } + + @Override + protected void configure(PoolDataSource connectionPool) throws Exception { + UniversalConnectionPool universalConnectionPool = + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .getConnectionPool(connectionPool.getConnectionPoolName()); + telemetry.registerMetrics(universalConnectionPool); + } + + @Override + protected void shutdown(PoolDataSource connectionPool) throws Exception { + UniversalConnectionPool universalConnectionPool = + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .getConnectionPool(connectionPool.getConnectionPoolName()); + telemetry.unregisterMetrics(universalConnectionPool); + } +} diff --git a/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts b/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts new file mode 100644 index 000000000000..2326f1bd8220 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/testing/build.gradle.kts @@ -0,0 +1,11 @@ +plugins { + id("otel.java-conventions") +} + +dependencies { + api(project(":testing-common")) + api("org.mockito:mockito-core") + api("org.mockito:mockito-junit-jupiter") + + compileOnly("com.oracle.database.jdbc:ucp:11.2.0.4") +} diff --git a/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java b/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java new file mode 100644 index 000000000000..6c167f2fca78 --- /dev/null +++ b/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java @@ -0,0 +1,94 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.oracleucp; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.db.DbConnectionPoolMetricsAssertions; +import io.opentelemetry.instrumentation.testing.junit.db.MockDriver; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.concurrent.TimeUnit; +import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; +import oracle.ucp.jdbc.PoolDataSource; +import oracle.ucp.jdbc.PoolDataSourceFactory; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public abstract class AbstractOracleUcpInstrumentationTest { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.orcale-ucp-11.2"; + + protected abstract InstrumentationExtension testing(); + + protected abstract void configure(PoolDataSource connectionPool) throws Exception; + + protected abstract void shutdown(PoolDataSource connectionPool) throws Exception; + + @BeforeAll + static void setUpMocks() throws SQLException { + MockDriver.register(); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void shouldReportMetrics(boolean setExplicitPoolName) throws Exception { + // given + PoolDataSource connectionPool = PoolDataSourceFactory.getPoolDataSource(); + connectionPool.setConnectionFactoryClassName(MockDriver.class.getName()); + connectionPool.setURL("jdbc:mock:testDatabase"); + if (setExplicitPoolName) { + connectionPool.setConnectionPoolName("testPool"); + } + + // when + Connection connection = connectionPool.getConnection(); + configure(connectionPool); + TimeUnit.MILLISECONDS.sleep(100); + connection.close(); + + // then + DbConnectionPoolMetricsAssertions.create( + testing(), INSTRUMENTATION_NAME, connectionPool.getConnectionPoolName()) + .disableMinIdleConnections() + .disableMaxIdleConnections() + .disableConnectionTimeouts() + .disableCreateTime() + .disableWaitTime() + .disableUseTime() + .assertConnectionPoolEmitsMetrics(); + + // when + // this one too shouldn't cause any problems when called more than once + connectionPool.getConnection().close(); + connectionPool.getConnection().close(); + + shutdown(connectionPool); + UniversalConnectionPoolManagerImpl.getUniversalConnectionPoolManager() + .destroyConnectionPool(connectionPool.getConnectionPoolName()); + + // sleep exporter interval + Thread.sleep(100); + testing().clearData(); + Thread.sleep(100); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "db.client.connections.usage", AbstractIterableAssert::isEmpty); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "db.client.connections.max", AbstractIterableAssert::isEmpty); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "db.client.connections.pending_requests", + AbstractIterableAssert::isEmpty); + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 2d226c637b1b..d9af8055e40f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -342,6 +342,9 @@ include(":instrumentation:opentelemetry-api:opentelemetry-api-1.4:javaagent") include(":instrumentation:opentelemetry-api:opentelemetry-api-1.10:javaagent") include(":instrumentation:opentelemetry-instrumentation-api:javaagent") include(":instrumentation:opentelemetry-instrumentation-api:testing") +include(":instrumentation:oracle-ucp-11.2:javaagent") +include(":instrumentation:oracle-ucp-11.2:library") +include(":instrumentation:oracle-ucp-11.2:testing") include(":instrumentation:oshi:javaagent") include(":instrumentation:oshi:library") include(":instrumentation:oshi:testing") diff --git a/testing-common/build.gradle.kts b/testing-common/build.gradle.kts index 998c002cb8d5..2b15c2dc6ab3 100644 --- a/testing-common/build.gradle.kts +++ b/testing-common/build.gradle.kts @@ -42,11 +42,10 @@ dependencies { api(project(":instrumentation-api")) api("org.assertj:assertj-core") - // Needs to be api dependency due to Spock restriction. api("org.awaitility:awaitility") - api("com.google.guava:guava") + api("org.mockito:mockito-core") compileOnly(project(":testing:armeria-shaded-for-testing", configuration = "shadow")) diff --git a/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/MockDriver.java b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/MockDriver.java new file mode 100644 index 000000000000..f278d6306c22 --- /dev/null +++ b/testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/db/MockDriver.java @@ -0,0 +1,59 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.testing.junit.db; + +import java.sql.Connection; +import java.sql.Driver; +import java.sql.DriverManager; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.Properties; +import java.util.logging.Logger; +import org.mockito.Mockito; + +public final class MockDriver implements Driver { + private static final MockDriver INSTANCE = new MockDriver(); + + public static void register() throws SQLException { + DriverManager.registerDriver(INSTANCE); + } + + @Override + public Connection connect(String url, Properties info) { + return Mockito.mock(Connection.class); + } + + @Override + public boolean acceptsURL(String url) { + return url.startsWith("jdbc:mock:"); + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info) { + return new DriverPropertyInfo[0]; + } + + @Override + public int getMajorVersion() { + return 0; + } + + @Override + public int getMinorVersion() { + return 0; + } + + @Override + public boolean jdbcCompliant() { + return false; + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + throw new SQLFeatureNotSupportedException("Feature not supported"); + } +} From 35898f6e511d11c8c6aa2c72527617062fedb28d Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Thu, 26 May 2022 17:04:33 +0300 Subject: [PATCH 2/3] add additional instrumentation name --- .../oracleucp/OracleUcpInstrumentationModule.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java index e1a648adbe04..a8ed07266acc 100644 --- a/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java +++ b/instrumentation/oracle-ucp-11.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/oracleucp/OracleUcpInstrumentationModule.java @@ -16,7 +16,7 @@ public class OracleUcpInstrumentationModule extends InstrumentationModule { public OracleUcpInstrumentationModule() { - super("oracle-ucp"); + super("oracle-ucp", "oracle-ucp-11.2"); } @Override From e12341ac8bcaa0483d98054b982b59e3c6c63b5e Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Fri, 3 Jun 2022 00:27:38 +0300 Subject: [PATCH 3/3] change asserting no metrics reported after shutdown --- .../AbstractOracleUcpInstrumentationTest.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java b/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java index 6c167f2fca78..639240214874 100644 --- a/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java +++ b/instrumentation/oracle-ucp-11.2/testing/src/main/java/io/opentelemetry/instrumentation/oracleucp/AbstractOracleUcpInstrumentationTest.java @@ -5,16 +5,20 @@ package io.opentelemetry.instrumentation.oracleucp; +import static org.assertj.core.api.Assertions.assertThat; + import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; import io.opentelemetry.instrumentation.testing.junit.db.DbConnectionPoolMetricsAssertions; import io.opentelemetry.instrumentation.testing.junit.db.MockDriver; import java.sql.Connection; import java.sql.SQLException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import java.util.concurrent.TimeUnit; import oracle.ucp.admin.UniversalConnectionPoolManagerImpl; import oracle.ucp.jdbc.PoolDataSource; import oracle.ucp.jdbc.PoolDataSourceFactory; -import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; @@ -79,16 +83,17 @@ void shouldReportMetrics(boolean setExplicitPoolName) throws Exception { Thread.sleep(100); // then - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, "db.client.connections.usage", AbstractIterableAssert::isEmpty); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, "db.client.connections.max", AbstractIterableAssert::isEmpty); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "db.client.connections.pending_requests", - AbstractIterableAssert::isEmpty); + Set metricNames = + new HashSet<>( + Arrays.asList( + "db.client.connections.usage", + "db.client.connections.max", + "db.client.connections.pending_requests")); + assertThat(testing().metrics()) + .filteredOn( + metricData -> + metricData.getInstrumentationScopeInfo().getName().equals(INSTRUMENTATION_NAME) + && metricNames.contains(metricData.getName())) + .isEmpty(); } }