diff --git a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java index e792130eeb..76b07961e1 100644 --- a/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java +++ b/instrumentation-api-semconv/src/main/java/io/opentelemetry/instrumentation/api/metrics/db/DbConnectionPoolMetrics.java @@ -10,13 +10,14 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.LongCounter; import io.opentelemetry.api.metrics.Meter; import io.opentelemetry.api.metrics.MeterBuilder; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; +import io.opentelemetry.api.metrics.ObservableMeasurement; import io.opentelemetry.instrumentation.api.internal.EmbeddedInstrumentationProperties; -import java.util.function.LongSupplier; /** * A helper class that models the - measurement.record(usedConnectionsGetter.getAsLong(), usedConnectionsAttributes)); + .buildObserver(); } - public ObservableLongUpDownCounter idleConnections(LongSupplier idleConnectionsGetter) { - return meter - .upDownCounterBuilder("db.client.connections.usage") - .setUnit("connections") - .setDescription( - "The number of connections that are currently in state described by the state attribute.") - .buildWithCallback( - measurement -> - measurement.record(idleConnectionsGetter.getAsLong(), idleConnectionsAttributes)); - } - - public ObservableLongUpDownCounter minIdleConnections(LongSupplier minIdleConnectionsGetter) { + public ObservableLongMeasurement minIdleConnections() { return meter .upDownCounterBuilder("db.client.connections.idle.min") .setUnit("connections") .setDescription("The minimum number of idle open connections allowed.") - .buildWithCallback( - measurement -> measurement.record(minIdleConnectionsGetter.getAsLong(), attributes)); + .buildObserver(); } - public ObservableLongUpDownCounter maxIdleConnections(LongSupplier maxIdleConnectionsGetter) { + public ObservableLongMeasurement maxIdleConnections() { return meter .upDownCounterBuilder("db.client.connections.idle.max") .setUnit("connections") .setDescription("The maximum number of idle open connections allowed.") - .buildWithCallback( - measurement -> measurement.record(maxIdleConnectionsGetter.getAsLong(), attributes)); + .buildObserver(); } - public ObservableLongUpDownCounter maxConnections(LongSupplier maxConnectionsGetter) { + public ObservableLongMeasurement maxConnections() { return meter .upDownCounterBuilder("db.client.connections.max") .setUnit("connections") .setDescription("The maximum number of open connections allowed.") - .buildWithCallback( - measurement -> measurement.record(maxConnectionsGetter.getAsLong(), attributes)); + .buildObserver(); } - public ObservableLongUpDownCounter pendingRequestsForConnection( - LongSupplier pendingRequestsGetter) { + public ObservableLongMeasurement pendingRequestsForConnection() { return meter .upDownCounterBuilder("db.client.connections.pending_requests") .setUnit("requests") .setDescription( "The number of pending requests for an open connection, cumulative for the entire pool.") - .buildWithCallback( - measurement -> measurement.record(pendingRequestsGetter.getAsLong(), attributes)); + .buildObserver(); + } + + public BatchCallback batchCallback( + Runnable callback, + ObservableMeasurement observableMeasurement, + ObservableMeasurement... additionalMeasurements) { + return meter.batchCallback(callback, observableMeasurement, additionalMeasurements); } // TODO: should be a BoundLongCounter @@ -151,8 +141,15 @@ public final class DbConnectionPoolMetrics { .build(); } - // TODO: should be removed once bound instruments are back public Attributes getAttributes() { return attributes; } + + public Attributes getUsedConnectionsAttributes() { + return usedConnectionsAttributes; + } + + public Attributes getIdleConnectionsAttributes() { + return idleConnectionsAttributes; + } } diff --git a/instrumentation/apache-dbcp-2.0/library/src/main/java/io/opentelemetry/instrumentation/apachedbcp/DataSourceMetrics.java b/instrumentation/apache-dbcp-2.0/library/src/main/java/io/opentelemetry/instrumentation/apachedbcp/DataSourceMetrics.java index faea361e3e..705aa516f2 100644 --- a/instrumentation/apache-dbcp-2.0/library/src/main/java/io/opentelemetry/instrumentation/apachedbcp/DataSourceMetrics.java +++ b/instrumentation/apache-dbcp-2.0/library/src/main/java/io/opentelemetry/instrumentation/apachedbcp/DataSourceMetrics.java @@ -6,10 +6,10 @@ package io.opentelemetry.instrumentation.apachedbcp; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; 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.apache.commons.dbcp2.BasicDataSourceMXBean; @@ -20,31 +20,44 @@ final class DataSourceMetrics { // a weak map does not make sense here because each Meter holds a reference to the dataSource // all instrumented/known implementations of BasicDataSourceMXBean do not implement // equals()/hashCode(), so it's safe to keep them in a plain ConcurrentHashMap - private static final Map> - dataSourceMetrics = new ConcurrentHashMap<>(); + private static final Map dataSourceMetrics = + new ConcurrentHashMap<>(); public static void registerMetrics( OpenTelemetry openTelemetry, BasicDataSourceMXBean dataSource, String dataSourceName) { DbConnectionPoolMetrics metrics = DbConnectionPoolMetrics.create(openTelemetry, INSTRUMENTATION_NAME, dataSourceName); - List meters = - Arrays.asList( - metrics.usedConnections(dataSource::getNumActive), - metrics.idleConnections(dataSource::getNumIdle), - metrics.minIdleConnections(dataSource::getMinIdle), - metrics.maxIdleConnections(dataSource::getMaxIdle), - metrics.maxConnections(dataSource::getMaxTotal)); + ObservableLongMeasurement connections = metrics.connections(); + ObservableLongMeasurement minIdleConnections = metrics.minIdleConnections(); + ObservableLongMeasurement maxIdleConnections = metrics.maxIdleConnections(); + ObservableLongMeasurement maxConnections = metrics.maxConnections(); - dataSourceMetrics.put(dataSource, meters); + Attributes attributes = metrics.getAttributes(); + Attributes usedConnectionsAttributes = metrics.getUsedConnectionsAttributes(); + Attributes idleConnectionsAttributes = metrics.getIdleConnectionsAttributes(); + + BatchCallback callback = + metrics.batchCallback( + () -> { + connections.record(dataSource.getNumActive(), usedConnectionsAttributes); + connections.record(dataSource.getNumIdle(), idleConnectionsAttributes); + minIdleConnections.record(dataSource.getMinIdle(), attributes); + maxIdleConnections.record(dataSource.getMaxIdle(), attributes); + maxConnections.record(dataSource.getMaxTotal(), attributes); + }, + connections, + minIdleConnections, + maxIdleConnections, + maxConnections); + + dataSourceMetrics.put(dataSource, callback); } public static void unregisterMetrics(BasicDataSourceMXBean dataSource) { - List observableInstruments = dataSourceMetrics.remove(dataSource); - if (observableInstruments != null) { - for (ObservableLongUpDownCounter observable : observableInstruments) { - observable.close(); - } + BatchCallback callback = dataSourceMetrics.remove(dataSource); + if (callback != null) { + callback.close(); } } diff --git a/instrumentation/c3p0-0.9/library/src/main/java/io/opentelemetry/instrumentation/c3p0/ConnectionPoolMetrics.java b/instrumentation/c3p0-0.9/library/src/main/java/io/opentelemetry/instrumentation/c3p0/ConnectionPoolMetrics.java index 6255771f98..d46b5d690f 100644 --- a/instrumentation/c3p0-0.9/library/src/main/java/io/opentelemetry/instrumentation/c3p0/ConnectionPoolMetrics.java +++ b/instrumentation/c3p0-0.9/library/src/main/java/io/opentelemetry/instrumentation/c3p0/ConnectionPoolMetrics.java @@ -7,38 +7,42 @@ package io.opentelemetry.instrumentation.c3p0; import com.mchange.v2.c3p0.PooledDataSource; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; import io.opentelemetry.instrumentation.api.metrics.db.DbConnectionPoolMetrics; import java.sql.SQLException; -import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.LongSupplier; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.annotation.Nullable; final class ConnectionPoolMetrics { + + private static final Logger logger = Logger.getLogger(ConnectionPoolMetrics.class.getName()); + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.c3p0-0.9"; // a weak map does not make sense here because each Meter holds a reference to the dataSource // PooledDataSource implements equals() & hashCode() in IdentityTokenResolvable, // that's why we wrap it with IdentityDataSourceKey that uses identity comparison instead - private static final Map> - dataSourceMetrics = new ConcurrentHashMap<>(); + private static final Map dataSourceMetrics = + new ConcurrentHashMap<>(); public static void registerMetrics(OpenTelemetry openTelemetry, PooledDataSource dataSource) { dataSourceMetrics.compute( new IdentityDataSourceKey(dataSource), - (key, existingCounters) -> - ConnectionPoolMetrics.createMeters(openTelemetry, key, existingCounters)); + (key, existingCallback) -> + ConnectionPoolMetrics.createMeters(openTelemetry, key, existingCallback)); } - private static List createMeters( + private static BatchCallback createMeters( OpenTelemetry openTelemetry, IdentityDataSourceKey key, - List existingCounters) { + @Nullable BatchCallback existingCallback) { // remove old counters from the registry in case they were already there - removeMetersFromRegistry(existingCounters); + removeMetersFromRegistry(existingCallback); PooledDataSource dataSource = key.dataSource; @@ -46,25 +50,38 @@ final class ConnectionPoolMetrics { DbConnectionPoolMetrics.create( openTelemetry, INSTRUMENTATION_NAME, dataSource.getDataSourceName()); - return Arrays.asList( - metrics.usedConnections(wrapThrowingSupplier(dataSource::getNumBusyConnectionsDefaultUser)), - metrics.idleConnections(wrapThrowingSupplier(dataSource::getNumIdleConnectionsDefaultUser)), - metrics.pendingRequestsForConnection( - wrapThrowingSupplier(dataSource::getNumThreadsAwaitingCheckoutDefaultUser))); + ObservableLongMeasurement connections = metrics.connections(); + ObservableLongMeasurement pendingRequestsForConnection = metrics.pendingRequestsForConnection(); + + Attributes attributes = metrics.getAttributes(); + Attributes usedConnectionsAttributes = metrics.getUsedConnectionsAttributes(); + Attributes idleConnectionsAttributes = metrics.getIdleConnectionsAttributes(); + + return metrics.batchCallback( + () -> { + try { + connections.record( + dataSource.getNumBusyConnectionsDefaultUser(), usedConnectionsAttributes); + connections.record( + dataSource.getNumIdleConnectionsDefaultUser(), idleConnectionsAttributes); + pendingRequestsForConnection.record( + dataSource.getNumThreadsAwaitingCheckoutDefaultUser(), attributes); + } catch (SQLException e) { + logger.log(Level.FINE, "Failed to get C3P0 datasource metric", e); + } + }, + connections, + pendingRequestsForConnection); } public static void unregisterMetrics(PooledDataSource dataSource) { - List meters = - dataSourceMetrics.remove(new IdentityDataSourceKey(dataSource)); - removeMetersFromRegistry(meters); + BatchCallback callback = dataSourceMetrics.remove(new IdentityDataSourceKey(dataSource)); + removeMetersFromRegistry(callback); } - private static void removeMetersFromRegistry( - @Nullable List observableInstruments) { - if (observableInstruments != null) { - for (ObservableLongUpDownCounter observable : observableInstruments) { - observable.close(); - } + private static void removeMetersFromRegistry(@Nullable BatchCallback callback) { + if (callback != null) { + callback.close(); } } @@ -103,20 +120,5 @@ final class ConnectionPoolMetrics { } } - static LongSupplier wrapThrowingSupplier(DataSourceIntSupplier supplier) { - return () -> { - try { - return supplier.getAsInt(); - } catch (SQLException e) { - throw new IllegalStateException("Failed to get C3P0 datasource metric", e); - } - }; - } - - @FunctionalInterface - interface DataSourceIntSupplier { - int getAsInt() throws SQLException; - } - private ConnectionPoolMetrics() {} } diff --git a/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTracker.java b/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTracker.java index a472c673a6..51481a174e 100644 --- a/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTracker.java +++ b/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTracker.java @@ -7,10 +7,9 @@ package io.opentelemetry.instrumentation.hikaricp; import com.zaxxer.hikari.metrics.IMetricsTracker; import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.api.metrics.LongCounter; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; -import java.util.List; import java.util.concurrent.TimeUnit; final class OpenTelemetryMetricsTracker implements IMetricsTracker { @@ -19,7 +18,7 @@ final class OpenTelemetryMetricsTracker implements IMetricsTracker { private final IMetricsTracker userMetricsTracker; - private final List observableInstruments; + private final BatchCallback callback; private final LongCounter timeouts; private final DoubleHistogram createTime; private final DoubleHistogram waitTime; @@ -28,14 +27,14 @@ final class OpenTelemetryMetricsTracker implements IMetricsTracker { OpenTelemetryMetricsTracker( IMetricsTracker userMetricsTracker, - List observableInstruments, + BatchCallback callback, LongCounter timeouts, DoubleHistogram createTime, DoubleHistogram waitTime, DoubleHistogram useTime, Attributes attributes) { this.userMetricsTracker = userMetricsTracker; - this.observableInstruments = observableInstruments; + this.callback = callback; this.timeouts = timeouts; this.createTime = createTime; this.waitTime = waitTime; @@ -70,9 +69,7 @@ final class OpenTelemetryMetricsTracker implements IMetricsTracker { @Override public void close() { - for (ObservableLongUpDownCounter observable : observableInstruments) { - observable.close(); - } + callback.close(); userMetricsTracker.close(); } } diff --git a/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTrackerFactory.java b/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTrackerFactory.java index 6e4095f312..7303990bc6 100644 --- a/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTrackerFactory.java +++ b/instrumentation/hikaricp-3.0/library/src/main/java/io/opentelemetry/instrumentation/hikaricp/OpenTelemetryMetricsTrackerFactory.java @@ -9,10 +9,10 @@ import com.zaxxer.hikari.metrics.IMetricsTracker; import com.zaxxer.hikari.metrics.MetricsTrackerFactory; import com.zaxxer.hikari.metrics.PoolStats; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; import io.opentelemetry.instrumentation.api.metrics.db.DbConnectionPoolMetrics; -import java.util.Arrays; -import java.util.List; import javax.annotation.Nullable; final class OpenTelemetryMetricsTrackerFactory implements MetricsTrackerFactory { @@ -38,17 +38,32 @@ final class OpenTelemetryMetricsTrackerFactory implements MetricsTrackerFactory DbConnectionPoolMetrics metrics = DbConnectionPoolMetrics.create(openTelemetry, INSTRUMENTATION_NAME, poolName); - List observableInstruments = - Arrays.asList( - metrics.usedConnections(poolStats::getActiveConnections), - metrics.idleConnections(poolStats::getIdleConnections), - metrics.minIdleConnections(poolStats::getMinConnections), - metrics.maxConnections(poolStats::getMaxConnections), - metrics.pendingRequestsForConnection(poolStats::getPendingThreads)); + ObservableLongMeasurement connections = metrics.connections(); + ObservableLongMeasurement minIdleConnections = metrics.minIdleConnections(); + ObservableLongMeasurement maxConnections = metrics.maxConnections(); + ObservableLongMeasurement pendingRequestsForConnection = metrics.pendingRequestsForConnection(); + + Attributes attributes = metrics.getAttributes(); + Attributes usedConnectionsAttributes = metrics.getUsedConnectionsAttributes(); + Attributes idleConnectionsAttributes = metrics.getIdleConnectionsAttributes(); + + BatchCallback callback = + metrics.batchCallback( + () -> { + connections.record(poolStats.getActiveConnections(), usedConnectionsAttributes); + connections.record(poolStats.getIdleConnections(), idleConnectionsAttributes); + minIdleConnections.record(poolStats.getMinConnections(), attributes); + maxConnections.record(poolStats.getMaxConnections(), attributes); + pendingRequestsForConnection.record(poolStats.getPendingThreads(), attributes); + }, + connections, + minIdleConnections, + maxConnections, + pendingRequestsForConnection); return new OpenTelemetryMetricsTracker( userMetricsTracker, - observableInstruments, + callback, metrics.connectionTimeouts(), metrics.connectionCreateTime(), metrics.connectionWaitTime(), 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 index 5f8cf02b1b..7defb03d2c 100644 --- 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 @@ -6,10 +6,10 @@ package io.opentelemetry.instrumentation.oracleucp; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; 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; @@ -20,8 +20,8 @@ final class ConnectionPoolMetrics { // 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<>(); + private static final Map dataSourceMetrics = + new ConcurrentHashMap<>(); public static void registerMetrics( OpenTelemetry openTelemetry, UniversalConnectionPool connectionPool) { @@ -29,27 +29,40 @@ final class ConnectionPoolMetrics { connectionPool, (unused) -> createMeters(openTelemetry, connectionPool)); } - private static List createMeters( + private static BatchCallback 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())); + ObservableLongMeasurement connections = metrics.connections(); + ObservableLongMeasurement maxConnections = metrics.maxConnections(); + ObservableLongMeasurement pendingRequestsForConnection = metrics.pendingRequestsForConnection(); + + Attributes attributes = metrics.getAttributes(); + Attributes usedConnectionsAttributes = metrics.getUsedConnectionsAttributes(); + Attributes idleConnectionsAttributes = metrics.getIdleConnectionsAttributes(); + + return metrics.batchCallback( + () -> { + connections.record( + connectionPool.getBorrowedConnectionsCount(), usedConnectionsAttributes); + connections.record( + connectionPool.getAvailableConnectionsCount(), idleConnectionsAttributes); + maxConnections.record( + connectionPool.getStatistics().getPeakConnectionsCount(), attributes); + pendingRequestsForConnection.record( + connectionPool.getStatistics().getPendingRequestsCount(), attributes); + }, + connections, + maxConnections, + pendingRequestsForConnection); } public static void unregisterMetrics(UniversalConnectionPool connectionPool) { - List observableInstruments = - dataSourceMetrics.remove(connectionPool); - if (observableInstruments != null) { - for (ObservableLongUpDownCounter observable : observableInstruments) { - observable.close(); - } + BatchCallback callback = dataSourceMetrics.remove(connectionPool); + if (callback != null) { + callback.close(); } } diff --git a/instrumentation/tomcat/tomcat-jdbc/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatConnectionPoolMetrics.java b/instrumentation/tomcat/tomcat-jdbc/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatConnectionPoolMetrics.java index 79734df8e8..551c3a9ea9 100644 --- a/instrumentation/tomcat/tomcat-jdbc/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatConnectionPoolMetrics.java +++ b/instrumentation/tomcat/tomcat-jdbc/src/main/java/io/opentelemetry/javaagent/instrumentation/tomcat/jdbc/TomcatConnectionPoolMetrics.java @@ -7,10 +7,10 @@ package io.opentelemetry.javaagent.instrumentation.tomcat.jdbc; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; 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.apache.tomcat.jdbc.pool.DataSourceProxy; @@ -23,34 +23,48 @@ public final class TomcatConnectionPoolMetrics { // a weak map does not make sense here because each Meter holds a reference to the dataSource // DataSourceProxy does not implement equals()/hashCode(), so it's safe to keep them in a plain // ConcurrentHashMap - private static final Map> dataSourceMetrics = + private static final Map dataSourceMetrics = new ConcurrentHashMap<>(); public static void registerMetrics(DataSourceProxy dataSource) { - dataSourceMetrics.computeIfAbsent(dataSource, TomcatConnectionPoolMetrics::createCounters); + dataSourceMetrics.computeIfAbsent(dataSource, TomcatConnectionPoolMetrics::createInstruments); } - private static List createCounters(DataSourceProxy dataSource) { - + private static BatchCallback createInstruments(DataSourceProxy dataSource) { DbConnectionPoolMetrics metrics = DbConnectionPoolMetrics.create( openTelemetry, INSTRUMENTATION_NAME, dataSource.getPoolName()); - return Arrays.asList( - metrics.usedConnections(dataSource::getActive), - metrics.idleConnections(dataSource::getIdle), - metrics.minIdleConnections(dataSource::getMinIdle), - metrics.maxIdleConnections(dataSource::getMaxIdle), - metrics.maxConnections(dataSource::getMaxActive), - metrics.pendingRequestsForConnection(dataSource::getWaitCount)); + ObservableLongMeasurement connections = metrics.connections(); + ObservableLongMeasurement minIdleConnections = metrics.minIdleConnections(); + ObservableLongMeasurement maxIdleConnections = metrics.maxIdleConnections(); + ObservableLongMeasurement maxConnections = metrics.maxConnections(); + ObservableLongMeasurement pendingRequestsForConnection = metrics.pendingRequestsForConnection(); + + Attributes attributes = metrics.getAttributes(); + Attributes usedConnectionsAttributes = metrics.getUsedConnectionsAttributes(); + Attributes idleConnectionsAttributes = metrics.getIdleConnectionsAttributes(); + + return metrics.batchCallback( + () -> { + connections.record(dataSource.getActive(), usedConnectionsAttributes); + connections.record(dataSource.getIdle(), idleConnectionsAttributes); + minIdleConnections.record(dataSource.getMinIdle(), attributes); + maxIdleConnections.record(dataSource.getMaxIdle(), attributes); + maxConnections.record(dataSource.getMaxActive(), attributes); + pendingRequestsForConnection.record(dataSource.getWaitCount(), attributes); + }, + connections, + minIdleConnections, + maxIdleConnections, + maxConnections, + pendingRequestsForConnection); } public static void unregisterMetrics(DataSourceProxy dataSource) { - List counters = dataSourceMetrics.remove(dataSource); - if (counters != null) { - for (ObservableLongUpDownCounter meter : counters) { - meter.close(); - } + BatchCallback callback = dataSourceMetrics.remove(dataSource); + if (callback != null) { + callback.close(); } } diff --git a/instrumentation/vibur-dbcp-11.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/viburdbcp/ViburDbcpInstrumentationModule.java b/instrumentation/vibur-dbcp-11.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/viburdbcp/ViburDbcpInstrumentationModule.java index ea83bd6b8c..b3a3387c72 100644 --- a/instrumentation/vibur-dbcp-11.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/viburdbcp/ViburDbcpInstrumentationModule.java +++ b/instrumentation/vibur-dbcp-11.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/viburdbcp/ViburDbcpInstrumentationModule.java @@ -5,12 +5,14 @@ package io.opentelemetry.javaagent.instrumentation.viburdbcp; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; 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; +import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumentationModule.class) public class ViburDbcpInstrumentationModule extends InstrumentationModule { @@ -18,6 +20,14 @@ public class ViburDbcpInstrumentationModule extends InstrumentationModule { super("vibur-dbcp", "vibur-dbcp-11.0"); } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // ViburDBCPConfig was renamed to ViburConfig in 10.0; this matcher excludes all versions < 10.0 + // in 11.0, the ViburDBCPDataSource#getPool() method signature was changed - this is detected by + // muzzle + return hasClassesNamed("org.vibur.dbcp.ViburConfig"); + } + @Override public List typeInstrumentations() { return singletonList(new ViburDbcpDataSourceInstrumentation()); diff --git a/instrumentation/vibur-dbcp-11.0/library/src/main/java/io/opentelemetry/instrumentation/viburdbcp/ConnectionPoolMetrics.java b/instrumentation/vibur-dbcp-11.0/library/src/main/java/io/opentelemetry/instrumentation/viburdbcp/ConnectionPoolMetrics.java index 53ae305e86..af8eb383dc 100644 --- a/instrumentation/vibur-dbcp-11.0/library/src/main/java/io/opentelemetry/instrumentation/viburdbcp/ConnectionPoolMetrics.java +++ b/instrumentation/vibur-dbcp-11.0/library/src/main/java/io/opentelemetry/instrumentation/viburdbcp/ConnectionPoolMetrics.java @@ -6,10 +6,10 @@ package io.opentelemetry.instrumentation.viburdbcp; import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.metrics.ObservableLongUpDownCounter; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.api.metrics.BatchCallback; +import io.opentelemetry.api.metrics.ObservableLongMeasurement; 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; @@ -20,31 +20,40 @@ final class ConnectionPoolMetrics { // 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> - dataSourceMetrics = new ConcurrentHashMap<>(); + private static final Map dataSourceMetrics = + new ConcurrentHashMap<>(); public static void registerMetrics(OpenTelemetry openTelemetry, ViburDBCPDataSource dataSource) { dataSourceMetrics.computeIfAbsent( dataSource, (unused) -> createMeters(openTelemetry, dataSource)); } - private static List createMeters( + private static BatchCallback 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)); + ObservableLongMeasurement connections = metrics.connections(); + ObservableLongMeasurement maxConnections = metrics.maxConnections(); + + Attributes attributes = metrics.getAttributes(); + Attributes usedConnectionsAttributes = metrics.getUsedConnectionsAttributes(); + Attributes idleConnectionsAttributes = metrics.getIdleConnectionsAttributes(); + + return metrics.batchCallback( + () -> { + connections.record(dataSource.getPool().taken(), usedConnectionsAttributes); + connections.record(dataSource.getPool().remainingCreated(), idleConnectionsAttributes); + maxConnections.record(dataSource.getPoolMaxSize(), attributes); + }, + connections, + maxConnections); } public static void unregisterMetrics(ViburDBCPDataSource dataSource) { - List observableInstruments = dataSourceMetrics.remove(dataSource); - if (observableInstruments != null) { - for (ObservableLongUpDownCounter observable : observableInstruments) { - observable.close(); - } + BatchCallback callback = dataSourceMetrics.remove(dataSource); + if (callback != null) { + callback.close(); } }