Skip to content

Commit

Permalink
Implement Vibur DBCP connection pool metrics (#6092)
Browse files Browse the repository at this point in the history
* Implement Vibur DBCP connection pool metrics

* Apply suggestions from code review

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>

* address review comments

* don't check for metircs that aren't reported

* rework library test setup

Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com>
  • Loading branch information
laurit and trask authored May 26, 2022
1 parent 4586db4 commit b95b64b
Show file tree
Hide file tree
Showing 16 changed files with 557 additions and 82 deletions.
1 change: 1 addition & 0 deletions docs/standalone-library-instrumentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ that can be used if you prefer that over using the Java agent:
* [Spring RestTemplate](../instrumentation/spring/spring-web-3.1/library)
* [Spring Web MVC](../instrumentation/spring/spring-webmvc-3.1/library)
* [Spring WebFlux Client](../instrumentation/spring/spring-webflux-5.0/library)
* [Vibur DBCP](../instrumentation/vibur-dbcp-11.0/library)

And some libraries are publishing their own OpenTelemetry instrumentation (yay!), e.g.

Expand Down
1 change: 1 addition & 0 deletions docs/supported-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ These are the supported libraries and frameworks:
| [Vert.x HttpClient](https://vertx.io/docs/apidocs/io/vertx/core/http/HttpClient.html) | 3.0+ |
| [Vert.x Kafka Client](https://vertx.io/docs/vertx-kafka-client/java/) | 3.6+ |
| [Vert.x RxJava2](https://vertx.io/docs/vertx-rx/java2/) | 3.5+ |
| [Vibur DBCP](https://www.vibur.org/) | 11.0+ |

## Application Servers

Expand Down
20 changes: 20 additions & 0 deletions instrumentation/vibur-dbcp-11.0/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("org.vibur")
module.set("vibur-dbcp")
versions.set("[11.0,)")
assertInverse.set(true)
}
}

dependencies {
library("org.vibur:vibur-dbcp:11.0")

implementation(project(":instrumentation:vibur-dbcp-11.0:library"))

testImplementation(project(":instrumentation:vibur-dbcp-11.0:testing"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.viburdbcp;

import static io.opentelemetry.javaagent.instrumentation.viburdbcp.ViburSingletons.telemetry;
import static net.bytebuddy.matcher.ElementMatchers.named;
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 org.vibur.dbcp.ViburDBCPDataSource;

final class ViburDbcpDataSourceInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.vibur.dbcp.ViburDBCPDataSource");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("start").and(takesArguments(0)), this.getClass().getName() + "$StartAdvice");
transformer.applyAdviceToMethod(
named("close").and(takesArguments(0)), this.getClass().getName() + "$CloseAdvice");
}

@SuppressWarnings("unused")
public static class StartAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(@Advice.This ViburDBCPDataSource dataSource) {
telemetry().registerMetrics(dataSource);
}
}

@SuppressWarnings("unused")
public static class CloseAdvice {

@Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class)
public static void onExit(@Advice.This ViburDBCPDataSource dataSource) {
telemetry().unregisterMetrics(dataSource);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.viburdbcp;

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 ViburDbcpInstrumentationModule extends InstrumentationModule {
public ViburDbcpInstrumentationModule() {
super("vibur-dbcp", "vibur-dbcp-11.0");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ViburDbcpDataSourceInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.viburdbcp;

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.viburdbcp.ViburTelemetry;

public final class ViburSingletons {

private static final ViburTelemetry viburTelemetry =
ViburTelemetry.create(GlobalOpenTelemetry.get());

public static ViburTelemetry telemetry() {
return viburTelemetry;
}

private ViburSingletons() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.viburdbcp;

import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.viburdbcp.AbstractViburInstrumentationTest;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.vibur.dbcp.ViburDBCPDataSource;

class ViburInstrumentationTest extends AbstractViburInstrumentationTest {

@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();

@Override
protected InstrumentationExtension testing() {
return testing;
}

@Override
protected void configure(ViburDBCPDataSource viburDataSource) {}

@Override
protected void shutdown(ViburDBCPDataSource viburDataSource) {}
}
47 changes: 47 additions & 0 deletions instrumentation/vibur-dbcp-11.0/library/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Manual Instrumentation for Vibur DBCP

Provides OpenTelemetry instrumentation for [Vibur DBCP](https://www.vibur.org/).

## 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

<dependencies>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-viburdbcp-11.0</artifactId>
<version>OPENTELEMETRY_VERSION</version>
</dependency>
</dependencies>
```

For Gradle, add to your dependencies:

```groovy
implementation("io.opentelemetry.instrumentation:opentelemetry-viburdbcp-11.0:OPENTELEMETRY_VERSION")
```

### Usage

The instrumentation library allows registering `ViburDBCPDataSource` instances for collecting
OpenTelemetry-based metrics.

```java
ViburTelemetry viburTelemetry;

void configure(OpenTelemetry openTelemetry, ViburDBCPDataSource viburDataSource) {
viburTelemetry = ViburTelemetry.create(openTelemetry);
viburTelemetry.registerMetrics(viburDataSource);
}

void destroy(ViburDBCPDataSource viburDataSource) {
viburTelemetry.unregisterMetrics(viburDataSource);
}
```
10 changes: 10 additions & 0 deletions instrumentation/vibur-dbcp-11.0/library/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
plugins {
id("otel.library-instrumentation")
id("otel.nullaway-conventions")
}

dependencies {
library("org.vibur:vibur-dbcp:11.0")

testImplementation(project(":instrumentation:vibur-dbcp-11.0:testing"))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.viburdbcp;

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 org.vibur.dbcp.ViburDBCPDataSource;

final class ConnectionPoolMetrics {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.viburdbcp-11.0";

// a weak map does not make sense here because each Meter holds a reference to the dataSource
// ViburDBCPDataSource does not implement equals()/hashCode(), so it's safe to keep them in a
// plain ConcurrentHashMap
private static final Map<ViburDBCPDataSource, List<ObservableLongUpDownCounter>>
dataSourceMetrics = new ConcurrentHashMap<>();

public static void registerMetrics(OpenTelemetry openTelemetry, ViburDBCPDataSource dataSource) {
dataSourceMetrics.computeIfAbsent(
dataSource, (unused) -> createMeters(openTelemetry, dataSource));
}

private static List<ObservableLongUpDownCounter> createMeters(
OpenTelemetry openTelemetry, ViburDBCPDataSource dataSource) {
DbConnectionPoolMetrics metrics =
DbConnectionPoolMetrics.create(openTelemetry, INSTRUMENTATION_NAME, dataSource.getName());

return Arrays.asList(
metrics.usedConnections(() -> dataSource.getPool().taken()),
metrics.idleConnections(() -> dataSource.getPool().remainingCreated()),
metrics.maxConnections(dataSource::getPoolMaxSize));
}

public static void unregisterMetrics(ViburDBCPDataSource dataSource) {
List<ObservableLongUpDownCounter> observableInstruments = dataSourceMetrics.remove(dataSource);
if (observableInstruments != null) {
for (ObservableLongUpDownCounter observable : observableInstruments) {
observable.close();
}
}
}

private ConnectionPoolMetrics() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.viburdbcp;

import io.opentelemetry.api.OpenTelemetry;
import org.vibur.dbcp.ViburDBCPDataSource;

/** Entrypoint for instrumenting Vibur database connection pools. */
public final class ViburTelemetry {

/** Returns a new {@link ViburTelemetry} configured with the given {@link OpenTelemetry}. */
public static ViburTelemetry create(OpenTelemetry openTelemetry) {
return new ViburTelemetry(openTelemetry);
}

private final OpenTelemetry openTelemetry;

private ViburTelemetry(OpenTelemetry openTelemetry) {
this.openTelemetry = openTelemetry;
}

/** Start collecting metrics for given data source. */
public void registerMetrics(ViburDBCPDataSource dataSource) {
ConnectionPoolMetrics.registerMetrics(openTelemetry, dataSource);
}

/** Stop collecting metrics for given data source. */
public void unregisterMetrics(ViburDBCPDataSource dataSource) {
ConnectionPoolMetrics.unregisterMetrics(dataSource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.viburdbcp;

import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.vibur.dbcp.ViburDBCPDataSource;

class ViburInstrumentationTest extends AbstractViburInstrumentationTest {

@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();

private static ViburTelemetry telemetry;

@Override
protected InstrumentationExtension testing() {
return testing;
}

@BeforeAll
static void setup() {
telemetry = ViburTelemetry.create(testing.getOpenTelemetry());
}

@Override
protected void configure(ViburDBCPDataSource viburDataSource) {
telemetry.registerMetrics(viburDataSource);
}

@Override
protected void shutdown(ViburDBCPDataSource viburDataSource) {
telemetry.unregisterMetrics(viburDataSource);
}
}
11 changes: 11 additions & 0 deletions instrumentation/vibur-dbcp-11.0/testing/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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("org.vibur:vibur-dbcp:11.0")
}
Loading

0 comments on commit b95b64b

Please sign in to comment.