mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-08 21:38:15 -05:00
Migrate to Prometheus lib 1.x (#7880)
* Upgrade to Promethus java client 1.x and adapt the code to the new version Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> * Update CHANGELOG.md Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com> Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> --------- Signed-off-by: Fabio Di Fabio <fabio.difabio@consensys.net> Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
This commit is contained in:
16
CHANGELOG.md
16
CHANGELOG.md
@@ -4,6 +4,21 @@
|
||||
|
||||
### Breaking Changes
|
||||
- Removed Retesteth rpc service and commands [#7833](https://github.com/hyperledger/besu/pull/7783)
|
||||
- With the upgrade of the Prometheus Java Metrics library, there are the following changes:
|
||||
- Gauge names are not allowed to end with `total`, therefore the metric `besu_blockchain_difficulty_total` is losing the `_total` suffix
|
||||
- The `_created` timestamps are not returned by default, you can set the env var `BESU_OPTS="-Dio.prometheus.exporter.includeCreatedTimestamps=true"` to enable them
|
||||
- Some JVM metrics have changed name to adhere to the OTEL standard (see the table below), [Besu Full Grafana dashboard](https://grafana.com/grafana/dashboards/16455-besu-full/) is updated to support both names
|
||||
|
||||
| Old Name | New Name |
|
||||
|---------------------------------|---------------------------------|
|
||||
| jvm_memory_bytes_committed | jvm_memory_committed_bytes |
|
||||
| jvm_memory_bytes_init | jvm_memory_init_bytes |
|
||||
| jvm_memory_bytes_max | jvm_memory_max_bytes |
|
||||
| jvm_memory_bytes_used | jvm_memory_used_bytes |
|
||||
| jvm_memory_pool_bytes_committed | jvm_memory_pool_committed_bytes |
|
||||
| jvm_memory_pool_bytes_init | jvm_memory_pool_init_bytes |
|
||||
| jvm_memory_pool_bytes_max | jvm_memory_pool_max_bytes |
|
||||
| jvm_memory_pool_bytes_used | jvm_memory_pool_used_bytes |
|
||||
|
||||
### Upcoming Breaking Changes
|
||||
- Plugin API will be deprecating the BesuContext interface to be replaced with the ServiceManager interface.
|
||||
@@ -26,6 +41,7 @@
|
||||
- Add a method to check if a metric category is enabled to the plugin API [#7832](https://github.com/hyperledger/besu/pull/7832)
|
||||
- Add a new metric collector for counters which get their value from suppliers [#7894](https://github.com/hyperledger/besu/pull/7894)
|
||||
- Add account and state overrides to `eth_call` [#7801](https://github.com/hyperledger/besu/pull/7801) and `eth_estimateGas` [#7890](https://github.com/hyperledger/besu/pull/7890)
|
||||
- Prometheus Java Metrics library upgraded to version 1.3.3 [#7880](https://github.com/hyperledger/besu/pull/7880)
|
||||
|
||||
### Bug fixes
|
||||
- Fix registering new metric categories from plugins [#7825](https://github.com/hyperledger/besu/pull/7825)
|
||||
|
||||
@@ -1034,8 +1034,7 @@ public class RunnerBuilder {
|
||||
subscriptionManager, privacyParameters, context.getBlockchain().getGenesisBlockHeader());
|
||||
}
|
||||
|
||||
final Optional<MetricsService> metricsService =
|
||||
createMetricsService(vertx, metricsConfiguration);
|
||||
final Optional<MetricsService> metricsService = createMetricsService(metricsConfiguration);
|
||||
|
||||
final Optional<EthStatsService> ethStatsService;
|
||||
if (isEthStatsEnabled()) {
|
||||
@@ -1469,9 +1468,8 @@ public class RunnerBuilder {
|
||||
vertx, configuration, websocketMessageHandler, authenticationService, metricsSystem);
|
||||
}
|
||||
|
||||
private Optional<MetricsService> createMetricsService(
|
||||
final Vertx vertx, final MetricsConfiguration configuration) {
|
||||
return MetricsService.create(vertx, configuration, metricsSystem);
|
||||
private Optional<MetricsService> createMetricsService(final MetricsConfiguration configuration) {
|
||||
return MetricsService.create(configuration, metricsSystem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -53,7 +53,6 @@ import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.slf4j.Logger;
|
||||
@@ -458,8 +457,7 @@ public class BlocksSubCommand implements Runnable {
|
||||
parentCommand.parentCommand.metricsConfiguration();
|
||||
|
||||
Optional<MetricsService> metricsService =
|
||||
MetricsService.create(
|
||||
Vertx.vertx(), metricsConfiguration, parentCommand.parentCommand.getMetricsSystem());
|
||||
MetricsService.create(metricsConfiguration, parentCommand.parentCommand.getMetricsSystem());
|
||||
metricsService.ifPresent(MetricsService::start);
|
||||
return metricsService;
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ public class DebugMetrics implements JsonRpcMethod {
|
||||
private void addObservation(
|
||||
final Map<String, Object> observations, final Observation observation) {
|
||||
final Map<String, Object> categoryObservations =
|
||||
getNextMapLevel(observations, observation.getCategory().getName());
|
||||
if (observation.getLabels().isEmpty()) {
|
||||
categoryObservations.put(observation.getMetricName(), observation.getValue());
|
||||
getNextMapLevel(observations, observation.category().getName());
|
||||
if (observation.labels().isEmpty()) {
|
||||
categoryObservations.put(observation.metricName(), observation.value());
|
||||
} else {
|
||||
addLabelledObservation(categoryObservations, observation);
|
||||
}
|
||||
@@ -60,12 +60,12 @@ public class DebugMetrics implements JsonRpcMethod {
|
||||
|
||||
private void addLabelledObservation(
|
||||
final Map<String, Object> categoryObservations, final Observation observation) {
|
||||
final List<String> labels = observation.getLabels();
|
||||
Map<String, Object> values = getNextMapLevel(categoryObservations, observation.getMetricName());
|
||||
final List<String> labels = observation.labels();
|
||||
Map<String, Object> values = getNextMapLevel(categoryObservations, observation.metricName());
|
||||
for (int i = 0; i < labels.size() - 1; i++) {
|
||||
values = getNextMapLevel(values, labels.get(i));
|
||||
}
|
||||
values.put(labels.get(labels.size() - 1), observation.getValue());
|
||||
values.put(labels.get(labels.size() - 1), observation.value());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
@@ -183,7 +183,7 @@ public class DefaultBlockchain implements MutableBlockchain {
|
||||
|
||||
metricsSystem.createGauge(
|
||||
BLOCKCHAIN,
|
||||
"difficulty_total",
|
||||
"difficulty",
|
||||
"Total difficulty of the chainhead",
|
||||
() -> this.getChainHead().getTotalDifficulty().toBigInteger().doubleValue());
|
||||
|
||||
|
||||
@@ -464,35 +464,6 @@
|
||||
<sha256 value="463f4f89638aae82c654b3f2842e8d03f7a0d48194c426046dd9c95ce06326ee" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.docker-java" name="docker-java-api" version="3.4.0">
|
||||
<artifact name="docker-java-api-3.4.0.jar">
|
||||
<sha256 value="ad8e9f748380985e0b702bfb2356749a0966afa28a7d637aa3211217ae6a6f2e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="docker-java-api-3.4.0.pom">
|
||||
<sha256 value="241a015f07a6044f3e23dd33ad9a9c19a32acaa84a36cbda872d9fb19e4d1f6a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.docker-java" name="docker-java-parent" version="3.4.0">
|
||||
<artifact name="docker-java-parent-3.4.0.pom">
|
||||
<sha256 value="80fd330ede28e225aa43dee9de1f06bb2100f4c438de7af2f2c8f706412abbb0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.docker-java" name="docker-java-transport" version="3.4.0">
|
||||
<artifact name="docker-java-transport-3.4.0.jar">
|
||||
<sha256 value="a1a8ce872dbf92423a948443b88b9417283ead5d56cb5ed77803353658b97b34" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="docker-java-transport-3.4.0.pom">
|
||||
<sha256 value="56953cb59d2b5f3288d9ba2dbc6545fecb6f4012cc2216f1e4140cf6bd8137d4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.docker-java" name="docker-java-transport-zerodep" version="3.4.0">
|
||||
<artifact name="docker-java-transport-zerodep-3.4.0.jar">
|
||||
<sha256 value="aac3ba9ed78c73961d13d7b7dac51ba51fe9089629efd4f77ffa4e6e8c6e4048" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="docker-java-transport-zerodep-3.4.0.pom">
|
||||
<sha256 value="63a381030f3d7c7b236ebdb2c49be1c9ed377cb0e4ef1a945f21565abc7c4823" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.jk1" name="gradle-license-report" version="2.9">
|
||||
<artifact name="gradle-license-report-2.9.jar">
|
||||
<sha256 value="ebfd6da851654c53216eea9eda1485c12e0cd6de5a9919bf5da9735a021f32af" origin="Generated by Gradle"/>
|
||||
@@ -1924,14 +1895,6 @@
|
||||
<sha256 value="c8f826b8fcc7066adfd657d871540236a11424c18a4b38746855895f1112ac2a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-all" version="4.1.114.Final">
|
||||
<artifact name="netty-all-4.1.114.Final.jar">
|
||||
<sha256 value="bb34d3f8124cc27ed9a8f385ce216f409646ffddd1a9b9b163dec7720c7f849a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-all-4.1.114.Final.pom">
|
||||
<sha256 value="cf743ff8a0143fe3fc6a803f0b225014c23b5a2ea25632bf704c07845386d3af" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-all" version="4.1.115.Final">
|
||||
<artifact name="netty-all-4.1.115.Final.jar">
|
||||
<sha256 value="c1c3092e02e8037c18d14034919af68d573a71e7118db48129e6f299b9a71adc" origin="Generated by Gradle"/>
|
||||
@@ -1945,11 +1908,6 @@
|
||||
<sha256 value="2f113902364208bbb32d551e062b05ab219692fbc12bd48de1d83c7576e5b470" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-bom" version="4.1.114.Final">
|
||||
<artifact name="netty-bom-4.1.114.Final.pom">
|
||||
<sha256 value="0dfd08abc106f625be1d6eb498fa993de65c8b558070dfb11afa9a3394bb6612" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-bom" version="4.1.115.Final">
|
||||
<artifact name="netty-bom-4.1.115.Final.pom">
|
||||
<sha256 value="25dc8bb8337dfc186c49fc8cf4f6a5b6c7cf4146362f5f440dacadcd0df9761b" origin="Generated by Gradle"/>
|
||||
@@ -1960,14 +1918,6 @@
|
||||
<sha256 value="6afc7e751e0a90d2cb1168f6a4854d80bf3ba888104afab303233cd31684f4b4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-buffer" version="4.1.114.Final">
|
||||
<artifact name="netty-buffer-4.1.114.Final.jar">
|
||||
<sha256 value="436ea25725d92c1f590a857d46dc1c8f5c54d7f2775984db2655e5e3bc0d97c9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-buffer-4.1.114.Final.pom">
|
||||
<sha256 value="b3f07cf4271ee5a4a459ea8a0d9f4f16911a9355357831d05710ef64f8b8c5ce" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-buffer" version="4.1.115.Final">
|
||||
<artifact name="netty-buffer-4.1.115.Final.jar">
|
||||
<sha256 value="4a7b331d3770c566ab70eb02a0d1feed63b95cf6e4d68c8fe778c4c9de2d116d" origin="Generated by Gradle"/>
|
||||
@@ -1976,14 +1926,6 @@
|
||||
<sha256 value="94a3581ea9e4ffc385bb237960f68ef1e84089f164eae8629e8fd66d8aaf0139" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-4.1.114.Final.jar">
|
||||
<sha256 value="71d145491456b53212e38e24787bbbed0c77d850f7352e24e535d26a6eea913b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-4.1.114.Final.pom">
|
||||
<sha256 value="f55160abec463634c36459914cc60cb59489dcb723e87e9a71ac37bdf3706b84" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-4.1.115.Final.jar">
|
||||
<sha256 value="cd189afb70ec6eacfcdfdd3a5f472b4e705a5c91d5bd3ef0386421f2ae15ec77" origin="Generated by Gradle"/>
|
||||
@@ -1992,14 +1934,6 @@
|
||||
<sha256 value="ac166597e81179ca8300276605408910cc030efec12236ce9c38fefc16801aa0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-dns" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-dns-4.1.114.Final.jar">
|
||||
<sha256 value="7b97b856d062e7b2483384223dcee002ad23d526f827991f1140af6302ca120d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-dns-4.1.114.Final.pom">
|
||||
<sha256 value="24577daa4d8ab270fd6dc51d1ecca5be383e3df14450d35ae83e048d9981aa32" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-dns" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-dns-4.1.115.Final.jar">
|
||||
<sha256 value="23dd6806bcc326855f13e69838c6411d0490e6b1aeb12e217a19a3dd6ad3f10d" origin="Generated by Gradle"/>
|
||||
@@ -2008,14 +1942,6 @@
|
||||
<sha256 value="73976d702597a4d7597f82634fd17d1161a486972c7306b7e7cccb582533ea4e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-haproxy" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-haproxy-4.1.114.Final.jar">
|
||||
<sha256 value="a96216d347c7219f69b2f4f8743ce0aee6ac750b3691739745ebbcc10eb49528" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-haproxy-4.1.114.Final.pom">
|
||||
<sha256 value="a399be4607a97549d249ad76f2fcf6e092af63d344e3272efd44df21757ccfe0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-haproxy" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-haproxy-4.1.115.Final.jar">
|
||||
<sha256 value="7514f1d10dda3af9468b57bbb0f63fe0b8abb55dbb8190bede95319f37228322" origin="Generated by Gradle"/>
|
||||
@@ -2029,14 +1955,6 @@
|
||||
<sha256 value="80372e3e6e61ba8b3abe623964c986376568b6bbec705adb29d2a959cd7b9f8c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-http" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-http-4.1.114.Final.jar">
|
||||
<sha256 value="56150ce900f6d931fce37a7fb05d7d75478d6d0b8b556a21781972eb9c3ed7df" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-http-4.1.114.Final.pom">
|
||||
<sha256 value="e8fe9ebb862f1dd5c3a6be87e851a3b3b28a7eec4af8d70acbecb0c1e816f3ae" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-http" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-http-4.1.115.Final.jar">
|
||||
<sha256 value="e6dbe971c59373bbae9802021c63b9bc1d8800fead382863d67e79e79b023166" origin="Generated by Gradle"/>
|
||||
@@ -2050,14 +1968,6 @@
|
||||
<sha256 value="c7f0325aa4c75c9eae1e388306dc7d10426c6a2a6c8f712f876f58a4bc157ede" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-http2" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-http2-4.1.114.Final.jar">
|
||||
<sha256 value="e3e45427a46a8d5b03307a5bcd2eab3706b8b8d903c6a056fa3d1c9bf9738f24" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-http2-4.1.114.Final.pom">
|
||||
<sha256 value="a856ee4a1cc7d497b664b9125b3c90a690091ff771beca2a9858d51858a75850" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-http2" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-http2-4.1.115.Final.jar">
|
||||
<sha256 value="cbed9829a5d582e91e314e209edce9a0c2eb369f23bb4fb74a5bc8b7990222c2" origin="Generated by Gradle"/>
|
||||
@@ -2066,14 +1976,6 @@
|
||||
<sha256 value="88747b3e38e21cc1f42eaa44d301423482c0fc26c467b3f78fb9edfbde93a3e1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-memcache" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-memcache-4.1.114.Final.jar">
|
||||
<sha256 value="7ef9cfa35408cf43196ad41b7d31517d4a29fbc3c52f387f7c1930ebc02e3c3b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-memcache-4.1.114.Final.pom">
|
||||
<sha256 value="9748096cc41e8de6c78d4cea259ea222a7af70756f783b612b83c9f36e62e848" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-memcache" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-memcache-4.1.115.Final.jar">
|
||||
<sha256 value="c44561d5cdb0e28e63b00dccb51c8f293c8b23012e0299042804e054e2b05fab" origin="Generated by Gradle"/>
|
||||
@@ -2082,14 +1984,6 @@
|
||||
<sha256 value="cfcc9845b932975dfc13be8c19260fda48d0646003de0e017964bdcde24d37c6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-mqtt" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-mqtt-4.1.114.Final.jar">
|
||||
<sha256 value="4e5d5e05a26399f8431beb61fb4860b554938bbfba9d633cfb8c69444a089cc8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-mqtt-4.1.114.Final.pom">
|
||||
<sha256 value="e31ab2e3030a3a3b10ab85abe6547f9c214de55d5b69251dee76367c6bf6ab98" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-mqtt" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-mqtt-4.1.115.Final.jar">
|
||||
<sha256 value="54202f894eea7f235b8f9032b32a09396bbf92a68cb32a486981ced96eb85222" origin="Generated by Gradle"/>
|
||||
@@ -2098,14 +1992,6 @@
|
||||
<sha256 value="dc966366eac6d123a56357a34495400435ed2c48f9bab5b363d648ddb525abba" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-redis" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-redis-4.1.114.Final.jar">
|
||||
<sha256 value="8b31e03a59c4d460f9f6306a6b8ef1ee856f93a24ed94f8e47fc2cd0d77ffd0d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-redis-4.1.114.Final.pom">
|
||||
<sha256 value="520a82274a95583df659bb7b2a70ad8864396f02e6fefcd989f688f18f5748c9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-redis" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-redis-4.1.115.Final.jar">
|
||||
<sha256 value="eddc930ecb65055a202b4d959f7b6636085a468d45daf283725037d466fa4de1" origin="Generated by Gradle"/>
|
||||
@@ -2114,14 +2000,6 @@
|
||||
<sha256 value="3afc90b1246694810d9da24e60a057cc2db5e44f20d820dfee67357d621a2975" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-smtp" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-smtp-4.1.114.Final.jar">
|
||||
<sha256 value="78285748b6e50b2275e32579353638424afdc117e3919c9a01fd8deb4974a6d8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-smtp-4.1.114.Final.pom">
|
||||
<sha256 value="20219aaaaf7a534b5f0d142ca39f83234273069eb6f59779818e94f4c3630b1c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-smtp" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-smtp-4.1.115.Final.jar">
|
||||
<sha256 value="caa622c91fae41a0396b576c3803955371ae9ccde3ba6bf46117f4b641d0f737" origin="Generated by Gradle"/>
|
||||
@@ -2130,14 +2008,6 @@
|
||||
<sha256 value="f249c4744b45bea1147755c57a98d093b65925d684a249e8ec06d7bb360f687d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-socks" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-socks-4.1.114.Final.jar">
|
||||
<sha256 value="2e48cb5af7cae82acbb8dcb7b0c9d67b9dddff3d6dd262cb8911fd4b5fd62ee2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-socks-4.1.114.Final.pom">
|
||||
<sha256 value="374f2d39e95f0106a22d4e4c42c5ed6f377027a61c6e8f1bf72b0fb12823da06" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-socks" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-socks-4.1.115.Final.jar">
|
||||
<sha256 value="e9b1cc744dc6195894450b1fd4d271a821ab167fe21ae3c459b27cdadc70e81f" origin="Generated by Gradle"/>
|
||||
@@ -2146,14 +2016,6 @@
|
||||
<sha256 value="08b3c1acc77abdbadeef08c8cdf080e1bcceffe5f84751f60d89fc0bcbaaa2fc" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-stomp" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-stomp-4.1.114.Final.jar">
|
||||
<sha256 value="9b63156b727843485b137c87e2c7e4c7db2690f5ca93443080711a1804d685cf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-stomp-4.1.114.Final.pom">
|
||||
<sha256 value="d4e8f0900d3dd2e6d9f74bbae8b4b427c21c8b18f388a41b0205ae7f0335ac20" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-stomp" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-stomp-4.1.115.Final.jar">
|
||||
<sha256 value="9470312b5e263595a4d0a6a3cb3e64e5d11e87285fbfc267b845118d1c26fc7a" origin="Generated by Gradle"/>
|
||||
@@ -2162,14 +2024,6 @@
|
||||
<sha256 value="394fd6bdf7145aa180548d46f3297ed6a9e6a4a073fe0f743ca4df8217b1990a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-xml" version="4.1.114.Final">
|
||||
<artifact name="netty-codec-xml-4.1.114.Final.jar">
|
||||
<sha256 value="3a2055a9b38b603ea9fcbfbe891172daad60df99aecd99161f1c8adbedc95522" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-codec-xml-4.1.114.Final.pom">
|
||||
<sha256 value="e5fcbcb730801bf08dc5b8575b2545766a61331e0e285d05fd805602564deb5f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-codec-xml" version="4.1.115.Final">
|
||||
<artifact name="netty-codec-xml-4.1.115.Final.jar">
|
||||
<sha256 value="e1c5f043f027ca5f853076df16c233665011be60a81f5a6f7336093790a9dced" origin="Generated by Gradle"/>
|
||||
@@ -2183,14 +2037,6 @@
|
||||
<sha256 value="b9c78337616bf8d54d5515bab6cba1df2db46fdb01b412434cc7fbafe78345c5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-common" version="4.1.114.Final">
|
||||
<artifact name="netty-common-4.1.114.Final.jar">
|
||||
<sha256 value="d6b053b3b27cc568207f254902dcb6f95dd238c1b9d55ef719d2c4f8eb476223" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-common-4.1.114.Final.pom">
|
||||
<sha256 value="3c2e7d18133ac6d8e4f442cc5fa5fe0492f5cbf878bf0f7157c6d410da719f5d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-common" version="4.1.115.Final">
|
||||
<artifact name="netty-common-4.1.115.Final.jar">
|
||||
<sha256 value="39f1b5a2aaa4eab5d036dfd0486e35a4276df412e092d36b2d88b494705a134d" origin="Generated by Gradle"/>
|
||||
@@ -2204,14 +2050,6 @@
|
||||
<sha256 value="f575d446fa5cd546be4fce0bbf34c144041761b89da77971e30bf83d42905d1b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-handler" version="4.1.114.Final">
|
||||
<artifact name="netty-handler-4.1.114.Final.jar">
|
||||
<sha256 value="57be25ec6c8fa7052fe90119373d8bc979cd37fa0070a135c0e69f5f8e0ddad0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-handler-4.1.114.Final.pom">
|
||||
<sha256 value="694fe6fb8dfe9148a2326e77f56141fcebbccc2cb8b29f285776796164ec6a93" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-handler" version="4.1.115.Final">
|
||||
<artifact name="netty-handler-4.1.115.Final.jar">
|
||||
<sha256 value="5972028cc863b74927ce0d11fb8d58f65da2560bef5602fe8ce8903bd306ca07" origin="Generated by Gradle"/>
|
||||
@@ -2225,14 +2063,6 @@
|
||||
<sha256 value="8577aa45f16f435b12420a37b8c8d731df11d4dcb7e132c00fc588946274ed89" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-handler-proxy" version="4.1.114.Final">
|
||||
<artifact name="netty-handler-proxy-4.1.114.Final.jar">
|
||||
<sha256 value="35555b41624c8384de773bc3d17eb2447f5449842119db0435a366372aceefd1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-handler-proxy-4.1.114.Final.pom">
|
||||
<sha256 value="6cdaeb2a8f240fa1b72382476e2776969535678203d9902f3e63ef586ac0dbb7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-handler-proxy" version="4.1.115.Final">
|
||||
<artifact name="netty-handler-proxy-4.1.115.Final.jar">
|
||||
<sha256 value="807e67cfb17136927d11db42df62031169d1fa0883e13f254906994c84ffbe87" origin="Generated by Gradle"/>
|
||||
@@ -2241,14 +2071,6 @@
|
||||
<sha256 value="f9936b2da7adcecef5c4ffad772067b7de5d0e359b83e6fd39b4a49d11706a10" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-handler-ssl-ocsp" version="4.1.114.Final">
|
||||
<artifact name="netty-handler-ssl-ocsp-4.1.114.Final.jar">
|
||||
<sha256 value="c97e307e99bb9555adae0f01ddcd690965408aa1b653122fc7a02030dd73d8f7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-handler-ssl-ocsp-4.1.114.Final.pom">
|
||||
<sha256 value="d217e882bd1fa6fbc6bfa3966b68fcade287ec6806c26ab9b0d80d4fc9daa694" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-handler-ssl-ocsp" version="4.1.115.Final">
|
||||
<artifact name="netty-handler-ssl-ocsp-4.1.115.Final.jar">
|
||||
<sha256 value="d958f728bffe9a2f23f32a70917a2842feb4fac59baec581f89411c734b59cfe" origin="Generated by Gradle"/>
|
||||
@@ -2262,11 +2084,6 @@
|
||||
<sha256 value="96b055ae7af7f1e32db3d7261918d900fba15e1e589ccd682f9cfd5744249a51" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-parent" version="4.1.114.Final">
|
||||
<artifact name="netty-parent-4.1.114.Final.pom">
|
||||
<sha256 value="a1c8a553ae7e43bf0ed986d22f0379dcf0c2fb7fc3971dc6057ed5c9d04c5a68" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-parent" version="4.1.115.Final">
|
||||
<artifact name="netty-parent-4.1.115.Final.pom">
|
||||
<sha256 value="d832942b8e2a71c2ccfdd0247a8b840105ce40aaeec4b8de36358f28d93941e3" origin="Generated by Gradle"/>
|
||||
@@ -2277,14 +2094,6 @@
|
||||
<sha256 value="d7524053459d425c9cbbbf93f0793504c4d0148c75fb49daab3e14d4e4d38ef0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver" version="4.1.114.Final">
|
||||
<artifact name="netty-resolver-4.1.114.Final.jar">
|
||||
<sha256 value="19661e7f1dbdee97fe99a227fbed0696d29c3cdf3f8f2d9839a790695c2bf0ac" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-resolver-4.1.114.Final.pom">
|
||||
<sha256 value="86843905401f6605d575f4858b58c1eadcf1e3c0bccd417787cb0ad0621cc120" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver" version="4.1.115.Final">
|
||||
<artifact name="netty-resolver-4.1.115.Final.jar">
|
||||
<sha256 value="7b3455d14f59828765a00573bc3967dc59379e874bd62a67eb1926d6512109d1" origin="Generated by Gradle"/>
|
||||
@@ -2298,14 +2107,6 @@
|
||||
<sha256 value="9d4607157d264015d8e8ae825d49fb8d8bf48fe227de99d82fb5570ac519c70a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver-dns" version="4.1.114.Final">
|
||||
<artifact name="netty-resolver-dns-4.1.114.Final.jar">
|
||||
<sha256 value="e29ab20626fee613cd44ab794dd80e4e7ed50551815ff2dc31e60ab473459f10" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-resolver-dns-4.1.114.Final.pom">
|
||||
<sha256 value="ed126dff491fe7175d0069141ca53ecb594c486c290c2ee13e99d25d72456fb4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver-dns" version="4.1.115.Final">
|
||||
<artifact name="netty-resolver-dns-4.1.115.Final.jar">
|
||||
<sha256 value="4aca31593e5896c64ab7e041bbc6c0d851bd9634ec3a4354208141a35576619f" origin="Generated by Gradle"/>
|
||||
@@ -2314,14 +2115,6 @@
|
||||
<sha256 value="8d1e3143825e0244e1dd614b2340deb00f4ee07ef615faa855bd195100776789" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver-dns-classes-macos" version="4.1.114.Final">
|
||||
<artifact name="netty-resolver-dns-classes-macos-4.1.114.Final.jar">
|
||||
<sha256 value="2bc4078cf269a99e09f28738d6022c385a31f4321e28115bae752ce16ce99618" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-resolver-dns-classes-macos-4.1.114.Final.pom">
|
||||
<sha256 value="e530a68c73ea6f1c0a78c616ff29ce0a7a7a22f86ebcef27fa02965e6acbd660" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver-dns-classes-macos" version="4.1.115.Final">
|
||||
<artifact name="netty-resolver-dns-classes-macos-4.1.115.Final.jar">
|
||||
<sha256 value="5fbac6346b3fc2fbdce7f7f24bb96a1b3190aa14d50021303a0a4437f3d373bc" origin="Generated by Gradle"/>
|
||||
@@ -2330,17 +2123,6 @@
|
||||
<sha256 value="221aabb31073eea980d9de5a00a5b74ddaa99cccae638ad485dce8897d880c72" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver-dns-native-macos" version="4.1.114.Final">
|
||||
<artifact name="netty-resolver-dns-native-macos-4.1.114.Final-osx-aarch_64.jar">
|
||||
<sha256 value="c63ffafc1becb7e1dca29c89b1c69ea74cc62abe3fcc3184ea7f089c76bac643" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-resolver-dns-native-macos-4.1.114.Final-osx-x86_64.jar">
|
||||
<sha256 value="a8ccb464f93d10bd70c0b1fbc96835cfa234623f353f880edc7a76d3115509a4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-resolver-dns-native-macos-4.1.114.Final.pom">
|
||||
<sha256 value="710dbb92f3edbe204aea06dada1671f2382cd369f0ea47e6d5e03d0aa684929c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-resolver-dns-native-macos" version="4.1.115.Final">
|
||||
<artifact name="netty-resolver-dns-native-macos-4.1.115.Final-osx-aarch_64.jar">
|
||||
<sha256 value="a386a63e9c3d5a66130d1eebe9818820ca04d79769ab18b65d934e6e3a74e4e5" origin="Generated by Gradle"/>
|
||||
@@ -2357,14 +2139,6 @@
|
||||
<sha256 value="ba89f115a8c30bdbcb6dba36849a047da803b496f3e856e4365d53df1403932d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-tcnative-boringssl-static" version="2.0.66.Final">
|
||||
<artifact name="netty-tcnative-boringssl-static-2.0.66.Final.jar">
|
||||
<sha256 value="df215103b6082caceef6b83ed5bbf61d2072688b8b248e9d86cc0bbdb785b5e4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-tcnative-boringssl-static-2.0.66.Final.pom">
|
||||
<sha256 value="7ea6c8ee65df3595dfc044ed2e95614acecab6da0b348b7a1e9361cba44fce2f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-tcnative-boringssl-static" version="2.0.69.Final">
|
||||
<artifact name="netty-tcnative-boringssl-static-2.0.69.Final.jar">
|
||||
<sha256 value="79dbae7f0c2a1f73a6b4bb992edb0ab94ebd3196f5e83a94784039fb96d9a040" origin="Generated by Gradle"/>
|
||||
@@ -2373,14 +2147,6 @@
|
||||
<sha256 value="d55ff689622b75dfeca232da2a98cefa1773b7717a318be18113ebf718d232f8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-tcnative-classes" version="2.0.66.Final">
|
||||
<artifact name="netty-tcnative-classes-2.0.66.Final.jar">
|
||||
<sha256 value="669a811a193dc1e7c9ef86cb547a4ab92f0f34cce8f9b842b9029bf5cfa07cc5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-tcnative-classes-2.0.66.Final.pom">
|
||||
<sha256 value="4942cd02e2916c1fb4f78cca8841f0f7219bb90f828ab7489a89027c2efa91eb" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-tcnative-classes" version="2.0.69.Final">
|
||||
<artifact name="netty-tcnative-classes-2.0.69.Final.jar">
|
||||
<sha256 value="0bbc2848cb099eb3f6f4ec36501b3600e1828457cda41d5330687f601ee04bef" origin="Generated by Gradle"/>
|
||||
@@ -2389,11 +2155,6 @@
|
||||
<sha256 value="59da0f949e2bee89f5711eb3e0c6fd8cf7914e6b70c007f36b9eb0688243015c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-tcnative-parent" version="2.0.66.Final">
|
||||
<artifact name="netty-tcnative-parent-2.0.66.Final.pom">
|
||||
<sha256 value="2b017fb3c1deeab5d1eaebec134659b7deae78c14d5ebfc3d49c0b3084ea7365" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-tcnative-parent" version="2.0.69.Final">
|
||||
<artifact name="netty-tcnative-parent-2.0.69.Final.pom">
|
||||
<sha256 value="2953027196637610c894f32bda705093cd8768315c9f1a573951a75f17869afc" origin="Generated by Gradle"/>
|
||||
@@ -2404,14 +2165,6 @@
|
||||
<sha256 value="8662bd5d7f62d372548f40ad896b87ea55c2b1d179029ef985837dce43a52269" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-4.1.114.Final.jar">
|
||||
<sha256 value="2a8609fe6a8b4c9d5965c6b901777b4bd0b26600647ee2aa7d4d93f4d5c780de" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-4.1.114.Final.pom">
|
||||
<sha256 value="9451b10917afd228c5892541d94acc3d6814c8d73589c4050c779a921fab9c82" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-4.1.115.Final.jar">
|
||||
<sha256 value="c3d71faaa736ffd2c9260ab0b498024b814c39c7d764bea8113fa98de6e2bdd2" origin="Generated by Gradle"/>
|
||||
@@ -2420,14 +2173,6 @@
|
||||
<sha256 value="9677471c5409adced1f6cdb65a3ad8015e2e970990aba497a4eeca18abc363f9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-classes-epoll" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-classes-epoll-4.1.114.Final.jar">
|
||||
<sha256 value="a90b4277df568be0562e08271060a28870c57ba5a91fe792057f11e986fe777e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-classes-epoll-4.1.114.Final.pom">
|
||||
<sha256 value="66c775019d67b3a40a6880265b90dc88b01660c53db7ae2f65cfea5e74994715" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-classes-epoll" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-classes-epoll-4.1.115.Final.jar">
|
||||
<sha256 value="40aa67b4463cca0ab346e393c87f6c37e8954d18ec8b78567d95b55aa1f2b3aa" origin="Generated by Gradle"/>
|
||||
@@ -2436,14 +2181,6 @@
|
||||
<sha256 value="e57cc702f24b733565741ce3a69d89a2b5f5dcbda34c52b779fd02ba68c40987" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-classes-kqueue" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-classes-kqueue-4.1.114.Final.jar">
|
||||
<sha256 value="8a07fed5985502dd68936f70b0dd5639d2e69c257c2300610936ce0e6da9847d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-classes-kqueue-4.1.114.Final.pom">
|
||||
<sha256 value="90cbf66fe7c2a185aaf2dc28b0136f8ef8ba8eb51be1c319bcbd7f7372af09f5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-classes-kqueue" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-classes-kqueue-4.1.115.Final.jar">
|
||||
<sha256 value="77d3a0930f86870b48e668635e214e0c81205f90f2012dc70c8417b59a839d04" origin="Generated by Gradle"/>
|
||||
@@ -2457,23 +2194,6 @@
|
||||
<sha256 value="c14b3e677409871ae1cb80436dd06945591ae201ce3cc96db6a52957c7b9e574" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-native-epoll" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-native-epoll-4.1.114.Final-linux-aarch_64.jar">
|
||||
<sha256 value="fba192991a1179f4f04c82d69a43b06136216eac5974f8a30881887058fa66ad" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-epoll-4.1.114.Final-linux-riscv64.jar">
|
||||
<sha256 value="e0b6914ed5333aa0e2b21d360e6eeee962629c13eece9f7ea8674ce36fa539f7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-epoll-4.1.114.Final-linux-x86_64.jar">
|
||||
<sha256 value="798713e4135de9bab7e4bd03a87b06e972e88d4cf4a1f951bbb2a3ea39ccca38" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-epoll-4.1.114.Final.jar">
|
||||
<sha256 value="d8991b8b096ea0f04d87fe3ade94804bbf6bce1e6019f886b7f71504741ba96f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-epoll-4.1.114.Final.pom">
|
||||
<sha256 value="92d4dee865dc002fef64e19e602b6f6616667da22568869fb1135715a0344bf8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-native-epoll" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar">
|
||||
<sha256 value="82051543cbb328b5b45803229ccddf921f3e1897f9d75358ea76143a65a82e48" origin="Generated by Gradle"/>
|
||||
@@ -2496,20 +2216,6 @@
|
||||
<sha256 value="d1bf448ced9703e41e63f539f8523246f2a163434ddf4330d213262e731d5707" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-native-kqueue" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-native-kqueue-4.1.114.Final-osx-aarch_64.jar">
|
||||
<sha256 value="68b6ddfe5b37b4024c2543460a165c034a91cc3215324d4e47a1308ea3eb4046" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-kqueue-4.1.114.Final-osx-x86_64.jar">
|
||||
<sha256 value="5a38fc4ebf199077c4925fa5253ce6ef789baa54210a035091902f0607245a19" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-kqueue-4.1.114.Final.jar">
|
||||
<sha256 value="643b1bd9b3f74eea5411f42875bdc64fb82b3a9413d66cea2ae46620073f5780" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-kqueue-4.1.114.Final.pom">
|
||||
<sha256 value="0ca766af3bb08dfa44747c7cac67b408d5b6cc1fed051febc1b423edc077afb7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-native-kqueue" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-native-kqueue-4.1.115.Final-osx-aarch_64.jar">
|
||||
<sha256 value="bd71c4e8f14098057963a0342ba0ce2e39592d9558f1cd0cfdea81f9686901f0" origin="Generated by Gradle"/>
|
||||
@@ -2529,14 +2235,6 @@
|
||||
<sha256 value="2ea70a1864ab38fcec2bdc28c69ee9756dbad58a4626c664554d3c860d540981" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-native-unix-common" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-native-unix-common-4.1.114.Final.jar">
|
||||
<sha256 value="fd64c07c9e068f80dc271f6277278246328a171be669abdfe0bc8b2226d980de" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-native-unix-common-4.1.114.Final.pom">
|
||||
<sha256 value="832b43c284e2c0cf0f57d62928fa72967b6aa6e5dc8766c6c9b2d01fddf7951d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-native-unix-common" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-native-unix-common-4.1.115.Final.jar">
|
||||
<sha256 value="4b03e716272657c296b0204b57c140b2b2ca96b1a746c92da41f595892ec6d88" origin="Generated by Gradle"/>
|
||||
@@ -2545,14 +2243,6 @@
|
||||
<sha256 value="96075271c578faadffec6f133991ea30884a69a41e07245a5ff8d5e7e5dd9f07" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-rxtx" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-rxtx-4.1.114.Final.jar">
|
||||
<sha256 value="8927af869c006d73921d9db739d747f82fe5568c82ad8c1982fef954c5ecb63d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-rxtx-4.1.114.Final.pom">
|
||||
<sha256 value="b82852b37ecedf8342eeee3cd69aae7658e0ed485ef7c2b0f2ba384dd223dd17" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-rxtx" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-rxtx-4.1.115.Final.jar">
|
||||
<sha256 value="33e80b3c468be74f448416d59edbf4618ea4917d697a3cbc4a53e13516bfd5ed" origin="Generated by Gradle"/>
|
||||
@@ -2561,14 +2251,6 @@
|
||||
<sha256 value="38713e766cdf792a6d309adaba05193419a1ee8f1aea63a68cdabfe736bf5acd" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-sctp" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-sctp-4.1.114.Final.jar">
|
||||
<sha256 value="5db192fb9c23615cf82862888c192f3ec98bae26410e1ac7ee40ecebe208e181" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-sctp-4.1.114.Final.pom">
|
||||
<sha256 value="95b0121474bc8938d7817cd969d345e04021cd77d60d7b3e9f5e875b426336bd" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-sctp" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-sctp-4.1.115.Final.jar">
|
||||
<sha256 value="016e8596b4b2357c2671a8691cafaaab7650689d01715cda2a0b85631f22f3c6" origin="Generated by Gradle"/>
|
||||
@@ -2577,14 +2259,6 @@
|
||||
<sha256 value="844fdde08ab3dd22f5cac9e6f211f29a75caa8d80d986e5f5d459f95eab06d26" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-udt" version="4.1.114.Final">
|
||||
<artifact name="netty-transport-udt-4.1.114.Final.jar">
|
||||
<sha256 value="2431ecd15cde787019de5aa0a48063e6d72758ff75e67ed5d0009f470f559009" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="netty-transport-udt-4.1.114.Final.pom">
|
||||
<sha256 value="faff00ad0c07c5316ce118b2a36276e03bb00feb2b86f4e80a351f417392cd1b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.netty" name="netty-transport-udt" version="4.1.115.Final">
|
||||
<artifact name="netty-transport-udt-4.1.115.Final.jar">
|
||||
<sha256 value="c505809dbb2d1b159eb1732dfa10c68f05da41ea9a479416690ff9a76908e4fb" origin="Generated by Gradle"/>
|
||||
@@ -2636,6 +2310,22 @@
|
||||
<sha256 value="16284ec312a8398560d75e0aa48053c1764d164eb0d777fc0440b9b36c4eb4a1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.opentelemetry" name="opentelemetry-bom" version="1.44.1">
|
||||
<artifact name="opentelemetry-bom-1.44.1.module">
|
||||
<sha256 value="325613776fb98e86c7c53a13b6d61fcede7f2584f7725ded6d84d1bdf458656d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentelemetry-bom-1.44.1.pom">
|
||||
<sha256 value="185c9efbbce4a7e59667078812a215554ed514b17dc1e319bfbc22bc011e62d7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.opentelemetry" name="opentelemetry-bom-alpha" version="1.44.1-alpha">
|
||||
<artifact name="opentelemetry-bom-alpha-1.44.1-alpha.module">
|
||||
<sha256 value="3bb69ad24a2ac599738138c7e7af71df0076f9ded15a176e78c9d0afdee67098" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentelemetry-bom-alpha-1.44.1-alpha.pom">
|
||||
<sha256 value="3347fb4d7a7ccb56def084d60b6416aead7360bcba57b8729dc3ef7e3db9d442" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.opentelemetry" name="opentelemetry-context" version="1.43.0">
|
||||
<artifact name="opentelemetry-context-1.43.0.jar">
|
||||
<sha256 value="83d54bed8a7aa74a8648d43974c743f470c9ceab3dea57f26a938667a2dc0160" origin="Generated by Gradle"/>
|
||||
@@ -2803,6 +2493,22 @@
|
||||
<sha256 value="cbab9b04189b0ddf7654f9029b151e8fb9bf78d906ad1fe4393510b76c8e74c8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.opentelemetry.instrumentation" name="opentelemetry-instrumentation-bom" version="2.10.0">
|
||||
<artifact name="opentelemetry-instrumentation-bom-2.10.0.module">
|
||||
<sha256 value="f7881be9e9c8f20cbccff71684ec2cc054bf12adf46dc671bb4e290bbd286b05" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentelemetry-instrumentation-bom-2.10.0.pom">
|
||||
<sha256 value="f785b7118ab4088d01662e73977f29a1495634caab1bb3c687f8fa00d2de3f96" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.opentelemetry.instrumentation" name="opentelemetry-instrumentation-bom-alpha" version="2.10.0-alpha">
|
||||
<artifact name="opentelemetry-instrumentation-bom-alpha-2.10.0-alpha.module">
|
||||
<sha256 value="f4f66eb95040a8a1ad0a1f55d0fd8d36fea9365ed577c29803402245ccd3c67e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="opentelemetry-instrumentation-bom-alpha-2.10.0-alpha.pom">
|
||||
<sha256 value="5d7a99d3acddcdaf98abbaa5325324cbcdb8fd773d2e39c76a4319f6f2a6520a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.opentelemetry.instrumentation" name="opentelemetry-okhttp-3.0" version="2.9.0-alpha">
|
||||
<artifact name="opentelemetry-okhttp-3.0-2.9.0-alpha.jar">
|
||||
<sha256 value="9e44c7a49b7d6afa762ad0c11c1d4fdc68062b69550c8d8821185dc8951e6400" origin="Generated by Gradle"/>
|
||||
@@ -2923,11 +2629,138 @@
|
||||
<sha256 value="5bf70db179616d067d416f5a779be9fc9e7cd736cfdb2bd82fb9eb245d44b150" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="client_java" version="1.3.4">
|
||||
<artifact name="client_java-1.3.4.pom">
|
||||
<sha256 value="5f8bc032a8fe3bc8e657dd6a3ab11b73a022e097a32337d1f28b2983b16f4f59" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="parent" version="0.16.0">
|
||||
<artifact name="parent-0.16.0.pom">
|
||||
<sha256 value="722b55119097b04d711479dfb60dd54b27b5920a1aeb7702027c44a215ffc596" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-bom" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-bom-1.3.4.pom">
|
||||
<sha256 value="6f2edcab929ea7862e3d755ab35527595c6d025f1d5e94ac4ac9c4ae268b7308" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-config" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-config-1.3.4.jar">
|
||||
<sha256 value="59a8736700c7618ab30469ef23d4d09c5a024e4c8c5bd2034e66e563e0ba4735" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-config-1.3.4.pom">
|
||||
<sha256 value="c1916224973a819cd62d42f9c9dbcf7ac030afbc65efa0fc55f7f6c339bebc7f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-core" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-core-1.3.4.jar">
|
||||
<sha256 value="c046b237c30cd9172e14d81283090a7ef5f805113ae35246cd20a4b5691b076e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-core-1.3.4.pom">
|
||||
<sha256 value="85d17e5b45b5dc4d6f106873793463bd59fe12b8b059f502e2cb9eecff6bfaae" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-exporter-common" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-exporter-common-1.3.4.jar">
|
||||
<sha256 value="3c95789e5c43d90658d976274aad34b8132392a24ee0777324eb70b71ed3e408" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-exporter-common-1.3.4.pom">
|
||||
<sha256 value="7a261664066f01f3092f84881292f7f266d5cdfbf376aa6b85d61a66db76f225" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-exporter-httpserver" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-exporter-httpserver-1.3.4.jar">
|
||||
<sha256 value="e4580db8417394b86946793e2450572c411fbb034722acbf684dee75b8cda052" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-exporter-httpserver-1.3.4.pom">
|
||||
<sha256 value="faa4a4ba55771920ae83c8982d8a0977aed3649b0a503c5ce0c864acb78bde64" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-exporter-pushgateway" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-exporter-pushgateway-1.3.4.jar">
|
||||
<sha256 value="2955047d79335aa0e9d922587ec8b6ac1cc6b52a08ecf6f75fc37d5ea2c55568" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-exporter-pushgateway-1.3.4.pom">
|
||||
<sha256 value="8328d9c944ecaeb9a3d5a1daf968d0a036ccb04454a828a254ffff3e2f05fe0b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-exposition-formats" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-exposition-formats-1.3.4.jar">
|
||||
<sha256 value="afcbce74029d4f77b6816004acfbb85b3382664a1cd7a462bb7718a1ad9dc134" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-exposition-formats-1.3.4.pom">
|
||||
<sha256 value="20f9c6ab1697f787d09737bb4587ae9f012034145b8f4a94d77c03ce9c8a396d" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-exposition-textformats" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-exposition-textformats-1.3.4.jar">
|
||||
<sha256 value="7fbb160e591fcbb1f95ab191f5c4b7159aa7396f72a21f4295e8fd2ea70eb419" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-exposition-textformats-1.3.4.pom">
|
||||
<sha256 value="fb89c867af52b9d524cbbad604cbc9761e94c9242afaeac3dab3643cf24ec820" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-instrumentation-guava" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-instrumentation-guava-1.3.4.jar">
|
||||
<sha256 value="f2c4a23722d7bed640aad79a2c2503e7054cb3bfe3ac4f38b2524ffb666aa560" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-instrumentation-guava-1.3.4.pom">
|
||||
<sha256 value="697022a098dafbfa73cf331be98d79f9287e35299cec03f3b5fcd0951013d179" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-instrumentation-jvm" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-instrumentation-jvm-1.3.4.jar">
|
||||
<sha256 value="cb0216eec70b6e040f511261adf7dbd77978ce0366a8a63e38763191e68948b6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-instrumentation-jvm-1.3.4.pom">
|
||||
<sha256 value="fbb5e2b323602005ce4234fad9ec2e7a9e222403aec3afd98353816ce692f806" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-model" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-model-1.3.4.jar">
|
||||
<sha256 value="5e29e9700e95ec4d69c48b411513362d665e1200a222d601bfa6f72800fac7d4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-model-1.3.4.pom">
|
||||
<sha256 value="385beb4a2b4c2829945e165ce4b0a1a476a3860036823771cabcc1efdc3799f5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-tracer" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-tracer-1.3.4.pom">
|
||||
<sha256 value="1472bac7e86837ffddfc6f2032f345aa95c5108e3fd34d79d67bd1a64f6e3662" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-tracer-common" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-tracer-common-1.3.4.jar">
|
||||
<sha256 value="66a0563d42633a42933c1e54e7e927661e882df8ee35b21024ef081c45763611" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-tracer-common-1.3.4.pom">
|
||||
<sha256 value="3cbd56e48a813a3ff5d54fb0f474e44014f92eefc5191efbe08600bf2bba5e8e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-tracer-initializer" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-tracer-initializer-1.3.4.jar">
|
||||
<sha256 value="f5d06533d60b27a54b050cebb9ffaf7f3028c059b306f279090101cc4c944aa9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-tracer-initializer-1.3.4.pom">
|
||||
<sha256 value="51187447c1eb2e5ea2623a8451e379af42e27f4c0b5079a75af86b75748c5da0" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-tracer-otel" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-tracer-otel-1.3.4.jar">
|
||||
<sha256 value="e1a22acb39f09dd96af8d330074f7d685840663ecbda83fde5903c62c15ecb21" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-tracer-otel-1.3.4.pom">
|
||||
<sha256 value="3388537f652d0b7f5feb16abee5a7c3a7002a1d4dc52ac5fc8d8463190fd3654" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="prometheus-metrics-tracer-otel-agent" version="1.3.4">
|
||||
<artifact name="prometheus-metrics-tracer-otel-agent-1.3.4.jar">
|
||||
<sha256 value="e7e191623273dfd5e6a6e9806e951a4facdc82e2fe426b6c3b27720677b41774" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="prometheus-metrics-tracer-otel-agent-1.3.4.pom">
|
||||
<sha256 value="13b9bbab7d6a2ca4425ea9886a8a00067bf184a5d2170d84a0f2c19f9d93be6e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient" version="0.16.0">
|
||||
<artifact name="simpleclient-0.16.0.jar">
|
||||
<sha256 value="22c374f237f7bc4fdb1f0ec2da379c0ba00e69ad2ffe8b6ade543d73062d377f" origin="Generated by Gradle"/>
|
||||
@@ -2936,11 +2769,6 @@
|
||||
<sha256 value="fec080d07ab15875d971c4ae81f951da61b5cff9991cf50e530ba8fca4770973" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_bom" version="0.16.0">
|
||||
<artifact name="simpleclient_bom-0.16.0.pom">
|
||||
<sha256 value="af441d3295de11a70e35febeb3d919ba2ed17552753163325b954d53594d55c3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_common" version="0.16.0">
|
||||
<artifact name="simpleclient_common-0.16.0.jar">
|
||||
<sha256 value="eba6ec26ce7e40cbb8725e05fb83247a9f4f44945b9e7522e3375dde67b9f059" origin="Generated by Gradle"/>
|
||||
@@ -2949,22 +2777,6 @@
|
||||
<sha256 value="77f01109ce1507bd7443e2272737674b27524faacb0dcb96e3b8ede0b6ab9eb6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_guava" version="0.16.0">
|
||||
<artifact name="simpleclient_guava-0.16.0.jar">
|
||||
<sha256 value="49959fe20423803b8958fe35ce6cdcc47e58e2251b191ad53eb7ef6fc46c4ae1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="simpleclient_guava-0.16.0.pom">
|
||||
<sha256 value="1d42aa72798870f20e997ae6c4d14ec3b26b3ef715602b2a8f32404e3657bdb1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_hotspot" version="0.16.0">
|
||||
<artifact name="simpleclient_hotspot-0.16.0.jar">
|
||||
<sha256 value="134f156cfeb44cbd3864065806ef5db5543c68af578d1d7ee59283e2e9853f73" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="simpleclient_hotspot-0.16.0.pom">
|
||||
<sha256 value="d216937de723120fb7a4c88b92c5bea1911afb88ba76d0d48f1769ad85b675e9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_httpserver" version="0.16.0">
|
||||
<artifact name="simpleclient_httpserver-0.16.0.jar">
|
||||
<sha256 value="cab87de10b6a474151cceebc3b634329a973ffb602ce6efef2c0fd97a90365c8" origin="Generated by Gradle"/>
|
||||
@@ -2973,14 +2785,6 @@
|
||||
<sha256 value="3c647fd6f561a21b19ed971d0419fd462d9f83f9347bc0a105a1c289eeaaaac4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_pushgateway" version="0.16.0">
|
||||
<artifact name="simpleclient_pushgateway-0.16.0.jar">
|
||||
<sha256 value="3277cf527702f21760073d4528fa247493a6de3116994dfd290d6bb677849d47" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="simpleclient_pushgateway-0.16.0.pom">
|
||||
<sha256 value="c6fa51afc562d31016c388efde34862d2e6c0f19db44de835f4e03f4579e6541" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="io.prometheus" name="simpleclient_tracer" version="0.16.0">
|
||||
<artifact name="simpleclient_tracer-0.16.0.pom">
|
||||
<sha256 value="3812bb22b95f81b4c3460e9e4e75c3ba72fa45e443a9fce5320842aabd0e99c2" origin="Generated by Gradle"/>
|
||||
@@ -5576,14 +5380,6 @@
|
||||
<sha256 value="965aeb2bedff369819bdde1bf7a0b3b89b8247dd69c88b86375d76163bb8c397" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains" name="annotations" version="17.0.0">
|
||||
<artifact name="annotations-17.0.0.jar">
|
||||
<sha256 value="195fb0da046d55bb042e91543484cf1da68b02bb7afbfe031f229e45ac84b3f2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="annotations-17.0.0.pom">
|
||||
<sha256 value="8390ee0b4dff7f568b1336e061f694e77d761a505169aed8964e75482c990eed" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-gradle-plugin-api" version="1.8.10">
|
||||
<artifact name="kotlin-gradle-plugin-api-1.8.10-gradle76.jar">
|
||||
<sha256 value="fc453e5efb4a644879a54e51d69df4874d0d2b77fce509fe030728119c43ecff" origin="Generated by Gradle"/>
|
||||
@@ -6108,14 +5904,6 @@
|
||||
<sha256 value="fb42d7d5bf585894076a6ce16caff7e690c184ed49e756a703620d5ac42fa2f3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.junit.vintage" name="junit-vintage-engine" version="5.11.2">
|
||||
<artifact name="junit-vintage-engine-5.11.2.jar">
|
||||
<sha256 value="b98cb692e669b3e90e826d2ede40fce1a8b1c8e0b58262935a609f0545b42ef9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="junit-vintage-engine-5.11.2.module">
|
||||
<sha256 value="4c45322e2f5f0bfe5a56a7a64a0f1b0aa3c94f797b32e16b63b3cc0a7bf83561" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.junit.vintage" name="junit-vintage-engine" version="5.5.2">
|
||||
<artifact name="junit-vintage-engine-5.5.2.jar">
|
||||
<sha256 value="350c19f11a1112c0f0cae7701f7e2cbbd2247d42460da096fa515a25b4a9754d" origin="Generated by Gradle"/>
|
||||
@@ -6525,14 +6313,6 @@
|
||||
<sha256 value="54ba23d87a2d438540c99ef8794a0856fc573a256b498678283c3c67ef18ada8" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.rnorth.duct-tape" name="duct-tape" version="1.0.8">
|
||||
<artifact name="duct-tape-1.0.8.jar">
|
||||
<sha256 value="31cef12ddec979d1f86d7cf708c41a17da523d05c685fd6642e9d0b2addb7240" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="duct-tape-1.0.8.pom">
|
||||
<sha256 value="2636f66debe6e12437632f23a67b9bc961b03633009e52527a3bbc871f4069b4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.rocksdb" name="rocksdbjni" version="9.7.3">
|
||||
<artifact name="rocksdbjni-9.7.3.jar">
|
||||
<sha256 value="5ca63e0e955f101f7af1c1fb8ce260b5d3a5701cea7d8c2852b9d56031a57221" origin="Generated by Gradle"/>
|
||||
@@ -6673,14 +6453,6 @@
|
||||
<sha256 value="b88a2db288863565727ee7cf80272aef5d90b33ed13a187bad4439d01f9144d7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.testcontainers" name="testcontainers" version="1.20.2">
|
||||
<artifact name="testcontainers-1.20.2.jar">
|
||||
<sha256 value="2a3ec0d7001c27a68ab87d4992e21d8fcacde9f3d0bb4c15fc41dc899cbb3b64" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="testcontainers-1.20.2.pom">
|
||||
<sha256 value="017e32e23584dc0e201a0e728d4aa89864fa4e6b75d14332950bdf79e6f06125" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.testcontainers" name="testcontainers-bom" version="1.19.4">
|
||||
<artifact name="testcontainers-bom-1.19.4.pom">
|
||||
<sha256 value="fc7f42a3c2a3d7a89ac7e01fa64c8a8e08294fd5abaa6009fead3d60d7628fa3" origin="Generated by Gradle"/>
|
||||
|
||||
@@ -55,11 +55,12 @@ dependencies {
|
||||
implementation 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure'
|
||||
implementation 'io.opentelemetry.semconv:opentelemetry-semconv'
|
||||
|
||||
implementation 'io.prometheus:simpleclient'
|
||||
implementation 'io.prometheus:simpleclient_common'
|
||||
implementation 'io.prometheus:simpleclient_guava'
|
||||
implementation 'io.prometheus:simpleclient_hotspot'
|
||||
implementation 'io.prometheus:simpleclient_pushgateway'
|
||||
implementation 'io.prometheus:prometheus-metrics-core'
|
||||
implementation 'io.prometheus:prometheus-metrics-instrumentation-guava'
|
||||
implementation 'io.prometheus:prometheus-metrics-instrumentation-jvm'
|
||||
implementation 'io.prometheus:prometheus-metrics-exporter-httpserver'
|
||||
implementation 'io.prometheus:prometheus-metrics-exporter-pushgateway'
|
||||
|
||||
implementation 'io.vertx:vertx-core'
|
||||
implementation 'io.vertx:vertx-web'
|
||||
|
||||
|
||||
@@ -18,12 +18,12 @@ import org.hyperledger.besu.metrics.opentelemetry.MetricsOtelPushService;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsHttpService;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsPushGatewayService;
|
||||
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
@@ -35,20 +35,18 @@ public interface MetricsService {
|
||||
/**
|
||||
* Create Metrics Service.
|
||||
*
|
||||
* @param vertx the vertx
|
||||
* @param configuration the configuration
|
||||
* @param metricsSystem the metrics system
|
||||
* @return the optional Metrics Service
|
||||
*/
|
||||
static Optional<MetricsService> create(
|
||||
final Vertx vertx,
|
||||
final MetricsConfiguration configuration,
|
||||
final MetricsSystem metricsSystem) {
|
||||
final MetricsConfiguration configuration, final MetricsSystem metricsSystem) {
|
||||
LoggerFactory.getLogger(MetricsService.class)
|
||||
.trace("Creating metrics service {}", configuration.getProtocol());
|
||||
if (configuration.getProtocol() == MetricsProtocol.PROMETHEUS) {
|
||||
if (configuration.isEnabled()) {
|
||||
return Optional.of(new MetricsHttpService(vertx, configuration, metricsSystem));
|
||||
return Optional.of(
|
||||
new MetricsHttpService(configuration, (PrometheusMetricsSystem) metricsSystem));
|
||||
} else if (configuration.isPushEnabled()) {
|
||||
return Optional.of(new MetricsPushGatewayService(configuration, metricsSystem));
|
||||
} else {
|
||||
|
||||
@@ -17,99 +17,14 @@ package org.hyperledger.besu.metrics;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
|
||||
/** The Observation. */
|
||||
public class Observation {
|
||||
private final MetricCategory category;
|
||||
private final String metricName;
|
||||
private final List<String> labels;
|
||||
private final Object value;
|
||||
|
||||
/**
|
||||
* Instantiates a new Observation.
|
||||
*
|
||||
* @param category the category
|
||||
* @param metricName the metric name
|
||||
* @param value the value
|
||||
* @param labels the labels
|
||||
*/
|
||||
public Observation(
|
||||
final MetricCategory category,
|
||||
final String metricName,
|
||||
final Object value,
|
||||
final List<String> labels) {
|
||||
this.category = category;
|
||||
this.metricName = metricName;
|
||||
this.value = value;
|
||||
this.labels = labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets category.
|
||||
*
|
||||
* @return the category
|
||||
*/
|
||||
public MetricCategory getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets metric name.
|
||||
*
|
||||
* @return the metric name
|
||||
*/
|
||||
public String getMetricName() {
|
||||
return metricName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets labels.
|
||||
*
|
||||
* @return the labels
|
||||
*/
|
||||
public List<String> getLabels() {
|
||||
return labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets value.
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
final Observation that = (Observation) o;
|
||||
return Objects.equals(category, that.category)
|
||||
&& Objects.equals(metricName, that.metricName)
|
||||
&& Objects.equals(labels, that.labels)
|
||||
&& Objects.equals(value, that.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(category, metricName, labels, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("category", category)
|
||||
.add("metricName", metricName)
|
||||
.add("labels", labels)
|
||||
.add("value", value)
|
||||
.toString();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The Observation.
|
||||
*
|
||||
* @param category the category
|
||||
* @param metricName the metric name
|
||||
* @param value the value
|
||||
* @param labels the labels
|
||||
*/
|
||||
public record Observation(
|
||||
MetricCategory category, String metricName, Object value, List<String> labels) {}
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.hyperledger.besu.plugin.services.metrics.ExternalSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledGauge;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
@@ -41,9 +42,6 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
/** The constant NO_OP_COUNTER. */
|
||||
public static final Counter NO_OP_COUNTER = new NoOpCounter();
|
||||
|
||||
/** The constant NO_OP_GAUGE. */
|
||||
public static final LabelledSuppliedMetric NO_OP_GAUGE = new NoOpValueCollector();
|
||||
|
||||
private static final OperationTimer.TimingContext NO_OP_TIMING_CONTEXT = () -> 0;
|
||||
|
||||
/** The constant NO_OP_OPERATION_TIMER. */
|
||||
@@ -65,18 +63,6 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
public static final LabelledMetric<OperationTimer> NO_OP_LABELLED_1_OPERATION_TIMER =
|
||||
new LabelCountingNoOpMetric<>(1, NO_OP_OPERATION_TIMER);
|
||||
|
||||
/** The constant NO_OP_LABELLED_1_GAUGE. */
|
||||
public static final LabelledSuppliedMetric NO_OP_LABELLED_1_GAUGE =
|
||||
new LabelledSuppliedNoOpMetric(1, NO_OP_GAUGE);
|
||||
|
||||
/** The constant NO_OP_LABELLED_2_GAUGE. */
|
||||
public static final LabelledSuppliedMetric NO_OP_LABELLED_2_GAUGE =
|
||||
new LabelledSuppliedNoOpMetric(2, NO_OP_GAUGE);
|
||||
|
||||
/** The constant NO_OP_LABELLED_3_GAUGE. */
|
||||
public static final LabelledSuppliedMetric NO_OP_LABELLED_3_GAUGE =
|
||||
new LabelledSuppliedNoOpMetric(3, NO_OP_GAUGE);
|
||||
|
||||
/** Default constructor */
|
||||
public NoOpMetricsSystem() {}
|
||||
|
||||
@@ -96,16 +82,7 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
* @return the counter labelled metric
|
||||
*/
|
||||
public static LabelledMetric<Counter> getCounterLabelledMetric(final int labelCount) {
|
||||
switch (labelCount) {
|
||||
case 1:
|
||||
return NO_OP_LABELLED_1_COUNTER;
|
||||
case 2:
|
||||
return NO_OP_LABELLED_2_COUNTER;
|
||||
case 3:
|
||||
return NO_OP_LABELLED_3_COUNTER;
|
||||
default:
|
||||
return new LabelCountingNoOpMetric<>(labelCount, NO_OP_COUNTER);
|
||||
}
|
||||
return new LabelCountingNoOpMetric<>(labelCount, NO_OP_COUNTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,11 +95,13 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackExternalSummary(
|
||||
public LabelledSuppliedSummary createLabelledSuppliedSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final Supplier<ExternalSummary> summarySupplier) {}
|
||||
final String... labelNames) {
|
||||
return getLabelledSuppliedSummary(labelNames.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelledMetric<OperationTimer> createLabelledTimer(
|
||||
@@ -141,11 +120,7 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
*/
|
||||
public static LabelledMetric<OperationTimer> getOperationTimerLabelledMetric(
|
||||
final int labelCount) {
|
||||
if (labelCount == 1) {
|
||||
return NO_OP_LABELLED_1_OPERATION_TIMER;
|
||||
} else {
|
||||
return new LabelCountingNoOpMetric<>(labelCount, NO_OP_OPERATION_TIMER);
|
||||
}
|
||||
return new LabelCountingNoOpMetric<>(labelCount, NO_OP_OPERATION_TIMER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -184,16 +159,17 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
* @return the labelled gauge
|
||||
*/
|
||||
public static LabelledSuppliedMetric getLabelledSuppliedMetric(final int labelCount) {
|
||||
switch (labelCount) {
|
||||
case 1:
|
||||
return NO_OP_LABELLED_1_GAUGE;
|
||||
case 2:
|
||||
return NO_OP_LABELLED_2_GAUGE;
|
||||
case 3:
|
||||
return NO_OP_LABELLED_3_GAUGE;
|
||||
default:
|
||||
return new LabelledSuppliedNoOpMetric(labelCount, NO_OP_GAUGE);
|
||||
}
|
||||
return new LabelledSuppliedNoOpMetric(labelCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets labelled supplied histogram.
|
||||
*
|
||||
* @param labelCount the label count
|
||||
* @return the labelled gauge
|
||||
*/
|
||||
public static LabelledSuppliedSummary getLabelledSuppliedSummary(final int labelCount) {
|
||||
return new LabelledSuppliedNoOpMetric(labelCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -249,7 +225,8 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
|
||||
/** The Labelled supplied NoOp metric. */
|
||||
@SuppressWarnings("removal") // remove when deprecated LabelledGauge is removed
|
||||
public static class LabelledSuppliedNoOpMetric implements LabelledSuppliedMetric, LabelledGauge {
|
||||
public static class LabelledSuppliedNoOpMetric
|
||||
implements LabelledSuppliedMetric, LabelledGauge, LabelledSuppliedSummary {
|
||||
/** The Label count. */
|
||||
final int labelCount;
|
||||
|
||||
@@ -257,22 +234,26 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
final List<String> labelValuesCache = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Labelled gauge NoOp metric.
|
||||
* Instantiates a new Labelled supplied NoOp metric.
|
||||
*
|
||||
* @param labelCount the label count
|
||||
* @param fakeMetric the fake metric
|
||||
*/
|
||||
public LabelledSuppliedNoOpMetric(
|
||||
final int labelCount, final LabelledSuppliedMetric fakeMetric) {
|
||||
public LabelledSuppliedNoOpMetric(final int labelCount) {
|
||||
this.labelCount = labelCount;
|
||||
this.fakeMetric = fakeMetric;
|
||||
}
|
||||
|
||||
/** The Fake metric. */
|
||||
final LabelledSuppliedMetric fakeMetric;
|
||||
|
||||
@Override
|
||||
public void labels(final DoubleSupplier valueSupplier, final String... labelValues) {
|
||||
internalLabels(valueSupplier, labelValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void labels(
|
||||
final Supplier<ExternalSummary> summarySupplier, final String... labelValues) {
|
||||
internalLabels(summarySupplier, labelValues);
|
||||
}
|
||||
|
||||
private void internalLabels(final Object valueSupplier, final String... labelValues) {
|
||||
final String labelValuesString = String.join(",", labelValues);
|
||||
Preconditions.checkArgument(
|
||||
!labelValuesCache.contains(labelValuesString),
|
||||
@@ -281,6 +262,7 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
labelValues.length == labelCount,
|
||||
"The count of labels used must match the count of labels expected.");
|
||||
Preconditions.checkNotNull(valueSupplier, "No valueSupplier specified");
|
||||
labelValuesCache.add(labelValuesString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,9 @@ import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.metrics.StandardMetricCategory;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.ExternalSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
@@ -41,7 +41,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
@@ -134,7 +133,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations(final MetricCategory category) {
|
||||
return streamObservations().filter(metricData -> metricData.getCategory().equals(category));
|
||||
return streamObservations().filter(metricData -> metricData.category().equals(category));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -246,11 +245,14 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackExternalSummary(
|
||||
public LabelledSuppliedSummary createLabelledSuppliedSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final Supplier<ExternalSummary> summarySupplier) {}
|
||||
final String... labelNames) {
|
||||
// not yet supported
|
||||
return (LabelledSuppliedSummary) NoOpMetricsSystem.getLabelledSuppliedMetric(labelNames.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelledMetric<OperationTimer> createLabelledTimer(
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.addLabelValues;
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.getLabelValues;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.prometheus.metrics.model.registry.Collector;
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
import io.prometheus.metrics.model.snapshots.SummarySnapshot;
|
||||
|
||||
/**
|
||||
* Abstract base class for Prometheus summary collectors. A summary provides a total count of
|
||||
* observations and a sum of all observed values, it calculates configurable quantiles over a
|
||||
* sliding time window.
|
||||
*/
|
||||
abstract class AbstractPrometheusSummary extends CategorizedPrometheusCollector {
|
||||
/** The Prometheus collector */
|
||||
protected Collector collector;
|
||||
|
||||
/**
|
||||
* Constructs a new AbstractPrometheusSummary.
|
||||
*
|
||||
* @param category The {@link MetricCategory} this collector is assigned to
|
||||
* @param name The name of this collector
|
||||
*/
|
||||
protected AbstractPrometheusSummary(final MetricCategory category, final String name) {
|
||||
super(category, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the identifier for this collector.
|
||||
*
|
||||
* @return The Prometheus name of the collector
|
||||
*/
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return collector.getPrometheusName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this collector with the given Prometheus registry.
|
||||
*
|
||||
* @param registry The Prometheus registry to register this collector with
|
||||
*/
|
||||
@Override
|
||||
public void register(final PrometheusRegistry registry) {
|
||||
registry.register(collector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this collector from the given Prometheus registry.
|
||||
*
|
||||
* @param registry The Prometheus registry to unregister this collector from
|
||||
*/
|
||||
@Override
|
||||
public void unregister(final PrometheusRegistry registry) {
|
||||
registry.unregister(collector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects the summary snapshot from the Prometheus collector.
|
||||
*
|
||||
* @return The collected summary snapshot
|
||||
*/
|
||||
private SummarySnapshot collect() {
|
||||
return (SummarySnapshot) collector.collect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the observations from the collected summary snapshot.
|
||||
*
|
||||
* @return A stream of observations
|
||||
*/
|
||||
@Override
|
||||
public Stream<Observation> streamObservations() {
|
||||
return collect().getDataPoints().stream()
|
||||
.flatMap(
|
||||
dataPoint -> {
|
||||
final var labelValues = getLabelValues(dataPoint.getLabels());
|
||||
final var quantiles = dataPoint.getQuantiles();
|
||||
final var observations = new ArrayList<Observation>(quantiles.size() + 2);
|
||||
|
||||
if (dataPoint.hasSum()) {
|
||||
observations.add(
|
||||
new Observation(
|
||||
category, name, dataPoint.getSum(), addLabelValues(labelValues, "sum")));
|
||||
}
|
||||
|
||||
if (dataPoint.hasCount()) {
|
||||
observations.add(
|
||||
new Observation(
|
||||
category,
|
||||
name,
|
||||
dataPoint.getCount(),
|
||||
addLabelValues(labelValues, "count")));
|
||||
}
|
||||
|
||||
quantiles.forEach(
|
||||
quantile ->
|
||||
observations.add(
|
||||
new Observation(
|
||||
category,
|
||||
name,
|
||||
quantile.getValue(),
|
||||
addLabelValues(
|
||||
labelValues,
|
||||
"quantile",
|
||||
Double.toString(quantile.getQuantile())))));
|
||||
|
||||
return observations.stream();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.prometheus.metrics.model.registry.Collector;
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
|
||||
|
||||
/**
|
||||
* Abstract base class for Prometheus supplied value collectors. A supplied value collector is one
|
||||
* which actual value is kept outside the metric system, for example in an external library or to
|
||||
* calculate the value only on demand when a metric scrape occurs. This class provides common
|
||||
* functionality for Prometheus supplied value collectors.
|
||||
*/
|
||||
abstract class AbstractPrometheusSuppliedValueCollector extends CategorizedPrometheusCollector
|
||||
implements LabelledSuppliedMetric {
|
||||
/** The collector */
|
||||
protected final Collector collector;
|
||||
|
||||
/** Map label values with the collector callback data */
|
||||
protected final Map<List<String>, CallbackData> labelledCallbackData = new ConcurrentHashMap<>();
|
||||
|
||||
protected AbstractPrometheusSuppliedValueCollector(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
super(category, name);
|
||||
this.collector = createCollector(help, labelNames);
|
||||
}
|
||||
|
||||
protected abstract Collector createCollector(final String help, final String... labelNames);
|
||||
|
||||
@Override
|
||||
public void labels(final DoubleSupplier valueSupplier, final String... labelValues) {
|
||||
final var valueList = List.of(labelValues);
|
||||
if (labelledCallbackData.putIfAbsent(valueList, new CallbackData(valueSupplier, labelValues))
|
||||
!= null) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("A collector has already been created for label values %s", valueList));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return collector.getPrometheusName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(final PrometheusRegistry registry) {
|
||||
registry.register(collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(final PrometheusRegistry registry) {
|
||||
registry.unregister(collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations() {
|
||||
final var snapshot = collector.collect();
|
||||
return snapshot.getDataPoints().stream().map(this::convertToObservation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the collected sample to an observation
|
||||
*
|
||||
* @param sample the collected sample
|
||||
* @return an observation
|
||||
*/
|
||||
protected abstract Observation convertToObservation(final DataPointSnapshot sample);
|
||||
|
||||
protected record CallbackData(DoubleSupplier valueSupplier, String[] labelValues) {}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
/** A Prometheus collector that is assigned to a category */
|
||||
public abstract class CategorizedPrometheusCollector implements PrometheusCollector {
|
||||
/** The {@link MetricCategory} this collector is assigned to */
|
||||
protected final MetricCategory category;
|
||||
|
||||
/** The name of this collector */
|
||||
protected final String name;
|
||||
|
||||
/** The prefixed name of this collector */
|
||||
protected final String prefixedName;
|
||||
|
||||
/**
|
||||
* Create a new collector assigned to the given category and with the given name, and computed the
|
||||
* prefixed name.
|
||||
*
|
||||
* @param category The {@link MetricCategory} this collector is assigned to
|
||||
* @param name The name of this collector
|
||||
*/
|
||||
protected CategorizedPrometheusCollector(final MetricCategory category, final String name) {
|
||||
this.category = category;
|
||||
this.name = name;
|
||||
this.prefixedName = prefixedName(category, name);
|
||||
}
|
||||
|
||||
private static String categoryPrefix(final MetricCategory category) {
|
||||
return category.getApplicationPrefix().orElse("") + category.getName() + "_";
|
||||
}
|
||||
|
||||
private static String prefixedName(final MetricCategory category, final String name) {
|
||||
return categoryPrefix(category) + name;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.DoubleSupplier;
|
||||
|
||||
import io.prometheus.client.Collector;
|
||||
import io.prometheus.client.Collector.MetricFamilySamples.Sample;
|
||||
|
||||
class CurrentValueCollector extends Collector {
|
||||
|
||||
private final String metricName;
|
||||
private final String help;
|
||||
private final DoubleSupplier valueSupplier;
|
||||
private final List<String> labelNames;
|
||||
private final List<String> labelValues;
|
||||
|
||||
public CurrentValueCollector(
|
||||
final String metricName, final String help, final DoubleSupplier valueSupplier) {
|
||||
this(metricName, help, emptyList(), emptyList(), valueSupplier);
|
||||
}
|
||||
|
||||
public CurrentValueCollector(
|
||||
final String metricName,
|
||||
final String help,
|
||||
final List<String> labelNames,
|
||||
final List<String> labelValues,
|
||||
final DoubleSupplier valueSupplier) {
|
||||
this.metricName = metricName;
|
||||
this.help = help;
|
||||
this.valueSupplier = valueSupplier;
|
||||
this.labelNames = labelNames;
|
||||
this.labelValues = labelValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetricFamilySamples> collect() {
|
||||
final Sample sample =
|
||||
new Sample(metricName, labelNames, labelValues, valueSupplier.getAsDouble());
|
||||
return singletonList(
|
||||
new MetricFamilySamples(metricName, Type.GAUGE, help, singletonList(sample)));
|
||||
}
|
||||
}
|
||||
@@ -15,61 +15,46 @@
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.prometheus.client.exporter.common.TextFormat;
|
||||
import io.vertx.core.Handler;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.vertx.core.http.HttpMethod;
|
||||
import io.vertx.core.http.HttpServer;
|
||||
import io.vertx.core.http.HttpServerOptions;
|
||||
import io.vertx.core.http.HttpServerResponse;
|
||||
import com.sun.net.httpserver.Authenticator;
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpPrincipal;
|
||||
import io.prometheus.metrics.exporter.httpserver.DefaultHandler;
|
||||
import io.prometheus.metrics.exporter.httpserver.HTTPServer;
|
||||
import io.vertx.core.net.HostAndPort;
|
||||
import io.vertx.ext.web.Router;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/** The Metrics http service. */
|
||||
public class MetricsHttpService implements MetricsService {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(MetricsHttpService.class);
|
||||
|
||||
private static final Authenticator.Result AUTHORIZED =
|
||||
new Authenticator.Success(new HttpPrincipal("metrics", "metrics"));
|
||||
private static final Authenticator.Result NOT_AUTHORIZED = new Authenticator.Failure(403);
|
||||
private static final InetSocketAddress EMPTY_SOCKET_ADDRESS = new InetSocketAddress("0.0.0.0", 0);
|
||||
|
||||
private final Vertx vertx;
|
||||
private final MetricsConfiguration config;
|
||||
private final MetricsSystem metricsSystem;
|
||||
|
||||
private HttpServer httpServer;
|
||||
private final PrometheusMetricsSystem metricsSystem;
|
||||
private HTTPServer httpServer;
|
||||
|
||||
/**
|
||||
* Instantiates a new Metrics http service.
|
||||
*
|
||||
* @param vertx the vertx
|
||||
* @param configuration the configuration
|
||||
* @param metricsSystem the metrics system
|
||||
*/
|
||||
public MetricsHttpService(
|
||||
final Vertx vertx,
|
||||
final MetricsConfiguration configuration,
|
||||
final MetricsSystem metricsSystem) {
|
||||
final MetricsConfiguration configuration, final PrometheusMetricsSystem metricsSystem) {
|
||||
validateConfig(configuration);
|
||||
this.vertx = vertx;
|
||||
this.config = configuration;
|
||||
this.metricsSystem = metricsSystem;
|
||||
}
|
||||
@@ -85,78 +70,39 @@ public class MetricsHttpService implements MetricsService {
|
||||
@Override
|
||||
public CompletableFuture<?> start() {
|
||||
LOG.info("Starting metrics http service on {}:{}", config.getHost(), config.getPort());
|
||||
// Create the HTTP server and a router object.
|
||||
httpServer =
|
||||
vertx.createHttpServer(
|
||||
new HttpServerOptions()
|
||||
.setHost(config.getHost())
|
||||
.setPort(config.getPort())
|
||||
.setIdleTimeout(config.getIdleTimeout())
|
||||
.setHandle100ContinueAutomatically(true)
|
||||
.setCompressionSupported(true));
|
||||
|
||||
final Router router = Router.router(vertx);
|
||||
try {
|
||||
httpServer =
|
||||
HTTPServer.builder()
|
||||
.hostname(config.getHost())
|
||||
.port(config.getPort())
|
||||
.registry(metricsSystem.getRegistry())
|
||||
.authenticator(
|
||||
new Authenticator() {
|
||||
@Override
|
||||
public Result authenticate(final HttpExchange exch) {
|
||||
return checkAllowlistHostHeader(exch);
|
||||
}
|
||||
})
|
||||
.defaultHandler(new RestrictedDefaultHandler())
|
||||
.buildAndStart();
|
||||
|
||||
// Verify Host header.
|
||||
router.route().handler(checkAllowlistHostHeader());
|
||||
|
||||
// Endpoint for AWS health check.
|
||||
router.route("/").method(HttpMethod.GET).handler(this::handleEmptyRequest);
|
||||
|
||||
// Endpoint for Prometheus metrics monitoring.
|
||||
router.route("/metrics").method(HttpMethod.GET).handler(this::metricsRequest);
|
||||
|
||||
final CompletableFuture<?> resultFuture = new CompletableFuture<>();
|
||||
httpServer
|
||||
.requestHandler(router)
|
||||
.listen(
|
||||
res -> {
|
||||
if (!res.failed()) {
|
||||
resultFuture.complete(null);
|
||||
final int actualPort = httpServer.actualPort();
|
||||
config.setActualPort(actualPort);
|
||||
LOG.info(
|
||||
"Metrics service started and listening on {}:{}", config.getHost(), actualPort);
|
||||
return;
|
||||
}
|
||||
httpServer = null;
|
||||
final Throwable cause = res.cause();
|
||||
if (cause instanceof SocketException) {
|
||||
resultFuture.completeExceptionally(
|
||||
new RuntimeException(
|
||||
String.format(
|
||||
"Failed to bind metrics listener to %s:%s (actual port %s): %s",
|
||||
config.getHost(),
|
||||
config.getPort(),
|
||||
config.getActualPort(),
|
||||
cause.getMessage())));
|
||||
return;
|
||||
}
|
||||
resultFuture.completeExceptionally(cause);
|
||||
});
|
||||
return resultFuture;
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} catch (final Throwable e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
private Handler<RoutingContext> checkAllowlistHostHeader() {
|
||||
return event -> {
|
||||
final Optional<String> hostHeader = getAndValidateHostHeader(event);
|
||||
if (config.getHostsAllowlist().contains("*")
|
||||
|| (hostHeader.isPresent() && hostIsInAllowlist(hostHeader.get()))) {
|
||||
event.next();
|
||||
} else {
|
||||
final HttpServerResponse response = event.response();
|
||||
if (!response.closed()) {
|
||||
response
|
||||
.setStatusCode(403)
|
||||
.putHeader("Content-Type", "application/json; charset=utf-8")
|
||||
.end("{\"message\":\"Host not authorized.\"}");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
private Authenticator.Result checkAllowlistHostHeader(final HttpExchange exch) {
|
||||
if (config.getHostsAllowlist().contains("*")) {
|
||||
return AUTHORIZED;
|
||||
}
|
||||
|
||||
private Optional<String> getAndValidateHostHeader(final RoutingContext event) {
|
||||
return Optional.ofNullable(event.request().authority()).map(HostAndPort::host);
|
||||
return Optional.ofNullable(exch.getRequestHeaders().getFirst("Host"))
|
||||
.map(host -> HostAndPort.parseAuthority(host, -1).host())
|
||||
.filter(this::hostIsInAllowlist)
|
||||
.map(unused -> AUTHORIZED)
|
||||
.orElse(NOT_AUTHORIZED);
|
||||
}
|
||||
|
||||
private boolean hostIsInAllowlist(final String hostHeader) {
|
||||
@@ -179,56 +125,12 @@ public class MetricsHttpService implements MetricsService {
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
final CompletableFuture<?> resultFuture = new CompletableFuture<>();
|
||||
httpServer.close(
|
||||
res -> {
|
||||
if (res.failed()) {
|
||||
resultFuture.completeExceptionally(res.cause());
|
||||
} else {
|
||||
httpServer = null;
|
||||
resultFuture.complete(null);
|
||||
}
|
||||
});
|
||||
return resultFuture;
|
||||
}
|
||||
|
||||
private void metricsRequest(final RoutingContext routingContext) {
|
||||
final Set<String> names = new TreeSet<>(routingContext.queryParam("name[]"));
|
||||
final HttpServerResponse response = routingContext.response();
|
||||
vertx.<String>executeBlocking(
|
||||
future -> {
|
||||
try {
|
||||
final ByteArrayOutputStream metrics = new ByteArrayOutputStream(16 * 1024);
|
||||
final OutputStreamWriter osw = new OutputStreamWriter(metrics, StandardCharsets.UTF_8);
|
||||
TextFormat.write004(
|
||||
osw,
|
||||
((PrometheusMetricsSystem) (metricsSystem))
|
||||
.getRegistry()
|
||||
.filteredMetricFamilySamples(names));
|
||||
osw.flush();
|
||||
osw.close();
|
||||
metrics.flush();
|
||||
metrics.close();
|
||||
future.complete(metrics.toString(StandardCharsets.UTF_8.name()));
|
||||
} catch (final IOException ioe) {
|
||||
future.fail(ioe);
|
||||
}
|
||||
},
|
||||
false,
|
||||
(res) -> {
|
||||
if (response.closed()) {
|
||||
// Request for metrics closed before response was generated
|
||||
return;
|
||||
}
|
||||
if (res.failed()) {
|
||||
LOG.error("Request for metrics failed", res.cause());
|
||||
response.setStatusCode(HttpResponseStatus.INTERNAL_SERVER_ERROR.code()).end();
|
||||
} else {
|
||||
response.setStatusCode(HttpResponseStatus.OK.code());
|
||||
response.putHeader("Content-Type", TextFormat.CONTENT_TYPE_004);
|
||||
response.end(res.result());
|
||||
}
|
||||
});
|
||||
try {
|
||||
httpServer.stop();
|
||||
return CompletableFuture.completedFuture(null);
|
||||
} catch (final Throwable e) {
|
||||
return CompletableFuture.failedFuture(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -240,7 +142,7 @@ public class MetricsHttpService implements MetricsService {
|
||||
if (httpServer == null) {
|
||||
return EMPTY_SOCKET_ADDRESS;
|
||||
}
|
||||
return new InetSocketAddress(config.getHost(), httpServer.actualPort());
|
||||
return new InetSocketAddress(config.getHost(), httpServer.getPort());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -248,11 +150,21 @@ public class MetricsHttpService implements MetricsService {
|
||||
if (httpServer == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(httpServer.actualPort());
|
||||
return Optional.of(httpServer.getPort());
|
||||
}
|
||||
|
||||
// Facilitate remote health-checks in AWS, inter alia.
|
||||
private void handleEmptyRequest(final RoutingContext routingContext) {
|
||||
routingContext.response().setStatusCode(201).end();
|
||||
private static class RestrictedDefaultHandler extends DefaultHandler {
|
||||
@Override
|
||||
public void handle(final HttpExchange exchange) throws IOException {
|
||||
if (!exchange.getRequestURI().getPath().equals("/")) {
|
||||
try {
|
||||
exchange.sendResponseHeaders(HTTP_NOT_FOUND, -1);
|
||||
} finally {
|
||||
exchange.close();
|
||||
}
|
||||
} else {
|
||||
super.handle(exchange);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.prometheus.client.exporter.PushGateway;
|
||||
import io.prometheus.metrics.exporter.pushgateway.PushGateway;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -71,7 +71,12 @@ public class MetricsPushGatewayService implements MetricsService {
|
||||
config.getPushHost(),
|
||||
config.getPushPort());
|
||||
|
||||
pushGateway = new PushGateway(config.getPushHost() + ":" + config.getPushPort());
|
||||
pushGateway =
|
||||
PushGateway.builder()
|
||||
.registry(((PrometheusMetricsSystem) metricsSystem).getRegistry())
|
||||
.address(config.getPushHost() + ":" + config.getPushPort())
|
||||
.job(config.getPrometheusJob())
|
||||
.build();
|
||||
|
||||
// Create the executor
|
||||
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
@@ -91,7 +96,7 @@ public class MetricsPushGatewayService implements MetricsService {
|
||||
scheduledExecutorService.shutdownNow();
|
||||
scheduledExecutorService.awaitTermination(30, TimeUnit.SECONDS);
|
||||
try {
|
||||
pushGateway.delete(config.getPrometheusJob());
|
||||
pushGateway.delete();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Could not clean up results on the Prometheus Push Gateway.", e);
|
||||
// Do not complete exceptionally, the gateway may be down and failures
|
||||
@@ -112,8 +117,7 @@ public class MetricsPushGatewayService implements MetricsService {
|
||||
|
||||
private void pushMetrics() {
|
||||
try {
|
||||
pushGateway.pushAdd(
|
||||
((PrometheusMetricsSystem) metricsSystem).getRegistry(), config.getPrometheusJob());
|
||||
pushGateway.pushAdd();
|
||||
} catch (final IOException e) {
|
||||
LOG.warn("Could not push metrics", e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
import io.prometheus.metrics.model.snapshots.Label;
|
||||
import io.prometheus.metrics.model.snapshots.Labels;
|
||||
|
||||
/** Wraps a native Prometheus collector inside the metric system */
|
||||
public interface PrometheusCollector {
|
||||
|
||||
/**
|
||||
* Get the identifier of the collector
|
||||
*
|
||||
* @return the identifier of the collector
|
||||
*/
|
||||
String getIdentifier();
|
||||
|
||||
/**
|
||||
* Register this collector to the specified registry
|
||||
*
|
||||
* @param registry the registry
|
||||
*/
|
||||
void register(final PrometheusRegistry registry);
|
||||
|
||||
/**
|
||||
* Unregister this collector from the specified registry
|
||||
*
|
||||
* @param registry the registry
|
||||
*/
|
||||
void unregister(final PrometheusRegistry registry);
|
||||
|
||||
/**
|
||||
* Stream the data points of this collector
|
||||
*
|
||||
* @return a stream of the data points of this collector
|
||||
*/
|
||||
Stream<Observation> streamObservations();
|
||||
|
||||
/**
|
||||
* Utility to get the label values as strings from native Prometheus labels
|
||||
*
|
||||
* @param labels the Prometheus labels
|
||||
* @return the label values as strings
|
||||
*/
|
||||
static List<String> getLabelValues(final Labels labels) {
|
||||
return labels.stream().map(Label::getValue).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add new values to an existing list of label values
|
||||
*
|
||||
* @param labelValues existing list of label values
|
||||
* @param values the values to add
|
||||
* @return a new list with new values appended to the original list
|
||||
*/
|
||||
static List<String> addLabelValues(final List<String> labelValues, final String... values) {
|
||||
final var newList = new ArrayList<String>(labelValues.size() + values.length);
|
||||
newList.addAll(labelValues);
|
||||
Collections.addAll(newList, values);
|
||||
return newList;
|
||||
}
|
||||
}
|
||||
@@ -14,34 +14,88 @@
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.getLabelValues;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
class PrometheusCounter implements LabelledMetric<Counter> {
|
||||
import java.util.stream.Stream;
|
||||
|
||||
private final io.prometheus.client.Counter counter;
|
||||
import io.prometheus.metrics.core.datapoints.CounterDataPoint;
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
|
||||
public PrometheusCounter(final io.prometheus.client.Counter counter) {
|
||||
this.counter = counter;
|
||||
/**
|
||||
* A Prometheus counter implementation for Besu metrics. This class provides a Prometheus counter
|
||||
* where the actual value is kept internally by the collector and methods are provided to increase
|
||||
* the value when needed.
|
||||
*/
|
||||
class PrometheusCounter extends CategorizedPrometheusCollector implements LabelledMetric<Counter> {
|
||||
private final io.prometheus.metrics.core.metrics.Counter counter;
|
||||
|
||||
public PrometheusCounter(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
super(category, name);
|
||||
this.counter =
|
||||
io.prometheus.metrics.core.metrics.Counter.builder()
|
||||
.name(this.prefixedName)
|
||||
.help(help)
|
||||
.labelNames(labelNames)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Counter labels(final String... labels) {
|
||||
return new UnlabelledCounter(counter.labels(labels));
|
||||
return new UnlabelledCounter(counter.labelValues(labels));
|
||||
}
|
||||
|
||||
private static class UnlabelledCounter implements Counter {
|
||||
private final io.prometheus.client.Counter.Child counter;
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return counter.getPrometheusName();
|
||||
}
|
||||
|
||||
private UnlabelledCounter(final io.prometheus.client.Counter.Child counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
@Override
|
||||
public void register(final PrometheusRegistry registry) {
|
||||
registry.register(counter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(final PrometheusRegistry registry) {
|
||||
registry.unregister(counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Streams the observations from the collected counter data points.
|
||||
*
|
||||
* @return A stream of observations
|
||||
*/
|
||||
@Override
|
||||
public Stream<Observation> streamObservations() {
|
||||
return counter.collect().getDataPoints().stream()
|
||||
.map(
|
||||
sample ->
|
||||
new Observation(
|
||||
category, name, sample.getValue(), getLabelValues(sample.getLabels())));
|
||||
}
|
||||
|
||||
/** A private record class representing an unlabelled counter. */
|
||||
private record UnlabelledCounter(CounterDataPoint counter) implements Counter {
|
||||
|
||||
/** Increments the counter by one. */
|
||||
@Override
|
||||
public void inc() {
|
||||
counter.inc();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the counter by the specified amount.
|
||||
*
|
||||
* @param amount The amount to increment the counter by
|
||||
*/
|
||||
@Override
|
||||
public void inc(final long amount) {
|
||||
counter.inc((double) amount);
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.getLabelValues;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.ToDoubleFunction;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import io.prometheus.metrics.instrumentation.guava.CacheMetricsCollector;
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
import io.prometheus.metrics.model.snapshots.CounterSnapshot;
|
||||
import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
|
||||
import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
|
||||
import io.prometheus.metrics.model.snapshots.MetricSnapshot;
|
||||
import io.vertx.core.impl.ConcurrentHashSet;
|
||||
|
||||
/**
|
||||
* A Prometheus Guava cache collector implementation for Besu metrics. This class provides a way to
|
||||
* expose metrics from Guava caches, it behaves differently from other collectors, since instead of
|
||||
* having one collector per cache, Prometheus provides only one collector for all caches, so we need
|
||||
* a Context that wraps the Prometheus single collector and handles its registration, while here we
|
||||
* keep the abstraction of one Prometheus collector for one Guava cache, and we also verify that
|
||||
* there is no collector name clash.
|
||||
*/
|
||||
class PrometheusGuavaCache extends CategorizedPrometheusCollector {
|
||||
/** Use to reduce the possibility of a name clash with other collectors */
|
||||
private static final String NAME_PREFIX = "__guavaCacheMetricsCollector__";
|
||||
|
||||
private final Cache<?, ?> cache;
|
||||
private final Context context;
|
||||
|
||||
public PrometheusGuavaCache(
|
||||
final MetricCategory category,
|
||||
final Context context,
|
||||
final String name,
|
||||
final Cache<?, ?> cache) {
|
||||
super(category, name);
|
||||
if (context.alreadyExists(name)) {
|
||||
throw new IllegalStateException("Cache already registered: " + name);
|
||||
}
|
||||
this.cache = cache;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return category.getName() + "." + NAME_PREFIX + "." + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(final PrometheusRegistry registry) {
|
||||
context.registerCache(registry, name, cache);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(final PrometheusRegistry registry) {
|
||||
context.unregisterCache(registry, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations() {
|
||||
return context.streamObservations(category, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Since Prometheus provides only one collector for all Guava caches, we only need to register
|
||||
* that collector once when the first Besu Guava cache collector is created, and unregister it
|
||||
* when the last is unregistered, so we have this context to keep track of that and also manage
|
||||
* the observations stream.
|
||||
*/
|
||||
static class Context {
|
||||
private static final Map<String, ToDoubleFunction<DataPointSnapshot>>
|
||||
COLLECTOR_VALUE_EXTRACTORS =
|
||||
Map.of(
|
||||
"guava_cache_eviction", Context::counterValueExtractor,
|
||||
"guava_cache_hit", Context::counterValueExtractor,
|
||||
"guava_cache_miss", Context::counterValueExtractor,
|
||||
"guava_cache_requests", Context::counterValueExtractor,
|
||||
"guava_cache_size", Context::gaugeValueExtractor);
|
||||
|
||||
private final CacheMetricsCollector cacheMetricsCollector = new CacheMetricsCollector();
|
||||
private final Set<String> cacheNames = new ConcurrentHashSet<>();
|
||||
private final AtomicBoolean collectorRegistered = new AtomicBoolean(false);
|
||||
|
||||
boolean alreadyExists(final String name) {
|
||||
return cacheNames.contains(name);
|
||||
}
|
||||
|
||||
void registerCache(
|
||||
final PrometheusRegistry registry, final String name, final Cache<?, ?> cache) {
|
||||
cacheMetricsCollector.addCache(name, cache);
|
||||
cacheNames.add(name);
|
||||
if (collectorRegistered.compareAndSet(false, true)) {
|
||||
registry.register(cacheMetricsCollector);
|
||||
}
|
||||
}
|
||||
|
||||
void unregisterCache(final PrometheusRegistry registry, final String name) {
|
||||
cacheMetricsCollector.removeCache(name);
|
||||
cacheNames.remove(name);
|
||||
if (cacheNames.isEmpty() && collectorRegistered.compareAndSet(true, false)) {
|
||||
registry.unregister(cacheMetricsCollector);
|
||||
}
|
||||
}
|
||||
|
||||
void clear() {
|
||||
cacheNames.forEach(cacheMetricsCollector::removeCache);
|
||||
cacheNames.clear();
|
||||
collectorRegistered.set(false);
|
||||
}
|
||||
|
||||
private Stream<Observation> streamObservations(
|
||||
final MetricCategory category, final String cacheName) {
|
||||
return cacheMetricsCollector.collect().stream()
|
||||
.flatMap(ms -> convertToObservations(category, cacheName, ms));
|
||||
}
|
||||
|
||||
private static Stream<Observation> convertToObservations(
|
||||
final MetricCategory category, final String cacheName, final MetricSnapshot snapshot) {
|
||||
final var prometheusName = snapshot.getMetadata().getPrometheusName();
|
||||
if (COLLECTOR_VALUE_EXTRACTORS.containsKey(prometheusName)) {
|
||||
return snapshotToObservations(category, cacheName, prometheusName, snapshot);
|
||||
}
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
private static Stream<Observation> snapshotToObservations(
|
||||
final MetricCategory category,
|
||||
final String cacheName,
|
||||
final String prometheusName,
|
||||
final MetricSnapshot snapshot) {
|
||||
return snapshot.getDataPoints().stream()
|
||||
.filter(gdps -> gdps.getLabels().get("cache").equals(cacheName))
|
||||
.map(
|
||||
gdps ->
|
||||
new Observation(
|
||||
category,
|
||||
prometheusName,
|
||||
COLLECTOR_VALUE_EXTRACTORS.get(prometheusName).applyAsDouble(gdps),
|
||||
getLabelValues(gdps.getLabels())));
|
||||
}
|
||||
|
||||
private static double gaugeValueExtractor(final DataPointSnapshot snapshot) {
|
||||
return ((GaugeSnapshot.GaugeDataPointSnapshot) snapshot).getValue();
|
||||
}
|
||||
|
||||
private static double counterValueExtractor(final DataPointSnapshot snapshot) {
|
||||
return ((CounterSnapshot.CounterDataPointSnapshot) snapshot).getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,59 +14,61 @@
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static java.util.Map.entry;
|
||||
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.metrics.StandardMetricCategory;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.ExternalSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.prometheus.client.Collector;
|
||||
import io.prometheus.client.Collector.MetricFamilySamples;
|
||||
import io.prometheus.client.Collector.MetricFamilySamples.Sample;
|
||||
import io.prometheus.client.CollectorRegistry;
|
||||
import io.prometheus.client.Counter;
|
||||
import io.prometheus.client.Histogram;
|
||||
import io.prometheus.client.Summary;
|
||||
import io.prometheus.client.guava.cache.CacheMetricsCollector;
|
||||
import io.prometheus.client.hotspot.BufferPoolsExports;
|
||||
import io.prometheus.client.hotspot.ClassLoadingExports;
|
||||
import io.prometheus.client.hotspot.GarbageCollectorExports;
|
||||
import io.prometheus.client.hotspot.MemoryPoolsExports;
|
||||
import io.prometheus.client.hotspot.StandardExports;
|
||||
import io.prometheus.client.hotspot.ThreadExports;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmBufferPoolMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmClassLoadingMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmCompilationMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmGarbageCollectorMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmMemoryMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmMemoryPoolAllocationMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmNativeMemoryMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmRuntimeInfoMetric;
|
||||
import io.prometheus.metrics.instrumentation.jvm.JvmThreadsMetrics;
|
||||
import io.prometheus.metrics.instrumentation.jvm.ProcessMetrics;
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
import io.vertx.core.impl.ConcurrentHashSet;
|
||||
|
||||
/** The Prometheus metrics system. */
|
||||
public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
private static final List<String> EXTERNAL_SUMMARY_LABELS = List.of("quantile");
|
||||
private static final Map<Double, Double> DEFAULT_SUMMARY_QUANTILES =
|
||||
Map.ofEntries(
|
||||
entry(0.2, 0.02),
|
||||
entry(0.5, 0.05),
|
||||
entry(0.8, 0.02),
|
||||
entry(0.95, 0.005),
|
||||
entry(0.99, 0.001),
|
||||
entry(1.0, 0.0));
|
||||
|
||||
private final Map<MetricCategory, Collection<Collector>> collectors = new ConcurrentHashMap<>();
|
||||
private final CollectorRegistry registry = new CollectorRegistry(true);
|
||||
private final Map<String, LabelledMetric<org.hyperledger.besu.plugin.services.metrics.Counter>>
|
||||
cachedCounters = new ConcurrentHashMap<>();
|
||||
private final Map<String, LabelledMetric<OperationTimer>> cachedTimers =
|
||||
private final Map<MetricCategory, Collection<PrometheusCollector>> collectors =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Set<String> totalSuffixedCounters = new ConcurrentHashSet<>();
|
||||
private final Map<MetricCategory, CacheMetricsCollector> guavaCacheCollectors =
|
||||
private final PrometheusRegistry registry = PrometheusRegistry.defaultRegistry;
|
||||
private final Map<CachedMetricKey, LabelledMetric<Counter>> cachedCounters =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Set<String> guavaCacheNames = new ConcurrentHashSet<>();
|
||||
private final Map<CachedMetricKey, LabelledMetric<OperationTimer>> cachedTimers =
|
||||
new ConcurrentHashMap<>();
|
||||
private final PrometheusGuavaCache.Context guavaCacheCollectorContext =
|
||||
new PrometheusGuavaCache.Context();
|
||||
|
||||
private final Set<MetricCategory> enabledCategories;
|
||||
private final boolean timersEnabled;
|
||||
@@ -85,15 +87,19 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
|
||||
/** Init. */
|
||||
public void init() {
|
||||
if (isCategoryEnabled(StandardMetricCategory.PROCESS)) {
|
||||
registerCollector(StandardMetricCategory.PROCESS, new StandardExports());
|
||||
}
|
||||
if (isCategoryEnabled(StandardMetricCategory.JVM)) {
|
||||
registerCollector(StandardMetricCategory.JVM, new MemoryPoolsExports());
|
||||
registerCollector(StandardMetricCategory.JVM, new BufferPoolsExports());
|
||||
registerCollector(StandardMetricCategory.JVM, new GarbageCollectorExports());
|
||||
registerCollector(StandardMetricCategory.JVM, new ThreadExports());
|
||||
registerCollector(StandardMetricCategory.JVM, new ClassLoadingExports());
|
||||
JvmThreadsMetrics.builder().register(registry);
|
||||
JvmBufferPoolMetrics.builder().register(registry);
|
||||
JvmClassLoadingMetrics.builder().register(registry);
|
||||
JvmCompilationMetrics.builder().register(registry);
|
||||
JvmGarbageCollectorMetrics.builder().register(registry);
|
||||
JvmMemoryMetrics.builder().register(registry);
|
||||
JvmMemoryPoolAllocationMetrics.builder().register(registry);
|
||||
JvmNativeMemoryMetrics.builder().register(registry);
|
||||
JvmRuntimeInfoMetric.builder().register(registry);
|
||||
}
|
||||
if (isCategoryEnabled(StandardMetricCategory.PROCESS)) {
|
||||
ProcessMetrics.builder().register(registry);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,22 +109,20 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelledMetric<org.hyperledger.besu.plugin.services.metrics.Counter> createLabelledCounter(
|
||||
public LabelledMetric<Counter> createLabelledCounter(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
final String metricName = convertToPrometheusCounterName(category, name);
|
||||
return cachedCounters.computeIfAbsent(
|
||||
metricName,
|
||||
(k) -> {
|
||||
CachedMetricKey.of(category, name),
|
||||
k -> {
|
||||
if (isCategoryEnabled(category)) {
|
||||
final Counter counter = Counter.build(metricName, help).labelNames(labelNames).create();
|
||||
final var counter = new PrometheusCounter(category, name, help, labelNames);
|
||||
registerCollector(category, counter);
|
||||
return new PrometheusCounter(counter);
|
||||
} else {
|
||||
return NoOpMetricsSystem.getCounterLabelledMetric(labelNames.length);
|
||||
return counter;
|
||||
}
|
||||
return NoOpMetricsSystem.getCounterLabelledMetric(labelNames.length);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -128,26 +132,16 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
final String metricName = convertToPrometheusName(category, name);
|
||||
return cachedTimers.computeIfAbsent(
|
||||
metricName,
|
||||
(k) -> {
|
||||
CachedMetricKey.of(category, name),
|
||||
k -> {
|
||||
if (timersEnabled && isCategoryEnabled(category)) {
|
||||
final Summary summary =
|
||||
Summary.build(metricName, help)
|
||||
.quantile(0.2, 0.02)
|
||||
.quantile(0.5, 0.05)
|
||||
.quantile(0.8, 0.02)
|
||||
.quantile(0.95, 0.005)
|
||||
.quantile(0.99, 0.001)
|
||||
.quantile(1.0, 0)
|
||||
.labelNames(labelNames)
|
||||
.create();
|
||||
final var summary =
|
||||
new PrometheusTimer(category, name, help, DEFAULT_SUMMARY_QUANTILES, labelNames);
|
||||
registerCollector(category, summary);
|
||||
return new PrometheusTimer(summary);
|
||||
} else {
|
||||
return NoOpMetricsSystem.getOperationTimerLabelledMetric(labelNames.length);
|
||||
return summary;
|
||||
}
|
||||
return NoOpMetricsSystem.getOperationTimerLabelledMetric(labelNames.length);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -157,85 +151,41 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
final String metricName = convertToPrometheusName(category, name);
|
||||
return cachedTimers.computeIfAbsent(
|
||||
metricName,
|
||||
(k) -> {
|
||||
CachedMetricKey.of(category, name),
|
||||
k -> {
|
||||
if (timersEnabled && isCategoryEnabled(category)) {
|
||||
final Histogram histogram =
|
||||
Histogram.build(metricName, help).labelNames(labelNames).buckets(1D).create();
|
||||
final var histogram =
|
||||
new PrometheusSimpleTimer(category, name, help, new double[] {1D}, labelNames);
|
||||
registerCollector(category, histogram);
|
||||
return new PrometheusSimpleTimer(histogram);
|
||||
} else {
|
||||
return NoOpMetricsSystem.getOperationTimerLabelledMetric(labelNames.length);
|
||||
return histogram;
|
||||
}
|
||||
return NoOpMetricsSystem.getOperationTimerLabelledMetric(labelNames.length);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGauge(
|
||||
public LabelledSuppliedSummary createLabelledSuppliedSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final DoubleSupplier valueSupplier) {
|
||||
final String metricName = convertToPrometheusName(category, name);
|
||||
final String... labelNames) {
|
||||
if (isCategoryEnabled(category)) {
|
||||
final Collector collector = new CurrentValueCollector(metricName, help, valueSupplier);
|
||||
registerCollector(category, collector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackExternalSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final Supplier<ExternalSummary> summarySupplier) {
|
||||
if (isCategoryEnabled(category)) {
|
||||
final var externalSummaryCollector =
|
||||
new Collector() {
|
||||
@Override
|
||||
public List<MetricFamilySamples> collect() {
|
||||
final var externalSummary = summarySupplier.get();
|
||||
|
||||
final var quantileValues =
|
||||
externalSummary.quantiles().stream()
|
||||
.map(
|
||||
quantile ->
|
||||
new Sample(
|
||||
name,
|
||||
EXTERNAL_SUMMARY_LABELS,
|
||||
List.of(Double.toString(quantile.quantile())),
|
||||
quantile.value()))
|
||||
.toList();
|
||||
|
||||
return List.of(
|
||||
new MetricFamilySamples(
|
||||
name, Type.SUMMARY, "RocksDB histogram for " + name, quantileValues));
|
||||
}
|
||||
};
|
||||
|
||||
registerCollector(category, externalSummaryCollector);
|
||||
final PrometheusSuppliedSummary summary =
|
||||
new PrometheusSuppliedSummary(category, name, help, labelNames);
|
||||
registerCollector(category, summary);
|
||||
return summary;
|
||||
}
|
||||
return NoOpMetricsSystem.getLabelledSuppliedSummary(labelNames.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGuavaCacheCollector(
|
||||
final MetricCategory category, final String name, final Cache<?, ?> cache) {
|
||||
if (isCategoryEnabled(category)) {
|
||||
if (guavaCacheNames.contains(name)) {
|
||||
throw new IllegalStateException("Cache already registered: " + name);
|
||||
}
|
||||
guavaCacheNames.add(name);
|
||||
final var guavaCacheCollector =
|
||||
guavaCacheCollectors.computeIfAbsent(
|
||||
category,
|
||||
unused -> {
|
||||
final var cmc = new CacheMetricsCollector();
|
||||
registerCollector(category, cmc);
|
||||
return cmc;
|
||||
});
|
||||
guavaCacheCollector.addCache(name, cache);
|
||||
final var cacheCollector =
|
||||
new PrometheusGuavaCache(category, guavaCacheCollectorContext, name, cache);
|
||||
registerCollector(category, cacheCollector);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +195,13 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
return createLabelledSuppliedMetric(category, Collector.Type.COUNTER, name, help, labelNames);
|
||||
if (isCategoryEnabled(category)) {
|
||||
final PrometheusSuppliedCounter counter =
|
||||
new PrometheusSuppliedCounter(category, name, help, labelNames);
|
||||
registerCollector(category, counter);
|
||||
return counter;
|
||||
}
|
||||
return NoOpMetricsSystem.getLabelledSuppliedMetric(labelNames.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -254,53 +210,38 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
return createLabelledSuppliedMetric(category, Collector.Type.GAUGE, name, help, labelNames);
|
||||
}
|
||||
|
||||
private LabelledSuppliedMetric createLabelledSuppliedMetric(
|
||||
final MetricCategory category,
|
||||
final Collector.Type type,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
final String metricName = convertToPrometheusName(category, name);
|
||||
if (isCategoryEnabled(category)) {
|
||||
final PrometheusSuppliedValueCollector suppliedValueCollector =
|
||||
new PrometheusSuppliedValueCollector(type, metricName, help, List.of(labelNames));
|
||||
registerCollector(category, suppliedValueCollector);
|
||||
return suppliedValueCollector;
|
||||
final PrometheusSuppliedGauge gauge =
|
||||
new PrometheusSuppliedGauge(category, name, help, labelNames);
|
||||
registerCollector(category, gauge);
|
||||
return gauge;
|
||||
}
|
||||
return NoOpMetricsSystem.getLabelledSuppliedMetric(labelNames.length);
|
||||
}
|
||||
|
||||
private void registerCollector(final MetricCategory category, final Collector collector) {
|
||||
final Collection<Collector> categoryCollectors =
|
||||
this.collectors.computeIfAbsent(
|
||||
category, key -> Collections.newSetFromMap(new ConcurrentHashMap<>()));
|
||||
|
||||
final List<String> newSamples =
|
||||
collector.collect().stream().map(metricFamilySamples -> metricFamilySamples.name).toList();
|
||||
private void registerCollector(
|
||||
final MetricCategory category, final PrometheusCollector collector) {
|
||||
final Collection<PrometheusCollector> categoryCollectors =
|
||||
this.collectors.computeIfAbsent(category, key -> new ConcurrentHashSet<>());
|
||||
|
||||
// unregister if already present
|
||||
categoryCollectors.stream()
|
||||
.filter(
|
||||
c ->
|
||||
c.collect().stream()
|
||||
.anyMatch(metricFamilySamples -> newSamples.contains(metricFamilySamples.name)))
|
||||
.filter(c -> c.getIdentifier().equals(collector.getIdentifier()))
|
||||
.findFirst()
|
||||
.ifPresent(
|
||||
c -> {
|
||||
categoryCollectors.remove(c);
|
||||
registry.unregister(c);
|
||||
c.unregister(registry);
|
||||
});
|
||||
|
||||
categoryCollectors.add(collector.register(registry));
|
||||
collector.register(registry);
|
||||
categoryCollectors.add(collector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations(final MetricCategory category) {
|
||||
return collectors.getOrDefault(category, Collections.emptySet()).stream()
|
||||
.flatMap(collector -> collector.collect().stream())
|
||||
.flatMap(familySamples -> convertSamplesToObservations(category, familySamples));
|
||||
.flatMap(PrometheusCollector::streamObservations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -308,137 +249,22 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
return collectors.keySet().stream().flatMap(this::streamObservations);
|
||||
}
|
||||
|
||||
PrometheusRegistry getRegistry() {
|
||||
return registry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
registry.clear();
|
||||
collectors.clear();
|
||||
cachedCounters.clear();
|
||||
cachedTimers.clear();
|
||||
guavaCacheCollectors.clear();
|
||||
guavaCacheNames.clear();
|
||||
guavaCacheCollectorContext.clear();
|
||||
}
|
||||
|
||||
private Stream<Observation> convertSamplesToObservations(
|
||||
final MetricCategory category, final MetricFamilySamples familySamples) {
|
||||
return familySamples.samples.stream()
|
||||
.map(sample -> createObservationFromSample(category, sample, familySamples));
|
||||
}
|
||||
|
||||
private Observation createObservationFromSample(
|
||||
final MetricCategory category, final Sample sample, final MetricFamilySamples familySamples) {
|
||||
if (familySamples.type == Collector.Type.HISTOGRAM) {
|
||||
return convertHistogramSampleNamesToLabels(category, sample, familySamples);
|
||||
private record CachedMetricKey(MetricCategory category, String name) {
|
||||
static CachedMetricKey of(final MetricCategory category, final String name) {
|
||||
return new CachedMetricKey(category, name);
|
||||
}
|
||||
if (familySamples.type == Collector.Type.SUMMARY) {
|
||||
return convertSummarySampleNamesToLabels(category, sample, familySamples);
|
||||
}
|
||||
if (familySamples.type == Collector.Type.COUNTER) {
|
||||
return convertCounterNamesToLabels(category, sample, familySamples);
|
||||
}
|
||||
return new Observation(
|
||||
category,
|
||||
convertFromPrometheusName(category, sample.name),
|
||||
sample.value,
|
||||
sample.labelValues);
|
||||
}
|
||||
|
||||
private Observation convertCounterNamesToLabels(
|
||||
final MetricCategory category, final Sample sample, final MetricFamilySamples familySamples) {
|
||||
final List<String> labelValues = new ArrayList<>(sample.labelValues);
|
||||
if (sample.name.endsWith("_created")) {
|
||||
labelValues.add("created");
|
||||
}
|
||||
|
||||
return new Observation(
|
||||
category,
|
||||
convertFromPrometheusCounterName(category, familySamples.name),
|
||||
sample.value,
|
||||
labelValues);
|
||||
}
|
||||
|
||||
private Observation convertHistogramSampleNamesToLabels(
|
||||
final MetricCategory category, final Sample sample, final MetricFamilySamples familySamples) {
|
||||
final List<String> labelValues = new ArrayList<>(sample.labelValues);
|
||||
if (sample.name.endsWith("_bucket")) {
|
||||
labelValues.add(labelValues.size() - 1, "bucket");
|
||||
} else {
|
||||
labelValues.add(sample.name.substring(sample.name.lastIndexOf("_") + 1));
|
||||
}
|
||||
return new Observation(
|
||||
category,
|
||||
convertFromPrometheusName(category, familySamples.name),
|
||||
sample.value,
|
||||
labelValues);
|
||||
}
|
||||
|
||||
private Observation convertSummarySampleNamesToLabels(
|
||||
final MetricCategory category, final Sample sample, final MetricFamilySamples familySamples) {
|
||||
final List<String> labelValues = new ArrayList<>(sample.labelValues);
|
||||
if (sample.name.endsWith("_sum")) {
|
||||
labelValues.add("sum");
|
||||
} else if (sample.name.endsWith("_count")) {
|
||||
labelValues.add("count");
|
||||
} else if (sample.name.endsWith("_created")) {
|
||||
labelValues.add("created");
|
||||
} else {
|
||||
labelValues.add(labelValues.size() - 1, "quantile");
|
||||
}
|
||||
return new Observation(
|
||||
category,
|
||||
convertFromPrometheusName(category, familySamples.name),
|
||||
sample.value,
|
||||
labelValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to prometheus name.
|
||||
*
|
||||
* @param category the category
|
||||
* @param name the name
|
||||
* @return the name as string
|
||||
*/
|
||||
public String convertToPrometheusName(final MetricCategory category, final String name) {
|
||||
return prometheusPrefix(category) + name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert to prometheus counter name. Prometheus adds a _total suffix to the name if not present,
|
||||
* so we remember if the original name already has it, to be able to convert back correctly
|
||||
*
|
||||
* @param category the category
|
||||
* @param name the name
|
||||
* @return the name as string
|
||||
*/
|
||||
public String convertToPrometheusCounterName(final MetricCategory category, final String name) {
|
||||
if (name.endsWith("_total")) {
|
||||
totalSuffixedCounters.add(name);
|
||||
}
|
||||
return convertToPrometheusName(category, name);
|
||||
}
|
||||
|
||||
private String convertFromPrometheusName(final MetricCategory category, final String metricName) {
|
||||
final String prefix = prometheusPrefix(category);
|
||||
return metricName.startsWith(prefix) ? metricName.substring(prefix.length()) : metricName;
|
||||
}
|
||||
|
||||
private String convertFromPrometheusCounterName(
|
||||
final MetricCategory category, final String metricName) {
|
||||
final String unPrefixedName = convertFromPrometheusName(category, metricName);
|
||||
return totalSuffixedCounters.contains(unPrefixedName + "_total")
|
||||
? unPrefixedName + "_total"
|
||||
: unPrefixedName;
|
||||
}
|
||||
|
||||
private String prometheusPrefix(final MetricCategory category) {
|
||||
return category.getApplicationPrefix().orElse("") + category.getName() + "_";
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets registry.
|
||||
*
|
||||
* @return the registry
|
||||
*/
|
||||
CollectorRegistry getRegistry() {
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,22 +14,86 @@
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.addLabelValues;
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.getLabelValues;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import io.prometheus.client.Histogram;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
class PrometheusSimpleTimer implements LabelledMetric<OperationTimer> {
|
||||
import io.prometheus.metrics.core.metrics.Histogram;
|
||||
import io.prometheus.metrics.model.registry.PrometheusRegistry;
|
||||
|
||||
/**
|
||||
* An implementation of Besu simple timer backed by a Prometheus histogram. The histogram samples
|
||||
* durations and counts them in configurable buckets. It also provides a sum of all observed values.
|
||||
*/
|
||||
class PrometheusSimpleTimer extends CategorizedPrometheusCollector
|
||||
implements LabelledMetric<OperationTimer> {
|
||||
|
||||
private final Histogram histogram;
|
||||
|
||||
public PrometheusSimpleTimer(final Histogram histogram) {
|
||||
this.histogram = histogram;
|
||||
public PrometheusSimpleTimer(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final double[] buckets,
|
||||
final String... labelNames) {
|
||||
super(category, name);
|
||||
this.histogram =
|
||||
Histogram.builder()
|
||||
.name(this.prefixedName)
|
||||
.help(help)
|
||||
.labelNames(labelNames)
|
||||
.classicOnly()
|
||||
.classicUpperBounds(buckets)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationTimer labels(final String... labels) {
|
||||
final Histogram.Child metric = histogram.labels(labels);
|
||||
return () -> metric.startTimer()::observeDuration;
|
||||
final var ddp = histogram.labelValues(labels);
|
||||
return () -> ddp.startTimer()::observeDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return histogram.getPrometheusName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void register(final PrometheusRegistry registry) {
|
||||
registry.register(histogram);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregister(final PrometheusRegistry registry) {
|
||||
registry.unregister(histogram);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations() {
|
||||
final var snapshot = histogram.collect();
|
||||
return snapshot.getDataPoints().stream()
|
||||
.flatMap(
|
||||
dataPoint -> {
|
||||
final var labelValues = getLabelValues(dataPoint.getLabels());
|
||||
if (!dataPoint.hasClassicHistogramData()) {
|
||||
throw new IllegalStateException("Only classic histogram are supported");
|
||||
}
|
||||
|
||||
return dataPoint.getClassicBuckets().stream()
|
||||
.map(
|
||||
bucket ->
|
||||
new Observation(
|
||||
category,
|
||||
name,
|
||||
bucket.getCount(),
|
||||
addLabelValues(
|
||||
labelValues, Double.toString(bucket.getUpperBound()))));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.getLabelValues;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.prometheus.metrics.core.metrics.CounterWithCallback;
|
||||
import io.prometheus.metrics.model.registry.Collector;
|
||||
import io.prometheus.metrics.model.snapshots.CounterSnapshot;
|
||||
import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
|
||||
|
||||
/**
|
||||
* A Prometheus supplied counter collector. A supplied counter collector is one which actual value
|
||||
* is kept outside the metric system, for example in an external library or to calculate the value
|
||||
* only on demand when a metric scrape occurs.
|
||||
*/
|
||||
class PrometheusSuppliedCounter extends AbstractPrometheusSuppliedValueCollector {
|
||||
|
||||
public PrometheusSuppliedCounter(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
super(category, name, help, labelNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collector createCollector(final String help, final String... labelNames) {
|
||||
return CounterWithCallback.builder()
|
||||
.name(this.prefixedName)
|
||||
.help(help)
|
||||
.labelNames(labelNames)
|
||||
.callback(this::callback)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void callback(final CounterWithCallback.Callback callback) {
|
||||
labelledCallbackData
|
||||
.values()
|
||||
.forEach(
|
||||
callbackData ->
|
||||
callback.call(
|
||||
callbackData.valueSupplier().getAsDouble(), callbackData.labelValues()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observation convertToObservation(final DataPointSnapshot sample) {
|
||||
final List<String> labelValues = getLabelValues(sample.getLabels());
|
||||
|
||||
return new Observation(
|
||||
category,
|
||||
name,
|
||||
((CounterSnapshot.CounterDataPointSnapshot) sample).getValue(),
|
||||
labelValues);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.prometheus.PrometheusCollector.getLabelValues;
|
||||
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledGauge;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.prometheus.metrics.core.metrics.GaugeWithCallback;
|
||||
import io.prometheus.metrics.model.registry.Collector;
|
||||
import io.prometheus.metrics.model.snapshots.DataPointSnapshot;
|
||||
import io.prometheus.metrics.model.snapshots.GaugeSnapshot;
|
||||
|
||||
/**
|
||||
* A Prometheus supplied gauge collector. A supplied gauge collector is one which actual value is
|
||||
* kept outside the metric system, for example in an external library or to calculate the value only
|
||||
* on demand when a metric scrape occurs.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
class PrometheusSuppliedGauge extends AbstractPrometheusSuppliedValueCollector
|
||||
implements LabelledGauge {
|
||||
|
||||
public PrometheusSuppliedGauge(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
super(category, name, help, labelNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Collector createCollector(final String help, final String... labelNames) {
|
||||
return GaugeWithCallback.builder()
|
||||
.name(this.prefixedName)
|
||||
.help(help)
|
||||
.labelNames(labelNames)
|
||||
.callback(this::callback)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void callback(final GaugeWithCallback.Callback callback) {
|
||||
labelledCallbackData
|
||||
.values()
|
||||
.forEach(
|
||||
callbackData ->
|
||||
callback.call(
|
||||
callbackData.valueSupplier().getAsDouble(), callbackData.labelValues()));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Observation convertToObservation(final DataPointSnapshot sample) {
|
||||
final List<String> labelValues = getLabelValues(sample.getLabels());
|
||||
|
||||
return new Observation(
|
||||
category, name, ((GaugeSnapshot.GaugeDataPointSnapshot) sample).getValue(), labelValues);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.metrics.ExternalSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.prometheus.metrics.core.metrics.SummaryWithCallback;
|
||||
import io.prometheus.metrics.model.snapshots.Quantile;
|
||||
import io.prometheus.metrics.model.snapshots.Quantiles;
|
||||
|
||||
/**
|
||||
* A Prometheus supplied summary collector. A supplied summary collector is one which actual value
|
||||
* is kept outside the metric system, for example in an external library or to calculate the value
|
||||
* only on demand when a metric scrape occurs.
|
||||
*/
|
||||
class PrometheusSuppliedSummary extends AbstractPrometheusSummary
|
||||
implements LabelledSuppliedSummary {
|
||||
/** Map label values with the collector callback data */
|
||||
protected final Map<List<String>, CallbackData> labelledCallbackData = new ConcurrentHashMap<>();
|
||||
|
||||
public PrometheusSuppliedSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
super(category, name);
|
||||
this.collector =
|
||||
SummaryWithCallback.builder()
|
||||
.name(name)
|
||||
.help(help)
|
||||
.labelNames(labelNames)
|
||||
.callback(this::callback)
|
||||
.build();
|
||||
}
|
||||
|
||||
private void callback(final SummaryWithCallback.Callback callback) {
|
||||
labelledCallbackData
|
||||
.values()
|
||||
.forEach(
|
||||
callbackData -> {
|
||||
final var externalSummary = callbackData.summarySupplier().get();
|
||||
final var quantilesBuilder = Quantiles.builder();
|
||||
externalSummary.quantiles().stream()
|
||||
.map(pq -> new Quantile(pq.quantile(), pq.value()))
|
||||
.forEach(quantilesBuilder::quantile);
|
||||
callback.call(
|
||||
externalSummary.count(), externalSummary.sum(), quantilesBuilder.build());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void labels(
|
||||
final Supplier<ExternalSummary> summarySupplier, final String... labelValues) {
|
||||
final var valueList = List.of(labelValues);
|
||||
if (labelledCallbackData.containsKey(valueList)) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("A collector has already been created for label values %s", valueList));
|
||||
}
|
||||
|
||||
labelledCallbackData.put(valueList, new CallbackData(summarySupplier, labelValues));
|
||||
}
|
||||
|
||||
protected record CallbackData(Supplier<ExternalSummary> summarySupplier, String[] labelValues) {}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledGauge;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.DoubleSupplier;
|
||||
|
||||
import io.prometheus.client.Collector;
|
||||
|
||||
/** The Prometheus supplied value collector. */
|
||||
@SuppressWarnings("removal") // remove when deprecated LabelledGauge is removed
|
||||
public class PrometheusSuppliedValueCollector extends Collector
|
||||
implements LabelledSuppliedMetric, LabelledGauge {
|
||||
private final Type type;
|
||||
private final String metricName;
|
||||
private final String help;
|
||||
private final List<String> labelNames;
|
||||
private final Map<List<String>, DoubleSupplier> observationsMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new Prometheus supplied value collector.
|
||||
*
|
||||
* @param type the type of the collector
|
||||
* @param metricName the metric name
|
||||
* @param help the help
|
||||
* @param labelNames the label names
|
||||
*/
|
||||
public PrometheusSuppliedValueCollector(
|
||||
final Type type, final String metricName, final String help, final List<String> labelNames) {
|
||||
this.type = type;
|
||||
this.metricName = metricName;
|
||||
this.help = help;
|
||||
this.labelNames = labelNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void labels(final DoubleSupplier valueSupplier, final String... labelValues) {
|
||||
validateLabelsCardinality(labelValues);
|
||||
if (observationsMap.putIfAbsent(List.of(labelValues), valueSupplier) != null) {
|
||||
final String labelValuesString = String.join(",", labelValues);
|
||||
throw new IllegalArgumentException(
|
||||
String.format("A gauge has already been created for label values %s", labelValuesString));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MetricFamilySamples> collect() {
|
||||
final List<MetricFamilySamples.Sample> samples = new ArrayList<>();
|
||||
observationsMap.forEach(
|
||||
(labels, valueSupplier) ->
|
||||
samples.add(
|
||||
new MetricFamilySamples.Sample(
|
||||
metricName, labelNames, labels, valueSupplier.getAsDouble())));
|
||||
return List.of(new MetricFamilySamples(metricName, type, help, samples));
|
||||
}
|
||||
|
||||
private void validateLabelsCardinality(final String... labelValues) {
|
||||
if (labelValues.length != labelNames.size()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Label values and label names must be the same cardinality");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,21 +15,37 @@
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import io.prometheus.client.Summary;
|
||||
import java.util.Map;
|
||||
|
||||
class PrometheusTimer implements LabelledMetric<OperationTimer> {
|
||||
import io.prometheus.metrics.core.datapoints.DistributionDataPoint;
|
||||
import io.prometheus.metrics.core.metrics.Summary;
|
||||
|
||||
private final Summary summary;
|
||||
/**
|
||||
* An implementation of Besu timer backed by a Prometheus summary. The summary provides a total
|
||||
* count of durations and a sum of all observed durations, it calculates configurable quantiles over
|
||||
* a sliding time window.
|
||||
*/
|
||||
class PrometheusTimer extends AbstractPrometheusSummary implements LabelledMetric<OperationTimer> {
|
||||
|
||||
public PrometheusTimer(final Summary summary) {
|
||||
this.summary = summary;
|
||||
public PrometheusTimer(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final Map<Double, Double> quantiles,
|
||||
final String... labelNames) {
|
||||
super(category, name);
|
||||
final var summaryBuilder =
|
||||
Summary.builder().name(this.prefixedName).help(help).labelNames(labelNames);
|
||||
quantiles.forEach(summaryBuilder::quantile);
|
||||
this.collector = summaryBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationTimer labels(final String... labels) {
|
||||
final Summary.Child metric = summary.labels(labels);
|
||||
final DistributionDataPoint metric = ((Summary) collector).labelValues(labels);
|
||||
return () -> metric.startTimer()::observeDuration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,9 @@ import static java.util.Arrays.asList;
|
||||
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.ExternalSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
@@ -30,7 +30,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
@@ -98,11 +97,13 @@ public class StubMetricsSystem implements ObservableMetricsSystem {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackExternalSummary(
|
||||
public LabelledSuppliedSummary createLabelledSuppliedSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final Supplier<ExternalSummary> summarySupplier) {}
|
||||
final String... labelNames) {
|
||||
return NoOpMetricsSystem.getLabelledSuppliedSummary(labelNames.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGauge(
|
||||
|
||||
@@ -50,9 +50,9 @@ import org.junit.jupiter.api.Test;
|
||||
public class OpenTelemetryMetricsSystemTest {
|
||||
|
||||
private static final Comparator<Observation> IGNORE_VALUES =
|
||||
Comparator.<Observation, String>comparing(observation -> observation.getCategory().getName())
|
||||
.thenComparing(Observation::getMetricName)
|
||||
.thenComparing((o1, o2) -> o1.getLabels().equals(o2.getLabels()) ? 0 : 1);
|
||||
Comparator.<Observation, String>comparing(observation -> observation.category().getName())
|
||||
.thenComparing(Observation::metricName)
|
||||
.thenComparing((o1, o2) -> o1.labels().equals(o2.labels()) ? 0 : 1);
|
||||
|
||||
@BeforeEach
|
||||
public void resetGlobalOpenTelemetry() {
|
||||
@@ -266,7 +266,7 @@ public class OpenTelemetryMetricsSystemTest {
|
||||
final LabelledMetric<Counter> counterN =
|
||||
localMetricSystem.createLabelledCounter(
|
||||
NETWORK, "ABC", "Not that kind of network", "show");
|
||||
assertThat(counterN).isSameAs(NoOpMetricsSystem.NO_OP_LABELLED_1_COUNTER);
|
||||
assertThat(counterN).isInstanceOf(NoOpMetricsSystem.LabelCountingNoOpMetric.class);
|
||||
|
||||
counterN.labels("show").inc();
|
||||
assertThat(localMetricSystem.streamObservations()).isEmpty();
|
||||
@@ -274,7 +274,7 @@ public class OpenTelemetryMetricsSystemTest {
|
||||
// do a category we are watching
|
||||
final LabelledMetric<Counter> counterR =
|
||||
localMetricSystem.createLabelledCounter(RPC, "name", "Not useful", "method");
|
||||
assertThat(counterR).isNotSameAs(NoOpMetricsSystem.NO_OP_LABELLED_1_COUNTER);
|
||||
assertThat(counterR).isNotInstanceOf(NoOpMetricsSystem.LabelCountingNoOpMetric.class);
|
||||
|
||||
counterR.labels("op").inc();
|
||||
assertThat(getObservation(localMetricSystem))
|
||||
|
||||
@@ -24,43 +24,50 @@ import java.net.InetSocketAddress;
|
||||
import java.util.Properties;
|
||||
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import io.prometheus.client.exporter.common.TextFormat;
|
||||
import io.vertx.core.Vertx;
|
||||
import io.prometheus.metrics.expositionformats.PrometheusTextFormatWriter;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class MetricsHttpServiceTest {
|
||||
|
||||
private static final Vertx vertx = Vertx.vertx();
|
||||
private PrometheusMetricsSystem metricsSystem;
|
||||
private MetricsHttpService service;
|
||||
private OkHttpClient client;
|
||||
private String baseUrl;
|
||||
|
||||
private static MetricsHttpService service;
|
||||
private static OkHttpClient client;
|
||||
private static String baseUrl;
|
||||
private void initServerAndClient(
|
||||
final MetricsConfiguration metricsConfiguration, final boolean start) {
|
||||
metricsSystem = (PrometheusMetricsSystem) MetricsSystemFactory.create(metricsConfiguration);
|
||||
service = createMetricsHttpService(metricsConfiguration, metricsSystem);
|
||||
if (start) {
|
||||
service.start().join();
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void initServerAndClient() {
|
||||
service = createMetricsHttpService();
|
||||
service.start().join();
|
||||
|
||||
// Build an OkHttp client.
|
||||
client = new OkHttpClient();
|
||||
baseUrl = urlForSocketAddress("http", service.socketAddress());
|
||||
}
|
||||
|
||||
private static MetricsHttpService createMetricsHttpService(final MetricsConfiguration config) {
|
||||
GlobalOpenTelemetry.resetForTest();
|
||||
return new MetricsHttpService(vertx, config, MetricsSystemFactory.create(config));
|
||||
private void initServerAndClient(final boolean start) {
|
||||
initServerAndClient(createMetricsConfig(), start);
|
||||
}
|
||||
|
||||
private static MetricsHttpService createMetricsHttpService() {
|
||||
private void initServerAndClient() {
|
||||
initServerAndClient(createMetricsConfig(), true);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void stopServer() {
|
||||
metricsSystem.shutdown();
|
||||
service.stop();
|
||||
}
|
||||
|
||||
private MetricsHttpService createMetricsHttpService(
|
||||
final MetricsConfiguration config, final PrometheusMetricsSystem metricsSystem) {
|
||||
GlobalOpenTelemetry.resetForTest();
|
||||
final MetricsConfiguration metricsConfiguration = createMetricsConfig();
|
||||
return new MetricsHttpService(
|
||||
vertx, metricsConfiguration, MetricsSystemFactory.create(metricsConfiguration));
|
||||
return new MetricsHttpService(config, metricsSystem);
|
||||
}
|
||||
|
||||
private static MetricsConfiguration createMetricsConfig() {
|
||||
@@ -71,15 +78,9 @@ public class MetricsHttpServiceTest {
|
||||
return MetricsConfiguration.builder().enabled(true).port(0).hostsAllowlist(singletonList("*"));
|
||||
}
|
||||
|
||||
/** Tears down the HTTP server. */
|
||||
@AfterAll
|
||||
public static void shutdownServer() {
|
||||
service.stop().join();
|
||||
vertx.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidCallToStart() {
|
||||
initServerAndClient();
|
||||
service
|
||||
.start()
|
||||
.whenComplete(
|
||||
@@ -88,6 +89,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void http404() throws Exception {
|
||||
initServerAndClient();
|
||||
try (final Response resp = client.newCall(buildGetRequest("/foo")).execute()) {
|
||||
assertThat(resp.code()).isEqualTo(404);
|
||||
}
|
||||
@@ -95,13 +97,15 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void handleEmptyRequest() throws Exception {
|
||||
initServerAndClient();
|
||||
try (final Response resp = client.newCall(buildGetRequest("")).execute()) {
|
||||
assertThat(resp.code()).isEqualTo(201);
|
||||
assertThat(resp.code()).isEqualTo(200);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getSocketAddressWhenActive() {
|
||||
initServerAndClient();
|
||||
final InetSocketAddress socketAddress = service.socketAddress();
|
||||
assertThat("127.0.0.1").isEqualTo(socketAddress.getAddress().getHostAddress());
|
||||
assertThat(socketAddress.getPort() > 0).isTrue();
|
||||
@@ -109,7 +113,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void getSocketAddressWhenStoppedIsEmpty() {
|
||||
final MetricsHttpService service = createMetricsHttpService();
|
||||
initServerAndClient(false);
|
||||
|
||||
final InetSocketAddress socketAddress = service.socketAddress();
|
||||
assertThat("0.0.0.0").isEqualTo(socketAddress.getAddress().getHostAddress());
|
||||
@@ -119,9 +123,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void getSocketAddressWhenBindingToAllInterfaces() {
|
||||
final MetricsConfiguration config = createMetricsConfigBuilder().host("0.0.0.0").build();
|
||||
final MetricsHttpService service = createMetricsHttpService(config);
|
||||
service.start().join();
|
||||
initServerAndClient(createMetricsConfigBuilder().host("0.0.0.0").build(), true);
|
||||
|
||||
try {
|
||||
final InetSocketAddress socketAddress = service.socketAddress();
|
||||
@@ -134,6 +136,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void metricsArePresent() throws Exception {
|
||||
initServerAndClient();
|
||||
final Request metricsRequest = new Request.Builder().url(baseUrl + "/metrics").build();
|
||||
try (final Response resp = client.newCall(metricsRequest).execute()) {
|
||||
assertThat(resp.code()).isEqualTo(200);
|
||||
@@ -148,6 +151,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void metricsArePresentWhenFiltered() throws Exception {
|
||||
initServerAndClient();
|
||||
final Request metricsRequest =
|
||||
new Request.Builder().url(baseUrl + "/metrics?name[]=jvm_threads_deadlocked").build();
|
||||
try (final Response resp = client.newCall(metricsRequest).execute()) {
|
||||
@@ -163,6 +167,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
@Test
|
||||
public void metricsAreAbsentWhenFiltered() throws Exception {
|
||||
initServerAndClient();
|
||||
final Request metricsRequest =
|
||||
new Request.Builder().url(baseUrl + "/metrics?name[]=does_not_exist").build();
|
||||
try (final Response resp = client.newCall(metricsRequest).execute()) {
|
||||
@@ -179,6 +184,7 @@ public class MetricsHttpServiceTest {
|
||||
@Test
|
||||
// There is only one available representation so content negotiation should not be used
|
||||
public void acceptHeaderIgnored() throws Exception {
|
||||
initServerAndClient();
|
||||
final Request metricsRequest =
|
||||
new Request.Builder().addHeader("Accept", "text/xml").url(baseUrl + "/metrics").build();
|
||||
try (final Response resp = client.newCall(metricsRequest).execute()) {
|
||||
@@ -189,7 +195,7 @@ public class MetricsHttpServiceTest {
|
||||
|
||||
// We should have JVM metrics already loaded, verify a simple key.
|
||||
assertThat(props).containsKey("jvm_threads_deadlocked");
|
||||
assertThat(resp.header("Content-Type")).contains(TextFormat.CONTENT_TYPE_004);
|
||||
assertThat(resp.header("Content-Type")).contains(PrometheusTextFormatWriter.CONTENT_TYPE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static java.util.Collections.singletonList;
|
||||
import static java.util.function.Predicate.not;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.BLOCKCHAIN;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.NETWORK;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.PEERS;
|
||||
@@ -38,33 +39,31 @@ import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class PrometheusMetricsSystemTest {
|
||||
|
||||
private static final Comparator<Observation> IGNORE_VALUES =
|
||||
Comparator.<Observation, String>comparing(observation -> observation.getCategory().getName())
|
||||
.thenComparing(Observation::getMetricName)
|
||||
.thenComparing((o1, o2) -> o1.getLabels().equals(o2.getLabels()) ? 0 : 1);
|
||||
private static final Comparator<Observation> WITH_VALUES =
|
||||
Comparator.<Observation, String>comparing(observation -> observation.getCategory().getName())
|
||||
.thenComparing(Observation::getMetricName)
|
||||
.thenComparing((o1, o2) -> o1.getLabels().equals(o2.getLabels()) ? 0 : 1)
|
||||
.thenComparing((o1, o2) -> o1.getValue().equals(o2.getValue()) ? 0 : 1);
|
||||
private PrometheusMetricsSystem metricsSystem;
|
||||
|
||||
@BeforeEach
|
||||
public void resetGlobalOpenTelemetry() {
|
||||
public void setUp() {
|
||||
metricsSystem = new PrometheusMetricsSystem(DEFAULT_METRIC_CATEGORIES, true);
|
||||
GlobalOpenTelemetry.resetForTest();
|
||||
}
|
||||
|
||||
private final ObservableMetricsSystem metricsSystem =
|
||||
new PrometheusMetricsSystem(DEFAULT_METRIC_CATEGORIES, true);
|
||||
@AfterEach
|
||||
public void tearDown() {
|
||||
metricsSystem.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateObservationFromCounter() {
|
||||
@@ -72,17 +71,11 @@ public class PrometheusMetricsSystemTest {
|
||||
|
||||
counter.inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactlyInAnyOrder(
|
||||
new Observation(PEERS, "connected", 1.0, emptyList()),
|
||||
new Observation(PEERS, "connected", null, List.of("created")));
|
||||
.containsExactlyInAnyOrder(new Observation(PEERS, "connected", 1.0, emptyList()));
|
||||
|
||||
counter.inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactly(
|
||||
new Observation(PEERS, "connected", 2.0, emptyList()),
|
||||
new Observation(PEERS, "connected", null, List.of("created")));
|
||||
.containsExactly(new Observation(PEERS, "connected", 2.0, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -95,17 +88,11 @@ public class PrometheusMetricsSystemTest {
|
||||
|
||||
counter1.labels().inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactly(
|
||||
new Observation(PEERS, "connected", 1.0, emptyList()),
|
||||
new Observation(PEERS, "connected", null, List.of("created")));
|
||||
.containsExactly(new Observation(PEERS, "connected", 1.0, emptyList()));
|
||||
|
||||
counter2.labels().inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactly(
|
||||
new Observation(PEERS, "connected", 2.0, emptyList()),
|
||||
new Observation(PEERS, "connected", null, List.of("created")));
|
||||
.containsExactly(new Observation(PEERS, "connected", 2.0, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -119,12 +106,9 @@ public class PrometheusMetricsSystemTest {
|
||||
counter.labels("value1").inc();
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactlyInAnyOrder(
|
||||
new Observation(PEERS, "connected_total", 2.0, singletonList("value1")),
|
||||
new Observation(PEERS, "connected_total", 1.0, singletonList("value2")),
|
||||
new Observation(PEERS, "connected_total", null, List.of("value1", "created")),
|
||||
new Observation(PEERS, "connected_total", null, List.of("value2", "created")));
|
||||
new Observation(PEERS, "connected_total", 1.0, singletonList("value2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -160,18 +144,12 @@ public class PrometheusMetricsSystemTest {
|
||||
|
||||
counter.inc(5);
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactly(
|
||||
new Observation(PEERS, "connected", 5.0, emptyList()),
|
||||
new Observation(PEERS, "connected", null, List.of("created")));
|
||||
.containsExactly(new Observation(PEERS, "connected", 5.0, emptyList()));
|
||||
|
||||
counter.inc(6);
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingDefaultElementComparator()
|
||||
.usingElementComparator(this::compareCounters)
|
||||
.containsExactly(
|
||||
new Observation(PEERS, "connected", 11.0, emptyList()),
|
||||
new Observation(PEERS, "connected", null, List.of("created")));
|
||||
.containsExactly(new Observation(PEERS, "connected", 11.0, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -179,20 +157,18 @@ public class PrometheusMetricsSystemTest {
|
||||
final OperationTimer timer = metricsSystem.createTimer(RPC, "request", "Some help");
|
||||
|
||||
final OperationTimer.TimingContext context = timer.startTimer();
|
||||
context.stopTimer();
|
||||
final var expected = context.stopTimer();
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(IGNORE_VALUES)
|
||||
.containsExactlyInAnyOrder(
|
||||
new Observation(RPC, "request", null, asList("quantile", "0.2")),
|
||||
new Observation(RPC, "request", null, asList("quantile", "0.5")),
|
||||
new Observation(RPC, "request", null, asList("quantile", "0.8")),
|
||||
new Observation(RPC, "request", null, asList("quantile", "0.95")),
|
||||
new Observation(RPC, "request", null, asList("quantile", "0.99")),
|
||||
new Observation(RPC, "request", null, asList("quantile", "1.0")),
|
||||
new Observation(RPC, "request", null, singletonList("sum")),
|
||||
new Observation(RPC, "request", null, singletonList("count")),
|
||||
new Observation(RPC, "request", null, singletonList("created")));
|
||||
new Observation(RPC, "request", expected, asList("quantile", "0.2")),
|
||||
new Observation(RPC, "request", expected, asList("quantile", "0.5")),
|
||||
new Observation(RPC, "request", expected, asList("quantile", "0.8")),
|
||||
new Observation(RPC, "request", expected, asList("quantile", "0.95")),
|
||||
new Observation(RPC, "request", expected, asList("quantile", "0.99")),
|
||||
new Observation(RPC, "request", expected, asList("quantile", "1.0")),
|
||||
new Observation(RPC, "request", expected, singletonList("sum")),
|
||||
new Observation(RPC, "request", 1L, singletonList("count")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -209,21 +185,19 @@ public class PrometheusMetricsSystemTest {
|
||||
final LabelledMetric<OperationTimer> timer =
|
||||
metricsSystem.createLabelledTimer(RPC, "request", "Some help", "methodName");
|
||||
|
||||
//noinspection EmptyTryBlock
|
||||
try (final OperationTimer.TimingContext ignored = timer.labels("method").startTimer()) {}
|
||||
final OperationTimer.TimingContext context = timer.labels("method").startTimer();
|
||||
final double expected = context.stopTimer();
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(IGNORE_VALUES) // We don't know how long it will actually take.
|
||||
.containsExactlyInAnyOrder(
|
||||
new Observation(RPC, "request", null, asList("method", "quantile", "0.2")),
|
||||
new Observation(RPC, "request", null, asList("method", "quantile", "0.5")),
|
||||
new Observation(RPC, "request", null, asList("method", "quantile", "0.8")),
|
||||
new Observation(RPC, "request", null, asList("method", "quantile", "0.95")),
|
||||
new Observation(RPC, "request", null, asList("method", "quantile", "0.99")),
|
||||
new Observation(RPC, "request", null, asList("method", "quantile", "1.0")),
|
||||
new Observation(RPC, "request", null, asList("method", "sum")),
|
||||
new Observation(RPC, "request", null, asList("method", "count")),
|
||||
new Observation(RPC, "request", null, asList("method", "created")));
|
||||
new Observation(RPC, "request", expected, asList("method", "quantile", "0.2")),
|
||||
new Observation(RPC, "request", expected, asList("method", "quantile", "0.5")),
|
||||
new Observation(RPC, "request", expected, asList("method", "quantile", "0.8")),
|
||||
new Observation(RPC, "request", expected, asList("method", "quantile", "0.95")),
|
||||
new Observation(RPC, "request", expected, asList("method", "quantile", "0.99")),
|
||||
new Observation(RPC, "request", expected, asList("method", "quantile", "1.0")),
|
||||
new Observation(RPC, "request", expected, asList("method", "sum")),
|
||||
new Observation(RPC, "request", 1L, asList("method", "count")));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -270,7 +244,7 @@ public class PrometheusMetricsSystemTest {
|
||||
// do a category we are not watching
|
||||
final LabelledMetric<Counter> counterN =
|
||||
localMetricSystem.createLabelledCounter(NETWORK, "ABC", "Not that kind of network", "show");
|
||||
assertThat(counterN).isSameAs(NoOpMetricsSystem.NO_OP_LABELLED_1_COUNTER);
|
||||
assertThat(counterN).isInstanceOf(NoOpMetricsSystem.LabelCountingNoOpMetric.class);
|
||||
|
||||
counterN.labels("show").inc();
|
||||
assertThat(localMetricSystem.streamObservations()).isEmpty();
|
||||
@@ -278,7 +252,7 @@ public class PrometheusMetricsSystemTest {
|
||||
// do a category we are watching
|
||||
final LabelledMetric<Counter> counterR =
|
||||
localMetricSystem.createLabelledCounter(RPC, "name", "Not useful", "method");
|
||||
assertThat(counterR).isNotSameAs(NoOpMetricsSystem.NO_OP_LABELLED_1_COUNTER);
|
||||
assertThat(counterR).isNotInstanceOf(NoOpMetricsSystem.LabelCountingNoOpMetric.class);
|
||||
|
||||
counterR.labels("op").inc();
|
||||
assertThat(localMetricSystem.streamObservations())
|
||||
@@ -314,17 +288,28 @@ public class PrometheusMetricsSystemTest {
|
||||
assertThat(localMetricSystem).isInstanceOf(PrometheusMetricsSystem.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateObservationFromGuavaCache() throws ExecutionException {
|
||||
final Cache<String, String> guavaCache =
|
||||
CacheBuilder.newBuilder().maximumSize(1).recordStats().build();
|
||||
metricsSystem.createGuavaCacheCollector(BLOCKCHAIN, "test", guavaCache);
|
||||
|
||||
guavaCache.put("a", "b");
|
||||
guavaCache.get("a", () -> "b");
|
||||
guavaCache.get("z", () -> "x");
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactlyInAnyOrder(
|
||||
new Observation(BLOCKCHAIN, "guava_cache_size", 1.0, List.of("test")),
|
||||
new Observation(BLOCKCHAIN, "guava_cache_requests", 2.0, List.of("test")),
|
||||
new Observation(BLOCKCHAIN, "guava_cache_hit", 1.0, List.of("test")),
|
||||
new Observation(BLOCKCHAIN, "guava_cache_miss", 1.0, List.of("test")),
|
||||
new Observation(BLOCKCHAIN, "guava_cache_eviction", 1.0, List.of("test")));
|
||||
}
|
||||
|
||||
private boolean isCreatedSample(final Observation obs) {
|
||||
// Simple client 0.10.0 add a _created sample to every counter, histogram and summary, that we
|
||||
// may want to ignore
|
||||
return obs.getLabels().contains("created");
|
||||
}
|
||||
|
||||
private int compareCounters(final Observation obs1, final Observation obs2) {
|
||||
// for created samples ignore values
|
||||
if (obs1.getLabels().contains("created") && obs2.getLabels().contains("created")) {
|
||||
return IGNORE_VALUES.compare(obs1, obs2);
|
||||
}
|
||||
return WITH_VALUES.compare(obs1, obs2);
|
||||
return obs.labels().contains("created");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ public class RocksDBStats {
|
||||
|
||||
for (final var histogramType : HISTOGRAM_TYPES) {
|
||||
|
||||
metricsSystem.trackExternalSummary(
|
||||
metricsSystem.createSummary(
|
||||
KVSTORE_ROCKSDB_STATS,
|
||||
KVSTORE_ROCKSDB_STATS.getName() + "_" + histogramType.name().toLowerCase(Locale.ROOT),
|
||||
"RocksDB histogram for " + histogramType.name(),
|
||||
|
||||
@@ -30,7 +30,7 @@ dependencies {
|
||||
api platform('io.grpc:grpc-bom:1.68.0')
|
||||
api platform('io.netty:netty-bom:4.1.115.Final')
|
||||
api platform('io.opentelemetry:opentelemetry-bom:1.43.0')
|
||||
api platform('io.prometheus:simpleclient_bom:0.16.0')
|
||||
api platform('io.prometheus:prometheus-metrics-bom:1.3.4')
|
||||
api platform('io.vertx:vertx-stack-depchain:4.5.10')
|
||||
api platform('org.apache.logging.log4j:log4j-bom:2.24.1')
|
||||
api platform('org.assertj:assertj-bom:3.26.3')
|
||||
|
||||
@@ -71,7 +71,7 @@ Calculated : ${currentHash}
|
||||
tasks.register('checkAPIChanges', FileStateChecker) {
|
||||
description = "Checks that the API for the Plugin-API project does not change without deliberate thought"
|
||||
files = sourceSets.main.allJava.files
|
||||
knownHash = 'PKvPlngg7BfdZ4Jinh0IUsyFOLvNaQU72VD4BHia/WM='
|
||||
knownHash = '0suP4G0+vTbIvbBfaH+pOpNTEDaf2Hq+byXDyHc2i2E='
|
||||
}
|
||||
check.dependsOn('checkAPIChanges')
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import org.hyperledger.besu.plugin.services.metrics.ExternalSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledGauge;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledSuppliedSummary;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
@@ -184,7 +185,13 @@ public interface MetricsSystem extends BesuService {
|
||||
* @param help A human readable description of the metric.
|
||||
* @param valueSupplier A supplier for the double value to be presented.
|
||||
*/
|
||||
void createGauge(MetricCategory category, String name, String help, DoubleSupplier valueSupplier);
|
||||
default void createGauge(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final DoubleSupplier valueSupplier) {
|
||||
createLabelledSuppliedGauge(category, name, help).labels(valueSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a gauge for displaying integer values.
|
||||
@@ -219,7 +226,21 @@ public interface MetricsSystem extends BesuService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Track a summary that is computed externally to this metric system. Useful when existing
|
||||
* Create a summary with assigned labels, that is computed externally to this metric system.
|
||||
* Useful when existing libraries calculate the summary data on their own, and we want to export
|
||||
* that summary via the configured metric system. A notable example are RocksDB statistics.
|
||||
*
|
||||
* @param category The {@link MetricCategory} this external summary is assigned to.
|
||||
* @param name A name for the metric.
|
||||
* @param help A human readable description of the metric.
|
||||
* @param labelNames An array of labels to assign to the supplier summary.
|
||||
* @return The created labelled supplied summary
|
||||
*/
|
||||
LabelledSuppliedSummary createLabelledSuppliedSummary(
|
||||
MetricCategory category, String name, String help, String... labelNames);
|
||||
|
||||
/**
|
||||
* Create a summary that is computed externally to this metric system. Useful when existing
|
||||
* libraries calculate the summary data on their own, and we want to export that summary via the
|
||||
* configured metric system. A notable example are RocksDB statistics.
|
||||
*
|
||||
@@ -228,8 +249,13 @@ public interface MetricsSystem extends BesuService {
|
||||
* @param help A human readable description of the metric.
|
||||
* @param summarySupplier A supplier to retrieve the summary data when needed.
|
||||
*/
|
||||
void trackExternalSummary(
|
||||
MetricCategory category, String name, String help, Supplier<ExternalSummary> summarySupplier);
|
||||
default void createSummary(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final Supplier<ExternalSummary> summarySupplier) {
|
||||
createLabelledSuppliedSummary(category, name, help).labels(summarySupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect metrics from Guava cache.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright contributors to Besu.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.plugin.services.metrics;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** The interface Labelled supplied summary. */
|
||||
public interface LabelledSuppliedSummary {
|
||||
/**
|
||||
* Labels.
|
||||
*
|
||||
* @param summarySupplier the summary supplier
|
||||
* @param labelValues the label values
|
||||
*/
|
||||
void labels(final Supplier<ExternalSummary> summarySupplier, final String... labelValues);
|
||||
}
|
||||
Reference in New Issue
Block a user