Implement HikariCP library instrumentation
MODIFY
docs/standalone-library-instrumentation.md
docs/standalone-library-instrumentation.md
@@ -11,6 +11,7 @@ that can be used if you prefer that over using the Java agent:
* [gRPC](../instrumentation/grpc-1.6/library)
* [Guava](../instrumentation/guava-10.0/library)
* [GraphQL Java](../instrumentation/graphql-java-12.0/library)
+* [HikariCP](../instrumentation/hikaricp-3.0/library)
* [JDBC](../instrumentation/jdbc/library)
* [Ktor 1.0](../instrumentation/ktor/ktor-1.0/library)
* [Ktor 2.0](../instrumentation/ktor/ktor-2.0/library)

MODIFY
instrumentation/hikaricp-3.0/javaagent/build.gradle.kts
instrumentation/hikaricp-3.0/javaagent/build.gradle.kts
@@ -16,4 +16,8 @@ muzzle {
dependencies {
library("com.zaxxer:HikariCP:3.0.0")
+
+ implementation(project(":instrumentation:hikaricp-3.0:library"))
+
+ testImplementation(project(":instrumentation:hikaricp-3.0:testing"))
}

MODIFY
instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariPoolInstrumentation.java
instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariPoolInstrumentation.java
@@ -39,11 +39,9 @@ public class HikariPoolInstrumentation implements TypeInstrumentation {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
- @Advice.Argument(value = 0, readOnly = false) MetricsTrackerFactory metricsTrackerFactory) {
+ @Advice.Argument(value = 0, readOnly = false) MetricsTrackerFactory userMetricsTracker) {
- if (!(metricsTrackerFactory instanceof OpenTelemetryMetricsTrackerFactory)) {
- metricsTrackerFactory = new OpenTelemetryMetricsTrackerFactory(metricsTrackerFactory);
- }
+ userMetricsTracker = HikariSingletons.createMetricsTrackerFactory(userMetricsTracker);
}
}
}

ADD
instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariSingletons.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.hikaricp;
+
+import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
+import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.instrumentation.hikaricp.HikariTelemetry;
+import javax.annotation.Nullable;
+
+public final class HikariSingletons {
+
+ private static final HikariTelemetry hikariTelemetry =
+ HikariTelemetry.create(GlobalOpenTelemetry.get());
+
+ public static MetricsTrackerFactory createMetricsTrackerFactory(
+ @Nullable MetricsTrackerFactory delegate) {
+ return hikariTelemetry.createMetricsTrackerFactory(delegate);
+ }
+
+ private HikariSingletons() {}
+}

ADD
instrumentation/hikaricp-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariInstrumentationTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.hikaricp;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
+import io.opentelemetry.instrumentation.hikaricp.AbstractHikariInstrumentationTest;
+import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import javax.annotation.Nullable;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class HikariInstrumentationTest extends AbstractHikariInstrumentationTest {
+
+ @RegisterExtension
+ static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
+
+ @Override
+ protected InstrumentationExtension testing() {
+ return testing;
+ }
+
+ @Override
+ protected void configure(HikariConfig poolConfig, @Nullable MetricsTrackerFactory userTracker) {
+ if (userTracker != null) {
+ poolConfig.setMetricsTrackerFactory(userTracker);
+ }
+ }
+}

