Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stabilize MetricProducer, allow custom MetricReaders #5835

Merged
merged 4 commits into from
Sep 27, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,12 @@
Comparing source compatibility of against
No changes.
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.CollectionRegistration (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) java.util.Collection<io.opentelemetry.sdk.metrics.data.MetricData> collectAllMetrics()
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.export.CollectionRegistration noop()
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.export.MetricProducer (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.util.Collection<io.opentelemetry.sdk.metrics.data.MetricData> produce(io.opentelemetry.sdk.resources.Resource)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder registerMetricProducer(io.opentelemetry.sdk.metrics.export.MetricProducer)
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
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 java.io.IOException;
import java.io.OutputStream;
import java.io.UncheckedIOException;
Expand Down Expand Up @@ -60,7 +59,7 @@ public final class PrometheusHttpServer implements MetricReader {

private final HttpServer server;
private final ExecutorService executor;
private volatile MetricProducer metricProducer = MetricProducer.noop();
private volatile CollectionRegistration collectionRegistration = CollectionRegistration.noop();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it currently possible to use the OC bridge with the Prometheus exporter?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure - the OC metrics will go to any registered MetricReader. Built in MetricReaders include PrometheusHttpServer, PeriodicMetricReader (to support any push based MetricExporter), and InMemoryMetricReader for testing.


/**
* Returns a new {@link PrometheusHttpServer} which can be registered to an {@link
Expand All @@ -83,7 +82,7 @@ public static PrometheusHttpServerBuilder builder() {
throw new UncheckedIOException("Could not create Prometheus HTTP server", e);
}
MetricsHandler metricsHandler =
new MetricsHandler(() -> getMetricProducer().collectAllMetrics());
new MetricsHandler(() -> collectionRegistration.collectAllMetrics());
server.createContext("/", metricsHandler);
server.createContext("/metrics", metricsHandler);
server.createContext("/-/healthy", HealthHandler.INSTANCE);
Expand All @@ -110,10 +109,6 @@ private static HttpServer createServer(String host, int port) throws IOException
throw exception;
}

private MetricProducer getMetricProducer() {
return metricProducer;
}

private void start() {
// server.start must be called from a daemon thread for it to be a daemon.
if (Thread.currentThread().isDaemon()) {
Expand All @@ -131,13 +126,13 @@ private void start() {
}

@Override
public void register(CollectionRegistration registration) {
this.metricProducer = MetricProducer.asMetricProducer(registration);
public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) {
return AggregationTemporality.CUMULATIVE;
}

@Override
public AggregationTemporality getAggregationTemporality(InstrumentType instrumentType) {
return AggregationTemporality.CUMULATIVE;
public void register(CollectionRegistration registration) {
this.collectionRegistration = registration;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,18 @@
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
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.internal.data.ImmutableDoublePointData;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableGaugeData;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableLongPointData;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableMetricData;
import io.opentelemetry.sdk.metrics.internal.data.ImmutableSumData;
import io.opentelemetry.sdk.metrics.internal.export.MetricProducer;
import io.opentelemetry.sdk.resources.Resource;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executors;
Expand All @@ -56,7 +57,6 @@

class PrometheusHttpServerTest {
private static final AtomicReference<List<MetricData>> metricData = new AtomicReference<>();
private static final MetricProducer metricProducer = metricData::get;

static PrometheusHttpServer prometheusServer;
static WebClient client;
Expand All @@ -68,7 +68,13 @@ class PrometheusHttpServerTest {
static void beforeAll() {
// Register the SDK metric producer with the prometheus reader.
prometheusServer = PrometheusHttpServer.builder().setHost("localhost").setPort(0).build();
prometheusServer.register(metricProducer);
prometheusServer.register(
new CollectionRegistration() {
@Override
public Collection<MetricData> collectAllMetrics() {
return metricData.get();
}
});

client =
WebClient.builder("http://localhost:" + prometheusServer.getAddress().getPort())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.opencensusshim;

import io.opencensus.metrics.Metrics;
import io.opencensus.metrics.export.MetricProducerManager;
import io.opentelemetry.opencensusshim.internal.metrics.MetricAdapter;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.data.MetricData;
import io.opentelemetry.sdk.metrics.export.MetricProducer;
import io.opentelemetry.sdk.metrics.export.MetricReader;
import io.opentelemetry.sdk.resources.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* {@link MetricProducer} for OpenCensus metrics, which allows {@link MetricReader}s to read from
* both OpenTelemetry and OpenCensus metrics.
*
* <p>To use, register with {@link SdkMeterProviderBuilder#registerMetricProducer(MetricProducer)}.
*/
public final class OpenCensusMetricProducer implements MetricProducer {
private final MetricProducerManager openCensusMetricStorage;

OpenCensusMetricProducer(MetricProducerManager openCensusMetricStorage) {
this.openCensusMetricStorage = openCensusMetricStorage;
}

/**
* Constructs a new {@link OpenCensusMetricProducer} that reports against the given {@link
* Resource}.
*/
public static MetricProducer create() {
return new OpenCensusMetricProducer(Metrics.getExportComponent().getMetricProducerManager());
}

@Override
public Collection<MetricData> produce(Resource resource) {
List<MetricData> result = new ArrayList<>();
openCensusMetricStorage
.getAllMetricProducer()
.forEach(
producer ->
producer
.getMetrics()
.forEach(metric -> result.add(MetricAdapter.convert(resource, metric))));
return result;
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
import io.opencensus.trace.TraceOptions;
import io.opencensus.trace.Tracestate;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.sdk.metrics.internal.export.MetricProducer;
import io.opentelemetry.opencensusshim.OpenCensusMetricProducer;
import io.opentelemetry.sdk.metrics.export.MetricProducer;
import io.opentelemetry.sdk.resources.Resource;
import java.time.Duration;
import java.util.Arrays;
Expand All @@ -31,8 +32,7 @@
import org.junit.jupiter.api.Test;

class OpenCensusMetricProducerTest {
private final MetricProducer openCensusMetrics =
OpenCensusMetricProducer.create(Resource.empty());
private final MetricProducer openCensusMetrics = OpenCensusMetricProducer.create();

private static final Measure.MeasureLong LATENCY_MS =
Measure.MeasureLong.create("task_latency", "The task latency in milliseconds", "ms");
Expand Down Expand Up @@ -69,7 +69,7 @@ void extractHistogram() throws InterruptedException {
.atMost(Duration.ofSeconds(10))
.untilAsserted(
() ->
assertThat(openCensusMetrics.collectAllMetrics())
assertThat(openCensusMetrics.produce(Resource.empty()))
.satisfiesExactly(
metric ->
assertThat(metric)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import io.opencensus.stats.Stats;
import io.opencensus.stats.StatsRecorder;
import io.opencensus.stats.View;
import io.opentelemetry.opencensusshim.OpenCensusMetricProducer;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import java.time.Duration;
Expand All @@ -26,7 +27,10 @@ class OpenCensusMetricsTest {
void capturesOpenCensusAndOtelMetrics() throws InterruptedException {
InMemoryMetricReader reader = InMemoryMetricReader.create();
SdkMeterProvider otelMetrics =
SdkMeterProvider.builder().registerMetricReader(OpenCensusMetrics.attachTo(reader)).build();
SdkMeterProvider.builder()
.registerMetricReader(reader)
.registerMetricProducer(OpenCensusMetricProducer.create())
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This demonstrates how you would register a custom metric producer with SdkMeterProvider. Nice and tidy.

All the MetricData produced flow to any registered MetricReaders.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great.

.build();
// Record an otel metric.
otelMetrics.meterBuilder("otel").build().counterBuilder("otel.sum").build().add(1);
// Record an OpenCensus metric.
Expand All @@ -47,7 +51,7 @@ void capturesOpenCensusAndOtelMetrics() throws InterruptedException {
.untilAsserted(
() ->
assertThat(reader.collectAllMetrics())
.satisfiesExactly(
.satisfiesExactlyInAnyOrder(
metric ->
assertThat(metric).hasName("otel.sum").hasLongSumSatisfying(sum -> {}),
metric ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ void stringRepresentation() {
+ "clock=SystemClock{}, "
+ "resource=Resource{schemaUrl=null, attributes={service.name=\"otel-test\"}}, "
+ "metricReaders=[PeriodicMetricReader{exporter=MockMetricExporter{}, intervalNanos=60000000000}], "
+ "metricProducers=[], "
+ "views=[RegisteredView{instrumentSelector=InstrumentSelector{instrumentName=instrument}, view=View{name=new-instrument, aggregation=DefaultAggregation, attributesProcessor=NoopAttributesProcessor{}, cardinalityLimit=2000}}]"
+ "}, "
+ "loggerProvider=SdkLoggerProvider{"
Expand Down
Loading