mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-09 22:07:59 -05:00
Add OTLP metrics support (#1492)
* Add OTLP metrics support Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Don't automatically push to grpc. Only allow if pushEnabled is set to true Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Code review Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Missed refactoring Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Add missing header Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Don't make otel depend on the push enabled flag Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Expose JUL logs to log4j2 Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Code review fixes and make sure not to start push gateway if not set to prometheus Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * spotless Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Fix unit test Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * code review feedback Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Add changelog entry Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
* Updated the libraries for secp256k1 and AltBN series precompiles. These updates provide significant performance improvements to those areas. [\#1499](https://github.com/hyperledger/besu/pull/1499)
|
||||
* Provide MegaGas/second measurements in the log when doing a full block import, such as the catch up phase of a fast sync. [\#1512](https://github.com/hyperledger/besu/pull/1512)
|
||||
* Added new endpoints to get miner data, `eth_getMinerDataByBlockHash` and `eth_getMinerDataByBlockNumber`. [\#1538](https://github.com/hyperledger/besu/pull/1538)
|
||||
* Added direct support for OpenTelemetry metrics [\#1492](https://github.com/hyperledger/besu/pull/1492)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
|
||||
@@ -34,8 +34,8 @@ import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
|
||||
import org.hyperledger.besu.ethereum.permissioning.PermissioningConfiguration;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProvider;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBuilder;
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.BesuConfiguration;
|
||||
import org.hyperledger.besu.plugin.services.BesuEvents;
|
||||
import org.hyperledger.besu.plugin.services.PicoCLIOptions;
|
||||
@@ -128,7 +128,7 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner {
|
||||
node, storageService, securityModuleService, commonPluginConfiguration));
|
||||
|
||||
final ObservableMetricsSystem metricsSystem =
|
||||
PrometheusMetricsSystem.init(node.getMetricsConfiguration());
|
||||
MetricsSystemFactory.create(node.getMetricsConfiguration());
|
||||
final List<EnodeURL> bootnodes =
|
||||
node.getConfiguration().getBootnodes().stream()
|
||||
.map(EnodeURL::fromURI)
|
||||
|
||||
@@ -70,6 +70,7 @@ dependencies {
|
||||
|
||||
|
||||
runtimeOnly 'org.apache.logging.log4j:log4j-slf4j-impl'
|
||||
runtimeOnly 'org.apache.logging.log4j:log4j-jul'
|
||||
runtimeOnly 'com.splunk.logging:splunk-library-javalogging'
|
||||
runtimeOnly 'org.fusesource.jansi:jansi' // for color logging in windows
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.hyperledger.besu.ethereum.p2p.network.NetworkRunner;
|
||||
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURL;
|
||||
import org.hyperledger.besu.ethereum.stratum.StratumServer;
|
||||
import org.hyperledger.besu.ethstats.EthStatsService;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsService;
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.nat.NatService;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
@@ -93,9 +93,9 @@ import org.hyperledger.besu.ethereum.transaction.TransactionSimulator;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.ethstats.EthStatsService;
|
||||
import org.hyperledger.besu.ethstats.util.NetstatsUrl;
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsService;
|
||||
import org.hyperledger.besu.nat.NatMethod;
|
||||
import org.hyperledger.besu.nat.NatService;
|
||||
import org.hyperledger.besu.nat.core.NatManager;
|
||||
@@ -603,10 +603,7 @@ public class RunnerBuilder {
|
||||
createPrivateTransactionObserver(subscriptionManager, privacyParameters);
|
||||
}
|
||||
|
||||
Optional<MetricsService> metricsService = Optional.empty();
|
||||
if (metricsConfiguration.isEnabled() || metricsConfiguration.isPushEnabled()) {
|
||||
metricsService = Optional.of(createMetricsService(vertx, metricsConfiguration));
|
||||
}
|
||||
Optional<MetricsService> metricsService = createMetricsService(vertx, metricsConfiguration);
|
||||
|
||||
final Optional<EthStatsService> ethStatsService;
|
||||
if (!Strings.isNullOrEmpty(ethstatsUrl)) {
|
||||
@@ -884,7 +881,7 @@ public class RunnerBuilder {
|
||||
return new WebSocketService(vertx, configuration, websocketRequestHandler);
|
||||
}
|
||||
|
||||
private MetricsService createMetricsService(
|
||||
private Optional<MetricsService> createMetricsService(
|
||||
final Vertx vertx, final MetricsConfiguration configuration) {
|
||||
return MetricsService.create(vertx, configuration, metricsSystem);
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.RpcApis.DEFAULT_JSON_RPC
|
||||
import static org.hyperledger.besu.ethereum.api.jsonrpc.websocket.WebSocketConfiguration.DEFAULT_WEBSOCKET_PORT;
|
||||
import static org.hyperledger.besu.ethereum.permissioning.QuorumPermissioningConfiguration.QIP714_DEFAULT_BLOCK;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
|
||||
import static org.hyperledger.besu.metrics.MetricsProtocol.PROMETHEUS;
|
||||
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PORT;
|
||||
import static org.hyperledger.besu.metrics.prometheus.MetricsConfiguration.DEFAULT_METRICS_PUSH_PORT;
|
||||
import static org.hyperledger.besu.nat.kubernetes.KubernetesNatManager.DEFAULT_BESU_SERVICE_NAME_FILTER;
|
||||
@@ -116,10 +117,11 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStorageProviderBui
|
||||
import org.hyperledger.besu.ethereum.worldstate.PrunerConfiguration;
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
import org.hyperledger.besu.metrics.MetricCategoryRegistryImpl;
|
||||
import org.hyperledger.besu.metrics.MetricsProtocol;
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.StandardMetricCategory;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.vertx.VertxMetricsAdapterFactory;
|
||||
import org.hyperledger.besu.nat.NatMethod;
|
||||
import org.hyperledger.besu.plugin.services.BesuConfiguration;
|
||||
@@ -648,6 +650,13 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
description = "Set to start the metrics exporter (default: ${DEFAULT-VALUE})")
|
||||
private final Boolean isMetricsEnabled = false;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
|
||||
@Option(
|
||||
names = {"--metrics-protocol"},
|
||||
description =
|
||||
"Metrics protocol, one of PROMETHEUS, OPENTELEMETRY or NONE. (default: ${DEFAULT-VALUE})")
|
||||
private MetricsProtocol metricsProtocol = PROMETHEUS;
|
||||
|
||||
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
|
||||
@Option(
|
||||
names = {"--metrics-host"},
|
||||
@@ -1022,7 +1031,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
private BesuController besuController;
|
||||
private BesuConfiguration pluginCommonConfiguration;
|
||||
private final Supplier<ObservableMetricsSystem> metricsSystem =
|
||||
Suppliers.memoize(() -> PrometheusMetricsSystem.init(metricsConfiguration()));
|
||||
Suppliers.memoize(() -> MetricsSystemFactory.create(metricsConfiguration()));
|
||||
private Vertx vertx;
|
||||
private EnodeDnsConfiguration enodeDnsConfiguration;
|
||||
|
||||
@@ -1147,6 +1156,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
commandLine.registerConverter(Bytes.class, Bytes::fromHexString);
|
||||
commandLine.registerConverter(Level.class, Level::valueOf);
|
||||
commandLine.registerConverter(SyncMode.class, SyncMode::fromString);
|
||||
commandLine.registerConverter(MetricsProtocol.class, MetricsProtocol::fromString);
|
||||
commandLine.registerConverter(UInt256.class, (arg) -> UInt256.valueOf(new BigInteger(arg)));
|
||||
commandLine.registerConverter(Wei.class, (arg) -> Wei.of(Long.parseUnsignedLong(arg)));
|
||||
commandLine.registerConverter(PositiveNumber.class, PositiveNumber::fromString);
|
||||
@@ -1734,6 +1744,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
.enabled(isMetricsEnabled)
|
||||
.host(metricsHost)
|
||||
.port(metricsPort)
|
||||
.protocol(metricsProtocol)
|
||||
.metricCategories(metricCategories)
|
||||
.pushEnabled(isMetricsPushEnabled)
|
||||
.pushHost(metricsPushHost)
|
||||
|
||||
@@ -33,8 +33,8 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Address;
|
||||
import org.hyperledger.besu.ethereum.core.MiningParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsService;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
@@ -434,18 +434,13 @@ public class BlocksSubCommand implements Runnable {
|
||||
}
|
||||
|
||||
private static Optional<MetricsService> initMetrics(final BlocksSubCommand parentCommand) {
|
||||
Optional<MetricsService> metricsService = Optional.empty();
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
parentCommand.parentCommand.metricsConfiguration();
|
||||
if (metricsConfiguration.isEnabled() || metricsConfiguration.isPushEnabled()) {
|
||||
metricsService =
|
||||
Optional.of(
|
||||
MetricsService.create(
|
||||
Vertx.vertx(),
|
||||
metricsConfiguration,
|
||||
parentCommand.parentCommand.getMetricsSystem()));
|
||||
metricsService.ifPresent(MetricsService::start);
|
||||
}
|
||||
|
||||
Optional<MetricsService> metricsService =
|
||||
MetricsService.create(
|
||||
Vertx.vertx(), metricsConfiguration, parentCommand.parentCommand.getMetricsSystem());
|
||||
metricsService.ifPresent(MetricsService::start);
|
||||
return metricsService;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,6 +92,7 @@ rpc-ws-authentication-jwt-public-key-file="none"
|
||||
|
||||
# Prometheus Metrics Endpoint
|
||||
metrics-enabled=false
|
||||
metrics-protocol="prometheus"
|
||||
metrics-host="8.6.7.5"
|
||||
metrics-port=309
|
||||
metrics-category=["RPC"]
|
||||
@@ -164,4 +165,4 @@ Xethstats-contact="contact@mail.n"
|
||||
|
||||
# feature flags
|
||||
Xsecp256k1-native-enabled=false
|
||||
Xaltbn128-native-enabled=false
|
||||
Xaltbn128-native-enabled=false
|
||||
|
||||
@@ -469,6 +469,8 @@ applicationDefaultJvmArgs = [
|
||||
// We shutdown log4j ourselves, as otherwise this shutdown hook runs before our own and whatever
|
||||
// happens during shutdown is not logged.
|
||||
'-Dlog4j.shutdownHookEnabled=false',
|
||||
// Redirect java.util.logging loggers to use log4j2.
|
||||
'-Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager',
|
||||
// Suppress Java JPMS warnings. Document the reason for each suppression.
|
||||
// Bouncy Castle needs access to sun.security.provider, which is not open by default.
|
||||
'--add-opens',
|
||||
|
||||
@@ -28,9 +28,9 @@ import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueStoragePrefixedKeyBlockchainStorage;
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
|
||||
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
|
||||
|
||||
@@ -164,7 +164,7 @@ public class DefaultBlockchainTest {
|
||||
final Blockchain blockchain =
|
||||
DefaultBlockchain.create(
|
||||
createStorage(kvStore),
|
||||
PrometheusMetricsSystem.init(MetricsConfiguration.builder().enabled(true).build()),
|
||||
MetricsSystemFactory.create(MetricsConfiguration.builder().enabled(true).build()),
|
||||
0);
|
||||
|
||||
for (int i = 0; i < blocks.size(); i++) {
|
||||
|
||||
@@ -185,7 +185,7 @@ public class EvmToolCommand implements Runnable {
|
||||
: GenesisFileModule.createGenesisModule(genesisFile)
|
||||
: GenesisFileModule.createGenesisModule(network))
|
||||
.evmToolCommandOptionsModule(daggerOptions)
|
||||
.metricsSystemModule(new PrometheusMetricsSystemModule())
|
||||
.metricsSystemModule(new MetricsSystemModule())
|
||||
.build();
|
||||
|
||||
final BlockHeader blockHeader =
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.hyperledger.besu.evmtool;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import dagger.Module;
|
||||
@@ -27,6 +29,6 @@ public class MetricsSystemModule {
|
||||
|
||||
@Provides
|
||||
MetricsSystem getMetricsSystem() {
|
||||
throw new RuntimeException("Abstract");
|
||||
return MetricsSystemFactory.create(MetricsConfiguration.builder().build());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +45,19 @@ dependencyManagement {
|
||||
|
||||
dependency 'info.picocli:picocli:4.5.0'
|
||||
|
||||
dependency 'io.grpc:grpc-netty:1.33.0'
|
||||
|
||||
dependency 'io.kubernetes:client-java:5.0.0'
|
||||
|
||||
dependency 'io.pkts:pkts-core:3.0.7'
|
||||
|
||||
dependency group: 'io.opentelemetry.instrumentation.auto', name: 'opentelemetry-javaagent', version: '0.8.0', classifier: 'all'
|
||||
|
||||
dependency 'io.opentelemetry:opentelemetry-api:0.9.1'
|
||||
dependency 'io.opentelemetry:opentelemetry-sdk:0.9.1'
|
||||
dependency 'io.opentelemetry:opentelemetry-exporters-otlp:0.9.1'
|
||||
dependency 'io.opentelemetry:opentelemetry-extension-runtime-metrics:0.9.1'
|
||||
|
||||
dependency 'io.prometheus:simpleclient:0.9.0'
|
||||
dependency 'io.prometheus:simpleclient_common:0.9.0'
|
||||
dependency 'io.prometheus:simpleclient_hotspot:0.9.0'
|
||||
@@ -75,6 +82,7 @@ dependencyManagement {
|
||||
|
||||
dependency 'org.apache.logging.log4j:log4j-api:2.13.3'
|
||||
dependency 'org.apache.logging.log4j:log4j-core:2.13.3'
|
||||
dependency 'org.apache.logging.log4j:log4j-jul:2.13.3'
|
||||
dependency 'org.apache.logging.log4j:log4j-slf4j-impl:2.13.3'
|
||||
dependency 'org.fusesource.jansi:jansi:1.8'
|
||||
|
||||
|
||||
@@ -38,6 +38,12 @@ dependencies {
|
||||
implementation project(':plugin-api')
|
||||
|
||||
implementation 'com.google.guava:guava'
|
||||
implementation 'io.grpc:grpc-netty'
|
||||
implementation 'io.opentelemetry:opentelemetry-api'
|
||||
implementation 'io.opentelemetry:opentelemetry-sdk'
|
||||
implementation 'io.opentelemetry:opentelemetry-extension-runtime-metrics'
|
||||
implementation 'io.opentelemetry:opentelemetry-exporters-otlp'
|
||||
|
||||
implementation 'io.prometheus:simpleclient'
|
||||
implementation 'io.prometheus:simpleclient_common'
|
||||
implementation 'io.prometheus:simpleclient_hotspot'
|
||||
|
||||
@@ -11,19 +11,21 @@
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
package org.hyperledger.besu.metrics;
|
||||
|
||||
package org.hyperledger.besu.evmtool;
|
||||
/** Enumeration of metrics protocols supported by Besu. */
|
||||
public enum MetricsProtocol {
|
||||
PROMETHEUS,
|
||||
OPENTELEMETRY,
|
||||
NONE;
|
||||
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
public class PrometheusMetricsSystemModule extends MetricsSystemModule {
|
||||
|
||||
@Override
|
||||
public MetricsSystem getMetricsSystem() {
|
||||
return PrometheusMetricsSystem.init(MetricsConfiguration.builder().build());
|
||||
public static MetricsProtocol fromString(final String str) {
|
||||
for (final MetricsProtocol mode : MetricsProtocol.values()) {
|
||||
if (mode.name().equalsIgnoreCase(str)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,13 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
package org.hyperledger.besu.metrics;
|
||||
|
||||
import org.hyperledger.besu.metrics.opentelemetry.MetricsOtelGrpcPushService;
|
||||
import org.hyperledger.besu.metrics.opentelemetry.OpenTelemetrySystem;
|
||||
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.plugin.services.MetricsSystem;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -21,18 +26,33 @@ import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.vertx.core.Vertx;
|
||||
|
||||
/**
|
||||
* Service responsible for exposing metrics to the outside, either through a port and network
|
||||
* interface or pushing.
|
||||
*/
|
||||
public interface MetricsService {
|
||||
|
||||
static MetricsService create(
|
||||
static Optional<MetricsService> create(
|
||||
final Vertx vertx,
|
||||
final MetricsConfiguration configuration,
|
||||
final MetricsSystem metricsSystem) {
|
||||
if (configuration.isEnabled()) {
|
||||
return new MetricsHttpService(vertx, configuration, metricsSystem);
|
||||
} else if (configuration.isPushEnabled()) {
|
||||
return new MetricsPushGatewayService(configuration, metricsSystem);
|
||||
if (configuration.getProtocol() == MetricsProtocol.PROMETHEUS) {
|
||||
if (configuration.isEnabled()) {
|
||||
return Optional.of(new MetricsHttpService(vertx, configuration, metricsSystem));
|
||||
} else if (configuration.isPushEnabled()) {
|
||||
return Optional.of(new MetricsPushGatewayService(configuration, metricsSystem));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} else if (configuration.getProtocol() == MetricsProtocol.OPENTELEMETRY) {
|
||||
if (configuration.isEnabled()) {
|
||||
return Optional.of(
|
||||
new MetricsOtelGrpcPushService(configuration, (OpenTelemetrySystem) metricsSystem));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException("No metrics service enabled.");
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static org.hyperledger.besu.metrics.MetricsProtocol.OPENTELEMETRY;
|
||||
import static org.hyperledger.besu.metrics.MetricsProtocol.PROMETHEUS;
|
||||
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.opentelemetry.OpenTelemetrySystem;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.metrics.prometheus.PrometheusMetricsSystem;
|
||||
|
||||
/** Creates a new metric system based on configuration. */
|
||||
public class MetricsSystemFactory {
|
||||
|
||||
private MetricsSystemFactory() {}
|
||||
|
||||
/**
|
||||
* Creates and starts a new metric system to observe the behavior of the client
|
||||
*
|
||||
* @param metricsConfiguration the configuration of the metric system
|
||||
* @return a new metric system
|
||||
*/
|
||||
public static ObservableMetricsSystem create(final MetricsConfiguration metricsConfiguration) {
|
||||
if (!metricsConfiguration.isEnabled() && !metricsConfiguration.isPushEnabled()) {
|
||||
return new NoOpMetricsSystem();
|
||||
}
|
||||
if (PROMETHEUS.equals(metricsConfiguration.getProtocol())) {
|
||||
final PrometheusMetricsSystem metricsSystem =
|
||||
new PrometheusMetricsSystem(
|
||||
metricsConfiguration.getMetricCategories(), metricsConfiguration.isTimersEnabled());
|
||||
metricsSystem.init();
|
||||
return metricsSystem;
|
||||
} else if (OPENTELEMETRY.equals(metricsConfiguration.getProtocol())) {
|
||||
final OpenTelemetrySystem metricsSystem =
|
||||
new OpenTelemetrySystem(
|
||||
metricsConfiguration.getMetricCategories(),
|
||||
metricsConfiguration.isTimersEnabled(),
|
||||
metricsConfiguration.getPrometheusJob());
|
||||
metricsSystem.initDefaults();
|
||||
return metricsSystem;
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid metrics protocol " + metricsConfiguration.getProtocol());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.metrics;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface ObservableMetricsSystem extends MetricsSystem {
|
||||
@@ -24,4 +25,22 @@ public interface ObservableMetricsSystem extends MetricsSystem {
|
||||
Stream<Observation> streamObservations(MetricCategory category);
|
||||
|
||||
Stream<Observation> streamObservations();
|
||||
|
||||
/**
|
||||
* Provides an immutable view into the metric categories enabled for metric collection.
|
||||
*
|
||||
* @return the set of enabled metric categories.
|
||||
*/
|
||||
Set<MetricCategory> getEnabledCategories();
|
||||
|
||||
/**
|
||||
* Checks if a particular category of metrics is enabled.
|
||||
*
|
||||
* @param category the category to check
|
||||
* @return true if the category is enabled, false otherwise
|
||||
*/
|
||||
default boolean isCategoryEnabled(final MetricCategory category) {
|
||||
return getEnabledCategories().stream()
|
||||
.anyMatch(metricCategory -> metricCategory.getName().equals(category.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@ 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 java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -98,6 +100,11 @@ public class NoOpMetricsSystem implements ObservableMetricsSystem {
|
||||
return Stream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MetricCategory> getEnabledCategories() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public static class LabelCountingNoOpMetric<T> implements LabelledMetric<T> {
|
||||
|
||||
final int labelCount;
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.opentelemetry;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.opentelemetry.exporters.otlp.OtlpGrpcMetricExporter;
|
||||
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
|
||||
|
||||
public class MetricsOtelGrpcPushService implements MetricsService {
|
||||
|
||||
private final MetricsConfiguration configuration;
|
||||
private final OpenTelemetrySystem metricsSystem;
|
||||
private IntervalMetricReader periodicReader;
|
||||
|
||||
public MetricsOtelGrpcPushService(
|
||||
final MetricsConfiguration configuration, final OpenTelemetrySystem metricsSystem) {
|
||||
this.configuration = configuration;
|
||||
this.metricsSystem = metricsSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> start() {
|
||||
OtlpGrpcMetricExporter exporter = OtlpGrpcMetricExporter.getDefault();
|
||||
IntervalMetricReader.Builder builder =
|
||||
IntervalMetricReader.builder()
|
||||
.setExportIntervalMillis(configuration.getPushInterval() * 1000L)
|
||||
.readEnvironmentVariables()
|
||||
.readSystemProperties()
|
||||
.setMetricProducers(
|
||||
Collections.singleton(metricsSystem.getMeterSdkProvider().getMetricProducer()))
|
||||
.setMetricExporter(exporter);
|
||||
this.periodicReader = builder.build();
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<?> stop() {
|
||||
if (periodicReader != null) {
|
||||
periodicReader.shutdown();
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Integer> getPort() {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.opentelemetry;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.opentelemetry.common.Labels;
|
||||
import io.opentelemetry.metrics.LongCounter;
|
||||
|
||||
public class OpenTelemetryCounter implements LabelledMetric<Counter> {
|
||||
|
||||
private final LongCounter counter;
|
||||
private final String[] labelNames;
|
||||
|
||||
public OpenTelemetryCounter(final LongCounter counter, final String... labelNames) {
|
||||
this.counter = counter;
|
||||
this.labelNames = labelNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Counter labels(final String... labelValues) {
|
||||
List<String> labelKeysAndValues = new ArrayList<>();
|
||||
for (int i = 0; i < labelNames.length; i++) {
|
||||
labelKeysAndValues.add(labelNames[i]);
|
||||
labelKeysAndValues.add(labelValues[i]);
|
||||
}
|
||||
final Labels labels = Labels.of(labelKeysAndValues.toArray(new String[] {}));
|
||||
LongCounter.BoundLongCounter boundLongCounter = counter.bind(labels);
|
||||
return new OpenTelemetryCounter.UnlabelledCounter(boundLongCounter);
|
||||
}
|
||||
|
||||
private static class UnlabelledCounter implements Counter {
|
||||
private final LongCounter.BoundLongCounter counter;
|
||||
|
||||
private UnlabelledCounter(final LongCounter.BoundLongCounter counter) {
|
||||
this.counter = counter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inc() {
|
||||
counter.add(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inc(final long amount) {
|
||||
counter.add(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* 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.opentelemetry;
|
||||
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
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.Counter;
|
||||
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 java.lang.management.GarbageCollectorMXBean;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryPoolMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.EnumSet;
|
||||
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.stream.Stream;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.opentelemetry.common.Attributes;
|
||||
import io.opentelemetry.common.Labels;
|
||||
import io.opentelemetry.metrics.DoubleValueObserver;
|
||||
import io.opentelemetry.metrics.DoubleValueRecorder;
|
||||
import io.opentelemetry.metrics.LongCounter;
|
||||
import io.opentelemetry.metrics.LongSumObserver;
|
||||
import io.opentelemetry.metrics.LongUpDownSumObserver;
|
||||
import io.opentelemetry.metrics.Meter;
|
||||
import io.opentelemetry.sdk.metrics.MeterSdkProvider;
|
||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||
import io.opentelemetry.sdk.resources.Resource;
|
||||
import io.opentelemetry.sdk.resources.ResourceAttributes;
|
||||
|
||||
/** Metrics system relying on the native OpenTelemetry format. */
|
||||
public class OpenTelemetrySystem implements ObservableMetricsSystem {
|
||||
private static final String TYPE_LABEL_KEY = "type";
|
||||
private static final String AREA_LABEL_KEY = "area";
|
||||
private static final String POOL_LABEL_KEY = "pool";
|
||||
private static final String USED = "used";
|
||||
private static final String COMMITTED = "committed";
|
||||
private static final String MAX = "max";
|
||||
private static final String HEAP = "heap";
|
||||
private static final String NON_HEAP = "non_heap";
|
||||
|
||||
private final Set<MetricCategory> enabledCategories;
|
||||
private final boolean timersEnabled;
|
||||
private final Map<String, LabelledMetric<Counter>> cachedCounters = new ConcurrentHashMap<>();
|
||||
private final Map<String, LabelledMetric<OperationTimer>> cachedTimers =
|
||||
new ConcurrentHashMap<>();
|
||||
private final MeterSdkProvider meterSdkProvider;
|
||||
|
||||
public OpenTelemetrySystem(
|
||||
final Set<MetricCategory> enabledCategories,
|
||||
final boolean timersEnabled,
|
||||
final String jobName) {
|
||||
this.enabledCategories = ImmutableSet.copyOf(enabledCategories);
|
||||
this.timersEnabled = timersEnabled;
|
||||
Resource resource =
|
||||
Resource.getDefault()
|
||||
.merge(
|
||||
Resource.create(
|
||||
Attributes.newBuilder()
|
||||
.setAttribute(ResourceAttributes.SERVICE_NAME, jobName)
|
||||
.build()));
|
||||
this.meterSdkProvider = MeterSdkProvider.builder().setResource(resource).build();
|
||||
}
|
||||
|
||||
MeterSdkProvider getMeterSdkProvider() {
|
||||
return meterSdkProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations(final MetricCategory category) {
|
||||
return streamObservations().filter(metricData -> metricData.getCategory().equals(category));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Observation> streamObservations() {
|
||||
Collection<MetricData> metricsList = meterSdkProvider.getMetricProducer().collectAllMetrics();
|
||||
return metricsList.stream().map(this::convertToObservations).flatMap(stream -> stream);
|
||||
}
|
||||
|
||||
private Stream<Observation> convertToObservations(final MetricData metricData) {
|
||||
List<Observation> observations = new ArrayList<>();
|
||||
MetricCategory category =
|
||||
categoryNameToMetricCategory(metricData.getInstrumentationLibraryInfo().getName());
|
||||
for (MetricData.Point point : metricData.getPoints()) {
|
||||
List<String> labels = new ArrayList<>();
|
||||
point.getLabels().forEach((k, v) -> labels.add(v));
|
||||
observations.add(
|
||||
new Observation(
|
||||
category, metricData.getName(), extractValue(metricData.getType(), point), labels));
|
||||
}
|
||||
return observations.stream();
|
||||
}
|
||||
|
||||
private MetricCategory categoryNameToMetricCategory(final String name) {
|
||||
Set<MetricCategory> categories =
|
||||
ImmutableSet.<MetricCategory>builder()
|
||||
.addAll(EnumSet.allOf(BesuMetricCategory.class))
|
||||
.addAll(EnumSet.allOf(StandardMetricCategory.class))
|
||||
.build();
|
||||
for (MetricCategory category : categories) {
|
||||
if (category.getName().equals(name)) {
|
||||
return category;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid metric category: " + name);
|
||||
}
|
||||
|
||||
private Object extractValue(final MetricData.Type type, final MetricData.Point point) {
|
||||
switch (type) {
|
||||
case NON_MONOTONIC_LONG:
|
||||
case MONOTONIC_LONG:
|
||||
return ((MetricData.LongPoint) point).getValue();
|
||||
case NON_MONOTONIC_DOUBLE:
|
||||
case MONOTONIC_DOUBLE:
|
||||
return ((MetricData.DoublePoint) point).getValue();
|
||||
case SUMMARY:
|
||||
return ((MetricData.SummaryPoint) point).getPercentileValues();
|
||||
default:
|
||||
throw new UnsupportedOperationException("Unsupported type " + type);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelledMetric<Counter> createLabelledCounter(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
return cachedCounters.computeIfAbsent(
|
||||
name,
|
||||
(k) -> {
|
||||
if (isCategoryEnabled(category)) {
|
||||
final Meter meter = meterSdkProvider.get(category.getName());
|
||||
|
||||
final LongCounter counter = meter.longCounterBuilder(name).setDescription(help).build();
|
||||
return new OpenTelemetryCounter(counter, labelNames);
|
||||
} else {
|
||||
return NoOpMetricsSystem.getCounterLabelledMetric(labelNames.length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public LabelledMetric<OperationTimer> createLabelledTimer(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final String... labelNames) {
|
||||
return cachedTimers.computeIfAbsent(
|
||||
name,
|
||||
(k) -> {
|
||||
if (timersEnabled && isCategoryEnabled(category)) {
|
||||
final Meter meter = meterSdkProvider.get(category.getName());
|
||||
|
||||
final DoubleValueRecorder recorder =
|
||||
meter.doubleValueRecorderBuilder(name).setDescription(help).build();
|
||||
return new OpenTelemetryTimer(recorder, labelNames);
|
||||
} else {
|
||||
return NoOpMetricsSystem.getOperationTimerLabelledMetric(labelNames.length);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createGauge(
|
||||
final MetricCategory category,
|
||||
final String name,
|
||||
final String help,
|
||||
final DoubleSupplier valueSupplier) {
|
||||
if (isCategoryEnabled(category)) {
|
||||
final Meter meter = meterSdkProvider.get(category.getName());
|
||||
DoubleValueObserver observer =
|
||||
meter.doubleValueObserverBuilder(name).setDescription(help).build();
|
||||
observer.setCallback(result -> result.observe(valueSupplier.getAsDouble(), Labels.empty()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MetricCategory> getEnabledCategories() {
|
||||
return enabledCategories;
|
||||
}
|
||||
|
||||
public void initDefaults() {
|
||||
if (isCategoryEnabled(StandardMetricCategory.JVM)) {
|
||||
collectGC();
|
||||
}
|
||||
}
|
||||
|
||||
private void collectGC() {
|
||||
final List<GarbageCollectorMXBean> garbageCollectors =
|
||||
ManagementFactory.getGarbageCollectorMXBeans();
|
||||
final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
final List<MemoryPoolMXBean> poolBeans = ManagementFactory.getMemoryPoolMXBeans();
|
||||
final Meter meter = meterSdkProvider.get(StandardMetricCategory.JVM.getName());
|
||||
final LongSumObserver gcMetric =
|
||||
meter
|
||||
.longSumObserverBuilder("jvm.gc.collection")
|
||||
.setDescription("Time spent in a given JVM garbage collector in milliseconds.")
|
||||
.setUnit("ms")
|
||||
.build();
|
||||
final List<Labels> labelSets = new ArrayList<>(garbageCollectors.size());
|
||||
for (final GarbageCollectorMXBean gc : garbageCollectors) {
|
||||
labelSets.add(Labels.of("gc", gc.getName()));
|
||||
}
|
||||
|
||||
gcMetric.setCallback(
|
||||
resultLongObserver -> {
|
||||
for (int i = 0; i < garbageCollectors.size(); i++) {
|
||||
resultLongObserver.observe(
|
||||
garbageCollectors.get(i).getCollectionTime(), labelSets.get(i));
|
||||
}
|
||||
});
|
||||
|
||||
final LongUpDownSumObserver areaMetric =
|
||||
meter
|
||||
.longUpDownSumObserverBuilder("jvm.memory.area")
|
||||
.setDescription("Bytes of a given JVM memory area.")
|
||||
.setUnit("By")
|
||||
.build();
|
||||
final Labels usedHeap = Labels.of(TYPE_LABEL_KEY, USED, AREA_LABEL_KEY, HEAP);
|
||||
final Labels usedNonHeap = Labels.of(TYPE_LABEL_KEY, USED, AREA_LABEL_KEY, NON_HEAP);
|
||||
final Labels committedHeap = Labels.of(TYPE_LABEL_KEY, COMMITTED, AREA_LABEL_KEY, HEAP);
|
||||
final Labels committedNonHeap = Labels.of(TYPE_LABEL_KEY, COMMITTED, AREA_LABEL_KEY, NON_HEAP);
|
||||
// TODO: Decide if max is needed or not. May be derived with some approximation from max(used).
|
||||
final Labels maxHeap = Labels.of(TYPE_LABEL_KEY, MAX, AREA_LABEL_KEY, HEAP);
|
||||
final Labels maxNonHeap = Labels.of(TYPE_LABEL_KEY, MAX, AREA_LABEL_KEY, NON_HEAP);
|
||||
areaMetric.setCallback(
|
||||
resultLongObserver -> {
|
||||
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
|
||||
MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
|
||||
resultLongObserver.observe(heapUsage.getUsed(), usedHeap);
|
||||
resultLongObserver.observe(nonHeapUsage.getUsed(), usedNonHeap);
|
||||
resultLongObserver.observe(heapUsage.getUsed(), committedHeap);
|
||||
resultLongObserver.observe(nonHeapUsage.getUsed(), committedNonHeap);
|
||||
resultLongObserver.observe(heapUsage.getUsed(), maxHeap);
|
||||
resultLongObserver.observe(nonHeapUsage.getUsed(), maxNonHeap);
|
||||
});
|
||||
|
||||
final LongUpDownSumObserver poolMetric =
|
||||
meter
|
||||
.longUpDownSumObserverBuilder("jvm.memory.pool")
|
||||
.setDescription("Bytes of a given JVM memory pool.")
|
||||
.setUnit("By")
|
||||
.build();
|
||||
final List<Labels> usedLabelSets = new ArrayList<>(poolBeans.size());
|
||||
final List<Labels> committedLabelSets = new ArrayList<>(poolBeans.size());
|
||||
final List<Labels> maxLabelSets = new ArrayList<>(poolBeans.size());
|
||||
for (final MemoryPoolMXBean pool : poolBeans) {
|
||||
usedLabelSets.add(Labels.of(TYPE_LABEL_KEY, USED, POOL_LABEL_KEY, pool.getName()));
|
||||
committedLabelSets.add(Labels.of(TYPE_LABEL_KEY, COMMITTED, POOL_LABEL_KEY, pool.getName()));
|
||||
maxLabelSets.add(Labels.of(TYPE_LABEL_KEY, MAX, POOL_LABEL_KEY, pool.getName()));
|
||||
}
|
||||
poolMetric.setCallback(
|
||||
resultLongObserver -> {
|
||||
for (int i = 0; i < poolBeans.size(); i++) {
|
||||
MemoryUsage poolUsage = poolBeans.get(i).getUsage();
|
||||
resultLongObserver.observe(poolUsage.getUsed(), usedLabelSets.get(i));
|
||||
resultLongObserver.observe(poolUsage.getCommitted(), committedLabelSets.get(i));
|
||||
// TODO: Decide if max is needed or not. May be derived with some approximation from
|
||||
// max(used).
|
||||
resultLongObserver.observe(poolUsage.getMax(), maxLabelSets.get(i));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.opentelemetry;
|
||||
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.opentelemetry.common.Labels;
|
||||
import io.opentelemetry.metrics.DoubleValueRecorder;
|
||||
|
||||
public class OpenTelemetryTimer implements LabelledMetric<OperationTimer> {
|
||||
|
||||
private final DoubleValueRecorder recorder;
|
||||
private final String[] labelNames;
|
||||
|
||||
public OpenTelemetryTimer(final DoubleValueRecorder recorder, final String... labelNames) {
|
||||
this.recorder = recorder;
|
||||
this.labelNames = labelNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OperationTimer labels(final String... labelValues) {
|
||||
List<String> labelKeysAndValues = new ArrayList<>();
|
||||
for (int i = 0; i < labelNames.length; i++) {
|
||||
labelKeysAndValues.add(labelNames[i]);
|
||||
labelKeysAndValues.add(labelValues[i]);
|
||||
}
|
||||
final Labels labels = Labels.of(labelKeysAndValues.toArray(new String[] {}));
|
||||
return () -> {
|
||||
final long startTime = System.nanoTime();
|
||||
return () -> {
|
||||
long elapsed = System.nanoTime() - startTime;
|
||||
recorder.record(elapsed, labels);
|
||||
return elapsed / 1e9;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.DEFAULT_METRIC_CATEGORIES;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsProtocol;
|
||||
import org.hyperledger.besu.plugin.services.metrics.MetricCategory;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -30,12 +31,13 @@ import com.google.common.base.MoreObjects;
|
||||
public class MetricsConfiguration {
|
||||
private static final String DEFAULT_METRICS_HOST = "127.0.0.1";
|
||||
public static final int DEFAULT_METRICS_PORT = 9545;
|
||||
|
||||
private static final MetricsProtocol DEFAULT_METRICS_PROTOCOL = MetricsProtocol.PROMETHEUS;
|
||||
private static final String DEFAULT_METRICS_PUSH_HOST = "127.0.0.1";
|
||||
public static final int DEFAULT_METRICS_PUSH_PORT = 9001;
|
||||
public static final Boolean DEFAULT_TIMERS_ENABLED = true;
|
||||
|
||||
private final boolean enabled;
|
||||
private final MetricsProtocol protocol;
|
||||
private final int port;
|
||||
private int actualPort;
|
||||
private final String host;
|
||||
@@ -55,6 +57,7 @@ public class MetricsConfiguration {
|
||||
private MetricsConfiguration(
|
||||
final boolean enabled,
|
||||
final int port,
|
||||
final MetricsProtocol protocol,
|
||||
final String host,
|
||||
final Set<MetricCategory> metricCategories,
|
||||
final boolean pushEnabled,
|
||||
@@ -66,6 +69,7 @@ public class MetricsConfiguration {
|
||||
final boolean timersEnabled) {
|
||||
this.enabled = enabled;
|
||||
this.port = port;
|
||||
this.protocol = protocol;
|
||||
this.host = host;
|
||||
this.metricCategories = metricCategories;
|
||||
this.pushEnabled = pushEnabled;
|
||||
@@ -81,6 +85,10 @@ public class MetricsConfiguration {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public MetricsProtocol getProtocol() {
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
@@ -139,6 +147,7 @@ public class MetricsConfiguration {
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper(this)
|
||||
.add("enabled", enabled)
|
||||
.add("protocol", protocol)
|
||||
.add("port", port)
|
||||
.add("host", host)
|
||||
.add("metricCategories", metricCategories)
|
||||
@@ -161,6 +170,7 @@ public class MetricsConfiguration {
|
||||
}
|
||||
final MetricsConfiguration that = (MetricsConfiguration) o;
|
||||
return enabled == that.enabled
|
||||
&& Objects.equals(protocol, that.protocol)
|
||||
&& port == that.port
|
||||
&& pushEnabled == that.pushEnabled
|
||||
&& pushPort == that.pushPort
|
||||
@@ -176,6 +186,7 @@ public class MetricsConfiguration {
|
||||
public int hashCode() {
|
||||
return Objects.hash(
|
||||
enabled,
|
||||
protocol,
|
||||
port,
|
||||
host,
|
||||
metricCategories,
|
||||
@@ -189,6 +200,7 @@ public class MetricsConfiguration {
|
||||
|
||||
public static class Builder {
|
||||
private boolean enabled = false;
|
||||
private MetricsProtocol protocol = DEFAULT_METRICS_PROTOCOL;
|
||||
private int port = DEFAULT_METRICS_PORT;
|
||||
private String host = DEFAULT_METRICS_HOST;
|
||||
private Set<MetricCategory> metricCategories = DEFAULT_METRIC_CATEGORIES;
|
||||
@@ -207,6 +219,11 @@ public class MetricsConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder protocol(final MetricsProtocol protocol) {
|
||||
this.protocol = protocol;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder port(final int port) {
|
||||
this.port = port;
|
||||
return this;
|
||||
@@ -268,6 +285,7 @@ public class MetricsConfiguration {
|
||||
return new MetricsConfiguration(
|
||||
enabled,
|
||||
port,
|
||||
protocol,
|
||||
host,
|
||||
metricCategories,
|
||||
pushEnabled,
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.metrics.prometheus;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.collect.Streams.stream;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -45,7 +46,7 @@ import io.vertx.ext.web.RoutingContext;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
class MetricsHttpService implements MetricsService {
|
||||
public class MetricsHttpService implements MetricsService {
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private static final InetSocketAddress EMPTY_SOCKET_ADDRESS = new InetSocketAddress("0.0.0.0", 0);
|
||||
@@ -56,7 +57,7 @@ class MetricsHttpService implements MetricsService {
|
||||
|
||||
private HttpServer httpServer;
|
||||
|
||||
MetricsHttpService(
|
||||
public MetricsHttpService(
|
||||
final Vertx vertx,
|
||||
final MetricsConfiguration configuration,
|
||||
final MetricsSystem metricsSystem) {
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsService;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -29,7 +30,7 @@ import io.prometheus.client.exporter.PushGateway;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
class MetricsPushGatewayService implements MetricsService {
|
||||
public class MetricsPushGatewayService implements MetricsService {
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private PushGateway pushGateway;
|
||||
@@ -37,7 +38,7 @@ class MetricsPushGatewayService implements MetricsService {
|
||||
private final MetricsConfiguration config;
|
||||
private final MetricsSystem metricsSystem;
|
||||
|
||||
MetricsPushGatewayService(
|
||||
public MetricsPushGatewayService(
|
||||
final MetricsConfiguration configuration, final MetricsSystem metricsSystem) {
|
||||
this.metricsSystem = metricsSystem;
|
||||
validateConfig(configuration);
|
||||
|
||||
@@ -14,9 +14,6 @@
|
||||
*/
|
||||
package org.hyperledger.besu.metrics.prometheus;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.singleton;
|
||||
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.metrics.StandardMetricCategory;
|
||||
@@ -33,6 +30,7 @@ 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.collect.ImmutableSet;
|
||||
@@ -61,35 +59,24 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
private final Set<MetricCategory> enabledCategories;
|
||||
private final boolean timersEnabled;
|
||||
|
||||
PrometheusMetricsSystem(
|
||||
public PrometheusMetricsSystem(
|
||||
final Set<MetricCategory> enabledCategories, final boolean timersEnabled) {
|
||||
this.enabledCategories = ImmutableSet.copyOf(enabledCategories);
|
||||
this.timersEnabled = timersEnabled;
|
||||
}
|
||||
|
||||
public static ObservableMetricsSystem init(final MetricsConfiguration metricsConfiguration) {
|
||||
if (!metricsConfiguration.isEnabled() && !metricsConfiguration.isPushEnabled()) {
|
||||
return new NoOpMetricsSystem();
|
||||
}
|
||||
final PrometheusMetricsSystem metricsSystem =
|
||||
new PrometheusMetricsSystem(
|
||||
metricsConfiguration.getMetricCategories(), metricsConfiguration.isTimersEnabled());
|
||||
if (metricsSystem.isCategoryEnabled(StandardMetricCategory.PROCESS)) {
|
||||
metricsSystem.collectors.put(
|
||||
StandardMetricCategory.PROCESS,
|
||||
singleton(new StandardExports().register(metricsSystem.registry)));
|
||||
}
|
||||
if (metricsSystem.isCategoryEnabled(StandardMetricCategory.JVM)) {
|
||||
metricsSystem.collectors.put(
|
||||
StandardMetricCategory.JVM,
|
||||
asList(
|
||||
new MemoryPoolsExports().register(metricsSystem.registry),
|
||||
new BufferPoolsExports().register(metricsSystem.registry),
|
||||
new GarbageCollectorExports().register(metricsSystem.registry),
|
||||
new ThreadExports().register(metricsSystem.registry),
|
||||
new ClassLoadingExports().register(metricsSystem.registry)));
|
||||
}
|
||||
return metricsSystem;
|
||||
public void init() {
|
||||
addCollector(StandardMetricCategory.PROCESS, StandardExports::new);
|
||||
addCollector(StandardMetricCategory.JVM, MemoryPoolsExports::new);
|
||||
addCollector(StandardMetricCategory.JVM, BufferPoolsExports::new);
|
||||
addCollector(StandardMetricCategory.JVM, GarbageCollectorExports::new);
|
||||
addCollector(StandardMetricCategory.JVM, ThreadExports::new);
|
||||
addCollector(StandardMetricCategory.JVM, ClassLoadingExports::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MetricCategory> getEnabledCategories() {
|
||||
return enabledCategories;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -154,14 +141,10 @@ public class PrometheusMetricsSystem implements ObservableMetricsSystem {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCategoryEnabled(final MetricCategory category) {
|
||||
return enabledCategories.stream()
|
||||
.anyMatch(metricCategory -> metricCategory.getName().equals(category.getName()));
|
||||
}
|
||||
|
||||
public void addCollector(final MetricCategory category, final Collector metric) {
|
||||
public void addCollector(
|
||||
final MetricCategory category, final Supplier<Collector> metricSupplier) {
|
||||
if (isCategoryEnabled(category)) {
|
||||
addCollectorUnchecked(category, metric);
|
||||
addCollectorUnchecked(category, metricSupplier.get());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,11 @@ 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 java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.DoubleSupplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -90,6 +92,11 @@ public class StubMetricsSystem implements ObservableMetricsSystem {
|
||||
throw new UnsupportedOperationException("Observations aren't actually recorded");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<MetricCategory> getEnabledCategories() {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
public static class StubLabelledCounter implements LabelledMetric<Counter> {
|
||||
private final Map<List<String>, StubCounter> metrics = new HashMap<>();
|
||||
|
||||
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* 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.opentelemetry;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
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;
|
||||
import static org.hyperledger.besu.metrics.BesuMetricCategory.RPC;
|
||||
import static org.hyperledger.besu.metrics.MetricsProtocol.OPENTELEMETRY;
|
||||
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.prometheus.MetricsConfiguration;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import io.opentelemetry.sdk.metrics.data.MetricData;
|
||||
import org.junit.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);
|
||||
|
||||
private final ObservableMetricsSystem metricsSystem =
|
||||
new OpenTelemetrySystem(DEFAULT_METRIC_CATEGORIES, true, "job");
|
||||
|
||||
@Test
|
||||
public void shouldCreateObservationFromCounter() {
|
||||
final Counter counter = metricsSystem.createCounter(PEERS, "connected", "Some help string");
|
||||
|
||||
counter.inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactly(new Observation(PEERS, "connected", 1L, emptyList()));
|
||||
|
||||
counter.inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactly(new Observation(PEERS, "connected", 2L, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleDuplicateCounterCreation() {
|
||||
final LabelledMetric<Counter> counter1 =
|
||||
metricsSystem.createLabelledCounter(PEERS, "connected", "Some help string");
|
||||
final LabelledMetric<Counter> counter2 =
|
||||
metricsSystem.createLabelledCounter(PEERS, "connected", "Some help string");
|
||||
assertThat(counter1).isEqualTo(counter2);
|
||||
|
||||
counter1.labels().inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactly(new Observation(PEERS, "connected", 1L, emptyList()));
|
||||
|
||||
counter2.labels().inc();
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactly(new Observation(PEERS, "connected", 2L, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateSeparateObservationsForEachCounterLabelValue() {
|
||||
final LabelledMetric<Counter> counter =
|
||||
metricsSystem.createLabelledCounter(PEERS, "connected", "Some help string", "labelName");
|
||||
|
||||
counter.labels("value1").inc();
|
||||
counter.labels("value2").inc();
|
||||
counter.labels("value1").inc();
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactlyInAnyOrder(
|
||||
new Observation(PEERS, "connected", 2L, singletonList("value1")),
|
||||
new Observation(PEERS, "connected", 1L, singletonList("value2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldIncrementCounterBySpecifiedAmount() {
|
||||
final Counter counter = metricsSystem.createCounter(PEERS, "connected", "Some help string");
|
||||
|
||||
counter.inc(5);
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactly(new Observation(PEERS, "connected", 5L, emptyList()));
|
||||
|
||||
counter.inc(6);
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.containsExactly(new Observation(PEERS, "connected", 11L, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateObservationsFromTimer() {
|
||||
final OperationTimer timer = metricsSystem.createTimer(RPC, "request", "Some help");
|
||||
|
||||
final OperationTimer.TimingContext context = timer.startTimer();
|
||||
context.stopTimer();
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(IGNORE_VALUES)
|
||||
.containsExactlyInAnyOrder(new Observation(RPC, "request", null, Collections.emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleDuplicateTimerCreation() {
|
||||
final LabelledMetric<OperationTimer> timer1 =
|
||||
metricsSystem.createLabelledTimer(RPC, "request", "Some help");
|
||||
final LabelledMetric<OperationTimer> timer2 =
|
||||
metricsSystem.createLabelledTimer(RPC, "request", "Some help");
|
||||
assertThat(timer1).isEqualTo(timer2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateObservationsFromTimerWithLabels() {
|
||||
final LabelledMetric<OperationTimer> timer =
|
||||
metricsSystem.createLabelledTimer(RPC, "request", "Some help", "methodName");
|
||||
|
||||
//noinspection EmptyTryBlock
|
||||
try (final OperationTimer.TimingContext ignored = timer.labels("method").startTimer()) {}
|
||||
//noinspection EmptyTryBlock
|
||||
try (final OperationTimer.TimingContext ignored = timer.labels("method").startTimer()) {}
|
||||
//noinspection EmptyTryBlock
|
||||
try (final OperationTimer.TimingContext ignored = timer.labels("method").startTimer()) {}
|
||||
//noinspection EmptyTryBlock
|
||||
try (final OperationTimer.TimingContext ignored = timer.labels("method").startTimer()) {}
|
||||
|
||||
assertThat(metricsSystem.streamObservations())
|
||||
.usingElementComparator(IGNORE_VALUES) // We don't know how long it will actually take.
|
||||
.containsExactlyInAnyOrder(new Observation(RPC, "request", null, singletonList("method")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCreateObservationsFromTimerWhenTimersDisabled() {
|
||||
final ObservableMetricsSystem metricsSystem =
|
||||
new OpenTelemetrySystem(DEFAULT_METRIC_CATEGORIES, false, "job");
|
||||
final LabelledMetric<OperationTimer> timer =
|
||||
metricsSystem.createLabelledTimer(RPC, "request", "Some help", "methodName");
|
||||
|
||||
//noinspection EmptyTryBlock
|
||||
try (final OperationTimer.TimingContext ignored = timer.labels("method").startTimer()) {}
|
||||
|
||||
assertThat(metricsSystem.streamObservations()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCreateObservationFromGauge() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder()
|
||||
.metricCategories(ImmutableSet.of(BesuMetricCategory.RPC))
|
||||
.enabled(true)
|
||||
.protocol(OPENTELEMETRY)
|
||||
.build();
|
||||
final ObservableMetricsSystem localMetricSystem =
|
||||
MetricsSystemFactory.create(metricsConfiguration);
|
||||
localMetricSystem.createGauge(RPC, "myValue", "Help", () -> 7d);
|
||||
List<MetricData.ValueAtPercentile> values = new ArrayList<>();
|
||||
values.add(MetricData.ValueAtPercentile.create(0, 7d));
|
||||
values.add(MetricData.ValueAtPercentile.create(100, 7d));
|
||||
|
||||
assertThat(localMetricSystem.streamObservations())
|
||||
.containsExactlyInAnyOrder(new Observation(RPC, "myValue", values, emptyList()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldOnlyObserveEnabledMetrics() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder()
|
||||
.metricCategories(ImmutableSet.of(BesuMetricCategory.RPC))
|
||||
.enabled(true)
|
||||
.protocol(OPENTELEMETRY)
|
||||
.build();
|
||||
final ObservableMetricsSystem localMetricSystem =
|
||||
MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
// 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);
|
||||
|
||||
counterN.labels("show").inc();
|
||||
assertThat(localMetricSystem.streamObservations()).isEmpty();
|
||||
|
||||
// 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);
|
||||
|
||||
counterR.labels("op").inc();
|
||||
assertThat(localMetricSystem.streamObservations())
|
||||
.containsExactly(new Observation(RPC, "name", (long) 1, singletonList("op")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsNoOpMetricsWhenAllDisabled() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder()
|
||||
.enabled(false)
|
||||
.pushEnabled(false)
|
||||
.protocol(OPENTELEMETRY)
|
||||
.build();
|
||||
final MetricsSystem localMetricSystem = MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
assertThat(localMetricSystem).isInstanceOf(NoOpMetricsSystem.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsPrometheusMetricsWhenEnabled() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder()
|
||||
.enabled(true)
|
||||
.pushEnabled(false)
|
||||
.protocol(OPENTELEMETRY)
|
||||
.build();
|
||||
final MetricsSystem localMetricSystem = MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
assertThat(localMetricSystem).isInstanceOf(OpenTelemetrySystem.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsNoOpMetricsWhenPushEnabled() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder()
|
||||
.enabled(false)
|
||||
.pushEnabled(true)
|
||||
.protocol(OPENTELEMETRY)
|
||||
.build();
|
||||
final MetricsSystem localMetricSystem = MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
assertThat(localMetricSystem).isInstanceOf(OpenTelemetrySystem.class);
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,8 @@ import static java.util.Collections.singletonList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.util.NetworkUtility.urlForSocketAddress;
|
||||
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Properties;
|
||||
|
||||
@@ -53,13 +55,13 @@ public class MetricsHttpServiceTest {
|
||||
}
|
||||
|
||||
private static MetricsHttpService createMetricsHttpService(final MetricsConfiguration config) {
|
||||
return new MetricsHttpService(vertx, config, PrometheusMetricsSystem.init(config));
|
||||
return new MetricsHttpService(vertx, config, MetricsSystemFactory.create(config));
|
||||
}
|
||||
|
||||
private static MetricsHttpService createMetricsHttpService() {
|
||||
final MetricsConfiguration metricsConfiguration = createMetricsConfig();
|
||||
return new MetricsHttpService(
|
||||
vertx, metricsConfiguration, PrometheusMetricsSystem.init(metricsConfiguration));
|
||||
vertx, metricsConfiguration, MetricsSystemFactory.create(metricsConfiguration));
|
||||
}
|
||||
|
||||
private static MetricsConfiguration createMetricsConfig() {
|
||||
|
||||
@@ -26,6 +26,7 @@ import static org.hyperledger.besu.metrics.BesuMetricCategory.RPC;
|
||||
import static org.hyperledger.besu.metrics.StandardMetricCategory.JVM;
|
||||
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
import org.hyperledger.besu.metrics.MetricsSystemFactory;
|
||||
import org.hyperledger.besu.metrics.ObservableMetricsSystem;
|
||||
import org.hyperledger.besu.metrics.Observation;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
@@ -34,6 +35,7 @@ import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
@@ -160,7 +162,7 @@ public class PrometheusMetricsSystemTest {
|
||||
@Test
|
||||
public void shouldNotCreateObservationsFromTimerWhenTimersDisabled() {
|
||||
final ObservableMetricsSystem metricsSystem =
|
||||
new PrometheusMetricsSystem(DEFAULT_METRIC_CATEGORIES, false);
|
||||
new PrometheusMetricsSystem(Collections.emptySet(), false);
|
||||
final LabelledMetric<OperationTimer> timer =
|
||||
metricsSystem.createLabelledTimer(RPC, "request", "Some help", "methodName");
|
||||
|
||||
@@ -196,7 +198,7 @@ public class PrometheusMetricsSystemTest {
|
||||
.enabled(true)
|
||||
.build();
|
||||
final ObservableMetricsSystem localMetricSystem =
|
||||
PrometheusMetricsSystem.init(metricsConfiguration);
|
||||
MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
// do a category we are not watching
|
||||
final LabelledMetric<Counter> counterN =
|
||||
@@ -220,7 +222,7 @@ public class PrometheusMetricsSystemTest {
|
||||
public void returnsNoOpMetricsWhenAllDisabled() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder().enabled(false).pushEnabled(false).build();
|
||||
final MetricsSystem localMetricSystem = PrometheusMetricsSystem.init(metricsConfiguration);
|
||||
final MetricsSystem localMetricSystem = MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
assertThat(localMetricSystem).isInstanceOf(NoOpMetricsSystem.class);
|
||||
}
|
||||
@@ -229,7 +231,7 @@ public class PrometheusMetricsSystemTest {
|
||||
public void returnsPrometheusMetricsWhenEnabled() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder().enabled(true).pushEnabled(false).build();
|
||||
final MetricsSystem localMetricSystem = PrometheusMetricsSystem.init(metricsConfiguration);
|
||||
final MetricsSystem localMetricSystem = MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
assertThat(localMetricSystem).isInstanceOf(PrometheusMetricsSystem.class);
|
||||
}
|
||||
@@ -238,7 +240,7 @@ public class PrometheusMetricsSystemTest {
|
||||
public void returnsNoOpMetricsWhenPushEnabled() {
|
||||
final MetricsConfiguration metricsConfiguration =
|
||||
MetricsConfiguration.builder().enabled(false).pushEnabled(true).build();
|
||||
final MetricsSystem localMetricSystem = PrometheusMetricsSystem.init(metricsConfiguration);
|
||||
final MetricsSystem localMetricSystem = MetricsSystemFactory.create(metricsConfiguration);
|
||||
|
||||
assertThat(localMetricSystem).isInstanceOf(PrometheusMetricsSystem.class);
|
||||
}
|
||||
|
||||
@@ -173,6 +173,9 @@ public class RocksDBStats {
|
||||
final Statistics stats,
|
||||
final PrometheusMetricsSystem metricsSystem,
|
||||
final MetricCategory category) {
|
||||
if (!metricsSystem.isCategoryEnabled(category)) {
|
||||
return;
|
||||
}
|
||||
for (final TickerType ticker : TICKERS) {
|
||||
final String promCounterName = ticker.name().toLowerCase();
|
||||
metricsSystem.createLongGauge(
|
||||
@@ -183,7 +186,7 @@ public class RocksDBStats {
|
||||
}
|
||||
|
||||
for (final HistogramType histogram : HISTOGRAMS) {
|
||||
metricsSystem.addCollector(category, histogramToCollector(stats, histogram));
|
||||
metricsSystem.addCollector(category, () -> histogramToCollector(stats, histogram));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user