Skip to content

Commit

Permalink
MetricReader and MetricExporter can determine default aggregation by …
Browse files Browse the repository at this point in the history
…instrument (#4472)

* MetricReader and MetricExporter can determine default aggregation by instrument

* Add since annotations
  • Loading branch information
jack-berg committed Jun 28, 2022
1 parent 31be1dc commit 0860b38
Show file tree
Hide file tree
Showing 25 changed files with 379 additions and 252 deletions.
20 changes: 19 additions & 1 deletion docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt
@@ -1,2 +1,20 @@
Comparing source compatibility of against
No changes.
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector getDefault()
+++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.Aggregation getDefaultAggregation(io.opentelemetry.sdk.metrics.InstrumentType)
+++ NEW ANNOTATION: java.lang.FunctionalInterface
**** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.MetricExporter (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW INTERFACE: io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector
+++* NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.Aggregation getDefaultAggregation(io.opentelemetry.sdk.metrics.InstrumentType)
**** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.export.MetricReader (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW INTERFACE: io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector
+++* NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.Aggregation getDefaultAggregation(io.opentelemetry.sdk.metrics.InstrumentType)
*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.metrics.export.PeriodicMetricReader (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
===* UNCHANGED INTERFACE: io.opentelemetry.sdk.metrics.export.MetricReader
+++ NEW INTERFACE: io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector
+++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.Aggregation getDefaultAggregation(io.opentelemetry.sdk.metrics.InstrumentType)
Expand Up @@ -223,6 +223,7 @@ void builder_addPropertiesSupplier() {
void builder_addMeterProviderCustomizer() {
Mockito.lenient().when(metricReader.shutdown()).thenReturn(CompletableResultCode.ofSuccess());
when(metricReader.forceFlush()).thenReturn(CompletableResultCode.ofSuccess());
when(metricReader.getDefaultAggregation(any())).thenCallRealMethod();

SdkMeterProvider sdkMeterProvider =
builder
Expand Down
Expand Up @@ -20,7 +20,7 @@
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.View;
import io.opentelemetry.sdk.metrics.internal.view.AttributesProcessor;
import io.opentelemetry.sdk.metrics.internal.view.ViewRegistryBuilder;
import io.opentelemetry.sdk.metrics.internal.view.RegisteredView;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
Expand All @@ -40,9 +40,7 @@ void registerViews_FullConfig() {
ViewConfig.registerViews(builder, resourceFileInputStream("full-config.yaml"));

assertThat(builder)
.extracting(
"viewRegistryBuilder", as(InstanceOfAssertFactories.type(ViewRegistryBuilder.class)))
.extracting("orderedViews", as(InstanceOfAssertFactories.list(Object.class)))
.extracting("registeredViews", as(InstanceOfAssertFactories.list(RegisteredView.class)))
.hasSize(2);
}

Expand Down
Expand Up @@ -8,6 +8,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -42,6 +43,7 @@
@ExtendWith(MockitoExtension.class)
class OpenTelemetrySdkTest {

@Mock private MetricExporter metricExporter;
@Mock private SdkTracerProvider tracerProvider;
@Mock private SdkMeterProvider meterProvider;
@Mock private SdkLogEmitterProvider logEmitterProvider;
Expand Down Expand Up @@ -185,11 +187,12 @@ void getMeter() {

@Test
void meterBuilder() {
when(metricExporter.getDefaultAggregation(any())).thenCallRealMethod();
OpenTelemetrySdk openTelemetry =
OpenTelemetrySdk.builder()
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class)))
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.build())
.build();
assertThat(openTelemetry.meterBuilder("instr"))
Expand All @@ -198,11 +201,12 @@ void meterBuilder() {

@Test
void meterBuilder_ViaProvider() {
when(metricExporter.getDefaultAggregation(any())).thenCallRealMethod();
OpenTelemetrySdk openTelemetry =
OpenTelemetrySdk.builder()
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class)))
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.build())
.build();
assertThat(openTelemetry.getMeterProvider().meterBuilder("instr"))
Expand All @@ -225,16 +229,17 @@ void getMeterProvider() {
// Demonstrates how clear or confusing is SDK configuration
@Test
void fullOpenTelemetrySdkConfigurationDemo() {
when(metricExporter.getDefaultAggregation(any())).thenCallRealMethod();
OpenTelemetrySdk.builder()
.setMeterProvider(
SdkMeterProvider.builder()
.setResource(Resource.empty())
.setClock(mock(Clock.class))
.registerMetricReader(
PeriodicMetricReader.builder(mock(MetricExporter.class))
PeriodicMetricReader.builder(metricExporter)
.setInterval(Duration.ofSeconds(10))
.build())
.registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class)))
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.registerView(
InstrumentSelector.builder().setName("name").build(),
View.builder().setName("new-name").build())
Expand All @@ -261,10 +266,11 @@ void fullOpenTelemetrySdkConfigurationDemo() {
// Demonstrates how clear or confusing is SDK configuration
@Test
void trivialOpenTelemetrySdkConfigurationDemo() {
when(metricExporter.getDefaultAggregation(any())).thenCallRealMethod();
OpenTelemetrySdk.builder()
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class)))
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.build())
.setTracerProvider(
SdkTracerProvider.builder()
Expand All @@ -278,10 +284,11 @@ void trivialOpenTelemetrySdkConfigurationDemo() {
// Demonstrates how clear or confusing is SDK configuration
@Test
void minimalOpenTelemetrySdkConfigurationDemo() {
when(metricExporter.getDefaultAggregation(any())).thenCallRealMethod();
OpenTelemetrySdk.builder()
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class)))
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.build())
.setTracerProvider(
SdkTracerProvider.builder()
Expand All @@ -294,7 +301,7 @@ void minimalOpenTelemetrySdkConfigurationDemo() {
OpenTelemetrySdk.builder()
.setMeterProvider(
SdkMeterProvider.builder()
.registerMetricReader(PeriodicMetricReader.create(mock(MetricExporter.class)))
.registerMetricReader(PeriodicMetricReader.create(metricExporter))
.registerView(
InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(),
View.builder().setAggregation(Aggregation.explicitBucketHistogram()).build())
Expand All @@ -313,7 +320,7 @@ void minimalOpenTelemetrySdkConfigurationDemo() {
void stringRepresentation() {
SpanExporter spanExporter = mock(SpanExporter.class);
when(spanExporter.toString()).thenReturn("MockSpanExporter{}");
MetricExporter metricExporter = mock(MetricExporter.class);
when(metricExporter.getDefaultAggregation(any())).thenCallRealMethod();
when(metricExporter.toString()).thenReturn("MockMetricExporter{}");
Resource resource =
Resource.builder().put(AttributeKey.stringKey("service.name"), "otel-test").build();
Expand Down
Expand Up @@ -104,8 +104,7 @@ final CallbackRegistration registerLongAsynchronousInstrument(

final SdkObservableMeasurement buildObservableMeasurement(
InstrumentType type, InstrumentValueType valueType) {
InstrumentDescriptor descriptor = makeDescriptor(type, valueType);
return meterSharedState.registerObservableMeasurement(descriptor, meterProviderSharedState);
return meterSharedState.registerObservableMeasurement(makeDescriptor(type, valueType));
}

@FunctionalInterface
Expand Down
Expand Up @@ -19,6 +19,7 @@
import io.opentelemetry.sdk.metrics.internal.export.MetricProducer;
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader;
import io.opentelemetry.sdk.metrics.internal.state.MeterProviderSharedState;
import io.opentelemetry.sdk.metrics.internal.view.RegisteredView;
import io.opentelemetry.sdk.metrics.internal.view.ViewRegistry;
import io.opentelemetry.sdk.resources.Resource;
import java.io.Closeable;
Expand All @@ -40,6 +41,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<RegisteredView> registeredViews;
private final List<RegisteredReader> registeredReaders;
private final MeterProviderSharedState sharedState;
private final ComponentRegistry<SdkMeter> registry;
Expand All @@ -51,16 +53,21 @@ public static SdkMeterProviderBuilder builder() {
}

SdkMeterProvider(
List<RegisteredReader> registeredReaders,
List<RegisteredView> registeredViews,
List<MetricReader> metricReaders,
Clock clock,
Resource resource,
ViewRegistry viewRegistry,
ExemplarFilter exemplarFilter) {
long startEpochNanos = clock.now();
this.registeredReaders = registeredReaders;
this.registeredViews = registeredViews;
this.registeredReaders =
metricReaders.stream()
.map(
reader ->
RegisteredReader.create(reader, ViewRegistry.create(reader, registeredViews)))
.collect(toList());
this.sharedState =
MeterProviderSharedState.create(
clock, resource, viewRegistry, exemplarFilter, startEpochNanos);
MeterProviderSharedState.create(clock, resource, exemplarFilter, startEpochNanos);
this.registry =
new ComponentRegistry<>(
instrumentationLibraryInfo ->
Expand Down Expand Up @@ -144,7 +151,7 @@ public String toString() {
+ ", metricReaders="
+ registeredReaders.stream().map(RegisteredReader::getReader).collect(toList())
+ ", views="
+ sharedState.getViewRegistry().getViews()
+ registeredViews
+ "}";
}

Expand Down
Expand Up @@ -10,9 +10,7 @@
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
import io.opentelemetry.sdk.metrics.internal.debug.SourceInfo;
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
import io.opentelemetry.sdk.metrics.internal.export.RegisteredReader;
import io.opentelemetry.sdk.metrics.internal.view.ViewRegistry;
import io.opentelemetry.sdk.metrics.internal.view.ViewRegistryBuilder;
import io.opentelemetry.sdk.metrics.internal.view.RegisteredView;
import io.opentelemetry.sdk.resources.Resource;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -34,8 +32,8 @@ public final class SdkMeterProviderBuilder {

private Clock clock = Clock.getDefault();
private Resource resource = Resource.getDefault();
private final ViewRegistryBuilder viewRegistryBuilder = ViewRegistry.builder();
private final List<RegisteredReader> registeredReaders = new ArrayList<>();
private final List<MetricReader> metricReaders = new ArrayList<>();
private final List<RegisteredView> registeredViews = new ArrayList<>();
private ExemplarFilter exemplarFilter = DEFAULT_EXEMPLAR_FILTER;

SdkMeterProviderBuilder() {}
Expand Down Expand Up @@ -96,8 +94,9 @@ SdkMeterProviderBuilder setExemplarFilter(ExemplarFilter filter) {
public SdkMeterProviderBuilder registerView(InstrumentSelector selector, View view) {
Objects.requireNonNull(selector, "selector");
Objects.requireNonNull(view, "view");
viewRegistryBuilder.addView(
selector, view, view.getAttributesProcessor(), SourceInfo.fromCurrentStack());
registeredViews.add(
RegisteredView.create(
selector, view, view.getAttributesProcessor(), SourceInfo.fromCurrentStack()));
return this;
}

Expand All @@ -107,13 +106,12 @@ public SdkMeterProviderBuilder registerView(InstrumentSelector selector, View vi
* <p>Note: custom implementations of {@link MetricReader} are not currently supported.
*/
public SdkMeterProviderBuilder registerMetricReader(MetricReader reader) {
registeredReaders.add(RegisteredReader.create(reader));
metricReaders.add(reader);
return this;
}

/** Returns an {@link SdkMeterProvider} built with the configuration of this builder. */
public SdkMeterProvider build() {
return new SdkMeterProvider(
registeredReaders, clock, resource, viewRegistryBuilder.build(), exemplarFilter);
return new SdkMeterProvider(registeredViews, metricReaders, clock, resource, exemplarFilter);
}
}
@@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.sdk.metrics.export;

import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;

/**
* A functional interface that selects default {@link Aggregation} based on {@link InstrumentType}.
*
* @since 1.16.0
*/
@FunctionalInterface
public interface DefaultAggregationSelector {

/**
* The default implementation of {@link DefaultAggregationSelector} which returns the default
* aggregation for each instrument.
*/
static DefaultAggregationSelector getDefault() {
return instrumentType -> Aggregation.defaultAggregation();
}

/**
* Return the default aggregation for the {@link InstrumentType}.
*
* <p>The default aggregation is used when an instrument does not match any views.
*/
Aggregation getDefaultAggregation(InstrumentType instrumentType);
}
Expand Up @@ -6,6 +6,8 @@
package io.opentelemetry.sdk.metrics.export;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.data.MetricData;
Expand All @@ -22,7 +24,19 @@
*
* @since 1.14.0
*/
public interface MetricExporter extends AggregationTemporalitySelector, Closeable {
public interface MetricExporter
extends AggregationTemporalitySelector, DefaultAggregationSelector, Closeable {

/**
* Return the default aggregation for the {@link InstrumentType}.
*
* @see DefaultAggregationSelector#getDefaultAggregation(InstrumentType)
* @since 1.16.0
*/
@Override
default Aggregation getDefaultAggregation(InstrumentType instrumentType) {
return Aggregation.defaultAggregation();
}

/**
* Exports the {@code metrics}. The caller (i.e. {@link PeriodicMetricReader} will not call export
Expand Down
Expand Up @@ -6,6 +6,8 @@
package io.opentelemetry.sdk.metrics.export;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;

/**
Expand All @@ -16,7 +18,7 @@
*
* @since 1.14.0
*/
public interface MetricReader extends AggregationTemporalitySelector {
public interface MetricReader extends AggregationTemporalitySelector, DefaultAggregationSelector {

/**
* Called by {@link SdkMeterProvider} and supplies the {@link MetricReader} with a handle to
Expand All @@ -27,6 +29,17 @@ public interface MetricReader extends AggregationTemporalitySelector {
*/
void register(CollectionRegistration registration);

/**
* Return the default aggregation for the {@link InstrumentType}.
*
* @see DefaultAggregationSelector#getDefaultAggregation(InstrumentType)
* @since 1.16.0
*/
@Override
default Aggregation getDefaultAggregation(InstrumentType instrumentType) {
return Aggregation.defaultAggregation();
}

/**
* Read and export the metrics.
*
Expand Down
Expand Up @@ -6,6 +6,7 @@
package io.opentelemetry.sdk.metrics.export;

import io.opentelemetry.sdk.common.CompletableResultCode;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
Expand Down Expand Up @@ -68,6 +69,11 @@ public AggregationTemporality getAggregationTemporality(InstrumentType instrumen
return exporter.getAggregationTemporality(instrumentType);
}

@Override
public Aggregation getDefaultAggregation(InstrumentType instrumentType) {
return exporter.getDefaultAggregation(instrumentType);
}

@Override
public CompletableResultCode forceFlush() {
return scheduled.doRun();
Expand Down

0 comments on commit 0860b38

Please sign in to comment.