Add base2ExponentialHistogram to Aggregation (#5143)

This commit is contained in:
jack-berg 2023-02-05 10:54:44 -06:00 committed by GitHub
parent 33e6bf30c5
commit 92403b01b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 84 additions and 42 deletions

View File

@ -1,4 +1,8 @@
Comparing source compatibility of against
*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.Aggregation (not serializable)
=== CLASS FILE FORMAT VERSION: 52.0 <- 52.0
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.Aggregation base2ExponentialBucketHistogram()
+++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.Aggregation base2ExponentialBucketHistogram(int, int)
+++ NEW INTERFACE: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.data.ExponentialHistogramBuckets (not serializable)
+++ CLASS FILE FORMAT VERSION: 52.0 <- n.a.
+++ NEW SUPERCLASS: java.lang.Object

View File

@ -5,14 +5,17 @@
package io.opentelemetry.exporter.internal.otlp;
import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram;
import io.opentelemetry.exporter.internal.retry.RetryPolicy;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
@ -175,11 +178,15 @@ public final class OtlpConfigUtil {
if (defaultHistogramAggregation == null) {
return;
}
if (defaultHistogramAggregation.equalsIgnoreCase("EXPONENTIAL_BUCKET_HISTOGRAM")) {
// TODO(jack-berg): remove EXPONENTIAL_BUCKET_HISTOGRAM in 1.24.0
if (defaultHistogramAggregation.equalsIgnoreCase("EXPONENTIAL_BUCKET_HISTOGRAM")
|| AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram())
.equalsIgnoreCase(defaultHistogramAggregation)) {
defaultAggregationSelectorConsumer.accept(
DefaultAggregationSelector.getDefault()
.with(InstrumentType.HISTOGRAM, ExponentialHistogramAggregation.getDefault()));
} else if (!defaultHistogramAggregation.equalsIgnoreCase("EXPLICIT_BUCKET_HISTOGRAM")) {
.with(InstrumentType.HISTOGRAM, Aggregation.base2ExponentialBucketHistogram()));
} else if (!AggregationUtil.aggregationName(explicitBucketHistogram())
.equalsIgnoreCase(defaultHistogramAggregation)) {
throw new ConfigurationException(
"Unrecognized default histogram aggregation: " + defaultHistogramAggregation);
}

View File

@ -17,11 +17,11 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import com.google.common.collect.ImmutableMap;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.DefaultConfigProperties;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.InstrumentType;
import io.opentelemetry.sdk.metrics.data.AggregationTemporality;
import io.opentelemetry.sdk.metrics.export.AggregationTemporalitySelector;
import io.opentelemetry.sdk.metrics.export.DefaultAggregationSelector;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
@ -376,20 +376,34 @@ class OtlpConfigUtilTest {
"otel.exporter.otlp.metrics.default.histogram.aggregation", "foo")))
.isInstanceOf(ConfigurationException.class)
.hasMessageContaining("Unrecognized default histogram aggregation:");
assertThat(
configureHistogramDefaultAggregation(
ImmutableMap.of(
"otel.exporter.otlp.metrics.default.histogram.aggregation",
"base2_exponential_bucket_histogram"))
.getDefaultAggregation(InstrumentType.HISTOGRAM))
.isEqualTo(Aggregation.base2ExponentialBucketHistogram());
assertThat(
configureHistogramDefaultAggregation(
ImmutableMap.of(
"otel.exporter.otlp.metrics.default.histogram.aggregation",
"BASE2_EXPONENTIAL_BUCKET_HISTOGRAM"))
.getDefaultAggregation(InstrumentType.HISTOGRAM))
.isEqualTo(Aggregation.base2ExponentialBucketHistogram());
assertThat(
configureHistogramDefaultAggregation(
ImmutableMap.of(
"otel.exporter.otlp.metrics.default.histogram.aggregation",
"exponential_bucket_histogram"))
.getDefaultAggregation(InstrumentType.HISTOGRAM))
.isEqualTo(ExponentialHistogramAggregation.getDefault());
.isEqualTo(Aggregation.base2ExponentialBucketHistogram());
assertThat(
configureHistogramDefaultAggregation(
ImmutableMap.of(
"otel.exporter.otlp.metrics.default.histogram.aggregation",
"EXPONENTIAL_BUCKET_HISTOGRAM"))
.getDefaultAggregation(InstrumentType.HISTOGRAM))
.isEqualTo(ExponentialHistogramAggregation.getDefault());
.isEqualTo(Aggregation.base2ExponentialBucketHistogram());
assertThat(
configureHistogramDefaultAggregation(

View File

@ -100,7 +100,7 @@ The [OpenTelemetry Protocol (OTLP)](https://github.com/open-telemetry/openteleme
| otel.exporter.otlp.metrics.protocol | OTEL_EXPORTER_OTLP_METRICS_PROTOCOL | The transport protocol to use on OTLP metric requests. Options include `grpc` and `http/protobuf`. Default is `grpc`. |
| otel.exporter.otlp.logs.protocol | OTEL_EXPORTER_OTLP_LOGS_PROTOCOL | The transport protocol to use on OTLP log requests. Options include `grpc` and `http/protobuf`. Default is `grpc`. |
| otel.exporter.otlp.metrics.temporality.preference | OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE | The preferred output aggregation temporality. Options include `DELTA` and `CUMULATIVE`. If `CUMULATIVE`, all instruments will have cumulative temporality. If `DELTA`, counter (sync and async) and histograms will be delta, up down counters (sync and async) will be cumulative. Default is `CUMULATIVE`. |
| otel.exporter.otlp.metrics.default.histogram.aggregation | OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION | The preferred default histogram aggregation. Options include `EXPONENTIAL_BUCKET_HISTOGRAM` and `EXPLICIT_BUCKET_HISTOGRAM`. Default is `EXPLICIT_BUCKET_HISTOGRAM`. |
| otel.exporter.otlp.metrics.default.histogram.aggregation | OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION | The preferred default histogram aggregation. Options include `BASE2_EXPONENTIAL_BUCKET_HISTOGRAM` and `EXPLICIT_BUCKET_HISTOGRAM`. Default is `EXPLICIT_BUCKET_HISTOGRAM`. |
| otel.experimental.exporter.otlp.retry.enabled | OTEL_EXPERIMENTAL_EXPORTER_OTLP_RETRY_ENABLED | If `true`, enable [experimental retry support](#otlp-exporter-retry). Default is `false`. |
To configure the service name for the OTLP exporter, add the `service.name` key

View File

@ -17,7 +17,6 @@ import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.View;
import io.opentelemetry.sdk.metrics.ViewBuilder;
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregationUtil;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
@ -204,15 +203,16 @@ public final class ViewConfig {
return Aggregation.explicitBucketHistogram(bucketBoundaries);
}
}
if (ExponentialHistogramAggregation.getDefault().equals(aggregation)) {
if (Aggregation.base2ExponentialBucketHistogram().equals(aggregation)) {
Integer maxBuckets;
try {
maxBuckets = getAsType(aggregationArgs, "max_buckets", Integer.class);
} catch (IllegalStateException e) {
throw new ConfigurationException("max_buckets must be an integer", e);
}
// TODO: support configuring max_scale
if (maxBuckets != null) {
return ExponentialHistogramAggregation.create(maxBuckets, 20);
return Aggregation.base2ExponentialBucketHistogram(maxBuckets, 20);
}
}
return aggregation;

View File

@ -198,7 +198,7 @@ class ViewConfigTest {
assertThatThrownBy(
() ->
ViewConfig.toAggregation(
"exponential_bucket_histogram", ImmutableMap.of("max_buckets", "four")))
"base2_exponential_bucket_histogram", ImmutableMap.of("max_buckets", "four")))
.isInstanceOf(ConfigurationException.class)
.hasMessage("max_buckets must be an integer");
}
@ -224,11 +224,12 @@ class ViewConfigTest {
.isEqualTo(Arrays.asList(1.0, 2.0));
// Exponential histogram
assertThat(ViewConfig.toAggregation("exponential_bucket_histogram", Collections.emptyMap()))
.isEqualTo(ExponentialHistogramAggregation.getDefault());
assertThat(
ViewConfig.toAggregation("base2_exponential_bucket_histogram", Collections.emptyMap()))
.isEqualTo(Aggregation.base2ExponentialBucketHistogram());
assertThat(
ViewConfig.toAggregation(
"exponential_bucket_histogram", ImmutableMap.of("max_buckets", 20)))
"base2_exponential_bucket_histogram", ImmutableMap.of("max_buckets", 20)))
.isInstanceOf(ExponentialHistogramAggregation.class)
.extracting("maxBuckets", as(InstanceOfAssertFactories.INTEGER))
.isEqualTo(20);

View File

@ -18,7 +18,6 @@ import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
import io.opentelemetry.sdk.metrics.internal.SdkMeterProviderUtil;
import io.opentelemetry.sdk.metrics.internal.exemplar.ExemplarFilter;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
@ -113,8 +112,9 @@ public class HistogramCollectBenchmark {
@SuppressWarnings("ImmutableEnumChecker")
public enum AggregationGenerator {
EXPLICIT_BUCKET_HISTOGRAM(Aggregation.explicitBucketHistogram()),
DEFAULT_EXPONENTIAL_BUCKET_HISTOGRAM(ExponentialHistogramAggregation.getDefault()),
ZERO_MAX_SCALE_EXPONENTIAL_BUCKET_HISTOGRAM(ExponentialHistogramAggregation.create(160, 0));
DEFAULT_BASE2_EXPONENTIAL_BUCKET_HISTOGRAM(Aggregation.base2ExponentialBucketHistogram()),
ZERO_MAX_SCALE_BASE2_EXPONENTIAL_BUCKET_HISTOGRAM(
Aggregation.base2ExponentialBucketHistogram(160, 0));
private final Aggregation aggregation;

View File

@ -9,6 +9,7 @@ import io.opentelemetry.sdk.metrics.data.MetricDataType;
import io.opentelemetry.sdk.metrics.internal.view.DefaultAggregation;
import io.opentelemetry.sdk.metrics.internal.view.DropAggregation;
import io.opentelemetry.sdk.metrics.internal.view.ExplicitBucketHistogramAggregation;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import io.opentelemetry.sdk.metrics.internal.view.LastValueAggregation;
import io.opentelemetry.sdk.metrics.internal.view.SumAggregation;
import java.util.List;
@ -67,4 +68,26 @@ public interface Aggregation {
static Aggregation explicitBucketHistogram(List<Double> bucketBoundaries) {
return ExplicitBucketHistogramAggregation.create(bucketBoundaries);
}
/**
* Aggregates measurements into a base-2 {@link MetricDataType#EXPONENTIAL_HISTOGRAM} using the
* default {@code maxBuckets} and {@code maxScale}.
*/
static Aggregation base2ExponentialBucketHistogram() {
return ExponentialHistogramAggregation.getDefault();
}
/**
* Aggregates measurements into a base-2 {@link MetricDataType#EXPONENTIAL_HISTOGRAM}.
*
* @param maxBuckets the max number of positive buckets and negative buckets (max total buckets is
* 2 * {@code maxBuckets} + 1 zero bucket).
* @param maxScale the maximum and initial scale. If measurements can't fit in a particular scale
* given the {@code maxBuckets}, the scale is reduced until the measurements can be
* accommodated. Setting maxScale may reduce the number of downscales. Additionally, the
* performance of computing bucket index is improved when scale is {@code <= 0}.
*/
static Aggregation base2ExponentialBucketHistogram(int maxBuckets, int maxScale) {
return ExponentialHistogramAggregation.create(maxBuckets, maxScale);
}
}

View File

@ -6,7 +6,6 @@
package io.opentelemetry.sdk.metrics.internal.aggregator;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import java.util.HashMap;
import java.util.Map;
@ -24,7 +23,8 @@ public class AggregationUtil {
private static final String AGGREGATION_LAST_VALUE = "last_value";
private static final String AGGREGATION_DROP = "drop";
private static final String AGGREGATION_EXPLICIT_BUCKET_HISTOGRAM = "explicit_bucket_histogram";
private static final String AGGREGATION_EXPONENTIAL_HISTOGRAM = "exponential_bucket_histogram";
private static final String AGGREGATION_BASE2_EXPONENTIAL_HISTOGRAM =
"base2_exponential_bucket_histogram";
static {
aggregationByName = new HashMap<>();
@ -34,9 +34,8 @@ public class AggregationUtil {
aggregationByName.put(AGGREGATION_DROP, Aggregation.drop());
aggregationByName.put(
AGGREGATION_EXPLICIT_BUCKET_HISTOGRAM, Aggregation.explicitBucketHistogram());
// TODO(jack-berg): Use Aggregation.exponentialHistogram() when available
aggregationByName.put(
AGGREGATION_EXPONENTIAL_HISTOGRAM, ExponentialHistogramAggregation.getDefault());
AGGREGATION_BASE2_EXPONENTIAL_HISTOGRAM, Aggregation.base2ExponentialBucketHistogram());
nameByAggregation = new HashMap<>();
nameByAggregation.put(Aggregation.defaultAggregation().getClass(), AGGREGATION_DEFAULT);
@ -45,9 +44,9 @@ public class AggregationUtil {
nameByAggregation.put(Aggregation.drop().getClass(), AGGREGATION_DROP);
nameByAggregation.put(
Aggregation.explicitBucketHistogram().getClass(), AGGREGATION_EXPLICIT_BUCKET_HISTOGRAM);
// TODO(jack-berg): Use Aggregation.exponentialHistogram() when available
nameByAggregation.put(
ExponentialHistogramAggregation.getDefault().getClass(), AGGREGATION_EXPONENTIAL_HISTOGRAM);
Aggregation.base2ExponentialBucketHistogram().getClass(),
AGGREGATION_BASE2_EXPONENTIAL_HISTOGRAM);
}
private AggregationUtil() {}

View File

@ -9,7 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.opentelemetry.sdk.metrics.internal.aggregator.AggregatorFactory;
import io.opentelemetry.sdk.metrics.internal.descriptor.InstrumentDescriptor;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import java.util.Collections;
import org.junit.jupiter.api.Test;
@ -28,11 +27,10 @@ class AggregationTest {
assertThat(Aggregation.explicitBucketHistogram(Collections.singletonList(1.0d)))
.asString()
.contains("ExplicitBucketHistogramAggregation");
// TODO(jack-berg): Use Aggregation.exponentialHistogram() when available
assertThat(ExponentialHistogramAggregation.getDefault())
assertThat(Aggregation.base2ExponentialBucketHistogram())
.asString()
.isEqualTo("ExponentialHistogramAggregation{maxBuckets=160,maxScale=20}");
assertThat(ExponentialHistogramAggregation.create(1, 0))
assertThat(Aggregation.base2ExponentialBucketHistogram(1, 0))
.asString()
.isEqualTo("ExponentialHistogramAggregation{maxBuckets=1,maxScale=0}");
}
@ -88,9 +86,8 @@ class AggregationTest {
assertThat(explicitHistogram.isCompatibleWithInstrument(observableGauge)).isFalse();
assertThat(explicitHistogram.isCompatibleWithInstrument(histogram)).isTrue();
// TODO(jack-berg): Use Aggregation.exponentialHistogram() when available
AggregatorFactory exponentialHistogram =
((AggregatorFactory) ExponentialHistogramAggregation.getDefault());
((AggregatorFactory) Aggregation.base2ExponentialBucketHistogram());
assertThat(exponentialHistogram.isCompatibleWithInstrument(counter)).isTrue();
assertThat(exponentialHistogram.isCompatibleWithInstrument(observableCounter)).isFalse();
assertThat(exponentialHistogram.isCompatibleWithInstrument(upDownCounter)).isFalse();

View File

@ -16,7 +16,6 @@ import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import io.opentelemetry.sdk.testing.time.TestClock;
@ -183,7 +182,7 @@ class SdkDoubleHistogramTest {
.registerView(
InstrumentSelector.builder().setType(InstrumentType.HISTOGRAM).build(),
View.builder()
.setAggregation(ExponentialHistogramAggregation.create(5, 20))
.setAggregation(Aggregation.base2ExponentialBucketHistogram(5, 20))
.build())
.build();
DoubleHistogram doubleHistogram =

View File

@ -16,7 +16,6 @@ import io.opentelemetry.api.metrics.LongHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.internal.testing.slf4j.SuppressLogger;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
import io.opentelemetry.sdk.testing.time.TestClock;
@ -184,7 +183,7 @@ class SdkLongHistogramTest {
.registerView(
InstrumentSelector.builder().setType(InstrumentType.HISTOGRAM).build(),
View.builder()
.setAggregation(ExponentialHistogramAggregation.create(5, 20))
.setAggregation(Aggregation.base2ExponentialBucketHistogram(5, 20))
.build())
.build();
LongHistogram longHistogram =

View File

@ -9,7 +9,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import io.opentelemetry.sdk.metrics.Aggregation;
import io.opentelemetry.sdk.metrics.internal.view.ExponentialHistogramAggregation;
import org.junit.jupiter.api.Test;
class AggregationUtilTest {
@ -23,8 +22,8 @@ class AggregationUtilTest {
assertThat(AggregationUtil.forName("EXplicit_BUCKet_HISTOGRAM"))
.isEqualTo(Aggregation.explicitBucketHistogram());
assertThat(AggregationUtil.forName("drop")).isEqualTo(Aggregation.drop());
assertThat(AggregationUtil.forName("exponential_bucket_histogram"))
.isEqualTo(ExponentialHistogramAggregation.getDefault());
assertThat(AggregationUtil.forName("base2_exponential_bucket_histogram"))
.isEqualTo(Aggregation.base2ExponentialBucketHistogram());
assertThatThrownBy(() -> AggregationUtil.forName("foo"))
.isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Unrecognized aggregation name foo");
@ -39,8 +38,8 @@ class AggregationUtilTest {
assertThat(AggregationUtil.aggregationName(Aggregation.drop())).isEqualTo("drop");
assertThat(AggregationUtil.aggregationName(Aggregation.explicitBucketHistogram()))
.isEqualTo("explicit_bucket_histogram");
assertThat(AggregationUtil.aggregationName(ExponentialHistogramAggregation.getDefault()))
.isEqualTo("exponential_bucket_histogram");
assertThat(AggregationUtil.aggregationName(Aggregation.base2ExponentialBucketHistogram()))
.isEqualTo("base2_exponential_bucket_histogram");
assertThatThrownBy(() -> AggregationUtil.aggregationName(new Aggregation() {}))
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("Unrecognized aggregation");

View File

@ -177,7 +177,7 @@ class ViewRegistryTest {
instrumentType ->
instrumentType == InstrumentType.HISTOGRAM
|| instrumentType == InstrumentType.OBSERVABLE_GAUGE
? ExponentialHistogramAggregation.getDefault()
? Aggregation.base2ExponentialBucketHistogram()
: Aggregation.defaultAggregation();
ViewRegistry viewRegistry =
@ -211,7 +211,7 @@ class ViewRegistryTest {
registeredView(
InstrumentSelector.builder().setName("*").build(),
View.builder()
.setAggregation(ExponentialHistogramAggregation.getDefault())
.setAggregation(Aggregation.base2ExponentialBucketHistogram())
.build())));
// At this point, no warning logs should have been produced.
assertThat(logs.getEvents()).hasSize(0);
@ -225,7 +225,7 @@ class ViewRegistryTest {
INSTRUMENTATION_SCOPE_INFO))
.isEqualTo(Collections.singletonList(DEFAULT_REGISTERED_VIEW));
logs.assertContains(
"Instrument default aggregation exponential_bucket_histogram is incompatible with instrument default of type OBSERVABLE_GAUGE");
"Instrument default aggregation base2_exponential_bucket_histogram is incompatible with instrument default of type OBSERVABLE_GAUGE");
}
@Test