diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt index d339d345d3c..ee7e96820cc 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-metrics.txt @@ -1,2 +1,40 @@ Comparing source compatibility of opentelemetry-sdk-metrics-1.60.0-SNAPSHOT.jar against opentelemetry-sdk-metrics-1.59.0.jar -No changes. \ No newline at end of file +*** MODIFIED INTERFACE: PUBLIC ABSTRACT io.opentelemetry.sdk.metrics.Aggregation (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + === UNCHANGED METHOD: PUBLIC STATIC io.opentelemetry.sdk.metrics.Aggregation base2ExponentialBucketHistogram(int, int) + +++ NEW ANNOTATION: java.lang.Deprecated + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.Aggregation base2ExponentialBucketHistogram(io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions) + === UNCHANGED METHOD: PUBLIC STATIC io.opentelemetry.sdk.metrics.Aggregation explicitBucketHistogram(java.util.List) + +++ NEW ANNOTATION: java.lang.Deprecated + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.Aggregation explicitBucketHistogram(io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions) ++++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions$Builder builder() + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions getDefault() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) int getMaxBuckets() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) int getMaxScale() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) boolean getRecordMinMax() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions$Builder toBuilder() ++++ NEW CLASS: PUBLIC(+) ABSTRACT(+) STATIC(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions$Builder (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions build() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions$Builder setMaxBuckets(int) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions$Builder setMaxScale(int) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions$Builder setRecordMinMax(boolean) ++++ NEW CLASS: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions$Builder builder() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) java.util.List getBucketBoundaries() + +++ NEW ANNOTATION: javax.annotation.Nullable + +++ NEW METHOD: PUBLIC(+) STATIC(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions getDefault() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) boolean getRecordMinMax() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions$Builder toBuilder() ++++ NEW CLASS: PUBLIC(+) ABSTRACT(+) STATIC(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions$Builder (not serializable) + +++ CLASS FILE FORMAT VERSION: 52.0 <- n.a. + +++ NEW SUPERCLASS: java.lang.Object + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions build() + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions$Builder setBucketBoundaries(java.util.List) + +++ NEW METHOD: PUBLIC(+) ABSTRACT(+) io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions$Builder setRecordMinMax(boolean) diff --git a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java index de31edef6e4..5ac24663c4b 100644 --- a/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java +++ b/exporters/prometheus/src/test/java/io/opentelemetry/exporter/prometheus/PrometheusMetricReaderTest.java @@ -19,6 +19,7 @@ import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Scope; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.SdkMeterProvider; import io.opentelemetry.sdk.metrics.View; @@ -782,7 +783,12 @@ void exponentialHistogramBucketConversion() { .registerView( InstrumentSelector.builder().setName("my.exponential.histogram").build(), View.builder() - .setAggregation(Aggregation.base2ExponentialBucketHistogram(160, otelScale)) + .setAggregation( + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(160) + .setMaxScale(otelScale) + .build())) .build()) .build() .meterBuilder("test") diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java index ca2441f12f9..3d2059bc4bc 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactory.java @@ -10,6 +10,8 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.Base2ExponentialBucketHistogramAggregationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExplicitBucketHistogramAggregationModel; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions; +import io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions; import java.util.List; final class AggregationFactory implements Factory { @@ -37,15 +39,20 @@ public Aggregation create(AggregationModel model, DeclarativeConfigContext conte model.getBase2ExponentialBucketHistogram(); if (exponentialBucketHistogram != null) { Integer maxScale = exponentialBucketHistogram.getMaxScale(); - if (maxScale == null) { - maxScale = 20; - } Integer maxSize = exponentialBucketHistogram.getMaxSize(); - if (maxSize == null) { - maxSize = 160; + Boolean recordMinMax = exponentialBucketHistogram.getRecordMinMax(); + Base2ExponentialHistogramOptions.Builder builder = Base2ExponentialHistogramOptions.builder(); + if (maxScale != null) { + builder.setMaxScale(maxScale); + } + if (maxSize != null) { + builder.setMaxBuckets(maxSize); + } + if (recordMinMax != null) { + builder.setRecordMinMax(recordMinMax); } try { - return Aggregation.base2ExponentialBucketHistogram(maxSize, maxScale); + return Aggregation.base2ExponentialBucketHistogram(builder.build()); } catch (IllegalArgumentException e) { throw new DeclarativeConfigException("Invalid exponential bucket histogram", e); } @@ -54,11 +61,16 @@ public Aggregation create(AggregationModel model, DeclarativeConfigContext conte model.getExplicitBucketHistogram(); if (explicitBucketHistogram != null) { List boundaries = explicitBucketHistogram.getBoundaries(); - if (boundaries == null) { - return Aggregation.explicitBucketHistogram(); + Boolean recordMinMax = explicitBucketHistogram.getRecordMinMax(); + ExplicitBucketHistogramOptions.Builder builder = ExplicitBucketHistogramOptions.builder(); + if (boundaries != null) { + builder.setBucketBoundaries(boundaries); + } + if (recordMinMax != null) { + builder.setRecordMinMax(recordMinMax); } try { - return Aggregation.explicitBucketHistogram(boundaries); + return Aggregation.explicitBucketHistogram(builder.build()); } catch (IllegalArgumentException e) { throw new DeclarativeConfigException("Invalid explicit bucket histogram", e); } diff --git a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/metric/viewconfig/ViewConfig.java b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/metric/viewconfig/ViewConfig.java index 9c08770d072..67cc972ba1a 100644 --- a/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/metric/viewconfig/ViewConfig.java +++ b/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/metric/viewconfig/ViewConfig.java @@ -10,6 +10,8 @@ import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions; +import io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions; import io.opentelemetry.sdk.metrics.InstrumentSelector; import io.opentelemetry.sdk.metrics.InstrumentSelectorBuilder; import io.opentelemetry.sdk.metrics.InstrumentType; @@ -203,7 +205,8 @@ static Aggregation toAggregation(String aggregationName, Map agg if (Aggregation.explicitBucketHistogram().equals(aggregation)) { List bucketBoundaries = getBucketBoundaries(aggregationArgs); if (bucketBoundaries != null) { - return Aggregation.explicitBucketHistogram(bucketBoundaries); + return Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder().setBucketBoundaries(bucketBoundaries).build()); } } if (Aggregation.base2ExponentialBucketHistogram().equals(aggregation)) { @@ -215,7 +218,8 @@ static Aggregation toAggregation(String aggregationName, Map agg } // TODO: support configuring max_scale if (maxBuckets != null) { - return Aggregation.base2ExponentialBucketHistogram(maxBuckets, 20); + return Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder().setMaxBuckets(maxBuckets).build()); } } return aggregation; diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactoryTest.java index 8cf4ccc6187..9b043bf54a7 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/AggregationFactoryTest.java @@ -15,6 +15,8 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.LastValueAggregationModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SumAggregationModel; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions; +import io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions; import java.util.Arrays; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -51,7 +53,11 @@ private static Stream createTestCases() { new Base2ExponentialBucketHistogramAggregationModel() .withMaxSize(2) .withMaxScale(2)), - Aggregation.base2ExponentialBucketHistogram(2, 2)), + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(2) + .setMaxScale(2) + .build())), Arguments.of( new AggregationModel() .withExplicitBucketHistogram( @@ -62,6 +68,67 @@ private static Stream createTestCases() { .withExplicitBucketHistogram( new ExplicitBucketHistogramAggregationModel() .withBoundaries(Arrays.asList(1.0, 2.0))), - Aggregation.explicitBucketHistogram(Arrays.asList(1.0, 2.0)))); + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(1.0, 2.0)) + .build())), + // Test recordMinMax parameter for explicit bucket histogram + Arguments.of( + new AggregationModel() + .withExplicitBucketHistogram( + new ExplicitBucketHistogramAggregationModel() + .withBoundaries(Arrays.asList(1.0, 2.0)) + .withRecordMinMax(true)), + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(1.0, 2.0)) + .setRecordMinMax(true) + .build())), + Arguments.of( + new AggregationModel() + .withExplicitBucketHistogram( + new ExplicitBucketHistogramAggregationModel() + .withBoundaries(Arrays.asList(1.0, 2.0)) + .withRecordMinMax(false)), + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(1.0, 2.0)) + .setRecordMinMax(false) + .build())), + Arguments.of( + new AggregationModel() + .withExplicitBucketHistogram( + new ExplicitBucketHistogramAggregationModel() + .withBoundaries(null) + .withRecordMinMax(false)), + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder().setRecordMinMax(false).build())), + // Test recordMinMax parameter for exponential bucket histogram + Arguments.of( + new AggregationModel() + .withBase2ExponentialBucketHistogram( + new Base2ExponentialBucketHistogramAggregationModel() + .withMaxSize(2) + .withMaxScale(2) + .withRecordMinMax(true)), + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(2) + .setMaxScale(2) + .setRecordMinMax(true) + .build())), + Arguments.of( + new AggregationModel() + .withBase2ExponentialBucketHistogram( + new Base2ExponentialBucketHistogramAggregationModel() + .withMaxSize(2) + .withMaxScale(2) + .withRecordMinMax(false)), + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(2) + .setMaxScale(2) + .setRecordMinMax(false) + .build()))); } } diff --git a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java index 0ce2834dbff..1911c8fbbac 100644 --- a/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java +++ b/sdk-extensions/incubator/src/test/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ViewFactoryTest.java @@ -14,6 +14,7 @@ import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.IncludeExcludeModel; import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ViewStreamModel; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions; import io.opentelemetry.sdk.metrics.View; import java.util.Arrays; import java.util.Collections; @@ -43,7 +44,11 @@ void create() { .setAttributeFilter( IncludeExcludePredicate.createExactMatching( Arrays.asList("foo", "bar"), Collections.singletonList("baz"))) - .setAggregation(Aggregation.explicitBucketHistogram(Arrays.asList(1.0, 2.0))) + .setAggregation( + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(1.0, 2.0)) + .build())) .build(); View view = diff --git a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java index 71bf89803cc..30c78a0500e 100644 --- a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java +++ b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramAggregationParam.java @@ -17,19 +17,21 @@ public enum HistogramAggregationParam { new DoubleExplicitBucketHistogramAggregator( ExplicitBucketHistogramUtils.createBoundaryArray( ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES), + /* recordMinMax= */ true, ExemplarReservoirFactory.noSamples(), IMMUTABLE_DATA)), EXPLICIT_SINGLE_BUCKET( new DoubleExplicitBucketHistogramAggregator( ExplicitBucketHistogramUtils.createBoundaryArray(Collections.emptyList()), + /* recordMinMax= */ true, ExemplarReservoirFactory.noSamples(), IMMUTABLE_DATA)), EXPONENTIAL_SMALL_CIRCULAR_BUFFER( new DoubleBase2ExponentialHistogramAggregator( - ExemplarReservoirFactory.noSamples(), 20, 0, IMMUTABLE_DATA)), + ExemplarReservoirFactory.noSamples(), 20, 0, /* recordMinMax= */ true, IMMUTABLE_DATA)), EXPONENTIAL_CIRCULAR_BUFFER( new DoubleBase2ExponentialHistogramAggregator( - ExemplarReservoirFactory.noSamples(), 160, 0, IMMUTABLE_DATA)); + ExemplarReservoirFactory.noSamples(), 160, 0, /* recordMinMax= */ true, IMMUTABLE_DATA)); private final Aggregator aggregator; diff --git a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramCollectBenchmark.java b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramCollectBenchmark.java index be316162e7b..0b44e1a6e92 100644 --- a/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramCollectBenchmark.java +++ b/sdk/metrics/src/jmh/java/io/opentelemetry/sdk/metrics/internal/aggregator/HistogramCollectBenchmark.java @@ -9,6 +9,7 @@ import io.opentelemetry.api.metrics.DoubleHistogram; import io.opentelemetry.sdk.common.CompletableResultCode; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions; import io.opentelemetry.sdk.metrics.ExemplarFilter; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.SdkMeterProvider; @@ -113,7 +114,8 @@ public enum AggregationGenerator { EXPLICIT_BUCKET_HISTOGRAM(Aggregation.explicitBucketHistogram()), DEFAULT_BASE2_EXPONENTIAL_BUCKET_HISTOGRAM(Aggregation.base2ExponentialBucketHistogram()), ZERO_MAX_SCALE_BASE2_EXPONENTIAL_BUCKET_HISTOGRAM( - Aggregation.base2ExponentialBucketHistogram(160, 0)); + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder().setMaxBuckets(160).setMaxScale(0).build())); private final Aggregation aggregation; diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Aggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Aggregation.java index ba8470a8ed9..08be08b55b5 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Aggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Aggregation.java @@ -6,6 +6,7 @@ package io.opentelemetry.sdk.metrics; import io.opentelemetry.sdk.metrics.data.MetricDataType; +import io.opentelemetry.sdk.metrics.internal.aggregator.ExplicitBucketHistogramUtils; import io.opentelemetry.sdk.metrics.internal.view.Base2ExponentialHistogramAggregation; import io.opentelemetry.sdk.metrics.internal.view.DefaultAggregation; import io.opentelemetry.sdk.metrics.internal.view.DropAggregation; @@ -64,9 +65,25 @@ static Aggregation explicitBucketHistogram() { * * @param bucketBoundaries A list of (inclusive) upper bounds for the histogram. Should be in * order from lowest to highest. + * @deprecated Use {@link #explicitBucketHistogram(ExplicitBucketHistogramOptions)} instead. */ + @Deprecated static Aggregation explicitBucketHistogram(List bucketBoundaries) { - return ExplicitBucketHistogramAggregation.create(bucketBoundaries); + return explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder().setBucketBoundaries(bucketBoundaries).build()); + } + + /** + * Aggregates measurements into an explicit bucket {@link MetricDataType#HISTOGRAM}. + * + * @param options histogram options + */ + static Aggregation explicitBucketHistogram(ExplicitBucketHistogramOptions options) { + List boundaries = options.getBucketBoundaries(); + if (boundaries == null) { + boundaries = ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES; + } + return ExplicitBucketHistogramAggregation.create(boundaries, options.getRecordMinMax()); } /** @@ -89,8 +106,25 @@ static Aggregation base2ExponentialBucketHistogram() { * accommodated. Setting maxScale may reduce the number of downscales. Additionally, the * performance of computing bucket index is improved when scale is {@code <= 0}. * @since 1.23.0 + * @deprecated Use {@link #base2ExponentialBucketHistogram(Base2ExponentialHistogramOptions)} + * instead. */ + @Deprecated static Aggregation base2ExponentialBucketHistogram(int maxBuckets, int maxScale) { - return Base2ExponentialHistogramAggregation.create(maxBuckets, maxScale); + return base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(maxBuckets) + .setMaxScale(maxScale) + .build()); + } + + /** + * Aggregates measurements into a base-2 {@link MetricDataType#EXPONENTIAL_HISTOGRAM}. + * + * @param options histogram options + */ + static Aggregation base2ExponentialBucketHistogram(Base2ExponentialHistogramOptions options) { + return Base2ExponentialHistogramAggregation.create( + options.getMaxBuckets(), options.getMaxScale(), options.getRecordMinMax()); } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Base2ExponentialHistogramOptions.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Base2ExponentialHistogramOptions.java new file mode 100644 index 00000000000..d260a24a875 --- /dev/null +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/Base2ExponentialHistogramOptions.java @@ -0,0 +1,109 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics; + +import static io.opentelemetry.api.internal.Utils.checkArgument; + +import com.google.auto.value.AutoValue; +import javax.annotation.concurrent.Immutable; + +/** + * Options for configuring base-2 exponential histogram aggregations. + * + * @see Aggregation#base2ExponentialBucketHistogram(Base2ExponentialHistogramOptions) + */ +@AutoValue +@Immutable +public abstract class Base2ExponentialHistogramOptions { + + /** The default maximum number of buckets. */ + private static final int DEFAULT_MAX_BUCKETS = 160; + + /** The default maximum scale. */ + private static final int DEFAULT_MAX_SCALE = 20; + + private static final Base2ExponentialHistogramOptions DEFAULT = builder().build(); + + Base2ExponentialHistogramOptions() {} + + /** Returns the default {@link Base2ExponentialHistogramOptions}. */ + public static Base2ExponentialHistogramOptions getDefault() { + return DEFAULT; + } + + /** Returns a new {@link Builder} for {@link Base2ExponentialHistogramOptions}. */ + public static Builder builder() { + return new AutoValue_Base2ExponentialHistogramOptions.Builder() + .setMaxBuckets(DEFAULT_MAX_BUCKETS) + .setMaxScale(DEFAULT_MAX_SCALE) + .setRecordMinMax(true); + } + + /** Returns a {@link Builder} initialized to the same property values as the current instance. */ + public abstract Builder toBuilder(); + + /** + * Returns the max number of positive and negative buckets. Defaults to {@value + * #DEFAULT_MAX_BUCKETS}. + */ + public abstract int getMaxBuckets(); + + /** Returns the maximum and initial scale. Defaults to {@value #DEFAULT_MAX_SCALE}. */ + public abstract int getMaxScale(); + + /** Returns whether min and max values should be recorded. Defaults to {@code true}. */ + public abstract boolean getRecordMinMax(); + + /** Builder for {@link Base2ExponentialHistogramOptions}. */ + @AutoValue.Builder + public abstract static class Builder { + + Builder() {} + + /** + * Sets the max number of positive buckets and negative buckets (max total buckets is 2 * {@code + * maxBuckets} + 1 zero bucket). + * + * @param maxBuckets the maximum number of buckets + */ + public abstract Builder setMaxBuckets(int maxBuckets); + + /** + * Sets 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}. + * + * @param maxScale the maximum scale + */ + public abstract Builder setMaxScale(int maxScale); + + /** + * Sets whether min and max values should be recorded. + * + * @param recordMinMax whether to record min and max values + */ + public abstract Builder setRecordMinMax(boolean recordMinMax); + + abstract Base2ExponentialHistogramOptions autoBuild(); + + /** + * Returns a new {@link Base2ExponentialHistogramOptions} with the configuration of this + * builder. + * + * @throws IllegalArgumentException if {@code maxBuckets} is less than 2, or if {@code maxScale} + * is not between -10 and 20 (inclusive). + */ + public Base2ExponentialHistogramOptions build() { + Base2ExponentialHistogramOptions options = autoBuild(); + checkArgument(options.getMaxBuckets() >= 2, "maxBuckets must be >= 2"); + checkArgument( + options.getMaxScale() >= -10 && options.getMaxScale() <= 20, + "maxScale must be -10 <= x <= 20"); + return options; + } + } +} diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ExplicitBucketHistogramOptions.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ExplicitBucketHistogramOptions.java new file mode 100644 index 00000000000..d8ca702c8ef --- /dev/null +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/ExplicitBucketHistogramOptions.java @@ -0,0 +1,74 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.sdk.metrics; + +import com.google.auto.value.AutoValue; +import java.util.List; +import javax.annotation.Nullable; +import javax.annotation.concurrent.Immutable; + +/** + * Options for configuring explicit bucket histogram aggregations. + * + * @see Aggregation#explicitBucketHistogram(ExplicitBucketHistogramOptions) + */ +@AutoValue +@Immutable +public abstract class ExplicitBucketHistogramOptions { + + private static final ExplicitBucketHistogramOptions DEFAULT = builder().build(); + + ExplicitBucketHistogramOptions() {} + + /** Returns the default {@link ExplicitBucketHistogramOptions}. */ + public static ExplicitBucketHistogramOptions getDefault() { + return DEFAULT; + } + + /** Returns a new {@link Builder} for {@link ExplicitBucketHistogramOptions}. */ + public static Builder builder() { + return new AutoValue_ExplicitBucketHistogramOptions.Builder().setRecordMinMax(true); + } + + /** Returns a {@link Builder} initialized to the same property values as the current instance. */ + public abstract Builder toBuilder(); + + /** + * Returns the bucket boundaries, or {@code null} if the default OTel bucket boundaries should be + * used. + */ + @Nullable + public abstract List getBucketBoundaries(); + + /** Returns whether min and max values should be recorded. Defaults to {@code true}. */ + public abstract boolean getRecordMinMax(); + + /** Builder for {@link ExplicitBucketHistogramOptions}. */ + @AutoValue.Builder + public abstract static class Builder { + + Builder() {} + + /** + * Sets the bucket boundaries. If not set, the default OTel bucket boundaries are used. + * + * @param bucketBoundaries a list of (inclusive) upper bounds, in order from lowest to highest + */ + public abstract Builder setBucketBoundaries(@Nullable List bucketBoundaries); + + /** + * Sets whether min and max values should be recorded. + * + * @param recordMinMax whether to record min and max values + */ + public abstract Builder setRecordMinMax(boolean recordMinMax); + + /** + * Returns a new {@link ExplicitBucketHistogramOptions} with the configuration of this builder. + */ + public abstract ExplicitBucketHistogramOptions build(); + } +} diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java index 28c18a78d46..072f79a3e1c 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregator.java @@ -40,27 +40,34 @@ public final class DoubleBase2ExponentialHistogramAggregator private final ExemplarReservoirFactory reservoirFactory; private final int maxBuckets; private final int maxScale; + private final boolean recordMinMax; private final MemoryMode memoryMode; /** * Constructs an exponential histogram aggregator. * * @param reservoirFactory Supplier of exemplar reservoirs per-stream. + * @param maxBuckets maximum number of buckets in each of the positive and negative ranges + * @param maxScale maximum scale factor + * @param recordMinMax whether to record min and max values + * @param memoryMode The {@link MemoryMode} to use in this aggregator. */ public DoubleBase2ExponentialHistogramAggregator( ExemplarReservoirFactory reservoirFactory, int maxBuckets, int maxScale, + boolean recordMinMax, MemoryMode memoryMode) { this.reservoirFactory = reservoirFactory; this.maxBuckets = maxBuckets; this.maxScale = maxScale; + this.recordMinMax = recordMinMax; this.memoryMode = memoryMode; } @Override public AggregatorHandle createHandle() { - return new Handle(reservoirFactory, maxBuckets, maxScale, memoryMode); + return new Handle(reservoirFactory, maxBuckets, maxScale, recordMinMax, memoryMode); } @Override @@ -82,6 +89,7 @@ public MetricData toMetricData( static final class Handle extends AggregatorHandle { private final int maxBuckets; private final int maxScale; + private final boolean recordMinMax; @Nullable private DoubleBase2ExponentialHistogramBuckets positiveBuckets; @Nullable private DoubleBase2ExponentialHistogramBuckets negativeBuckets; private long zeroCount; @@ -99,10 +107,12 @@ static final class Handle extends AggregatorHandle 0, - this.min, - this.count > 0, - this.max, + recordMinMax && this.count > 0, + recordMinMax ? this.min : 0, + recordMinMax && this.count > 0, + recordMinMax ? this.max : 0, resolveBuckets( this.positiveBuckets, currentScale, reset, /* reusableBuckets= */ null), resolveBuckets( @@ -149,10 +159,10 @@ protected synchronized ExponentialHistogramPointData doAggregateThenMaybeResetDo currentScale, sum, zeroCount, - this.count > 0, - this.min, - this.count > 0, - this.max, + recordMinMax && this.count > 0, + recordMinMax ? this.min : 0, + recordMinMax && this.count > 0, + recordMinMax ? this.max : 0, resolveBuckets( this.positiveBuckets, currentScale, reset, reusablePoint.getPositiveBuckets()), resolveBuckets( @@ -222,8 +232,10 @@ protected synchronized void doRecordDouble(double value) { sum += value; - this.min = Math.min(this.min, value); - this.max = Math.max(this.max, value); + if (recordMinMax) { + this.min = Math.min(this.min, value); + this.max = Math.max(this.max, value); + } count++; int c = Double.compare(value, 0); diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregator.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregator.java index 0d3ce15c1bf..3d7cf1ceb31 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregator.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregator.java @@ -38,6 +38,7 @@ public final class DoubleExplicitBucketHistogramAggregator implements Aggregator { private final double[] boundaries; + private final boolean recordMinMax; private final MemoryMode memoryMode; // a cache for converting to MetricData @@ -49,12 +50,17 @@ public final class DoubleExplicitBucketHistogramAggregator * Constructs an explicit bucket histogram aggregator. * * @param boundaries Bucket boundaries, in-order. + * @param recordMinMax whether to record min and max values * @param reservoirFactory Supplier of exemplar reservoirs per-stream. * @param memoryMode The {@link MemoryMode} to use in this aggregator. */ public DoubleExplicitBucketHistogramAggregator( - double[] boundaries, ExemplarReservoirFactory reservoirFactory, MemoryMode memoryMode) { + double[] boundaries, + boolean recordMinMax, + ExemplarReservoirFactory reservoirFactory, + MemoryMode memoryMode) { this.boundaries = boundaries; + this.recordMinMax = recordMinMax; this.memoryMode = memoryMode; List boundaryList = new ArrayList<>(this.boundaries.length); @@ -67,7 +73,7 @@ public DoubleExplicitBucketHistogramAggregator( @Override public AggregatorHandle createHandle() { - return new Handle(boundaryList, boundaries, reservoirFactory, memoryMode); + return new Handle(boundaryList, boundaries, recordMinMax, reservoirFactory, memoryMode); } @Override @@ -91,6 +97,7 @@ static final class Handle extends AggregatorHandle { private final List boundaryList; // read-only private final double[] boundaries; + private final boolean recordMinMax; private final Object lock = new Object(); @@ -115,11 +122,13 @@ static final class Handle extends AggregatorHandle { Handle( List boundaryList, double[] boundaries, + boolean recordMinMax, ExemplarReservoirFactory reservoirFactory, MemoryMode memoryMode) { super(reservoirFactory, /* isDoubleType= */ true); this.boundaryList = boundaryList; this.boundaries = boundaries; + this.recordMinMax = recordMinMax; this.counts = new long[this.boundaries.length + 1]; this.sum = 0; this.min = Double.MAX_VALUE; @@ -156,10 +165,10 @@ protected HistogramPointData doAggregateThenMaybeResetDoubles( epochNanos, attributes, sum, - this.count > 0, - this.min, - this.count > 0, - this.max, + recordMinMax && this.count > 0, + recordMinMax ? this.min : 0, + recordMinMax && this.count > 0, + recordMinMax ? this.max : 0, boundaryList, PrimitiveLongList.wrap(Arrays.copyOf(counts, counts.length)), exemplars); @@ -170,10 +179,10 @@ protected HistogramPointData doAggregateThenMaybeResetDoubles( epochNanos, attributes, sum, - this.count > 0, - this.min, - this.count > 0, - this.max, + recordMinMax && this.count > 0, + recordMinMax ? this.min : 0, + recordMinMax && this.count > 0, + recordMinMax ? this.max : 0, boundaryList, counts, exemplars); @@ -195,8 +204,10 @@ protected void doRecordDouble(double value) { synchronized (lock) { this.sum += value; - this.min = Math.min(this.min, value); - this.max = Math.max(this.max, value); + if (recordMinMax) { + this.min = Math.min(this.min, value); + this.max = Math.max(this.max, value); + } this.count++; this.counts[bucketIndex]++; } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java index 6e354d7069c..41455b0b1ac 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregation.java @@ -5,8 +5,6 @@ package io.opentelemetry.sdk.metrics.internal.view; -import static io.opentelemetry.api.internal.Utils.checkArgument; - import io.opentelemetry.sdk.common.Clock; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.common.internal.RandomSupplier; @@ -32,14 +30,17 @@ public final class Base2ExponentialHistogramAggregation implements Aggregation, private static final int DEFAULT_MAX_SCALE = 20; private static final Aggregation DEFAULT = - new Base2ExponentialHistogramAggregation(DEFAULT_MAX_BUCKETS, DEFAULT_MAX_SCALE); + new Base2ExponentialHistogramAggregation( + DEFAULT_MAX_BUCKETS, DEFAULT_MAX_SCALE, /* recordMinMax= */ true); private final int maxBuckets; private final int maxScale; + private final boolean recordMinMax; - private Base2ExponentialHistogramAggregation(int maxBuckets, int maxScale) { + private Base2ExponentialHistogramAggregation(int maxBuckets, int maxScale, boolean recordMinMax) { this.maxBuckets = maxBuckets; this.maxScale = maxScale; + this.recordMinMax = recordMinMax; } public static Aggregation getDefault() { @@ -55,12 +56,11 @@ public static Aggregation getDefault() { * 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 <= 0. + * @param recordMinMax whether to record min and max values * @return the aggregation */ - public static Aggregation create(int maxBuckets, int maxScale) { - checkArgument(maxBuckets >= 2, "maxBuckets must be >= 2"); - checkArgument(maxScale <= 20 && maxScale >= -10, "maxScale must be -10 <= x <= 20"); - return new Base2ExponentialHistogramAggregation(maxBuckets, maxScale); + public static Aggregation create(int maxBuckets, int maxScale, boolean recordMinMax) { + return new Base2ExponentialHistogramAggregation(maxBuckets, maxScale, recordMinMax); } @Override @@ -79,6 +79,7 @@ public Aggregator createAggregator( RandomSupplier.platformDefault())), maxBuckets, maxScale, + recordMinMax, memoryMode); } @@ -99,6 +100,8 @@ public String toString() { + maxBuckets + ",maxScale=" + maxScale + + ",recordMinMax=" + + recordMinMax + "}"; } } diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java index d44a48269bf..dac425543c2 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/DefaultAggregation.java @@ -45,7 +45,7 @@ private static Aggregation resolve(InstrumentDescriptor instrument, boolean with case HISTOGRAM: if (withAdvice && instrument.getAdvice().getExplicitBucketBoundaries() != null) { return ExplicitBucketHistogramAggregation.create( - instrument.getAdvice().getExplicitBucketBoundaries()); + instrument.getAdvice().getExplicitBucketBoundaries(), /* recordMinMax= */ true); } return ExplicitBucketHistogramAggregation.getDefault(); case OBSERVABLE_GAUGE: diff --git a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java index d4af55fd0ad..0a9f13f2f1f 100644 --- a/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java +++ b/sdk/metrics/src/main/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregation.java @@ -28,23 +28,26 @@ public final class ExplicitBucketHistogramAggregation implements Aggregation, Ag private static final Aggregation DEFAULT = new ExplicitBucketHistogramAggregation( - ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES); + ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES, + /* recordMinMax= */ true); public static Aggregation getDefault() { return DEFAULT; } - public static Aggregation create(List bucketBoundaries) { - return new ExplicitBucketHistogramAggregation(bucketBoundaries); + public static Aggregation create(List bucketBoundaries, boolean recordMinMax) { + return new ExplicitBucketHistogramAggregation(bucketBoundaries, recordMinMax); } private final List bucketBoundaries; private final double[] bucketBoundaryArray; + private final boolean recordMinMax; - private ExplicitBucketHistogramAggregation(List bucketBoundaries) { + private ExplicitBucketHistogramAggregation(List bucketBoundaries, boolean recordMinMax) { this.bucketBoundaries = bucketBoundaries; // We need to fail here if our bucket boundaries are ill-configured. this.bucketBoundaryArray = ExplicitBucketHistogramUtils.createBoundaryArray(bucketBoundaries); + this.recordMinMax = recordMinMax; } @Override @@ -56,6 +59,7 @@ public Aggregator createAggregator( return (Aggregator) new DoubleExplicitBucketHistogramAggregator( bucketBoundaryArray, + recordMinMax, ExemplarReservoirFactory.filtered( exemplarFilter, ExemplarReservoirFactory.histogramBucketReservoir( @@ -76,6 +80,11 @@ public boolean isCompatibleWithInstrument(InstrumentDescriptor instrumentDescrip @Override public String toString() { - return "ExplicitBucketHistogramAggregation(" + bucketBoundaries.toString() + ")"; + return "ExplicitBucketHistogramAggregation{" + + "bucketBoundaries=" + + bucketBoundaries + + ",recordMinMax=" + + recordMinMax + + "}"; } } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/AggregationTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/AggregationTest.java index be2502ec054..f907d090093 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/AggregationTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/AggregationTest.java @@ -25,15 +25,23 @@ void haveToString() { assertThat(Aggregation.explicitBucketHistogram()) .asString() .contains("ExplicitBucketHistogramAggregation"); - assertThat(Aggregation.explicitBucketHistogram(Collections.singletonList(1.0d))) + assertThat( + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.singletonList(1.0d)) + .build())) .asString() .contains("ExplicitBucketHistogramAggregation"); assertThat(Aggregation.base2ExponentialBucketHistogram()) .asString() - .isEqualTo("Base2ExponentialHistogramAggregation{maxBuckets=160,maxScale=20}"); - assertThat(Aggregation.base2ExponentialBucketHistogram(2, 0)) + .isEqualTo( + "Base2ExponentialHistogramAggregation{maxBuckets=160,maxScale=20,recordMinMax=true}"); + assertThat( + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder().setMaxBuckets(2).setMaxScale(0).build())) .asString() - .isEqualTo("Base2ExponentialHistogramAggregation{maxBuckets=2,maxScale=0}"); + .isEqualTo( + "Base2ExponentialHistogramAggregation{maxBuckets=2,maxScale=0,recordMinMax=true}"); } @Test diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ExplicitBucketBoundariesAdviceTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ExplicitBucketBoundariesAdviceTest.java index 16d4d05cac0..765d583e520 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ExplicitBucketBoundariesAdviceTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/ExplicitBucketBoundariesAdviceTest.java @@ -5,7 +5,6 @@ package io.opentelemetry.sdk.metrics; -import static io.opentelemetry.sdk.metrics.Aggregation.explicitBucketHistogram; import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; import io.github.netmikey.logunit.api.LogCapturer; @@ -111,7 +110,11 @@ void histogramWithAdviceAndViews(Function> hist .registerView( InstrumentSelector.builder().setType(InstrumentType.HISTOGRAM).build(), View.builder() - .setAggregation(explicitBucketHistogram(Collections.singletonList(50.0))) + .setAggregation( + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.singletonList(50.0)) + .build())) .build()) .build(); @@ -145,7 +148,10 @@ void histogramWithAdviceAndReaderAggregationPreference( DefaultAggregationSelector.getDefault() .with( InstrumentType.HISTOGRAM, - explicitBucketHistogram(Collections.singletonList(50.0)))); + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.singletonList(50.0)) + .build()))); meterProvider = SdkMeterProvider.builder().registerMetricReader(reader).build(); Consumer histogramRecorder = histogramBuilder.apply(meterProvider); diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkDoubleHistogramTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkDoubleHistogramTest.java index 4da66aca7df..c06948acf88 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkDoubleHistogramTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkDoubleHistogramTest.java @@ -186,7 +186,12 @@ void collectMetrics_ExponentialHistogramAggregation() { .registerView( InstrumentSelector.builder().setType(InstrumentType.HISTOGRAM).build(), View.builder() - .setAggregation(Aggregation.base2ExponentialBucketHistogram(5, 20)) + .setAggregation( + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(5) + .setMaxScale(20) + .build())) .build()) .build(); DoubleHistogram doubleHistogram = diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkLongHistogramTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkLongHistogramTest.java index 4ecde4c0c28..1f0321f55bf 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkLongHistogramTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkLongHistogramTest.java @@ -192,7 +192,12 @@ void collectMetrics_ExponentialHistogramScaleResets() { .registerView( InstrumentSelector.builder().setType(InstrumentType.HISTOGRAM).build(), View.builder() - .setAggregation(Aggregation.base2ExponentialBucketHistogram(5, 20)) + .setAggregation( + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(5) + .setMaxScale(20) + .build())) .build()) .build(); @@ -353,7 +358,12 @@ void collectMetrics_ExponentialHistogramAggregation() { .registerView( InstrumentSelector.builder().setType(InstrumentType.HISTOGRAM).build(), View.builder() - .setAggregation(Aggregation.base2ExponentialBucketHistogram(5, 20)) + .setAggregation( + Aggregation.base2ExponentialBucketHistogram( + Base2ExponentialHistogramOptions.builder() + .setMaxBuckets(5) + .setMaxScale(20) + .build())) .build()) .build(); LongHistogram longHistogram = diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkMeterProviderTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkMeterProviderTest.java index 1b6dcafb29a..d8036a5f724 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkMeterProviderTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/SdkMeterProviderTest.java @@ -216,7 +216,11 @@ void collectAllSyncInstruments_OverwriteTemporality() { sdkMeterProviderBuilder.registerView( InstrumentSelector.builder().setType(InstrumentType.COUNTER).build(), View.builder() - .setAggregation(Aggregation.explicitBucketHistogram(Collections.emptyList())) + .setAggregation( + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.emptyList()) + .build())) .build()); InMemoryMetricReader sdkMeterReader = InMemoryMetricReader.createDelta(); SdkMeterProvider sdkMeterProvider = @@ -271,7 +275,11 @@ void collectAllSyncInstruments_OverwriteTemporality() { @Test void collectAllSyncInstruments_DeltaHistogram() { registerViewForAllTypes( - sdkMeterProviderBuilder, Aggregation.explicitBucketHistogram(Collections.emptyList())); + sdkMeterProviderBuilder, + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.emptyList()) + .build())); InMemoryMetricReader sdkMeterReader = InMemoryMetricReader.createDelta(); SdkMeterProvider sdkMeterProvider = sdkMeterProviderBuilder.registerMetricReader(sdkMeterReader).build(); @@ -628,7 +636,11 @@ void viewSdk_AllowMultipleViewsPerSynchronousInstrument() { View.builder() .setName("not_test") .setDescription("not_desc") - .setAggregation(Aggregation.explicitBucketHistogram(Collections.emptyList())) + .setAggregation( + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.emptyList()) + .build())) .build()) .registerView( selector, diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java index 763d36fbec6..47486fab69e 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleBase2ExponentialHistogramAggregatorTest.java @@ -82,10 +82,14 @@ private static Stream provideAggregat for (MemoryMode memoryMode : MemoryMode.values()) { parameters.add( new DoubleBase2ExponentialHistogramAggregator( - ExemplarReservoirFactory.noSamples(), 160, 20, memoryMode)); + ExemplarReservoirFactory.noSamples(), 160, 20, /* recordMinMax= */ true, memoryMode)); parameters.add( new DoubleBase2ExponentialHistogramAggregator( - ExemplarReservoirFactory.noSamples(), 160, MAX_SCALE, memoryMode)); + ExemplarReservoirFactory.noSamples(), + 160, + MAX_SCALE, + /* recordMinMax= */ true, + memoryMode)); } return parameters.stream(); } @@ -98,7 +102,7 @@ private static int valueToIndex(int scale, double value) { private void initialize(MemoryMode memoryMode) { aggregator = new DoubleBase2ExponentialHistogramAggregator( - ExemplarReservoirFactory.noSamples(), 160, 20, memoryMode); + ExemplarReservoirFactory.noSamples(), 160, 20, /* recordMinMax= */ true, memoryMode); } @ParameterizedTest @@ -230,7 +234,8 @@ void testRecordingsAtLimits(DoubleBase2ExponentialHistogramAggregator aggregator @EnumSource(MemoryMode.class) void aggregateThenMaybeReset_WithExemplars(MemoryMode memoryMode) { DoubleBase2ExponentialHistogramAggregator agg = - new DoubleBase2ExponentialHistogramAggregator(reservoirFactory, 160, MAX_SCALE, memoryMode); + new DoubleBase2ExponentialHistogramAggregator( + reservoirFactory, 160, MAX_SCALE, /* recordMinMax= */ true, memoryMode); Attributes attributes = Attributes.builder().put("test", "value").build(); DoubleExemplarData exemplar = @@ -348,7 +353,8 @@ void testToMetricData(MemoryMode memoryMode) { .thenReturn(Collections.singletonList(exemplar)); DoubleBase2ExponentialHistogramAggregator cumulativeAggregator = - new DoubleBase2ExponentialHistogramAggregator(reservoirFactory, 160, MAX_SCALE, memoryMode); + new DoubleBase2ExponentialHistogramAggregator( + reservoirFactory, 160, MAX_SCALE, /* recordMinMax= */ true, memoryMode); AggregatorHandle aggregatorHandle = cumulativeAggregator.createHandle(); @@ -568,4 +574,27 @@ public void reusablePoint_emptyFirstThenRecordAndCheck() { assertThat(point.getPositiveBuckets().getBucketCounts()).isNotEmpty(); assertThat(point.getNegativeBuckets().getBucketCounts()).isNotEmpty(); } + + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testRecordMinMaxDisabled(MemoryMode memoryMode) { + DoubleBase2ExponentialHistogramAggregator aggregator = + new DoubleBase2ExponentialHistogramAggregator( + ExemplarReservoirFactory.noSamples(), 160, 20, /* recordMinMax= */ false, memoryMode); + AggregatorHandle aggregatorHandle = aggregator.createHandle(); + aggregatorHandle.recordDouble(0.5, Attributes.empty(), Context.current()); + aggregatorHandle.recordDouble(1.0, Attributes.empty(), Context.current()); + aggregatorHandle.recordDouble(12.0, Attributes.empty(), Context.current()); + aggregatorHandle.recordDouble(15.213, Attributes.empty(), Context.current()); + aggregatorHandle.recordDouble(-13.2, Attributes.empty(), Context.current()); + aggregatorHandle.recordDouble(-2.01, Attributes.empty(), Context.current()); + + ExponentialHistogramPointData point = + aggregatorHandle.aggregateThenMaybeReset(0, 1, Attributes.empty(), /* reset= */ true); + assertThat(point).isNotNull(); + assertThat(point.hasMin()).isFalse(); + assertThat(point.hasMax()).isFalse(); + assertThat(point.getSum()).isCloseTo(13.503, Offset.offset(0.0001)); + assertThat(point.getCount()).isEqualTo(6); + } } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregatorTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregatorTest.java index 2e425e5cd0c..66fd7dd43b6 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregatorTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/aggregator/DoubleExplicitBucketHistogramAggregatorTest.java @@ -70,7 +70,7 @@ public LongExemplarReservoir createLongExemplarReservoir() { private void init(MemoryMode memoryMode) { aggregator = new DoubleExplicitBucketHistogramAggregator( - boundaries, ExemplarReservoirFactory.noSamples(), memoryMode); + boundaries, /* recordMinMax= */ true, ExemplarReservoirFactory.noSamples(), memoryMode); } @ParameterizedTest @@ -123,7 +123,8 @@ void aggregateThenMaybeReset_WithExemplars(MemoryMode memoryMode) { List exemplars = Collections.singletonList(exemplar); Mockito.when(reservoir.collectAndResetDoubles(Attributes.empty())).thenReturn(exemplars); DoubleExplicitBucketHistogramAggregator aggregator = - new DoubleExplicitBucketHistogramAggregator(boundaries, reservoirFactory, memoryMode); + new DoubleExplicitBucketHistogramAggregator( + boundaries, /* recordMinMax= */ true, reservoirFactory, memoryMode); AggregatorHandle aggregatorHandle = aggregator.createHandle(); aggregatorHandle.recordDouble(0, attributes, Context.root()); assertThat( @@ -286,4 +287,27 @@ void testReusableDataMemoryMode() { // The point data instance should be reused assertThat(anotherPointData).isSameAs(pointData); } + + @ParameterizedTest + @EnumSource(MemoryMode.class) + void testRecordMinMaxDisabled(MemoryMode memoryMode) { + DoubleExplicitBucketHistogramAggregator aggregator = + new DoubleExplicitBucketHistogramAggregator( + boundaries, + /* recordMinMax= */ false, + ExemplarReservoirFactory.noSamples(), + memoryMode); + AggregatorHandle aggregatorHandle = aggregator.createHandle(); + aggregatorHandle.recordLong(20, Attributes.empty(), Context.current()); + aggregatorHandle.recordLong(5, Attributes.empty(), Context.current()); + aggregatorHandle.recordLong(150, Attributes.empty(), Context.current()); + aggregatorHandle.recordLong(2000, Attributes.empty(), Context.current()); + HistogramPointData point = + aggregatorHandle.aggregateThenMaybeReset(0, 1, Attributes.empty(), /* reset= */ true); + assertThat(point.getSum()).isEqualTo(2175); + assertThat(point.hasMin()).isFalse(); + assertThat(point.hasMax()).isFalse(); + assertThat(point.getBoundaries()).isEqualTo(boundariesList); + assertThat(point.getCounts()).isEqualTo(Arrays.asList(1L, 1L, 1L, 1L)); + } } diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java index ee10e75d679..54a4c36e841 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/Base2ExponentialHistogramAggregationTest.java @@ -13,6 +13,7 @@ import io.opentelemetry.context.Context; import io.opentelemetry.sdk.common.export.MemoryMode; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.Base2ExponentialHistogramOptions; import io.opentelemetry.sdk.metrics.ExemplarFilter; import io.opentelemetry.sdk.metrics.InstrumentType; import io.opentelemetry.sdk.metrics.InstrumentValueType; @@ -29,25 +30,27 @@ class Base2ExponentialHistogramAggregationTest { @Test void goodConfig() { assertThat(Base2ExponentialHistogramAggregation.getDefault()).isNotNull(); - assertThat(Base2ExponentialHistogramAggregation.create(10, 20)).isNotNull(); + assertThat(Base2ExponentialHistogramAggregation.create(10, 20, /* recordMinMax= */ true)) + .isNotNull(); } @Test void invalidConfig_Throws() { - assertThatThrownBy(() -> Base2ExponentialHistogramAggregation.create(0, 20)) + assertThatThrownBy(() -> Base2ExponentialHistogramOptions.builder().setMaxBuckets(0).build()) .isInstanceOf(IllegalArgumentException.class) .hasMessage("maxBuckets must be >= 2"); - assertThatThrownBy(() -> Base2ExponentialHistogramAggregation.create(2, 21)) + assertThatThrownBy(() -> Base2ExponentialHistogramOptions.builder().setMaxScale(21).build()) .isInstanceOf(IllegalArgumentException.class) .hasMessage("maxScale must be -10 <= x <= 20"); - assertThatThrownBy(() -> Base2ExponentialHistogramAggregation.create(2, -11)) + assertThatThrownBy(() -> Base2ExponentialHistogramOptions.builder().setMaxScale(-11).build()) .isInstanceOf(IllegalArgumentException.class) .hasMessage("maxScale must be -10 <= x <= 20"); } @Test void minimumBucketsCanAccommodateMaxRange() { - Aggregation aggregation = Base2ExponentialHistogramAggregation.create(2, 20); + Aggregation aggregation = + Base2ExponentialHistogramAggregation.create(2, 20, /* recordMinMax= */ true); Aggregator aggregator = ((AggregatorFactory) aggregation) .createAggregator( diff --git a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregationTest.java b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregationTest.java index 0569a59f710..9299babeb9f 100644 --- a/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregationTest.java +++ b/sdk/metrics/src/test/java/io/opentelemetry/sdk/metrics/internal/view/ExplicitBucketHistogramAggregationTest.java @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import io.opentelemetry.sdk.metrics.Aggregation; +import io.opentelemetry.sdk.metrics.ExplicitBucketHistogramOptions; import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Test; @@ -26,17 +27,33 @@ void badBuckets_throwArgumentException() { assertThatThrownBy( () -> Aggregation.explicitBucketHistogram( - Collections.singletonList(Double.NEGATIVE_INFINITY))) + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Collections.singletonList(Double.NEGATIVE_INFINITY)) + .build())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("invalid bucket boundary: -Inf"); assertThatThrownBy( - () -> Aggregation.explicitBucketHistogram(Arrays.asList(1.0, Double.POSITIVE_INFINITY))) + () -> + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(1.0, Double.POSITIVE_INFINITY)) + .build())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("invalid bucket boundary: +Inf"); - assertThatThrownBy(() -> Aggregation.explicitBucketHistogram(Arrays.asList(1.0, Double.NaN))) + assertThatThrownBy( + () -> + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(1.0, Double.NaN)) + .build())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("invalid bucket boundary: NaN"); - assertThatThrownBy(() -> Aggregation.explicitBucketHistogram(Arrays.asList(2.0, 1.0, 3.0))) + assertThatThrownBy( + () -> + Aggregation.explicitBucketHistogram( + ExplicitBucketHistogramOptions.builder() + .setBucketBoundaries(Arrays.asList(2.0, 1.0, 3.0)) + .build())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Bucket boundaries must be in increasing order: 2.0 >= 1.0"); }