Skip to content

Commit 3e91e2f

Browse files
authored
Polish "Improve Otlp Delta Aggregation with support for max and Histogram" (#3876)
See gh-3749
1 parent ea0fda9 commit 3e91e2f

File tree

18 files changed

+107
-41
lines changed

18 files changed

+107
-41
lines changed

implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpMeterRegistry.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import io.micrometer.core.instrument.*;
2323
import io.micrometer.core.instrument.config.NamingConvention;
2424
import io.micrometer.core.instrument.distribution.*;
25+
import io.micrometer.core.instrument.distribution.Histogram;
2526
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
2627
import io.micrometer.core.instrument.internal.DefaultGauge;
2728
import io.micrometer.core.instrument.internal.DefaultLongTaskTimer;
@@ -39,7 +40,6 @@
3940
import io.opentelemetry.proto.collector.metrics.v1.ExportMetricsServiceRequest;
4041
import io.opentelemetry.proto.common.v1.AnyValue;
4142
import io.opentelemetry.proto.common.v1.KeyValue;
42-
import io.opentelemetry.proto.metrics.v1.Histogram;
4343
import io.opentelemetry.proto.metrics.v1.*;
4444
import io.opentelemetry.proto.resource.v1.Resource;
4545

@@ -372,14 +372,14 @@ Metric writeHistogramSupport(HistogramSupport histogramSupport) {
372372
.addExplicitBounds(isTimeBased ? countAtBucket.bucket(getBaseTimeUnit()) : countAtBucket.bucket());
373373
histogramDataPoint.addBucketCounts((long) countAtBucket.count());
374374
}
375-
metricBuilder.setHistogram(Histogram.newBuilder()
375+
metricBuilder.setHistogram(io.opentelemetry.proto.metrics.v1.Histogram.newBuilder()
376376
.setAggregationTemporality(otlpAggregationTemporality)
377377
.addDataPoints(histogramDataPoint));
378378
return metricBuilder.build();
379379
}
380380

381381
return metricBuilder
382-
.setHistogram(Histogram.newBuilder()
382+
.setHistogram(io.opentelemetry.proto.metrics.v1.Histogram.newBuilder()
383383
.setAggregationTemporality(otlpAggregationTemporality)
384384
.addDataPoints(histogramDataPoint))
385385
.build();
@@ -388,7 +388,7 @@ Metric writeHistogramSupport(HistogramSupport histogramSupport) {
388388
// VisibleForTesting
389389
Metric writeFunctionTimer(FunctionTimer functionTimer) {
390390
return getMetricBuilder(functionTimer.getId())
391-
.setHistogram(Histogram.newBuilder()
391+
.setHistogram(io.opentelemetry.proto.metrics.v1.Histogram.newBuilder()
392392
.addDataPoints(HistogramDataPoint.newBuilder()
393393
.addAllAttributes(getTagsForId(functionTimer.getId()))
394394
.setStartTimeUnixNano(getStartTimeNanos((functionTimer)))
@@ -466,14 +466,13 @@ Iterable<KeyValue> getResourceAttributes() {
466466
return attributes;
467467
}
468468

469-
static io.micrometer.core.instrument.distribution.Histogram getHistogram(Clock clock,
470-
DistributionStatisticConfig distributionStatisticConfig, AggregationTemporality aggregationTemporality) {
469+
static Histogram getHistogram(Clock clock, DistributionStatisticConfig distributionStatisticConfig,
470+
AggregationTemporality aggregationTemporality) {
471471
return getHistogram(clock, distributionStatisticConfig, aggregationTemporality, 0);
472472
}
473473

474-
static io.micrometer.core.instrument.distribution.Histogram getHistogram(Clock clock,
475-
DistributionStatisticConfig distributionStatisticConfig, AggregationTemporality aggregationTemporality,
476-
long stepMillis) {
474+
static Histogram getHistogram(Clock clock, DistributionStatisticConfig distributionStatisticConfig,
475+
AggregationTemporality aggregationTemporality, long stepMillis) {
477476
// While publishing to OTLP, we export either Histogram datapoint / Summary
478477
// datapoint. So, we will make the histogram either of them and not both.
479478
// Though AbstractTimer/Distribution Summary prefers publishing percentiles,
@@ -488,7 +487,7 @@ static io.micrometer.core.instrument.distribution.Histogram getHistogram(Clock c
488487
.build()
489488
.merge(distributionStatisticConfig), true, false);
490489
}
491-
else if (AggregationTemporality.isDelta(aggregationTemporality) && stepMillis > 0) {
490+
if (AggregationTemporality.isDelta(aggregationTemporality) && stepMillis > 0) {
492491
return new OtlpStepBucketHistogram(clock, stepMillis, distributionStatisticConfig, true, false);
493492
}
494493
}

implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpStepDistributionSummary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class OtlpStepDistributionSummary extends AbstractDistributionSummary {
5050

5151
@Override
5252
protected void recordNonNegative(double amount) {
53-
count.add(1);
53+
count.add(1L);
5454
total.add(amount);
5555
max.record(amount);
5656
}

implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/OtlpStepTimer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class OtlpStepTimer extends AbstractTimer {
5454
@Override
5555
protected void recordNonNegative(final long amount, final TimeUnit unit) {
5656
final long nanoAmount = (long) TimeUtils.convert(amount, unit, TimeUnit.NANOSECONDS);
57-
count.add(1);
57+
count.add(1L);
5858
total.add(nanoAmount);
5959
max.record(nanoAmount);
6060
}

implementations/micrometer-registry-otlp/src/main/java/io/micrometer/registry/otlp/StepMax.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,15 @@
2222
import java.util.function.Supplier;
2323

2424
/**
25-
* A {@link StepValue} implementation that tracks max over a Step Interval.
25+
* A {@link StepValue} implementation that tracks max over a step interval.
2626
*
2727
* @author Lenin Jaganathan
28-
* @since 1.11.0
2928
*/
3029
class StepMax extends StepValue<Double> {
3130

32-
private final DoubleAccumulator current = new DoubleAccumulator(Double::max, 0);
31+
private final DoubleAccumulator current = new DoubleAccumulator(Double::max, 0d);
3332

34-
public StepMax(Clock clock, long stepMillis) {
33+
StepMax(Clock clock, long stepMillis) {
3534
super(clock, stepMillis);
3635
}
3736

@@ -45,7 +44,7 @@ protected Double noValue() {
4544
return 0.0;
4645
}
4746

48-
public void record(double value) {
47+
void record(double value) {
4948
current.accumulate(value);
5049
}
5150

implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpCumulativeMeterRegistryTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package io.micrometer.registry.otlp;
1717

1818
import io.micrometer.core.instrument.*;
19+
import io.micrometer.core.instrument.binder.BaseUnits;
1920
import io.opentelemetry.proto.metrics.v1.NumberDataPoint;
2021
import org.junit.jupiter.api.Test;
2122

@@ -201,7 +202,7 @@ void functionTimer() {
201202
@Test
202203
void distributionSummary() {
203204
DistributionSummary size = DistributionSummary.builder("http.response.size")
204-
.baseUnit("bytes")
205+
.baseUnit(BaseUnits.BYTES)
205206
.register(registry);
206207
size.record(100);
207208
size.record(15);
@@ -218,7 +219,7 @@ void distributionSummary() {
218219
@Test
219220
void distributionSummaryWithHistogramBuckets() {
220221
DistributionSummary size = DistributionSummary.builder("http.request.size")
221-
.baseUnit("bytes")
222+
.baseUnit(BaseUnits.BYTES)
222223
.publishPercentileHistogram()
223224
.register(registry);
224225
size.record(100);

implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpDeltaMeterRegistryCompatibilityTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
import java.time.Duration;
2323

24-
public class OtlpDeltaMeterRegistryCompatibilityTest extends MeterRegistryCompatibilityKit {
24+
class OtlpDeltaMeterRegistryCompatibilityTest extends MeterRegistryCompatibilityKit {
2525

2626
@Override
2727
public MeterRegistry registry() {

implementations/micrometer-registry-otlp/src/test/java/io/micrometer/registry/otlp/OtlpDeltaMeterRegistryTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public String get(String key) {
6565
return null;
6666
}
6767
};
68-
};
68+
}
6969

7070
@Test
7171
void gauge() {
@@ -321,7 +321,7 @@ void testMetricsStartAndEndTime() {
321321
Function<Meter, NumberDataPoint> getDataPoint = (meter) -> writeToMetric(meter).getSum().getDataPoints(0);
322322
assertThat(getDataPoint.apply(counter).getStartTimeUnixNano()).isEqualTo(0);
323323
assertThat(getDataPoint.apply(counter).getTimeUnixNano()).isEqualTo(60000000000L);
324-
clock.addSeconds(59);
324+
clock.addSeconds(otlpConfig().step().getSeconds() - 1);
325325
assertThat(getDataPoint.apply(counter).getStartTimeUnixNano()).isEqualTo(0);
326326
assertThat(getDataPoint.apply(counter).getTimeUnixNano()).isEqualTo(60000000000L);
327327
clock.addSeconds(1);

micrometer-core/src/main/java/io/micrometer/core/instrument/AbstractDistributionSummary.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,19 +29,34 @@ protected AbstractDistributionSummary(Id id, Clock clock, DistributionStatisticC
2929
this(id, scale, defaultHistogram(clock, distributionStatisticConfig, supportsAggregablePercentiles));
3030
}
3131

32+
/**
33+
* Creates an {@code AbstractDistributionSummary} instance.
34+
* @param id meter ID
35+
* @param scale scale
36+
* @param histogram histogram
37+
* @since 1.11.0
38+
*/
3239
protected AbstractDistributionSummary(Id id, double scale, @Nullable Histogram histogram) {
3340
super(id);
3441
this.scale = scale;
3542
this.histogram = histogram == null ? NoopHistogram.INSTANCE : histogram;
3643
}
3744

45+
/**
46+
* Creates a default histogram.
47+
* @param clock clock
48+
* @param distributionStatisticConfig distribution statistic configuration
49+
* @param supportsAggregablePercentiles whether to support aggregable percentiles
50+
* @return a default histogram
51+
* @since 1.11.0
52+
*/
3853
protected static Histogram defaultHistogram(Clock clock, DistributionStatisticConfig distributionStatisticConfig,
3954
boolean supportsAggregablePercentiles) {
4055
if (distributionStatisticConfig.isPublishingPercentiles()) {
4156
// hdr-based histogram
4257
return new TimeWindowPercentileHistogram(clock, distributionStatisticConfig, supportsAggregablePercentiles);
4358
}
44-
else if (distributionStatisticConfig.isPublishingHistogram()) {
59+
if (distributionStatisticConfig.isPublishingHistogram()) {
4560
// fixed boundary histograms, which have a slightly better memory footprint
4661
// when we don't need Micrometer-computed percentiles
4762
return new TimeWindowFixedBoundaryHistogram(clock, distributionStatisticConfig,

micrometer-core/src/main/java/io/micrometer/core/instrument/AbstractTimer.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,15 @@ protected AbstractTimer(Id id, Clock clock, DistributionStatisticConfig distribu
8686
defaultHistogram(clock, distributionStatisticConfig, supportsAggregablePercentiles));
8787
}
8888

89+
/**
90+
* Creates a new timer.
91+
* @param id The timer's name and tags.
92+
* @param clock The clock used to measure latency.
93+
* @param pauseDetector Compensation for coordinated omission.
94+
* @param baseTimeUnit The time scale of this timer.
95+
* @param histogram Histogram.
96+
* @since 1.11.0
97+
*/
8998
protected AbstractTimer(Id id, Clock clock, PauseDetector pauseDetector, TimeUnit baseTimeUnit,
9099
Histogram histogram) {
91100
super(id);
@@ -95,13 +104,23 @@ protected AbstractTimer(Id id, Clock clock, PauseDetector pauseDetector, TimeUni
95104
this.histogram = histogram;
96105
}
97106

107+
/**
108+
* Creates a default histogram.
109+
* @param clock The clock used to measure latency.
110+
* @param distributionStatisticConfig Configuration determining which distribution
111+
* statistics are sent.
112+
* @param supportsAggregablePercentiles Indicates whether the registry supports
113+
* percentile approximations from histograms.
114+
* @return a default histogram
115+
* @since 1.11.0
116+
*/
98117
protected static Histogram defaultHistogram(Clock clock, DistributionStatisticConfig distributionStatisticConfig,
99118
boolean supportsAggregablePercentiles) {
100119
if (distributionStatisticConfig.isPublishingPercentiles()) {
101120
// hdr-based histogram
102121
return new TimeWindowPercentileHistogram(clock, distributionStatisticConfig, supportsAggregablePercentiles);
103122
}
104-
else if (distributionStatisticConfig.isPublishingHistogram()) {
123+
if (distributionStatisticConfig.isPublishingHistogram()) {
105124
// fixed boundary histograms, which have a slightly better memory footprint
106125
// when we don't need Micrometer-computed percentiles
107126
return new TimeWindowFixedBoundaryHistogram(clock, distributionStatisticConfig,

micrometer-core/src/main/java/io/micrometer/core/instrument/cumulative/CumulativeDistributionSummary.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,15 @@ public CumulativeDistributionSummary(Id id, Clock clock, DistributionStatisticCo
5454
distributionStatisticConfig, supportsAggregablePercentiles));
5555
}
5656

57+
/**
58+
* Creates a {@code CumulativeDistributionSummary} instance.
59+
* @param id meter ID
60+
* @param clock clock
61+
* @param distributionStatisticConfig distribution statistic configuration
62+
* @param scale scale
63+
* @param histogram histogram
64+
* @since 1.11.0
65+
*/
5766
protected CumulativeDistributionSummary(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
5867
double scale, Histogram histogram) {
5968
super(id, scale, histogram);

micrometer-core/src/main/java/io/micrometer/core/instrument/cumulative/CumulativeTimer.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ public CumulativeTimer(Id id, Clock clock, DistributionStatisticConfig distribut
4848
AbstractTimer.defaultHistogram(clock, distributionStatisticConfig, supportsAggregablePercentiles));
4949
}
5050

51+
/**
52+
* Creates a {@code CumulativeTimer} instance.
53+
* @param id meter ID
54+
* @param clock clock
55+
* @param distributionStatisticConfig distribution statistic configuration
56+
* @param pauseDetector pause detector
57+
* @param baseTimeUnit base time unit
58+
* @param histogram histogram
59+
* @since 1.11.0
60+
*/
5161
protected CumulativeTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
5262
PauseDetector pauseDetector, TimeUnit baseTimeUnit, Histogram histogram) {
5363
super(id, clock, pauseDetector, baseTimeUnit, histogram);

micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/DistributionStatisticConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ private void validate(DistributionStatisticConfig distributionStatisticConfig) {
477477
}
478478

479479
if (config.maximumExpectedValue != null && config.maximumExpectedValue <= 0) {
480-
rejectConfig("maximumExpectedValue (" + config.minimumExpectedValue + ") must be greater than 0.");
480+
rejectConfig("maximumExpectedValue (" + config.maximumExpectedValue + ") must be greater than 0.");
481481
}
482482

483483
if ((config.minimumExpectedValue != null && config.maximumExpectedValue != null)

micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/FixedBoundaryHistogram.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,16 @@ void record(long value) {
5555
/**
5656
* The least bucket that is less than or equal to a sample.
5757
*/
58-
int leastLessThanOrEqualTo(long key) {
58+
int leastLessThanOrEqualTo(double key) {
5959
int low = 0;
6060
int high = buckets.length - 1;
6161

6262
while (low <= high) {
6363
int mid = (low + high) >>> 1;
64-
if (buckets[mid] < key)
64+
double value = buckets[mid];
65+
if (value < key)
6566
low = mid + 1;
66-
else if (buckets[mid] > key)
67+
else if (value > key)
6768
high = mid - 1;
6869
else
6970
return mid; // exact match

micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/StepBucketHistogram.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
import java.util.function.Supplier;
2727

2828
/**
29-
* A Histogram implementation which inherits the behaviour of Step Meters i,e this
30-
* histogram exhibits read and reset behaviour.
29+
* A Histogram implementation which inherits the behaviour of step meters, i.e. read and
30+
* reset behaviour.
3131
*
3232
* @author Lenin Jaganathan
3333
* @since 1.11.0
@@ -60,7 +60,7 @@ public void recordDouble(double value) {
6060

6161
@Override
6262
public HistogramSnapshot takeSnapshot(long count, double total, double max) {
63-
return new HistogramSnapshot(count, total, max, null, this.poll(), null);
63+
return new HistogramSnapshot(count, total, max, null, poll(), null);
6464
}
6565

6666
@Override
@@ -95,7 +95,9 @@ private static CountAtBucket[] getEmptyCounts(double[] buckets) {
9595
private static double[] getBucketsFromDistributionStatisticConfig(
9696
DistributionStatisticConfig distributionStatisticConfig, boolean supportsAggregablePercentiles) {
9797
if (distributionStatisticConfig.getMaximumExpectedValueAsDouble() == null
98-
|| distributionStatisticConfig.getMinimumExpectedValueAsDouble() == null) {
98+
|| distributionStatisticConfig.getMinimumExpectedValueAsDouble() == null
99+
|| distributionStatisticConfig.getMaximumExpectedValueAsDouble() <= 0
100+
|| distributionStatisticConfig.getMinimumExpectedValueAsDouble() <= 0) {
99101
throw new InvalidConfigurationException(
100102
"minimumExpectedValue and maximumExpectedValue should be greater than 0.");
101103
}

micrometer-core/src/main/java/io/micrometer/core/instrument/distribution/TimeWindowFixedBoundaryHistogram.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ void resetAccumulatedHistogram() {
109109
}
110110

111111
/**
112-
* For recording efficiency, We turn normal histogram into cumulative count histogram
112+
* For recording efficiency, we turn normal histogram into cumulative count histogram
113113
* only on calls to {@link #countsAtValues(Iterator<Double>)}.
114114
*/
115115
@Override

micrometer-core/src/main/java/io/micrometer/core/instrument/step/StepValue.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ public StepValue(final Clock clock, final long stepMillis) {
4343
this(clock, stepMillis, null);
4444
}
4545

46+
/**
47+
* Creates a {@code StepValue} instance.
48+
* @param clock clock
49+
* @param stepMillis step in milliseconds
50+
* @param initValue initial value
51+
* @since 1.11.0
52+
*/
4653
protected StepValue(final Clock clock, final long stepMillis, @Nullable final V initValue) {
4754
this.clock = clock;
4855
this.stepMillis = stepMillis;

micrometer-core/src/test/java/io/micrometer/core/instrument/distribution/StepHistogramTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,10 @@ void sloWithAggregablePercentilesTrue_sloBucketPlusPercentilesHistogramBuckets()
8686
.merge(distributionStatisticConfig);
8787
try (StepBucketHistogram histogram = new StepBucketHistogram(clock, step.toMillis(), config,
8888
supportsAggregablePercentiles, true)) {
89-
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).contains(new CountAtBucket(slo, 0));
89+
CountAtBucket sloBucket = new CountAtBucket(slo, 0);
90+
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).hasSizeGreaterThan(1).contains(sloBucket);
9091
clock.add(step);
91-
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).contains(new CountAtBucket(slo, 0));
92+
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).hasSizeGreaterThan(1).contains(sloBucket);
9293
}
9394
}
9495

@@ -103,9 +104,10 @@ void sloWithPercentileHistogramFalse_onlySloBucket() {
103104
.merge(getConfig(false));
104105
try (StepBucketHistogram histogram = new StepBucketHistogram(clock, step.toMillis(), config,
105106
supportsAggregablePercentiles, true)) {
106-
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).contains(new CountAtBucket(slo, 0));
107+
CountAtBucket sloBucket = new CountAtBucket(slo, 0);
108+
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).containsExactly(sloBucket);
107109
clock.add(step);
108-
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).contains(new CountAtBucket(slo, 0));
110+
assertThat(histogram.takeSnapshot(0, 0, 0).histogramCounts()).containsExactly(sloBucket);
109111
}
110112
}
111113

0 commit comments

Comments
 (0)