ADD
instrumentation/hikaricp-3.0/library/README.md
+# Manual Instrumentation for HikariCP
+
+Provides OpenTelemetry instrumentation for [HikariCP](https://github.com/brettwooldridge/HikariCP).
+
+## Quickstart
+
+### Add these dependencies to your project:
+
+Replace `OPENTELEMETRY_VERSION` with the latest stable
+[release](https://mvnrepository.com/artifact/io.opentelemetry). `Minimum version: 1.14.0`
+
+For Maven, add to your `pom.xml` dependencies:
+
+```xml
+
+<dependencies>
+ <dependency>
+ <groupId>io.opentelemetry.instrumentation</groupId>
+ <artifactId>opentelemetry-hikaricp-3.0</artifactId>
+ <version>OPENTELEMETRY_VERSION</version>
+ </dependency>
+</dependencies>
+```
+
+For Gradle, add to your dependencies:
+
+```groovy
+implementation("io.opentelemetry.instrumentation:opentelemetry-hikaricp-3.0:OPENTELEMETRY_VERSION")
+```
+
+### Usage
+
+The instrumentation library provides a `MetricsTrackerFactory` implementation that can be added to
+an instance of the `HikariConfig` (or `HikariDataSource`) to provide OpenTelemetry-based metrics.
+
+```java
+void configure(OpenTelemetry openTelemetry, HikariConfig connectionPoolConfig) {
+ HikariTelemetry telemetry = HikariTelemetry.create(openTelemetry);
+ connectionPoolConfig.setMetricsTrackerFactory(telemetry.createMetricsTrackerFactory());
+}
+```

ADD
instrumentation/hikaricp-3.0/library/build.gradle.kts
+plugins {
+ id("otel.library-instrumentation")
+ id("otel.nullaway-conventions")
+}
+
+dependencies {
+ library("com.zaxxer:HikariCP:3.0.0")
+
+ testImplementation(project(":instrumentation:hikaricp-3.0:testing"))
+}

ADD
instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/HikariTelemetry.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.hikaricp;
+
+import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
+import io.opentelemetry.api.OpenTelemetry;
+import javax.annotation.Nullable;
+
+/** Entrypoint for instrumenting Hikari database connection pools. */
+public final class HikariTelemetry {
+
+ /** Returns a new {@link HikariTelemetry} configured with the given {@link OpenTelemetry}. */
+ public static HikariTelemetry create(OpenTelemetry openTelemetry) {
+ return new HikariTelemetry(openTelemetry);
+ }
+
+ private final OpenTelemetry openTelemetry;
+
+ private HikariTelemetry(OpenTelemetry openTelemetry) {
+ this.openTelemetry = openTelemetry;
+ }
+
+ /**
+ * Returns a new {@link MetricsTrackerFactory} that can be registered using {@link
+ * com.zaxxer.hikari.HikariConfig#setMetricsTrackerFactory(MetricsTrackerFactory)}.
+ */
+ public MetricsTrackerFactory createMetricsTrackerFactory() {
+ return createMetricsTrackerFactory(null);
+ }
+
+ /**
+ * Returns a new {@link MetricsTrackerFactory} that can be registered using {@link
+ * com.zaxxer.hikari.HikariConfig#setMetricsTrackerFactory(MetricsTrackerFactory)}. The {@link
+ * com.zaxxer.hikari.metrics.IMetricsTracker} objects created by the returned factory will
+ * delegate to trackers created by the {@code delegate} metrics tracker factory, if it is not
+ * null.
+ */
+ public MetricsTrackerFactory createMetricsTrackerFactory(
+ @Nullable MetricsTrackerFactory delegate) {
+ return new OpenTelemetryMetricsTrackerFactory(openTelemetry, delegate);
+ }
+}

RENAME
instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/OpenTelemetryMetricsTracker.java
instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTracker.java
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.javaagent.instrumentation.hikaricp;
+package io.opentelemetry.instrumentation.hikaricp;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import io.opentelemetry.api.common.Attributes;

RENAME
instrumentation/hikaricp-3.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/hikaricp/OpenTelemetryMetricsTrackerFactory.java
instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTrackerFactory.java
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.javaagent.instrumentation.hikaricp;
+package io.opentelemetry.instrumentation.hikaricp;
import com.zaxxer.hikari.metrics.IMetricsTracker;
import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import com.zaxxer.hikari.metrics.PoolStats;
-import io.opentelemetry.api.GlobalOpenTelemetry;
+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 javax.annotation.Nullable;
-public final class OpenTelemetryMetricsTrackerFactory implements MetricsTrackerFactory {
+final class OpenTelemetryMetricsTrackerFactory implements MetricsTrackerFactory {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.hikaricp-3.0";
+ private final OpenTelemetry openTelemetry;
@Nullable private final MetricsTrackerFactory userMetricsFactory;
- public OpenTelemetryMetricsTrackerFactory(@Nullable MetricsTrackerFactory userMetricsFactory) {
+ OpenTelemetryMetricsTrackerFactory(
+ OpenTelemetry openTelemetry, @Nullable MetricsTrackerFactory userMetricsFactory) {
+ this.openTelemetry = openTelemetry;
this.userMetricsFactory = userMetricsFactory;
}
@@ -33,7 +36,7 @@ public final class OpenTelemetryMetricsTrackerFactory implements MetricsTrackerF
: userMetricsFactory.create(poolName, poolStats);
DbConnectionPoolMetrics metrics =
- DbConnectionPoolMetrics.create(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME, poolName);
+ DbConnectionPoolMetrics.create(openTelemetry, INSTRUMENTATION_NAME, poolName);
List<ObservableLongUpDownCounter> observableInstruments =
Arrays.asList(

ADD
instrumentation/hikaricp-3.0/library/src/test/java/io/opentelemetry/instrumentation/hikaricp/HikariInstrumentationTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.hikaricp;
+
+import com.zaxxer.hikari.HikariConfig;
+import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
+import javax.annotation.Nullable;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class HikariInstrumentationTest extends AbstractHikariInstrumentationTest {
+
+ @RegisterExtension
+ static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
+
+ @Override
+ protected InstrumentationExtension testing() {
+ return testing;
+ }
+
+ @Override
+ protected void configure(HikariConfig poolConfig, @Nullable MetricsTrackerFactory userTracker) {
+ poolConfig.setMetricsTrackerFactory(
+ HikariTelemetry.create(testing().getOpenTelemetry())
+ .createMetricsTrackerFactory(userTracker));
+ }
+}

ADD
instrumentation/hikaricp-3.0/testing/build.gradle.kts
+plugins {
+ id("otel.java-conventions")
+}
+
+dependencies {
+ api(project(":testing-common"))
+ api("org.mockito:mockito-core")
+ api("org.mockito:mockito-junit-jupiter")
+
+ compileOnly("com.zaxxer:HikariCP:3.0.0")
+}

RENAME
instrumentation/hikaricp-3.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/hikaricp/HikariCpInstrumentationTest.java
instrumentation/hikaricp-3.0/testing/src/main/java/io/opentelemetry/instrumentation/hikaricp/AbstractHikariInstrumentationTest.java
* SPDX-License-Identifier: Apache-2.0
*/
-package io.opentelemetry.javaagent.instrumentation.hikaricp;
+package io.opentelemetry.instrumentation.hikaricp;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.atLeastOnce;
@@ -13,12 +13,14 @@ import static org.mockito.Mockito.when;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.metrics.IMetricsTracker;
+import com.zaxxer.hikari.metrics.MetricsTrackerFactory;
import io.opentelemetry.instrumentation.testing.internal.AutoCleanupExtension;
-import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.db.DbConnectionPoolMetricsAssertions;
import java.sql.Connection;
+import java.sql.SQLException;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
import javax.sql.DataSource;
import org.assertj.core.api.AbstractIterableAssert;
import org.junit.jupiter.api.Test;
@@ -28,10 +30,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
-class HikariCpInstrumentationTest {
-
- @RegisterExtension
- static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
+public abstract class AbstractHikariInstrumentationTest {
@RegisterExtension static final AutoCleanupExtension cleanup = AutoCleanupExtension.create();
@@ -39,14 +38,20 @@ class HikariCpInstrumentationTest {
@Mock Connection connectionMock;
@Mock IMetricsTracker userMetricsMock;
+ protected abstract InstrumentationExtension testing();
+
+ protected abstract void configure(
+ HikariConfig poolConfig, @Nullable MetricsTrackerFactory userTracker);
+
@Test
- void shouldReportMetrics() throws Exception {
+ void shouldReportMetrics() throws SQLException, InterruptedException {
// given
when(dataSourceMock.getConnection()).thenReturn(connectionMock);
HikariDataSource hikariDataSource = new HikariDataSource();
hikariDataSource.setPoolName("testPool");
hikariDataSource.setDataSource(dataSourceMock);
+ configure(hikariDataSource, null);
// when
Connection hikariConnection = hikariDataSource.getConnection();
@@ -54,7 +59,7 @@ class HikariCpInstrumentationTest {
hikariConnection.close();
// then
- DbConnectionPoolMetricsAssertions.create(testing, "io.opentelemetry.hikaricp-3.0", "testPool")
+ DbConnectionPoolMetricsAssertions.create(testing(), "io.opentelemetry.hikaricp-3.0", "testPool")
.disableMaxIdleConnections()
// no timeouts happen during this test
.disableConnectionTimeouts()
@@ -65,37 +70,41 @@ class HikariCpInstrumentationTest {
// sleep exporter interval
Thread.sleep(100);
- testing.clearData();
+ testing().clearData();
Thread.sleep(100);
// then
- testing.waitAndAssertMetrics(
+ testing()
+ .waitAndAssertMetrics(
"io.opentelemetry.hikaricp-3.0",
"db.client.connections.usage",
AbstractIterableAssert::isEmpty);
- testing.waitAndAssertMetrics(
+ testing()
+ .waitAndAssertMetrics(
"io.opentelemetry.hikaricp-3.0",
"db.client.connections.idle.min",
AbstractIterableAssert::isEmpty);
- testing.waitAndAssertMetrics(
+ testing()
+ .waitAndAssertMetrics(
"io.opentelemetry.hikaricp-3.0",
"db.client.connections.max",
AbstractIterableAssert::isEmpty);
- testing.waitAndAssertMetrics(
+ testing()
+ .waitAndAssertMetrics(
"io.opentelemetry.hikaricp-3.0",
"db.client.connections.pending_requests",
AbstractIterableAssert::isEmpty);
}
@Test
- void shouldNotBreakCustomUserMetrics() throws Exception {
+ void shouldNotBreakCustomUserMetrics() throws SQLException, InterruptedException {
// given
when(dataSourceMock.getConnection()).thenReturn(connectionMock);
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setPoolName("anotherTestPool");
hikariConfig.setDataSource(dataSourceMock);
- hikariConfig.setMetricsTrackerFactory((poolName, poolStats) -> userMetricsMock);
+ configure(hikariConfig, (poolName, poolStats) -> userMetricsMock);
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
cleanup.deferCleanup(hikariDataSource);
@@ -107,7 +116,7 @@ class HikariCpInstrumentationTest {
// then
DbConnectionPoolMetricsAssertions.create(
- testing, "io.opentelemetry.hikaricp-3.0", "anotherTestPool")
+ testing(), "io.opentelemetry.hikaricp-3.0", "anotherTestPool")
.disableMaxIdleConnections()
// no timeouts happen during this test
.disableConnectionTimeouts()

MODIFY
settings.gradle.kts
settings.gradle.kts
@@ -221,6 +221,8 @@ include(":instrumentation:hibernate:hibernate-4.0:javaagent")
include(":instrumentation:hibernate:hibernate-common:javaagent")
include(":instrumentation:hibernate:hibernate-procedure-call-4.3:javaagent")
include(":instrumentation:hikaricp-3.0:javaagent")
+include(":instrumentation:hikaricp-3.0:library")
+include(":instrumentation:hikaricp-3.0:testing")
include(":instrumentation:http-url-connection:javaagent")
include(":instrumentation:hystrix-1.4:javaagent")
include(":instrumentation:java-http-client:javaagent")

Implement HikariCP library instrumentation (#6023)