Upgrade OpenTelemetry (#3675)

* Upgrade OpenTelemetry

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>

* remove a single sleep, poll with a for loop instead

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>

* use the new approach to send trace requests

Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com>

Co-authored-by: Sally MacFarlane <sally.macfarlane@consensys.net>
pull/4005/head
Antoine Toulme 2 years ago committed by GitHub
parent a0ac0ebad8
commit 78717ade1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      CHANGELOG.md
  2. 10
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java
  3. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ProcessBesuNodeRunner.java
  4. 10
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfiguration.java
  5. 11
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java
  6. 3
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  7. 3
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/NodeConfiguration.java
  8. 3
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/privacy/PrivacyNode.java
  9. 7
      acceptance-tests/tests/build.gradle
  10. 90
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/OpenTelemetryAcceptanceTest.java
  11. 29
      gradle/versions.gradle
  12. 4
      metrics/core/build.gradle
  13. 6
      metrics/core/src/main/java/org/hyperledger/besu/metrics/MetricsService.java
  14. 3
      metrics/core/src/main/java/org/hyperledger/besu/metrics/MetricsSystemFactory.java
  15. 57
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/DebugMetricReader.java
  16. 88
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/MetricsOtelGrpcPushService.java
  17. 48
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/MetricsOtelPushService.java
  18. 16
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetryCounter.java
  19. 2
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetryGauge.java
  20. 79
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetrySystem.java
  21. 2
      metrics/core/src/main/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetryTimer.java
  22. 66
      metrics/core/src/test/java/org/hyperledger/besu/metrics/opentelemetry/OpenTelemetryMetricsSystemTest.java

@ -7,6 +7,7 @@
### Bug Fixes
- Fixed a snapsync issue that can sometimes block the healing step [#3920](https://github.com/hyperledger/besu/pull/3920)
- Upgrade OpenTelemetry to version 1.15.0 [#3675](https://github.com/hyperledger/besu/pull/3675)
## 22.4.3

@ -127,6 +127,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
private Optional<Integer> exitCode = Optional.empty();
private Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration = Optional.empty();
private final boolean isStrictTxReplayProtectionEnabled;
private final Map<String, String> environment;
public BesuNode(
final String name,
@ -159,7 +160,8 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
final List<String> runCommand,
final Optional<KeyPair> keyPair,
final Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration,
final boolean isStrictTxReplayProtectionEnabled)
final boolean isStrictTxReplayProtectionEnabled,
final Map<String, String> environment)
throws IOException {
this.homeDirectory = dataPath.orElseGet(BesuNode::createTmpDataDirectory);
this.isStrictTxReplayProtectionEnabled = isStrictTxReplayProtectionEnabled;
@ -216,6 +218,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
this.isDnsEnabled = isDnsEnabled;
privacyParameters.ifPresent(this::setPrivacyParameters);
this.pkiKeyStoreConfiguration = pkiKeyStoreConfiguration;
this.environment = environment;
LOG.info("Created BesuNode {}", this);
}
@ -794,4 +797,9 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
public void setExitCode(final int exitValue) {
this.exitCode = Optional.of(exitValue);
}
@Override
public Map<String, String> getEnvironment() {
return environment;
}
}

@ -419,6 +419,8 @@ public class ProcessBesuNodeRunner implements BesuNodeRunner {
"JAVA_OPTS",
"-Djava.security.properties="
+ "acceptance-tests/tests/build/resources/test/acceptanceTesting.security");
// add additional environment variables
processBuilder.environment().putAll(node.getEnvironment());
try {
checkState(
isNotAliveOrphan(node.getName()),

@ -30,6 +30,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.Gene
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class BesuNodeConfiguration {
@ -65,6 +66,7 @@ public class BesuNodeConfiguration {
private final Optional<KeyPair> keyPair;
private final Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration;
private final boolean strictTxReplayProtectionEnabled;
private final Map<String, String> environment;
BesuNodeConfiguration(
final String name,
@ -97,7 +99,8 @@ public class BesuNodeConfiguration {
final List<String> runCommand,
final Optional<KeyPair> keyPair,
final Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration,
final boolean strictTxReplayProtectionEnabled) {
final boolean strictTxReplayProtectionEnabled,
final Map<String, String> environment) {
this.name = name;
this.miningParameters = miningParameters;
this.jsonRpcConfiguration = jsonRpcConfiguration;
@ -129,6 +132,7 @@ public class BesuNodeConfiguration {
this.keyPair = keyPair;
this.pkiKeyStoreConfiguration = pkiKeyStoreConfiguration;
this.strictTxReplayProtectionEnabled = strictTxReplayProtectionEnabled;
this.environment = environment;
}
public String getName() {
@ -254,4 +258,8 @@ public class BesuNodeConfiguration {
public boolean isStrictTxReplayProtectionEnabled() {
return strictTxReplayProtectionEnabled;
}
public Map<String, String> getEnvironment() {
return environment;
}
}

@ -44,7 +44,9 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class BesuNodeConfigurationBuilder {
@ -85,6 +87,7 @@ public class BesuNodeConfigurationBuilder {
private Optional<KeyPair> keyPair = Optional.empty();
private Optional<PkiKeyStoreConfiguration> pkiKeyStoreConfiguration = Optional.empty();
private Boolean strictTxReplayProtectionEnabled = false;
private Map<String, String> environment = new HashMap<>();
public BesuNodeConfigurationBuilder() {
// Check connections more frequently during acceptance tests to cut down on
@ -478,6 +481,11 @@ public class BesuNodeConfigurationBuilder {
return this;
}
public BesuNodeConfigurationBuilder environment(final Map<String, String> environment) {
this.environment = environment;
return this;
}
public BesuNodeConfiguration build() {
return new BesuNodeConfiguration(
name,
@ -510,6 +518,7 @@ public class BesuNodeConfigurationBuilder {
runCommand,
keyPair,
pkiKeyStoreConfiguration,
strictTxReplayProtectionEnabled);
strictTxReplayProtectionEnabled,
environment);
}
}

@ -88,7 +88,8 @@ public class BesuNodeFactory {
config.getRunCommand(),
config.getKeyPair(),
config.getPkiKeyStoreConfiguration(),
config.isStrictTxReplayProtectionEnabled());
config.isStrictTxReplayProtectionEnabled(),
config.getEnvironment());
}
public BesuNode createMinerNode(

@ -18,6 +18,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.Gene
import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public interface NodeConfiguration {
@ -59,4 +60,6 @@ public interface NodeConfiguration {
boolean isRevertReasonEnabled();
List<String> getStaticNodes();
Map<String, String> getEnvironment();
}

@ -127,7 +127,8 @@ public class PrivacyNode implements AutoCloseable {
List.of(),
Optional.empty(),
Optional.empty(),
besuConfig.isStrictTxReplayProtectionEnabled());
besuConfig.isStrictTxReplayProtectionEnabled(),
besuConfig.getEnvironment());
}
public void testEnclaveConnection(final List<PrivacyNode> otherNodes) {

@ -46,15 +46,16 @@ dependencies {
testImplementation 'com.github.tomakehurst:wiremock-jre8-standalone'
testImplementation 'commons-io:commons-io'
testImplementation 'io.grpc:grpc-all'
testImplementation 'io.grpc:grpc-core'
testImplementation 'io.grpc:grpc-netty'
testImplementation 'io.grpc:grpc-stub'
testImplementation 'io.jaegertracing:jaeger-client'
testImplementation 'io.jaegertracing:jaeger-proto'
testImplementation 'io.opentelemetry:opentelemetry-extension-trace-propagators'
testImplementation 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0'
testImplementation 'io.netty:netty-all'
testImplementation 'io.opentelemetry.proto:opentelemetry-proto'
testImplementation 'io.opentelemetry:opentelemetry-api'
testImplementation 'io.opentelemetry:opentelemetry-exporter-otlp'
testImplementation 'io.opentelemetry.proto:opentelemetry-proto'
testImplementation 'io.opentelemetry:opentelemetry-sdk'
testImplementation 'io.opentelemetry:opentelemetry-sdk-trace'
testImplementation 'io.opentracing.contrib:opentracing-okhttp3'

@ -26,7 +26,9 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.BesuNodeConf
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.io.Closer;
import com.google.protobuf.ByteString;
@ -34,7 +36,11 @@ import io.grpc.Server;
import io.grpc.Status;
import io.grpc.netty.NettyServerBuilder;
import io.grpc.stub.StreamObserver;
import io.jaegertracing.Configuration;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.context.propagation.ContextPropagators;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.extension.trace.propagation.B3Propagator;
import io.opentelemetry.instrumentation.okhttp.v3_0.OkHttpTelemetry;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceResponse;
import io.opentelemetry.proto.collector.metrics.v1.MetricsServiceGrpc;
@ -44,8 +50,9 @@ import io.opentelemetry.proto.collector.trace.v1.TraceServiceGrpc;
import io.opentelemetry.proto.metrics.v1.ResourceMetrics;
import io.opentelemetry.proto.trace.v1.ResourceSpans;
import io.opentelemetry.proto.trace.v1.Span;
import io.opentracing.Tracer;
import io.opentracing.contrib.okhttp3.TracingCallFactory;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import okhttp3.Call;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
@ -120,6 +127,7 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
@Before
public void setUp() throws Exception {
System.setProperty("root.log.level", "DEBUG");
Server server =
NettyServerBuilder.forPort(4317)
.addService(fakeTracesCollector)
@ -131,16 +139,26 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
MetricsConfiguration configuration =
MetricsConfiguration.builder()
.protocol(MetricsProtocol.OPENTELEMETRY)
.enabled(true)
.pushEnabled(true)
.port(0)
.hostsAllowlist(singletonList("*"))
.build();
Map<String, String> env = new HashMap<>();
env.put("OTEL_METRIC_EXPORT_INTERVAL", "1000");
env.put("OTEL_TRACES_SAMPLER", "always_on");
env.put("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317");
env.put("OTEL_EXPORTER_OTLP_INSECURE", "true");
env.put("OTEL_EXPORTER_OTLP_PROTOCOL", "grpc");
env.put("OTEL_BSP_SCHEDULE_DELAY", "1000");
env.put("OTEL_BSP_EXPORT_TIMEOUT", "3000");
metricsNode =
besu.create(
new BesuNodeConfigurationBuilder()
.name("metrics-node")
.jsonRpcEnabled()
.metricsConfiguration(configuration)
.environment(env)
.build());
cluster.start(metricsNode);
}
@ -170,11 +188,11 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
net.netVersion().verify(metricsNode);
List<ResourceSpans> spans = fakeTracesCollector.getReceivedSpans();
assertThat(spans.isEmpty()).isFalse();
Span internalSpan = spans.get(0).getInstrumentationLibrarySpans(0).getSpans(0);
Span internalSpan = spans.get(0).getScopeSpans(0).getSpans(0);
assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL);
ByteString parent = internalSpan.getParentSpanId();
assertThat(parent.isEmpty()).isFalse();
Span serverSpan = spans.get(0).getInstrumentationLibrarySpans(0).getSpans(1);
Span serverSpan = spans.get(0).getScopeSpans(0).getSpans(1);
assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER);
ByteString rootSpanId = serverSpan.getParentSpanId();
assertThat(rootSpanId.isEmpty()).isTrue();
@ -184,23 +202,26 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
@Test
public void traceReportingWithTraceId() {
Duration timeout = Duration.ofSeconds(1);
OkHttpClient okClient =
new OkHttpClient.Builder()
.connectTimeout(timeout)
.readTimeout(timeout)
.writeTimeout(timeout)
.build();
WaitUtils.waitFor(
30,
() -> {
// call the json RPC endpoint to generate a trace - with trace metadata of our own
Configuration config =
new Configuration("okhttp")
.withSampler(
Configuration.SamplerConfiguration.fromEnv().withType("const").withParam(1));
Tracer tracer = config.getTracer();
Call.Factory client = new TracingCallFactory(okClient, tracer);
OpenTelemetry openTelemetry =
OpenTelemetrySdk.builder()
.setPropagators(
ContextPropagators.create(
TextMapPropagator.composite(B3Propagator.injectingSingleHeader())))
.setTracerProvider(
SdkTracerProvider.builder().setSampler(Sampler.alwaysOn()).build())
.build();
Call.Factory client =
OkHttpTelemetry.builder(openTelemetry)
.build()
.newCallFactory(
new OkHttpClient.Builder()
.connectTimeout(timeout)
.readTimeout(timeout)
.writeTimeout(timeout)
.build());
Request request =
new Request.Builder()
.url("http://localhost:" + metricsNode.getJsonRpcPort().get())
@ -210,19 +231,22 @@ public class OpenTelemetryAcceptanceTest extends AcceptanceTestBase {
MediaType.get("application/json")))
.build();
Response response = client.newCall(request).execute();
assertThat(response.code()).isEqualTo(200);
response.close();
List<ResourceSpans> spans = new ArrayList<>(fakeTracesCollector.getReceivedSpans());
fakeTracesCollector.getReceivedSpans().clear();
assertThat(spans.isEmpty()).isFalse();
Span internalSpan = spans.get(0).getInstrumentationLibrarySpans(0).getSpans(0);
assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL);
ByteString parent = internalSpan.getParentSpanId();
assertThat(parent.isEmpty()).isFalse();
Span serverSpan = spans.get(0).getInstrumentationLibrarySpans(0).getSpans(1);
assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER);
ByteString rootSpanId = serverSpan.getParentSpanId();
assertThat(rootSpanId.isEmpty()).isFalse();
try {
assertThat(response.code()).isEqualTo(200);
List<ResourceSpans> spans = new ArrayList<>(fakeTracesCollector.getReceivedSpans());
assertThat(spans.isEmpty()).isFalse();
Span internalSpan = spans.get(0).getScopeSpans(0).getSpans(0);
assertThat(internalSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_INTERNAL);
ByteString parent = internalSpan.getParentSpanId();
assertThat(parent.isEmpty()).isFalse();
Span serverSpan = spans.get(0).getScopeSpans(0).getSpans(1);
assertThat(serverSpan.getKind()).isEqualTo(Span.SpanKind.SPAN_KIND_SERVER);
ByteString rootSpanId = serverSpan.getParentSpanId();
assertThat(rootSpanId.isEmpty()).isFalse();
} finally {
response.close();
fakeTracesCollector.getReceivedSpans().clear();
}
});
}
}

@ -60,14 +60,12 @@ dependencyManagement {
dependency 'info.picocli:picocli:4.6.3'
dependencySet(group: 'io.grpc', version: '1.47.0') {
entry'grpc-core'
entry'grpc-netty'
entry'grpc-stub'
entry 'grpc-all'
entry 'grpc-core'
entry 'grpc-netty'
entry 'grpc-stub'
}
dependency 'io.jaegertracing:jaeger-client:1.8.0'
dependency 'io.jaegertracing:jaeger-proto:0.7.0'
dependency 'io.kubernetes:client-java:15.0.1'
dependency 'io.netty:netty-all:4.1.78.Final'
@ -76,14 +74,17 @@ dependencyManagement {
dependency group: 'io.netty', name: 'netty-transport-native-kqueue', version:'4.1.78.Final', classifier: 'osx-x86_64'
dependency 'io.netty:netty-transport-native-unix-common:4.1.78.Final'
dependency 'io.opentelemetry:opentelemetry-api:1.6.0'
dependency 'io.opentelemetry:opentelemetry-exporter-otlp-metrics:1.6.0-alpha'
dependency 'io.opentelemetry:opentelemetry-exporter-otlp:1.6.0'
dependency 'io.opentelemetry:opentelemetry-extension-trace-propagators:1.6.0'
dependency 'io.opentelemetry:opentelemetry-sdk-trace:1.6.0'
dependency 'io.opentelemetry:opentelemetry-sdk:1.6.0'
dependency 'io.opentelemetry:opentelemetry-semconv:1.6.0-alpha'
dependency 'io.opentelemetry.proto:opentelemetry-proto:0.13.0-alpha'
dependency 'io.opentelemetry:opentelemetry-api:1.15.0'
dependency 'io.opentelemetry:opentelemetry-exporter-otlp-metrics:1.14.0'
dependency 'io.opentelemetry:opentelemetry-exporter-otlp:1.15.0'
dependency 'io.opentelemetry:opentelemetry-extension-trace-propagators:1.15.0'
dependency 'io.opentelemetry.proto:opentelemetry-proto:0.16.0-alpha'
dependency 'io.opentelemetry:opentelemetry-sdk-metrics:1.15.0'
dependency 'io.opentelemetry:opentelemetry-sdk-trace:1.15.0'
dependency 'io.opentelemetry:opentelemetry-sdk:1.15.0'
dependency 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.15.0-alpha'
dependency 'io.opentelemetry:opentelemetry-semconv:1.15.0-alpha'
dependency 'io.opentelemetry.instrumentation:opentelemetry-okhttp-3.0:1.15.0-alpha'
dependency 'io.opentracing.contrib:opentracing-okhttp3:3.0.0'
dependency 'io.opentracing:opentracing-api:0.33.0'

@ -41,7 +41,6 @@ dependencies {
implementation 'com.google.guava:guava'
implementation 'io.grpc:grpc-netty'
implementation 'io.grpc:grpc-core'
implementation 'io.jaegertracing:jaeger-proto'
implementation 'io.netty:netty-tcnative-boringssl-static'
implementation 'io.netty:netty-transport-native-epoll'
implementation 'io.netty:netty-all'
@ -49,9 +48,10 @@ dependencies {
implementation 'io.opentelemetry:opentelemetry-sdk'
implementation 'io.opentelemetry:opentelemetry-semconv'
implementation 'io.opentelemetry:opentelemetry-sdk-trace'
implementation 'io.opentelemetry:opentelemetry-sdk-metrics'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp'
implementation 'io.opentelemetry:opentelemetry-exporter-otlp-metrics'
implementation 'io.opentelemetry.proto:opentelemetry-proto'
implementation 'io.opentelemetry:opentelemetry-sdk-extension-autoconfigure'
implementation 'io.prometheus:simpleclient'
implementation 'io.prometheus:simpleclient_common'

@ -14,8 +14,7 @@
*/
package org.hyperledger.besu.metrics;
import org.hyperledger.besu.metrics.opentelemetry.MetricsOtelGrpcPushService;
import org.hyperledger.besu.metrics.opentelemetry.OpenTelemetrySystem;
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;
@ -49,8 +48,7 @@ public interface MetricsService {
}
} else if (configuration.getProtocol() == MetricsProtocol.OPENTELEMETRY) {
if (configuration.isEnabled()) {
return Optional.of(
new MetricsOtelGrpcPushService(configuration, (OpenTelemetrySystem) metricsSystem));
return Optional.of(new MetricsOtelPushService());
} else {
return Optional.empty();
}

@ -54,7 +54,8 @@ public class MetricsSystemFactory {
new OpenTelemetrySystem(
metricsConfiguration.getMetricCategories(),
metricsConfiguration.isTimersEnabled(),
metricsConfiguration.getPrometheusJob());
metricsConfiguration.getPrometheusJob(),
true);
metricsSystem.initDefaults();
return metricsSystem;
} else {

@ -0,0 +1,57 @@
/*
* Copyright Besu Contributors
*
* 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 java.util.Collection;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.CollectionRegistration;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import io.opentelemetry.sdk.metrics.internal.export.MetricProducer;
import org.jetbrains.annotations.NotNull;
class DebugMetricReader implements MetricReader {
private CollectionRegistration registration;
public DebugMetricReader() {}
public Collection<MetricData> getAllMetrics() {
return MetricProducer.asMetricProducer(this.registration).collectAllMetrics();
}
@Override
public void register(final @NotNull CollectionRegistration registration) {
this.registration = registration;
}
@Override
public CompletableResultCode forceFlush() {
return CompletableResultCode.ofSuccess();
}
@Override
public CompletableResultCode shutdown() {
return CompletableResultCode.ofSuccess();
}
@Override
public AggregationTemporality getAggregationTemporality(
final @NotNull InstrumentType instrumentType) {
return AggregationTemporality.CUMULATIVE;
}
}

@ -1,88 +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.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.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReader;
import io.opentelemetry.sdk.metrics.export.IntervalMetricReaderBuilder;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SpanProcessor;
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MetricsOtelGrpcPushService implements MetricsService {
private static final Logger LOG = LoggerFactory.getLogger(MetricsOtelGrpcPushService.class);
private final MetricsConfiguration configuration;
private final OpenTelemetrySystem metricsSystem;
private IntervalMetricReader periodicReader;
private SpanProcessor spanProcessor;
public MetricsOtelGrpcPushService(
final MetricsConfiguration configuration, final OpenTelemetrySystem metricsSystem) {
this.configuration = configuration;
this.metricsSystem = metricsSystem;
}
@Override
public CompletableFuture<?> start() {
LOG.info("Starting OpenTelemetry push service");
OtlpGrpcMetricExporter exporter = OtlpGrpcMetricExporter.getDefault();
IntervalMetricReaderBuilder builder =
IntervalMetricReader.builder()
.setExportIntervalMillis(configuration.getPushInterval() * 1000L)
.setMetricProducers(Collections.singleton(metricsSystem.getMeterSdkProvider()))
.setMetricExporter(exporter);
this.periodicReader = builder.buildAndStart();
this.spanProcessor = BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder().build()).build();
OpenTelemetrySdk.builder()
.setTracerProvider(SdkTracerProvider.builder().addSpanProcessor(spanProcessor).build())
.buildAndRegisterGlobal();
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<?> stop() {
if (periodicReader != null) {
periodicReader.shutdown();
}
if (spanProcessor != null) {
CompletableResultCode result = spanProcessor.shutdown();
CompletableFuture<?> future = new CompletableFuture<>();
result.whenComplete(() -> future.complete(null));
return future;
}
return CompletableFuture.completedFuture(null);
}
@Override
public Optional<Integer> getPort() {
return Optional.empty();
}
}

@ -0,0 +1,48 @@
/*
* Copyright Hyperledger Besu Contributors
*
* 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 java.util.Optional;
import java.util.concurrent.CompletableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MetricsOtelPushService implements MetricsService {
private static final Logger LOG = LoggerFactory.getLogger(MetricsOtelPushService.class);
public MetricsOtelPushService() {}
@Override
public CompletableFuture<?> start() {
LOG.info("Starting OpenTelemetry push service");
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture<?> stop() {
return CompletableFuture.completedFuture(null);
}
@Override
public Optional<Integer> getPort() {
return Optional.empty();
}
}

@ -19,7 +19,6 @@ import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.BoundLongCounter;
import io.opentelemetry.api.metrics.LongCounter;
public class OpenTelemetryCounter implements LabelledMetric<Counter> {
@ -39,25 +38,26 @@ public class OpenTelemetryCounter implements LabelledMetric<Counter> {
builder.put(labelNames[i], labelValues[i]);
}
final Attributes labels = builder.build();
BoundLongCounter boundLongCounter = counter.bind(labels);
return new OpenTelemetryCounter.UnlabelledCounter(boundLongCounter);
return new BoundLongCounter(counter, labels);
}
private static class UnlabelledCounter implements Counter {
private final BoundLongCounter counter;
private static class BoundLongCounter implements Counter {
private final LongCounter counter;
private final Attributes labels;
private UnlabelledCounter(final BoundLongCounter counter) {
private BoundLongCounter(final LongCounter counter, final Attributes labels) {
this.counter = counter;
this.labels = labels;
}
@Override
public void inc() {
counter.add(1);
counter.add(1, labels);
}
@Override
public void inc(final long amount) {
counter.add(amount);
counter.add(amount, labels);
}
}
}

@ -64,6 +64,6 @@ public class OpenTelemetryGauge implements LabelledGauge {
private void updater(final ObservableDoubleMeasurement measurement) {
observationsMap.forEach(
(labels, valueSupplier) -> measurement.observe(valueSupplier.getAsDouble(), labels));
(labels, valueSupplier) -> measurement.record(valueSupplier.getAsDouble(), labels));
}
}

@ -45,14 +45,16 @@ import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.data.DoubleHistogramPointData;
import io.opentelemetry.sdk.metrics.data.DoublePointData;
import io.opentelemetry.sdk.metrics.data.DoubleSummaryPointData;
import io.opentelemetry.sdk.metrics.data.HistogramPointData;
import io.opentelemetry.sdk.metrics.data.LongPointData;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.data.MetricDataType;
import io.opentelemetry.sdk.metrics.data.PointData;
import io.opentelemetry.sdk.metrics.data.SummaryPointData;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes;
import org.slf4j.Logger;
@ -77,25 +79,33 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
private final Map<String, LabelledMetric<Counter>> cachedCounters = new ConcurrentHashMap<>();
private final Map<String, LabelledMetric<OperationTimer>> cachedTimers =
new ConcurrentHashMap<>();
private final SdkMeterProvider meterSdkProvider;
private final SdkMeterProvider sdkMeterProvider;
private final DebugMetricReader debugMetricReader;
public OpenTelemetrySystem(
final Set<MetricCategory> enabledCategories,
final boolean timersEnabled,
final String jobName) {
final String jobName,
final boolean setAsGlobal) {
LOG.info("Starting OpenTelemetry metrics system");
this.enabledCategories = ImmutableSet.copyOf(enabledCategories);
this.timersEnabled = timersEnabled;
this.debugMetricReader = new DebugMetricReader();
Resource resource =
Resource.getDefault()
.merge(
Resource.create(
Attributes.builder().put(ResourceAttributes.SERVICE_NAME, jobName).build()));
this.meterSdkProvider = SdkMeterProvider.builder().setResource(resource).build();
}
SdkMeterProvider getMeterSdkProvider() {
return meterSdkProvider;
AutoConfiguredOpenTelemetrySdk autoSdk =
AutoConfiguredOpenTelemetrySdk.builder()
.addMeterProviderCustomizer(
(provider, config) ->
provider.setResource(resource).registerMetricReader(debugMetricReader))
.addTracerProviderCustomizer((provider, config) -> provider.setResource(resource))
.setResultAsGlobal(setAsGlobal)
.build();
OpenTelemetrySdk sdk = autoSdk.getOpenTelemetrySdk();
this.sdkMeterProvider = sdk.getSdkMeterProvider();
}
@Override
@ -105,14 +115,17 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
@Override
public Stream<Observation> streamObservations() {
Collection<MetricData> metricsList = meterSdkProvider.collectAllMetrics();
return metricsList.stream().map(this::convertToObservations).flatMap(stream -> stream);
Collection<MetricData> metricsList = this.debugMetricReader.getAllMetrics();
return metricsList.stream().flatMap(this::convertToObservations);
}
private Stream<Observation> convertToObservations(final MetricData metricData) {
List<Observation> observations = new ArrayList<>();
MetricCategory category =
categoryNameToMetricCategory(metricData.getInstrumentationLibraryInfo().getName());
categoryNameToMetricCategory(metricData.getInstrumentationScopeInfo().getName());
if (category == null) {
return Stream.empty();
}
Collection<?> points;
switch (metricData.getType()) {
case DOUBLE_GAUGE:
@ -122,13 +135,13 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
points = metricData.getDoubleSumData().getPoints();
break;
case SUMMARY:
points = metricData.getDoubleSummaryData().getPoints();
points = metricData.getData().getPoints();
break;
case LONG_SUM:
points = metricData.getLongSumData().getPoints();
break;
case HISTOGRAM:
points = metricData.getDoubleHistogramData().getPoints();
points = metricData.getData().getPoints();
break;
case LONG_GAUGE:
points = metricData.getLongGaugeData().getPoints();
@ -159,7 +172,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
return category;
}
}
throw new IllegalArgumentException("Invalid metric category: " + name);
return null;
}
private Object extractValue(final MetricDataType type, final PointData point) {
@ -170,9 +183,9 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
case DOUBLE_GAUGE:
return ((DoublePointData) point).getValue();
case SUMMARY:
return ((DoubleSummaryPointData) point).getPercentileValues();
return ((SummaryPointData) point).getValues();
case HISTOGRAM:
return ((DoubleHistogramPointData) point).getCounts();
return ((HistogramPointData) point).getCounts();
default:
throw new UnsupportedOperationException("Unsupported type " + type);
}
@ -189,7 +202,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
name,
(k) -> {
if (isCategoryEnabled(category)) {
final Meter meter = meterSdkProvider.get(category.getName());
final Meter meter = sdkMeterProvider.get(category.getName());
final LongCounter counter = meter.counterBuilder(name).setDescription(help).build();
return new OpenTelemetryCounter(counter, labelNames);
@ -210,7 +223,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
name,
(k) -> {
if (timersEnabled && isCategoryEnabled(category)) {
final Meter meter = meterSdkProvider.get(category.getName());
final Meter meter = sdkMeterProvider.get(category.getName());
return new OpenTelemetryTimer(name, help, meter, labelNames);
} else {
return NoOpMetricsSystem.getOperationTimerLabelledMetric(labelNames.length);
@ -226,11 +239,11 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
final DoubleSupplier valueSupplier) {
LOG.trace("Creating a gauge {}", name);
if (isCategoryEnabled(category)) {
final Meter meter = meterSdkProvider.get(category.getName());
final Meter meter = sdkMeterProvider.get(category.getName());
meter
.gaugeBuilder(name)
.setDescription(help)
.buildWithCallback(res -> res.observe(valueSupplier.getAsDouble(), Attributes.empty()));
.buildWithCallback(res -> res.record(valueSupplier.getAsDouble(), Attributes.empty()));
}
}
@ -243,7 +256,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
LOG.trace("Creating a labelled gauge {}", name);
if (isCategoryEnabled(category)) {
return new OpenTelemetryGauge(
name, help, meterSdkProvider.get(category.getName()), List.of(labelNames));
name, help, sdkMeterProvider.get(category.getName()), List.of(labelNames));
}
return NoOpMetricsSystem.getLabelledGauge(labelNames.length);
}
@ -264,7 +277,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
ManagementFactory.getGarbageCollectorMXBeans();
final MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
final List<MemoryPoolMXBean> poolBeans = ManagementFactory.getMemoryPoolMXBeans();
final Meter meter = meterSdkProvider.get(StandardMetricCategory.JVM.getName());
final Meter meter = sdkMeterProvider.get(StandardMetricCategory.JVM.getName());
final List<Attributes> labelSets = new ArrayList<>(garbageCollectors.size());
for (final GarbageCollectorMXBean gc : garbageCollectors) {
labelSets.add(Attributes.of(AttributeKey.stringKey("gc"), gc.getName()));
@ -276,7 +289,7 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
.buildWithCallback(
resultLongObserver -> {
for (int i = 0; i < garbageCollectors.size(); i++) {
resultLongObserver.observe(
resultLongObserver.record(
(double) garbageCollectors.get(i).getCollectionTime(), labelSets.get(i));
}
});
@ -297,12 +310,12 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
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);
resultLongObserver.record(heapUsage.getUsed(), usedHeap);
resultLongObserver.record(nonHeapUsage.getUsed(), usedNonHeap);
resultLongObserver.record(heapUsage.getUsed(), committedHeap);
resultLongObserver.record(nonHeapUsage.getUsed(), committedNonHeap);
resultLongObserver.record(heapUsage.getUsed(), maxHeap);
resultLongObserver.record(nonHeapUsage.getUsed(), maxNonHeap);
});
final List<Attributes> usedLabelSets = new ArrayList<>(poolBeans.size());
final List<Attributes> committedLabelSets = new ArrayList<>(poolBeans.size());
@ -322,11 +335,11 @@ public class OpenTelemetrySystem implements ObservableMetricsSystem {
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));
resultLongObserver.record(poolUsage.getUsed(), usedLabelSets.get(i));
resultLongObserver.record(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));
resultLongObserver.record(poolUsage.getMax(), maxLabelSets.get(i));
}
});
}

@ -51,7 +51,7 @@ public class OpenTelemetryTimer implements LabelledMetric<OperationTimer> {
meter
.gaugeBuilder(metricName)
.setDescription(help)
.buildWithCallback((measurement) -> measurement.observe((double) elapsed, labels));
.buildWithCallback((measurement) -> measurement.record((double) elapsed, labels));
return elapsed / 1e9;
};
};

@ -38,6 +38,8 @@ import org.hyperledger.besu.plugin.services.metrics.OperationTimer;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
@ -50,10 +52,23 @@ public class OpenTelemetryMetricsSystemTest {
.thenComparing((o1, o2) -> o1.getLabels().equals(o2.getLabels()) ? 0 : 1);
private final ObservableMetricsSystem metricsSystem =
new OpenTelemetrySystem(DEFAULT_METRIC_CATEGORIES, true, "job");
new OpenTelemetrySystem(DEFAULT_METRIC_CATEGORIES, true, "job", false);
private List<Observation> getObservation(final ObservableMetricsSystem metricsSystem)
throws InterruptedException {
for (int i = 0; i < 20; i++) {
Stream<Observation> observations = metricsSystem.streamObservations();
List<Observation> result = observations.collect(Collectors.toList());
if (!result.isEmpty()) {
return result;
}
Thread.sleep(100);
}
return null;
}
@Test
public void shouldCreateObservationFromCounter() {
public void shouldCreateObservationFromCounter() throws InterruptedException {
final Counter counter = metricsSystem.createCounter(PEERS, "connected", "Some help string");
counter.inc();
@ -61,12 +76,12 @@ public class OpenTelemetryMetricsSystemTest {
.containsExactly(new Observation(PEERS, "connected", 1L, emptyList()));
counter.inc();
assertThat(metricsSystem.streamObservations())
assertThat(getObservation(metricsSystem))
.containsExactly(new Observation(PEERS, "connected", 2L, emptyList()));
}
@Test
public void shouldHandleDuplicateCounterCreation() {
public void shouldHandleDuplicateCounterCreation() throws InterruptedException {
final LabelledMetric<Counter> counter1 =
metricsSystem.createLabelledCounter(PEERS, "connected", "Some help string");
final LabelledMetric<Counter> counter2 =
@ -78,7 +93,7 @@ public class OpenTelemetryMetricsSystemTest {
.containsExactly(new Observation(PEERS, "connected", 1L, emptyList()));
counter2.labels().inc();
assertThat(metricsSystem.streamObservations())
assertThat(getObservation(metricsSystem))
.containsExactly(new Observation(PEERS, "connected", 2L, emptyList()));
}
@ -98,7 +113,7 @@ public class OpenTelemetryMetricsSystemTest {
}
@Test
public void shouldIncrementCounterBySpecifiedAmount() {
public void shouldIncrementCounterBySpecifiedAmount() throws InterruptedException {
final Counter counter = metricsSystem.createCounter(PEERS, "connected", "Some help string");
counter.inc(5);
@ -106,7 +121,7 @@ public class OpenTelemetryMetricsSystemTest {
.containsExactly(new Observation(PEERS, "connected", 5L, emptyList()));
counter.inc(6);
assertThat(metricsSystem.streamObservations())
assertThat(getObservation(metricsSystem))
.containsExactly(new Observation(PEERS, "connected", 11L, emptyList()));
}
@ -153,7 +168,7 @@ public class OpenTelemetryMetricsSystemTest {
@Test
public void shouldNotCreateObservationsFromTimerWhenTimersDisabled() {
final ObservableMetricsSystem metricsSystem =
new OpenTelemetrySystem(DEFAULT_METRIC_CATEGORIES, false, "job");
new OpenTelemetrySystem(DEFAULT_METRIC_CATEGORIES, false, "job", false);
final LabelledMetric<OperationTimer> timer =
metricsSystem.createLabelledTimer(RPC, "request", "Some help", "methodName");
@ -171,8 +186,13 @@ public class OpenTelemetryMetricsSystemTest {
.enabled(true)
.protocol(OPENTELEMETRY)
.build();
final ObservableMetricsSystem localMetricSystem =
MetricsSystemFactory.create(metricsConfiguration);
final OpenTelemetrySystem localMetricSystem =
new OpenTelemetrySystem(
metricsConfiguration.getMetricCategories(),
metricsConfiguration.isTimersEnabled(),
metricsConfiguration.getPrometheusJob(),
false);
localMetricSystem.initDefaults();
localMetricSystem.createGauge(RPC, "myValue", "Help", () -> 7.0);
assertThat(localMetricSystem.streamObservations())
@ -195,15 +215,20 @@ public class OpenTelemetryMetricsSystemTest {
}
@Test
public void shouldOnlyObserveEnabledMetrics() {
public void shouldOnlyObserveEnabledMetrics() throws InterruptedException {
final MetricsConfiguration metricsConfiguration =
MetricsConfiguration.builder()
.metricCategories(ImmutableSet.of(BesuMetricCategory.RPC))
.enabled(true)
.protocol(OPENTELEMETRY)
.build();
final ObservableMetricsSystem localMetricSystem =
MetricsSystemFactory.create(metricsConfiguration);
final OpenTelemetrySystem localMetricSystem =
new OpenTelemetrySystem(
metricsConfiguration.getMetricCategories(),
metricsConfiguration.isTimersEnabled(),
metricsConfiguration.getPrometheusJob(),
false);
localMetricSystem.initDefaults();
// do a category we are not watching
final LabelledMetric<Counter> counterN =
@ -219,7 +244,7 @@ public class OpenTelemetryMetricsSystemTest {
assertThat(counterR).isNotSameAs(NoOpMetricsSystem.NO_OP_LABELLED_1_COUNTER);
counterR.labels("op").inc();
assertThat(localMetricSystem.streamObservations())
assertThat(getObservation(localMetricSystem))
.containsExactly(new Observation(RPC, "name", (long) 1, singletonList("op")));
}
@ -248,17 +273,4 @@ public class OpenTelemetryMetricsSystemTest {
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);
}
}

Loading…
Cancel
Save