Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions docs/modules/ROOT/pages/implementations/otlp.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,17 @@ Micrometer `Timer` and `DistributionSummary` support configuring xref:/concepts/
| publishPercentiles and serviceLevelObjectives
| Histogram
|===
__* The configuration `histogramFlavor` determines whether the OTLP DataPoint is a Histogram/Exponential Histogram.__
__* The configuration `histogramFlavorPerMeter` and `histogramFlavor` determine whether the OTLP DataPoint is a Histogram/Exponential Histogram.__

`OtlpMeterRegistry` supports 2 types of Histogram implementations (1.Explicit Bucket Histogram (or simply called Histogram), 2. Exponential Histogram) when `publishPercentileHistogram` is configured. The choice is chosen by setting `histogramFlavor` in `OtlpConfig` used by registry. When the implementation is exponential histogram, it also supports 2 additional properties
`OtlpMeterRegistry` supports 2 types of Histogram implementations (1.Explicit Bucket Histogram (or simply called Histogram), 2. Exponential Histogram) when `publishPercentileHistogram` is configured.
The type is determined by `histogramFlavorPerMeter` and `histogramFlavor` in `OtlpConfig` used by the registry.
When the implementation is the exponential histogram, additional configuration applies:

1. maxScale used to cap the maximum https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponential-scale[scale, window=_blank] used by the Exponential Histogram (defaults to 20).
2. maxBuckets determines the maximum number of https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponential-buckets[buckets, window=_blank] to be used for exponential histograms (defaults to 160).
1. `maxScale` used to cap the maximum https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponential-scale[scale, window=_blank] used by the Exponential Histogram (defaults to 20).
2. `maxBuckets` determines the maximum number of https://opentelemetry.io/docs/specs/otel/metrics/data-model/#exponential-buckets[buckets, window=_blank] to be used for exponential histograms (defaults to 160).
3. `maxBucketsPerMeter` overrides `maxBuckets` for specific meter names, giving more fine-grained configurability.

Since Exponential Histogram cannot have custom SLO's specified, explicit bucket histogram is used whenever `serviceLevelObjectives` are added.
Since Exponential Histogram cannot have custom SLO's specified, an explicit bucket histogram is used whenever `serviceLevelObjectives` are configured.

=== Configuration with Spring Boot
If you use Spring Boot, you can use the https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#actuator.metrics.customizing.per-meter-properties[per-meter properties, window=_blank] to configure this behavior.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@

import java.time.Duration;
import java.net.URLDecoder;
import java.util.Arrays;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -215,6 +212,7 @@ default Map<String, String> headers() {
* {@link HistogramFlavor#EXPLICIT_BUCKET_HISTOGRAM} is used for those meters.
* </p>
* @return - histogram flavor to be used
* @see #histogramFlavorPerMeter()
*
* @since 1.14.0
*/
Expand All @@ -229,6 +227,19 @@ default HistogramFlavor histogramFlavor() {
});
}

/**
* Configures the histogram flavor to use on a per-meter level. This will override the
* {@link #histogramFlavor()} configuration for matching Meters. The key is used to do
* an exact match on the Meter's name.
* @return mapping of meter name to histogram flavor
* @since 1.15.0
* @see #histogramFlavor()
*/
default Map<String, HistogramFlavor> histogramFlavorPerMeter() {
return getStringMap(this, "histogramFlavorPerMeter", HistogramFlavor::fromString)
.orElse(Collections.emptyMap());
}

/**
* Max scale to use for exponential histograms, if configured.
* @return maxScale
Expand All @@ -242,22 +253,39 @@ default int maxScale() {

/**
* Maximum number of buckets to be used for exponential histograms, if configured.
* This has no effect on explicit bucket histograms.
* This has no effect on explicit bucket histograms. This can be overridden per meter
* with {@link #maxBucketsPerMeter()}.
* @return - maxBuckets
* @see #histogramFlavor()
* @see #maxBucketsPerMeter()
*
* @since 1.14.0
*/
default int maxBucketCount() {
return getInteger(this, "maxBucketCount").orElse(160);
}

/**
* Configures the max bucket count to use on a per-meter level. This will override the
* {@link #maxBucketCount()} configuration for matching Meters. The key is used to do
* an exact match on the Meter's name. This has no effect on a meter if it does not
* have an exponential bucket histogram configured.
* @return mapping of meter name to max bucket count
* @since 1.15.0
* @see #maxBucketCount()
*/
default Map<String, Integer> maxBucketsPerMeter() {
return getStringMap(this, "maxBucketsPerMeter", Integer::parseInt).orElse(Collections.emptyMap());
}

