From 078d55abe58a8ae5b5fe85d3597c4f7fa45cd95d Mon Sep 17 00:00:00 2001 From: jack-berg <34418638+jack-berg@users.noreply.github.com> Date: Fri, 13 May 2022 11:52:04 -0500 Subject: [PATCH] Add SdkMeterProvider toString method (#4464) * Add SdkMeterProvider toString method * Fix build * Ignore null values in InstrumentSelector / View #toString() --- .../opentelemetry-sdk-metrics.txt | 13 +- .../prometheus/PrometheusHttpServer.java | 5 + .../prometheus/PrometheusHttpServerTest.java | 6 + .../opentelemetry/sdk/OpenTelemetrySdk.java | 9 +- .../sdk/OpenTelemetrySdkTest.java | 235 +++++++++++------- .../sdk/metrics/InstrumentSelector.java | 22 ++ .../sdk/metrics/SdkMeterProvider.java | 16 ++ .../io/opentelemetry/sdk/metrics/View.java | 15 ++ .../metrics/export/PeriodicMetricReader.java | 10 + .../state/MeterProviderSharedState.java | 4 +- .../internal/view/AttributesProcessor.java | 7 +- .../view/NoopAttributesProcessor.java | 31 +++ .../metrics/internal/view/RegisteredView.java | 10 + .../metrics/internal/view/ViewRegistry.java | 9 +- .../sdk/metrics/InstrumentSelectorTest.java | 23 ++ .../opentelemetry/sdk/metrics/ViewTest.java | 34 +++ .../export/PeriodicMetricReaderTest.java | 15 ++ .../internal/view/RegisteredViewTest.java | 44 ++++ .../exporter/InMemoryMetricReader.java | 5 + .../exporter/InMemoryMetricReaderTest.java | 6 + 20 files changed, 416 insertions(+), 103 deletions(-) create mode 100644 sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/NoopAttributesProcessor.java create mode 100644 sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java create mode 100644 sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredViewTest.java diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt index df26146497..3598f6e084 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt @@ -1,2 +1,13 @@ Comparing source compatibility of against -No changes. \ No newline at end of file +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.export.PeriodicMetricReader (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) java.lang.String toString() +*** MODIFIED CLASS: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.InstrumentSelector (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) java.lang.String toString() +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.SdkMeterProvider (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) java.lang.String toString() +*** MODIFIED CLASS: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.View (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) java.lang.String toString() diff --git a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java index bd3d5ed8fa..f828205e77 100644 --- a/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java +++ b/exporters/prometheus/src/main/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServer.java @@ -147,6 +147,11 @@ public final class PrometheusHttpServer implements Closeable, MetricReader { shutdown().join(10, TimeUnit.SECONDS); } + @Override + public String toString() { + return "PrometheusHttpServer{address=" + server.getAddress() + "}"; + } + // Visible for testing. InetSocketAddress getAddress() { return server.getAddress(); diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java index f231d89a93..2d487aeda3 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusHttpServerTest.java @@ -163,6 +163,12 @@ class PrometheusHttpServerTest { assertThat(response.contentUtf8()).isEqualTo("Exporter is Healthy."); } + @Test + void stringRepresentation() { + assertThat(prometheusServer.toString()) + .isEqualTo("PrometheusHttpServer{address=" + prometheusServer.getAddress() + "}"); + } + private static ImmutableList generateTestData() { return ImmutableList.of( ImmutableMetricData.createLongSum( diff --git a/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdk.java b/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdk.java index a915b2e207..a277eded17 100644 --- a/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdk.java +++ b/sdk/all/src/main/java/io/opentelemetry/sdk/OpenTelemetrySdk.java @@ -76,8 +76,13 @@ public final class OpenTelemetrySdk implements OpenTelemetry { @Override public String toString() { - // TODO(anuraaga): Add metrics / logs / propagators - return "OpenTelemetrySdk{" + "tracerProvider=" + tracerProvider.unobfuscate() + '}'; + // TODO(anuraaga): Add logs / propagators + return "OpenTelemetrySdk{" + + "tracerProvider=" + + tracerProvider.unobfuscate() + + ", meterProvider=" + + meterProvider.unobfuscate() + + "}"; } /** diff --git a/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java b/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java index bdf5432378..4de2d21915 100644 --- a/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java +++ b/sdk/all/src/test/java/io/opentelemetry/sdk/OpenTelemetrySdkTest.java @@ -14,13 +14,17 @@ import static org.mockito.Mockito.when; import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.common.Attributes; -import io.opentelemetry.api.trace.TracerProvider; import io.opentelemetry.context.propagation.ContextPropagators; import io.opentelemetry.context.propagation.TextMapPropagator; import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.logs.SdkLogEmitterProvider; +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.IdGenerator; import io.opentelemetry.sdk.trace.SdkTracerProvider; @@ -28,6 +32,7 @@ import io.opentelemetry.sdk.trace.SpanLimits; import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor; import io.opentelemetry.sdk.trace.export.SpanExporter; import io.opentelemetry.sdk.trace.samplers.Sampler; +import java.time.Duration; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -41,7 +46,6 @@ class OpenTelemetrySdkTest { @Mock private SdkMeterProvider meterProvider; @Mock private SdkLogEmitterProvider logEmitterProvider; @Mock private ContextPropagators propagators; - @Mock private Clock clock; @AfterEach void tearDown() { @@ -49,7 +53,7 @@ class OpenTelemetrySdkTest { } @Test - void testRegisterGlobal() { + void buildAndRegisterGlobal() { OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().setPropagators(propagators).buildAndRegisterGlobal(); assertThat(GlobalOpenTelemetry.get()).extracting("delegate").isSameAs(sdk); @@ -67,7 +71,7 @@ class OpenTelemetrySdkTest { } @Test - void castingGlobalToSdkFails() { + void buildAndRegisterGlobal_castingGlobalToSdkFails() { OpenTelemetrySdk.builder().buildAndRegisterGlobal(); assertThatThrownBy( @@ -79,40 +83,7 @@ class OpenTelemetrySdkTest { } @Test - void testShortcutVersions() { - assertThat(GlobalOpenTelemetry.getTracer("testTracer1")) - .isSameAs(GlobalOpenTelemetry.getTracerProvider().get("testTracer1")); - assertThat(GlobalOpenTelemetry.getTracer("testTracer2", "testVersion")) - .isSameAs(GlobalOpenTelemetry.getTracerProvider().get("testTracer2", "testVersion")); - assertThat( - GlobalOpenTelemetry.tracerBuilder("testTracer2") - .setInstrumentationVersion("testVersion") - .setSchemaUrl("https://example.invalid") - .build()) - .isSameAs( - GlobalOpenTelemetry.getTracerProvider() - .tracerBuilder("testTracer2") - .setInstrumentationVersion("testVersion") - .setSchemaUrl("https://example.invalid") - .build()); - - assertThat(GlobalOpenTelemetry.getMeter("testMeter1")) - .isSameAs(GlobalOpenTelemetry.getMeterProvider().get("testMeter1")); - assertThat( - GlobalOpenTelemetry.meterBuilder("testMeter2") - .setInstrumentationVersion("testVersion") - .setSchemaUrl("https://example.invalid") - .build()) - .isSameAs( - GlobalOpenTelemetry.getMeterProvider() - .meterBuilder("testMeter2") - .setInstrumentationVersion("testVersion") - .setSchemaUrl("https://example.invalid") - .build()); - } - - @Test - void testBuilderDefaults() { + void builderDefaults() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().build(); assertThat(openTelemetry.getTracerProvider()) .isInstanceOfSatisfying( @@ -129,7 +100,7 @@ class OpenTelemetrySdkTest { } @Test - void building() { + void builder() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder() .setTracerProvider(tracerProvider) @@ -151,54 +122,40 @@ class OpenTelemetrySdkTest { } @Test - void testConfiguration_tracerSettings() { - Resource resource = Resource.create(Attributes.builder().put("cat", "meow").build()); - IdGenerator idGenerator = mock(IdGenerator.class); - SpanLimits spanLimits = SpanLimits.getDefault(); - OpenTelemetrySdk openTelemetry = - OpenTelemetrySdk.builder() - .setTracerProvider( - SdkTracerProvider.builder() - .setClock(clock) - .setResource(resource) - .setIdGenerator(idGenerator) - .setSpanLimits(spanLimits) - .build()) - .build(); - TracerProvider unobfuscatedTracerProvider = - ((OpenTelemetrySdk.ObfuscatedTracerProvider) openTelemetry.getTracerProvider()) - .unobfuscate(); - - assertThat(unobfuscatedTracerProvider) - .isInstanceOfSatisfying( - SdkTracerProvider.class, - sdkTracerProvider -> - assertThat(sdkTracerProvider.getSpanLimits()).isEqualTo(spanLimits)); - // Since TracerProvider is in a different package, the only alternative to this reflective - // approach would be to make the fields public for testing which is worse than this. - assertThat(unobfuscatedTracerProvider) - .extracting("sharedState") - .hasFieldOrPropertyWithValue("clock", clock) - .hasFieldOrPropertyWithValue("resource", resource) - .hasFieldOrPropertyWithValue("idGenerator", idGenerator); + void getTracer() { + assertThat(GlobalOpenTelemetry.getTracer("testTracer1")) + .isSameAs(GlobalOpenTelemetry.getTracerProvider().get("testTracer1")); + assertThat(GlobalOpenTelemetry.getTracer("testTracer2", "testVersion")) + .isSameAs(GlobalOpenTelemetry.getTracerProvider().get("testTracer2", "testVersion")); + assertThat( + GlobalOpenTelemetry.tracerBuilder("testTracer2") + .setInstrumentationVersion("testVersion") + .setSchemaUrl("https://example.invalid") + .build()) + .isSameAs( + GlobalOpenTelemetry.getTracerProvider() + .tracerBuilder("testTracer2") + .setInstrumentationVersion("testVersion") + .setSchemaUrl("https://example.invalid") + .build()); } @Test - void testTracerBuilder() { + void tracerBuilder() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().build(); assertThat(openTelemetry.tracerBuilder("instr")) .isNotSameAs(OpenTelemetry.noop().tracerBuilder("instr")); } @Test - void testTracerBuilderViaProvider() { + void tracerBuilder_ViaProvider() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().build(); assertThat(openTelemetry.getTracerProvider().tracerBuilder("instr")) .isNotSameAs(OpenTelemetry.noop().tracerBuilder("instr")); } @Test - void testTracerProviderAccess() { + void getTracerProvider() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().setTracerProvider(tracerProvider).build(); assertThat(openTelemetry.getTracerProvider()) @@ -210,7 +167,50 @@ class OpenTelemetrySdkTest { } @Test - void testMeterProviderAccess() { + void getMeter() { + assertThat(GlobalOpenTelemetry.getMeter("testMeter1")) + .isSameAs(GlobalOpenTelemetry.getMeterProvider().get("testMeter1")); + assertThat( + GlobalOpenTelemetry.meterBuilder("testMeter2") + .setInstrumentationVersion("testVersion") + .setSchemaUrl("https://example.invalid") + .build()) + .isSameAs( + GlobalOpenTelemetry.getMeterProvider() + .meterBuilder("testMeter2") + .setInstrumentationVersion("testVersion") + .setSchemaUrl("https://example.invalid") + .build()); + } + + @Test + void meterBuilder() { + OpenTelemetrySdk openTelemetry = + OpenTelemetrySdk.builder() + .setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class))) + .build()) + .build(); + assertThat(openTelemetry.meterBuilder("instr")) + .isNotSameAs(OpenTelemetry.noop().meterBuilder("instr")); + } + + @Test + void meterBuilder_ViaProvider() { + OpenTelemetrySdk openTelemetry = + OpenTelemetrySdk.builder() + .setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class))) + .build()) + .build(); + assertThat(openTelemetry.getMeterProvider().meterBuilder("instr")) + .isNotSameAs(OpenTelemetry.noop().meterBuilder("instr")); + } + + @Test + void getMeterProvider() { OpenTelemetrySdk openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build(); assertThat(openTelemetry.getMeterProvider()) @@ -225,22 +225,35 @@ class OpenTelemetrySdkTest { // Demonstrates how clear or confusing is SDK configuration @Test void fullOpenTelemetrySdkConfigurationDemo() { - SpanLimits newConfig = SpanLimits.builder().setMaxNumberOfAttributes(512).build(); - - OpenTelemetrySdkBuilder sdkBuilder = - OpenTelemetrySdk.builder() - .setTracerProvider( - SdkTracerProvider.builder() - .setSampler(mock(Sampler.class)) - .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) - .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) - .setClock(mock(Clock.class)) - .setIdGenerator(mock(IdGenerator.class)) - .setResource(Resource.empty()) - .setSpanLimits(newConfig) - .build()); - - sdkBuilder.build(); + OpenTelemetrySdk.builder() + .setMeterProvider( + SdkMeterProvider.builder() + .setResource(Resource.empty()) + .setClock(mock(Clock.class)) + .registerMetricReader( + PeriodicMetricReader.builder(mock(MetricExporter.class)) + .setInterval(Duration.ofSeconds(10)) + .build()) + .registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class))) + .registerView( + InstrumentSelector.builder().setName("name").build(), + View.builder().setName("new-name").build()) + .registerView( + InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(), + View.builder().setAttributeFilter(key -> key.equals("foo")).build()) + .build()) + .setTracerProvider( + SdkTracerProvider.builder() + .setSampler(mock(Sampler.class)) + .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) + .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) + .setClock(mock(Clock.class)) + .setIdGenerator(mock(IdGenerator.class)) + .setResource(Resource.empty()) + .setSpanLimits(SpanLimits.builder().setMaxNumberOfAttributes(512).build()) + .build()) + .setPropagators(ContextPropagators.create(mock(TextMapPropagator.class))) + .build(); } // This is just a demonstration of the bare minimal required configuration in order to get useful @@ -249,6 +262,10 @@ class OpenTelemetrySdkTest { @Test void trivialOpenTelemetrySdkConfigurationDemo() { OpenTelemetrySdk.builder() + .setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class))) + .build()) .setTracerProvider( SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) @@ -262,6 +279,10 @@ class OpenTelemetrySdkTest { @Test void minimalOpenTelemetrySdkConfigurationDemo() { OpenTelemetrySdk.builder() + .setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class))) + .build()) .setTracerProvider( SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) @@ -271,6 +292,13 @@ class OpenTelemetrySdkTest { .build(); OpenTelemetrySdk.builder() + .setMeterProvider( + SdkMeterProvider.builder() + .registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class))) + .registerView( + InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(), + View.builder().setAggregation(Aggregation.explicitBucketHistogram()).build()) + .build()) .setTracerProvider( SdkTracerProvider.builder() .addSpanProcessor(SimpleSpanProcessor.create(mock(SpanExporter.class))) @@ -283,8 +311,10 @@ class OpenTelemetrySdkTest { @Test void stringRepresentation() { - SpanExporter exporter = mock(SpanExporter.class); - when(exporter.toString()).thenReturn("MockSpanExporter{}"); + SpanExporter spanExporter = mock(SpanExporter.class); + when(spanExporter.toString()).thenReturn("MockSpanExporter{}"); + MetricExporter metricExporter = mock(MetricExporter.class); + when(metricExporter.toString()).thenReturn("MockMetricExporter{}"); Resource resource = Resource.builder().put(AttributeKey.stringKey("service.name"), "otel-test").build(); OpenTelemetrySdk sdk = @@ -293,7 +323,16 @@ class OpenTelemetrySdkTest { SdkTracerProvider.builder() .setResource(resource) .addSpanProcessor( - SimpleSpanProcessor.create(SpanExporter.composite(exporter, exporter))) + SimpleSpanProcessor.create( + SpanExporter.composite(spanExporter, spanExporter))) + .build()) + .setMeterProvider( + SdkMeterProvider.builder() + .setResource(resource) + .registerMetricReader(PeriodicMetricReader.create(metricExporter)) + .registerView( + InstrumentSelector.builder().setName("instrument").build(), + View.builder().setName("new-instrument").build()) .build()) .build(); @@ -306,6 +345,14 @@ class OpenTelemetrySdkTest { + "resource=Resource{schemaUrl=null, attributes={service.name=\"otel-test\"}}, " + "spanLimitsSupplier=SpanLimitsValue{maxNumberOfAttributes=128, maxNumberOfEvents=128, maxNumberOfLinks=128, maxNumberOfAttributesPerEvent=128, maxNumberOfAttributesPerLink=128, maxAttributeValueLength=2147483647}, " + "sampler=ParentBased{root:AlwaysOnSampler,remoteParentSampled:AlwaysOnSampler,remoteParentNotSampled:AlwaysOffSampler,localParentSampled:AlwaysOnSampler,localParentNotSampled:AlwaysOffSampler}, " - + "spanProcessor=SimpleSpanProcessor{spanExporter=MultiSpanExporter{spanExporters=[MockSpanExporter{}, MockSpanExporter{}]}}}}"); + + "spanProcessor=SimpleSpanProcessor{spanExporter=MultiSpanExporter{spanExporters=[MockSpanExporter{}, MockSpanExporter{}]}}" + + "}, " + + "meterProvider=SdkMeterProvider{" + + "clock=SystemClock{}, " + + "resource=Resource{schemaUrl=null, attributes={service.name=\"otel-test\"}}, " + + "metricReaders=[PeriodicMetricReader{exporter=MockMetricExporter{}, intervalNanos=60000000000}], " + + "views=[RegisteredView{instrumentSelector=InstrumentSelector{instrumentName=instrument}, view=View{name=new-instrument, aggregation=DefaultAggregation, attributesProcessor=NoopAttributesProcessor{}}}]" + + "}" + + "}"); } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/InstrumentSelector.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/InstrumentSelector.java index 3fccf5045a..5d8dce9a21 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/InstrumentSelector.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/InstrumentSelector.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.metrics; import com.google.auto.value.AutoValue; +import java.util.StringJoiner; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -75,4 +76,25 @@ public abstract class InstrumentSelector { */ @Nullable public abstract String getMeterSchemaUrl(); + + @Override + public final String toString() { + StringJoiner joiner = new StringJoiner(", ", "InstrumentSelector{", "}"); + if (getInstrumentType() != null) { + joiner.add("instrumentType=" + getInstrumentType()); + } + if (getInstrumentName() != null) { + joiner.add("instrumentName=" + getInstrumentName()); + } + if (getMeterName() != null) { + joiner.add("meterName=" + getMeterName()); + } + if (getMeterVersion() != null) { + joiner.add("meterVersion=" + getMeterVersion()); + } + if (getMeterSchemaUrl() != null) { + joiner.add("meterSchemaUrl=" + getMeterSchemaUrl()); + } + return joiner.toString(); + } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterProvider.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterProvider.java index 32973e2ca3..e0a7149b72 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterProvider.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/SdkMeterProvider.java @@ -39,6 +39,7 @@ public final class SdkMeterProvider implements MeterProvider, Closeable { private static final Logger LOGGER = Logger.getLogger(SdkMeterProvider.class.getName()); static final String DEFAULT_METER_NAME = "unknown"; + private final List metricReaders; private final ComponentRegistry registry; private final MeterProviderSharedState sharedState; private final Map collectionInfoMap; @@ -62,6 +63,7 @@ public final class SdkMeterProvider implements MeterProvider, Closeable { ViewRegistry viewRegistry, ExemplarFilter exemplarFilter, long minimumCollectionIntervalNanos) { + this.metricReaders = metricReaders; this.sharedState = MeterProviderSharedState.create(clock, resource, viewRegistry, exemplarFilter); this.registry = @@ -142,6 +144,20 @@ public final class SdkMeterProvider implements MeterProvider, Closeable { shutdown().join(10, TimeUnit.SECONDS); } + @Override + public String toString() { + return "SdkMeterProvider{" + + "clock=" + + sharedState.getClock() + + ", resource=" + + sharedState.getResource() + + ", metricReaders=" + + metricReaders + + ", views=" + + sharedState.getViewRegistry().getViews() + + "}"; + } + /** Helper class to expose registered metric exports. */ private class LeasedMetricProducer implements MetricProducer { private final CollectionHandle handle; diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/View.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/View.java index c20c3f7bd5..b17b53e166 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/View.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/View.java @@ -7,6 +7,7 @@ package io.opentelemetry.sdk.metrics; import com.google.auto.value.AutoValue; import io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor; +import java.util.StringJoiner; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -47,4 +48,18 @@ public abstract class View { /** The attribute processor used for this view. */ abstract AttributesProcessor getAttributesProcessor(); + + @Override + public final String toString() { + StringJoiner joiner = new StringJoiner(", ", "View{", "}"); + if (getName() != null) { + joiner.add("name=" + getName()); + } + if (getDescription() != null) { + joiner.add("description=" + getDescription()); + } + joiner.add("aggregation=" + getAggregation()); + joiner.add("attributesProcessor=" + getAttributesProcessor()); + return joiner.toString(); + } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java index 41258393bd..41a81ecff8 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReader.java @@ -106,6 +106,16 @@ public final class PeriodicMetricReader implements MetricReader { start(); } + @Override + public String toString() { + return "PeriodicMetricReader{" + + "exporter=" + + exporter + + ", intervalNanos=" + + intervalNanos + + '}'; + } + void start() { synchronized (lock) { if (scheduledFuture != null) { diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/MeterProviderSharedState.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/MeterProviderSharedState.java index 0e5c1bf6cc..1a5352f69d 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/MeterProviderSharedState.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/state/MeterProviderSharedState.java @@ -33,10 +33,10 @@ public abstract class MeterProviderSharedState { public abstract Clock getClock(); /** Returns the {@link Resource} to attach telemetry to. */ - abstract Resource getResource(); + public abstract Resource getResource(); /** Returns the {@link ViewRegistry} for custom aggregation and metric definitions. */ - abstract ViewRegistry getViewRegistry(); + public abstract ViewRegistry getViewRegistry(); /** * Returns the timestamp when this {@code MeterProvider} was started, in nanoseconds since Unix diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java index a9e13c9c0d..18da0c5cd4 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/AttributesProcessor.java @@ -5,6 +5,8 @@ package io.opentelemetry.sdk.metrics.internal.view; +import static io.opentelemetry.sdk.metrics.internal.view.NoopAttributesProcessor.NOOP; + import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.api.common.AttributesBuilder; @@ -27,7 +29,8 @@ import javax.annotation.concurrent.Immutable; */ @Immutable public abstract class AttributesProcessor { - private AttributesProcessor() {} + + AttributesProcessor() {} /** * Manipulates a set of attributes, returning the desired set. @@ -142,8 +145,6 @@ public abstract class AttributesProcessor { }; } - static final AttributesProcessor NOOP = simple(incoming -> incoming); - /** A {@link AttributesProcessor} that runs a sequence of processors. */ @Immutable static final class JoinedAttributesProcessor extends AttributesProcessor { diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/NoopAttributesProcessor.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/NoopAttributesProcessor.java new file mode 100644 index 0000000000..184bd98d18 --- /dev/null +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/NoopAttributesProcessor.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.view; + +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.context.Context; + +class NoopAttributesProcessor extends AttributesProcessor { + + static final NoopAttributesProcessor NOOP = new NoopAttributesProcessor(); + + private NoopAttributesProcessor() {} + + @Override + public Attributes process(Attributes incoming, Context context) { + return incoming; + } + + @Override + public boolean usesContext() { + return false; + } + + @Override + public String toString() { + return "NoopAttributesProcessor{}"; + } +} diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredView.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredView.java index c715bc1b9a..43384eaaa6 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredView.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredView.java @@ -42,4 +42,14 @@ public abstract class RegisteredView { /** The {@link SourceInfo} from where the view was registered. */ public abstract SourceInfo getViewSourceInfo(); + + @Override + public final String toString() { + return "RegisteredView{" + + "instrumentSelector=" + + getInstrumentSelector() + + ", view=" + + getView() + + "}"; + } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ViewRegistry.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ViewRegistry.java index c7fd7bf0b5..d61f77a500 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ViewRegistry.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ViewRegistry.java @@ -5,6 +5,8 @@ package io.opentelemetry.sdk.metrics.internal.view; +import static io.opentelemetry.sdk.metrics.internal.view.NoopAttributesProcessor.NOOP; + import io.opentelemetry.sdk.common.InstrumentationScopeInfo; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder; @@ -36,7 +38,7 @@ public final class ViewRegistry { RegisteredView.create( InstrumentSelector.builder().setName("*").build(), DEFAULT_VIEW, - AttributesProcessor.NOOP, + NOOP, SourceInfo.noSourceInfo()); private static final Logger logger = Logger.getLogger(ViewRegistry.class.getName()); @@ -51,6 +53,11 @@ public final class ViewRegistry { return new ViewRegistryBuilder(); } + /** Return a list of all registered views. */ + public List getViews() { + return new ArrayList<>(reverseRegistration); + } + /** * Returns the metric {@link View} for a given instrument. * diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/InstrumentSelectorTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/InstrumentSelectorTest.java index 0bf3983e1c..68afed0c9c 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/InstrumentSelectorTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/InstrumentSelectorTest.java @@ -74,4 +74,27 @@ class InstrumentSelectorTest { .build(); assertThat(selector.getMeterSchemaUrl()).isEqualTo("http://bar.com"); } + + @Test + void stringRepresentation() { + assertThat(InstrumentSelector.builder().setName("name").build().toString()) + .isEqualTo("InstrumentSelector{instrumentName=name}"); + assertThat( + InstrumentSelector.builder() + .setType(InstrumentType.COUNTER) + .setName("name") + .setMeterName("meter") + .setMeterVersion("version") + .setMeterSchemaUrl("http://url.com") + .build() + .toString()) + .isEqualTo( + "InstrumentSelector{" + + "instrumentType=COUNTER, " + + "instrumentName=name, " + + "meterName=meter, " + + "meterVersion=version, " + + "meterSchemaUrl=http://url.com" + + "}"); + } } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java new file mode 100644 index 0000000000..b81b46cba1 --- /dev/null +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ViewTest.java @@ -0,0 +1,34 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +class ViewTest { + + @Test + void stringRepresentation() { + assertThat(View.builder().build().toString()) + .isEqualTo( + "View{aggregation=DefaultAggregation, attributesProcessor=NoopAttributesProcessor{}}"); + assertThat( + View.builder() + .setName("name") + .setDescription("description") + .setAggregation(Aggregation.sum()) + .build() + .toString()) + .isEqualTo( + "View{" + + "name=name, " + + "description=description, " + + "aggregation=SumAggregation, " + + "attributesProcessor=NoopAttributesProcessor{}" + + "}"); + } +} diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java index 110e152dcb..6f12941f86 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/export/PeriodicMetricReaderTest.java @@ -208,6 +208,21 @@ class PeriodicMetricReaderTest { .hasMessage("executor"); } + @Test + void stringRepresentation() { + when(metricExporter.toString()).thenReturn("MockMetricExporter{}"); + assertThat( + PeriodicMetricReader.builder(metricExporter) + .setInterval(Duration.ofSeconds(1)) + .build() + .toString()) + .isEqualTo( + "PeriodicMetricReader{" + + "exporter=MockMetricExporter{}, " + + "intervalNanos=1000000000" + + "}"); + } + private static class WaitingMetricExporter implements MetricExporter { private final AtomicBoolean hasShutdown = new AtomicBoolean(false); diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredViewTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredViewTest.java new file mode 100644 index 0000000000..1cbd79b4d0 --- /dev/null +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/RegisteredViewTest.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics.internal.view; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.InstrumentSelector; +import io.opentelemetry.sdk.metrics.InstrumentType; +import io.opentelemetry.sdk.metrics.View; +import io.opentelemetry.sdk.metrics.internal.debug.SourceInfo; +import org.junit.jupiter.api.Test; + +class RegisteredViewTest { + + @Test + void stringRepresentation() { + assertThat( + RegisteredView.create( + InstrumentSelector.builder() + .setName("name") + .setType(InstrumentType.COUNTER) + .setMeterName("meter-name") + .setMeterVersion("meter-version") + .setMeterSchemaUrl("meter-schema-url") + .build(), + View.builder() + .setName("name") + .setDescription("description") + .setAggregation(Aggregation.sum()) + .build(), + AttributesProcessor.noop(), + SourceInfo.fromCurrentStack()) + .toString()) + .isEqualTo( + "RegisteredView{" + + "instrumentSelector=InstrumentSelector{instrumentType=COUNTER, instrumentName=name, meterName=meter-name, meterVersion=meter-version, meterSchemaUrl=meter-schema-url}, " + + "view=View{name=name, description=description, aggregation=SumAggregation, attributesProcessor=NoopAttributesProcessor{}}" + + "}"); + } +} diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReader.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReader.java index 1e937d7ffc..eb6a65f3e2 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReader.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReader.java @@ -90,4 +90,9 @@ public class InMemoryMetricReader implements MetricReader { isShutdown.set(true); return CompletableResultCode.ofSuccess(); } + + @Override + public String toString() { + return "InMemoryMetricReader{aggregationTemporality=" + aggregationTemporality + "}"; + } } diff --git a/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReaderTest.java b/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReaderTest.java index 08e621b4e8..9df4a48c78 100644 --- a/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReaderTest.java +++ b/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/exporter/InMemoryMetricReaderTest.java @@ -89,4 +89,10 @@ class InMemoryMetricReaderTest { assertThat(cumulativeReader.collectAllMetrics()).hasSize(0); assertThat(deltaReader.collectAllMetrics()).hasSize(0); } + + @Test + void stringRepresentation() { + assertThat(deltaReader.toString()) + .isEqualTo("InMemoryMetricReader{aggregationTemporality=DELTA}"); + } }