-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added metrics for OkHttp connection pool #1875
- Loading branch information
Showing
4 changed files
with
252 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
...c/main/java/io/micrometer/core/instrument/binder/okhttp3/OkHttpConnectionPoolMetrics.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package io.micrometer.core.instrument.binder.okhttp3; | ||
|
||
import io.micrometer.core.instrument.Gauge; | ||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.micrometer.core.instrument.Tag; | ||
import io.micrometer.core.instrument.Tags; | ||
import io.micrometer.core.instrument.binder.MeterBinder; | ||
import okhttp3.ConnectionPool; | ||
|
||
import java.util.Collections; | ||
import java.util.Optional; | ||
|
||
/** | ||
* MeterBinder for collecting metrics of a given OkHttp {@link ConnectionPool}. | ||
* | ||
* Example usage: | ||
* <pre> | ||
* return new ConnectionPool(connectionPoolSize, connectionPoolKeepAliveMs, TimeUnit.MILLISECONDS); | ||
* new OkHttpConnectionPoolMetrics(connectionPool, "okhttp.pool", Tags.of()); | ||
* </pre> | ||
*/ | ||
public class OkHttpConnectionPoolMetrics implements MeterBinder { | ||
|
||
private static final String DEFAULT_NAME = "okhttp.pool"; | ||
|
||
private final ConnectionPool connectionPool; | ||
private final String name; | ||
private final Iterable<Tag> tags; | ||
private final Double maxIdleConnectionCount; | ||
|
||
/** | ||
* Creates a meter binder for the given connection pool. | ||
* Metrics will be exposed using {@link #DEFAULT_NAME} as name. | ||
* | ||
* @param connectionPool The connection pool to monitor. Must not be null. | ||
*/ | ||
public OkHttpConnectionPoolMetrics(ConnectionPool connectionPool) { | ||
this(connectionPool, DEFAULT_NAME, Collections.emptyList(), null); | ||
} | ||
|
||
/** | ||
* Creates a meter binder for the given connection pool. | ||
* | ||
* @param connectionPool The connection pool to monitor. Must not be null. | ||
* @param name The desired name for the exposed metrics. Must not be null. | ||
*/ | ||
public OkHttpConnectionPoolMetrics(ConnectionPool connectionPool, String name) { | ||
this(connectionPool, name, Collections.emptyList(), null); | ||
} | ||
|
||
/** | ||
* Creates a meter binder for the given connection pool. | ||
* Metrics will be exposed using {@link #DEFAULT_NAME} as name. | ||
* | ||
* @param connectionPool The connection pool to monitor. Must not be null. | ||
* @param tags A list of tags which will be passed for all meters. Must not be null. | ||
*/ | ||
public OkHttpConnectionPoolMetrics(ConnectionPool connectionPool, Iterable<Tag> tags) { | ||
this(connectionPool, DEFAULT_NAME, tags, null); | ||
} | ||
|
||
/** | ||
* Creates a meter binder for the given connection pool. | ||
* | ||
* @param connectionPool The connection pool to monitor. Must not be null. | ||
* @param name The desired name for the exposed metrics. Must not be null. | ||
* @param tags A list of tags which will be passed for all meters. Must not be null. | ||
*/ | ||
public OkHttpConnectionPoolMetrics(ConnectionPool connectionPool, String name, Iterable<Tag> tags) { | ||
this(connectionPool, name, tags, null); | ||
} | ||
|
||
/** | ||
* Creates a meter binder for the given connection pool. | ||
* | ||
* @param connectionPool The connection pool to monitor. Must not be null. | ||
* @param name The desired name for the exposed metrics. Must not be null. | ||
* @param tags A list of tags which will be passed for all meters. Must not be null. | ||
* @param maxIdleConnections The maximum number of idle connections this pool will hold. This | ||
* value is passed to the {@link ConnectionPool} constructor but is | ||
* not exposed by this instance. Therefore this meter allows to pass | ||
* it, to be able to monitor it. | ||
*/ | ||
public OkHttpConnectionPoolMetrics(ConnectionPool connectionPool, String name, Iterable<Tag> tags, Integer maxIdleConnections) { | ||
if (connectionPool == null) { | ||
throw new IllegalArgumentException("Given ConnectionPool must not be null."); | ||
} | ||
if (name == null) { | ||
throw new IllegalArgumentException("Given name must not be null."); | ||
} | ||
if (tags == null) { | ||
throw new IllegalArgumentException("Given list of tags must not be null."); | ||
} | ||
this.connectionPool = connectionPool; | ||
this.name = name; | ||
this.tags = tags; | ||
this.maxIdleConnectionCount = Optional.ofNullable(maxIdleConnections) | ||
.map(Integer::doubleValue) | ||
.orElse(null); | ||
} | ||
|
||
@Override | ||
public void bindTo(MeterRegistry registry) { | ||
Gauge.builder(name + ".connection.count", () -> Integer.valueOf(connectionPool.connectionCount()).doubleValue()) | ||
.tags(Tags.of(tags).and("state", "total")) | ||
.register(registry); | ||
Gauge.builder(name + ".connection.count", () -> Integer.valueOf(connectionPool.idleConnectionCount()).doubleValue()) | ||
.tags(Tags.of(tags).and("state", "idle")) | ||
.register(registry); | ||
if (this.maxIdleConnectionCount != null) { | ||
Gauge.builder(name + ".connection.limit", () -> this.maxIdleConnectionCount) | ||
.tags(Tags.of(tags).and("state", "idle")) | ||
.register(registry); | ||
} | ||
} | ||
|
||
} |
133 changes: 133 additions & 0 deletions
133
...st/java/io/micrometer/core/instrument/binder/okhttp3/OkHttpConnectionPoolMetricsTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
package io.micrometer.core.instrument.binder.okhttp3; | ||
|
||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.micrometer.core.instrument.MockClock; | ||
import io.micrometer.core.instrument.Tags; | ||
import io.micrometer.core.instrument.search.MeterNotFoundException; | ||
import io.micrometer.core.instrument.simple.SimpleConfig; | ||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry; | ||
import okhttp3.ConnectionPool; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.junit.jupiter.api.Assertions.assertThrows; | ||
import static org.mockito.Mockito.mock; | ||
import static org.mockito.Mockito.when; | ||
|
||
class OkHttpConnectionPoolMetricsTest { | ||
|
||
private MeterRegistry registry; | ||
private ConnectionPool connectionPool; | ||
|
||
@BeforeEach | ||
void setup() { | ||
registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); | ||
connectionPool = mock(ConnectionPool.class); | ||
} | ||
|
||
@Test | ||
void creationWithNullConnectionPoolThrowsException() { | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(null); | ||
}); | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(null, "irrelevant"); | ||
}); | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(null, Tags.empty()); | ||
}); | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(null, "irrelevant", Tags.empty()); | ||
}); | ||
} | ||
|
||
@Test | ||
void creationWithNullNameThrowsException() { | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(connectionPool, (String) null); | ||
}); | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(connectionPool, null, Tags.empty()); | ||
}); | ||
} | ||
|
||
@Test | ||
void creationWithNullTagsThrowsException() { | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(connectionPool, (Tags) null); | ||
}); | ||
assertThrows(IllegalArgumentException.class, () -> { | ||
new OkHttpConnectionPoolMetrics(connectionPool, "irrelevant.name", null); | ||
}); | ||
} | ||
|
||
@Test | ||
void instanceUsesDefaultName() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool); | ||
instance.bindTo(registry); | ||
registry.get("okhttp.pool.connection.count"); // does not throw MeterNotFoundException | ||
} | ||
|
||
@Test | ||
void instanceUsesDefaultNameAndGivenTag() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, Tags.of("foo", "bar")); | ||
instance.bindTo(registry); | ||
registry.get("okhttp.pool.connection.count").tags("foo", "bar"); // does not throw MeterNotFoundException | ||
} | ||
|
||
@Test | ||
void instanceUsesGivenName() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, "some.meter"); | ||
instance.bindTo(registry); | ||
registry.get("some.meter.connection.count"); // does not throw MeterNotFoundException | ||
} | ||
|
||
@Test | ||
void instanceUsesGivenNameAndTag() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, "another.meter", Tags.of("bar", "baz")); | ||
instance.bindTo(registry); | ||
registry.get("another.meter.connection.count").tags("bar", "baz"); // does not throw MeterNotFoundException | ||
} | ||
|
||
@Test | ||
void total() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, Tags.of("foo", "bar")); | ||
instance.bindTo(registry); | ||
when(connectionPool.connectionCount()).thenReturn(17); | ||
assertThat(registry.get("okhttp.pool.connection.count") | ||
.tags(Tags.of("foo", "bar").and("state", "total")) | ||
.gauge().value()).isEqualTo(17.0); | ||
} | ||
|
||
@Test | ||
void idle() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, Tags.of("foo", "bar")); | ||
instance.bindTo(registry); | ||
when(connectionPool.idleConnectionCount()).thenReturn(13); | ||
assertThat(registry.get("okhttp.pool.connection.count") | ||
.tags(Tags.of("foo", "bar").and("state", "idle")) | ||
.gauge().value()).isEqualTo(13.0); | ||
} | ||
|
||
@Test | ||
void maxIfGiven() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, "huge.pool", Tags.of("foo", "bar"), 1234); | ||
instance.bindTo(registry); | ||
assertThat(registry.get("huge.pool.connection.limit") | ||
.tags(Tags.of("foo", "bar")) | ||
.gauge().value()).isEqualTo(1234.0); | ||
} | ||
|
||
@Test | ||
void maxIfNotGiven() { | ||
OkHttpConnectionPoolMetrics instance = new OkHttpConnectionPoolMetrics(connectionPool, "huge.pool", Tags.of("foo", "bar"), null); | ||
instance.bindTo(registry); | ||
assertThrows(MeterNotFoundException.class, () -> { | ||
registry.get("huge.pool.connection.limit") | ||
.tags(Tags.of("foo", "bar")) | ||
.gauge(); | ||
}); | ||
} | ||
|
||
} |