@Override
default Validated<?> validate() {
return checkAll(this, c -> PushRegistryConfig.validate(c), checkRequired("url", OtlpConfig::url),
check("resourceAttributes", OtlpConfig::resourceAttributes),
check("baseTimeUnit", OtlpConfig::baseTimeUnit),
check("aggregationTemporality", OtlpConfig::aggregationTemporality));
check("aggregationTemporality", OtlpConfig::aggregationTemporality),
check("histogramFlavorPerMeter", OtlpConfig::histogramFlavorPerMeter),
check("maxBucketsPerMeter", OtlpConfig::maxBucketsPerMeter));
}

default TimeUnit baseTimeUnit() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,12 @@
class OtlpCumulativeDistributionSummary extends CumulativeDistributionSummary
implements StartTimeAwareMeter, OtlpHistogramSupport {

private final HistogramFlavor histogramFlavor;

private final long startTimeNanos;

OtlpCumulativeDistributionSummary(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
double scale, OtlpConfig otlpConfig) {
super(id, clock, distributionStatisticConfig, scale,
OtlpMeterRegistry.getHistogram(clock, distributionStatisticConfig, otlpConfig));
double scale, Histogram histogram) {
super(id, clock, distributionStatisticConfig, scale, histogram);
this.startTimeNanos = TimeUnit.MILLISECONDS.toNanos(clock.wallTime());
this.histogramFlavor = OtlpMeterRegistry.histogramFlavor(otlpConfig.histogramFlavor(),
distributionStatisticConfig);
}

@Override
Expand All @@ -48,7 +43,7 @@ public long getStartTimeNanos() {
@Override
@Nullable
public ExponentialHistogramSnapShot getExponentialHistogramSnapShot() {
if (histogramFlavor == HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM) {
if (histogram instanceof Base2ExponentialHistogram) {
return ((Base2ExponentialHistogram) histogram).getLatestExponentialHistogramSnapshot();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,11 @@

class OtlpCumulativeTimer extends CumulativeTimer implements StartTimeAwareMeter, OtlpHistogramSupport {

private final HistogramFlavor histogramFlavor;

private final long startTimeNanos;

OtlpCumulativeTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
PauseDetector pauseDetector, TimeUnit baseTimeUnit, OtlpConfig otlpConfig) {
super(id, clock, distributionStatisticConfig, pauseDetector, baseTimeUnit,
OtlpMeterRegistry.getHistogram(clock, distributionStatisticConfig, otlpConfig, baseTimeUnit));
this.histogramFlavor = OtlpMeterRegistry.histogramFlavor(otlpConfig.histogramFlavor(),
distributionStatisticConfig);
PauseDetector pauseDetector, TimeUnit baseTimeUnit, Histogram histogram) {
super(id, clock, distributionStatisticConfig, pauseDetector, baseTimeUnit, histogram);
this.startTimeNanos = TimeUnit.MILLISECONDS.toNanos(clock.wallTime());
}

Expand All @@ -48,7 +43,7 @@ public long getStartTimeNanos() {
@Override
@Nullable
public ExponentialHistogramSnapShot getExponentialHistogramSnapShot() {
if (histogramFlavor == HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM) {
if (histogram instanceof Base2ExponentialHistogram) {
return ((Base2ExponentialHistogram) histogram).getLatestExponentialHistogramSnapshot();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,16 +205,19 @@ protected Timer newTimer(Meter.Id id, DistributionStatisticConfig distributionSt
PauseDetector pauseDetector) {
return isCumulative()
? new OtlpCumulativeTimer(id, this.clock, distributionStatisticConfig, pauseDetector, getBaseTimeUnit(),
config)
: new OtlpStepTimer(id, clock, distributionStatisticConfig, pauseDetector, getBaseTimeUnit(), config);
getHistogram(id, distributionStatisticConfig, getBaseTimeUnit()))
: new OtlpStepTimer(id, clock, pauseDetector,
getHistogram(id, distributionStatisticConfig, getBaseTimeUnit()), config);
}

@Override
protected DistributionSummary newDistributionSummary(Meter.Id id,
DistributionStatisticConfig distributionStatisticConfig, double scale) {
return isCumulative()
? new OtlpCumulativeDistributionSummary(id, this.clock, distributionStatisticConfig, scale, config)
: new OtlpStepDistributionSummary(id, clock, distributionStatisticConfig, scale, config);
? new OtlpCumulativeDistributionSummary(id, clock, distributionStatisticConfig, scale,
getHistogram(id, distributionStatisticConfig))
: new OtlpStepDistributionSummary(id, clock, scale, getHistogram(id, distributionStatisticConfig),
config);
}

@Override
Expand Down Expand Up @@ -372,13 +375,12 @@ Iterable<KeyValue> getResourceAttributes() {
return attributes;
}

static Histogram getHistogram(Clock clock, DistributionStatisticConfig distributionStatisticConfig,
OtlpConfig otlpConfig) {
return getHistogram(clock, distributionStatisticConfig, otlpConfig, null);
private Histogram getHistogram(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig) {
return getHistogram(id, distributionStatisticConfig, null);
}

static Histogram getHistogram(final Clock clock, final DistributionStatisticConfig distributionStatisticConfig,
final OtlpConfig otlpConfig, @Nullable final TimeUnit baseTimeUnit) {
private Histogram getHistogram(Meter.Id id, DistributionStatisticConfig distributionStatisticConfig,
@Nullable TimeUnit baseTimeUnit) {
// While publishing to OTLP, we export either Histogram datapoint (Explicit
// ExponentialBuckets
// or Exponential) / Summary
Expand All @@ -387,21 +389,21 @@ static Histogram getHistogram(final Clock clock, final DistributionStatisticConf
// exporting of histograms over percentiles is preferred in OTLP.
if (distributionStatisticConfig.isPublishingHistogram()) {
if (HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM
.equals(histogramFlavor(otlpConfig.histogramFlavor(), distributionStatisticConfig))) {
.equals(histogramFlavor(id, config, distributionStatisticConfig))) {
Double minimumExpectedValue = distributionStatisticConfig.getMinimumExpectedValueAsDouble();
if (minimumExpectedValue == null) {
minimumExpectedValue = 0.0;
}

return otlpConfig.aggregationTemporality() == AggregationTemporality.DELTA
? new DeltaBase2ExponentialHistogram(otlpConfig.maxScale(), otlpConfig.maxBucketCount(),
minimumExpectedValue, baseTimeUnit, clock, otlpConfig.step().toMillis())
: new CumulativeBase2ExponentialHistogram(otlpConfig.maxScale(), otlpConfig.maxBucketCount(),
return config.aggregationTemporality() == AggregationTemporality.DELTA
? new DeltaBase2ExponentialHistogram(config.maxScale(), getMaxBuckets(id), minimumExpectedValue,
baseTimeUnit, clock, config.step().toMillis())
: new CumulativeBase2ExponentialHistogram(config.maxScale(), getMaxBuckets(id),
minimumExpectedValue, baseTimeUnit);
}

Histogram explicitBucketHistogram = getExplicitBucketHistogram(clock, distributionStatisticConfig,
otlpConfig.aggregationTemporality(), otlpConfig.step().toMillis());
config.aggregationTemporality(), config.step().toMillis());
if (explicitBucketHistogram != null) {
return explicitBucketHistogram;
}
Expand All @@ -413,8 +415,14 @@ static Histogram getHistogram(final Clock clock, final DistributionStatisticConf
return NoopHistogram.INSTANCE;
}

static HistogramFlavor histogramFlavor(HistogramFlavor preferredHistogramFlavor,
private int getMaxBuckets(Meter.Id id) {
return config.maxBucketsPerMeter().getOrDefault(id.getName(), config.maxBucketCount());
}

private HistogramFlavor histogramFlavor(Meter.Id id, OtlpConfig otlpConfig,
DistributionStatisticConfig distributionStatisticConfig) {
HistogramFlavor preferredHistogramFlavor = otlpConfig.histogramFlavorPerMeter()
.getOrDefault(id.getName(), otlpConfig.histogramFlavor());

final double[] serviceLevelObjectiveBoundaries = distributionStatisticConfig
.getServiceLevelObjectiveBoundaries();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/
package io.micrometer.registry.otlp;

import io.micrometer.common.lang.Nullable;
import io.micrometer.core.instrument.AbstractDistributionSummary;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.Histogram;
import io.micrometer.registry.otlp.internal.Base2ExponentialHistogram;
import io.micrometer.registry.otlp.internal.ExponentialHistogramSnapShot;

Expand All @@ -26,8 +27,6 @@

class OtlpStepDistributionSummary extends AbstractDistributionSummary implements OtlpHistogramSupport {

private final HistogramFlavor histogramFlavor;

private final LongAdder count = new LongAdder();

private final DoubleAdder total = new DoubleAdder();
Expand All @@ -40,18 +39,14 @@ class OtlpStepDistributionSummary extends AbstractDistributionSummary implements
* Create a new {@code OtlpStepDistributionSummary}.
* @param id ID
* @param clock clock
* @param distributionStatisticConfig distribution statistic configuration
* @param scale scale
* @param otlpConfig config for registry
*/
OtlpStepDistributionSummary(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
double scale, OtlpConfig otlpConfig) {
super(id, scale, OtlpMeterRegistry.getHistogram(clock, distributionStatisticConfig, otlpConfig));
OtlpStepDistributionSummary(Id id, Clock clock, double scale, Histogram histogram, OtlpConfig otlpConfig) {
super(id, scale, histogram);
this.countTotal = new OtlpStepTuple2<>(clock, otlpConfig.step().toMillis(), 0L, 0.0, count::sumThenReset,
total::sumThenReset);
this.max = new StepMax(clock, otlpConfig.step().toMillis());
this.histogramFlavor = OtlpMeterRegistry.histogramFlavor(otlpConfig.histogramFlavor(),
distributionStatisticConfig);
}

@Override
Expand All @@ -77,8 +72,9 @@ public double max() {
}

@Override
@Nullable
public ExponentialHistogramSnapShot getExponentialHistogramSnapShot() {
if (histogramFlavor == HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM) {
if (histogram instanceof Base2ExponentialHistogram) {
return ((Base2ExponentialHistogram) histogram).getLatestExponentialHistogramSnapshot();
}
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
*/
package io.micrometer.registry.otlp;

import io.micrometer.common.lang.Nullable;
import io.micrometer.core.instrument.AbstractTimer;
import io.micrometer.core.instrument.Clock;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.Histogram;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.micrometer.core.instrument.util.TimeUtils;
import io.micrometer.registry.otlp.internal.Base2ExponentialHistogram;
Expand All @@ -28,8 +29,6 @@

class OtlpStepTimer extends AbstractTimer implements OtlpHistogramSupport {

private final HistogramFlavor histogramFlavor;

private final LongAdder count = new LongAdder();

private final LongAdder total = new LongAdder();
Expand All @@ -42,20 +41,14 @@ class OtlpStepTimer extends AbstractTimer implements OtlpHistogramSupport {
* Create a new {@code OtlpStepTimer}.
* @param id ID
* @param clock clock
* @param distributionStatisticConfig distribution statistic configuration
* @param pauseDetector pause detector
* @param baseTimeUnit base time unit
* @param otlpConfig config of the registry
*/
OtlpStepTimer(Id id, Clock clock, DistributionStatisticConfig distributionStatisticConfig,
PauseDetector pauseDetector, TimeUnit baseTimeUnit, OtlpConfig otlpConfig) {
super(id, clock, pauseDetector, otlpConfig.baseTimeUnit(),
OtlpMeterRegistry.getHistogram(clock, distributionStatisticConfig, otlpConfig, baseTimeUnit));
OtlpStepTimer(Id id, Clock clock, PauseDetector pauseDetector, Histogram histogram, OtlpConfig otlpConfig) {
super(id, clock, pauseDetector, otlpConfig.baseTimeUnit(), histogram);
countTotal = new OtlpStepTuple2<>(clock, otlpConfig.step().toMillis(), 0L, 0L, count::sumThenReset,
total::sumThenReset);
max = new StepMax(clock, otlpConfig.step().toMillis());
this.histogramFlavor = OtlpMeterRegistry.histogramFlavor(otlpConfig.histogramFlavor(),
distributionStatisticConfig);
}

@Override
Expand Down Expand Up @@ -99,8 +92,9 @@ else if (histogram instanceof Base2ExponentialHistogram) {
}

@Override
@Nullable
public ExponentialHistogramSnapShot getExponentialHistogramSnapShot() {
if (histogramFlavor == HistogramFlavor.BASE2_EXPONENTIAL_BUCKET_HISTOGRAM) {
if (histogram instanceof Base2ExponentialHistogram) {
return ((Base2ExponentialHistogram) histogram).getLatestExponentialHistogramSnapshot();
}
return null;
Expand Down
Loading