io.micrometer.core.instrument.distribution.DistributionStatisticConfig Maven / Gradle / Ivy
/*
* Copyright 2017 VMware, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.micrometer.core.instrument.distribution;
import io.micrometer.core.instrument.internal.Mergeable;
import io.micrometer.core.lang.Nullable;
import java.time.Duration;
import java.util.NavigableSet;
import java.util.TreeSet;
import java.util.stream.LongStream;
/**
* Configures the distribution statistics that emanate from meters like
* {@link io.micrometer.core.instrument.Timer} and
* {@link io.micrometer.core.instrument.DistributionSummary}.
*
* These statistics include max, percentiles, percentile histograms, and SLO violations.
*
* Many distribution statistics are decayed to give greater weight to recent samples.
*
* @author Jon Schneider
*/
public class DistributionStatisticConfig implements Mergeable {
public static final DistributionStatisticConfig DEFAULT = builder().percentilesHistogram(false)
.percentilePrecision(1)
.minimumExpectedValue(1.0)
.maximumExpectedValue(Double.POSITIVE_INFINITY)
.expiry(Duration.ofMinutes(2))
.bufferLength(3)
.build();
public static final DistributionStatisticConfig NONE = builder().build();
@Nullable
private Boolean percentileHistogram;
@Nullable
private double[] percentiles;
@Nullable
private Integer percentilePrecision;
@Nullable
private double[] serviceLevelObjectives;
@Nullable
private Double minimumExpectedValue;
@Nullable
private Double maximumExpectedValue;
@Nullable
private Duration expiry;
@Nullable
private Integer bufferLength;
public static Builder builder() {
return new Builder();
}
/**
* Merges two configurations. Any options that are non-null in this configuration take
* precedence. Any options that are non-null in the parent are used otherwise.
* @param parent The configuration to merge with. The parent takes lower precedence
* than this configuration.
* @return A new, merged, immutable configuration.
*/
@Override
public DistributionStatisticConfig merge(DistributionStatisticConfig parent) {
return DistributionStatisticConfig.builder()
.percentilesHistogram(
this.percentileHistogram == null ? parent.percentileHistogram : this.percentileHistogram)
.percentiles(this.percentiles == null ? parent.percentiles : this.percentiles)
.serviceLevelObjectives(
this.serviceLevelObjectives == null ? parent.serviceLevelObjectives : this.serviceLevelObjectives)
.percentilePrecision(
this.percentilePrecision == null ? parent.percentilePrecision : this.percentilePrecision)
.minimumExpectedValue(
this.minimumExpectedValue == null ? parent.minimumExpectedValue : this.minimumExpectedValue)
.maximumExpectedValue(
this.maximumExpectedValue == null ? parent.maximumExpectedValue : this.maximumExpectedValue)
.expiry(this.expiry == null ? parent.expiry : this.expiry)
.bufferLength(this.bufferLength == null ? parent.bufferLength : this.bufferLength)
.build();
}
/**
* For internal use only.
* @param supportsAggregablePercentiles whether it supports aggregable percentiles
* @return histogram buckets
*/
public NavigableSet getHistogramBuckets(boolean supportsAggregablePercentiles) {
NavigableSet buckets = new TreeSet<>();
if (percentileHistogram != null && percentileHistogram && supportsAggregablePercentiles) {
buckets.addAll(PercentileHistogramBuckets.buckets(this));
buckets.add(minimumExpectedValue);
buckets.add(maximumExpectedValue);
}
if (serviceLevelObjectives != null) {
for (double sloBoundary : serviceLevelObjectives) {
buckets.add(sloBoundary);
}
}
return buckets;
}
/**
* Adds histogram buckets used to generate aggregable percentile approximations in
* monitoring systems that have query facilities to do so (e.g. Prometheus'
* {@code histogram_quantile}, Atlas' {@code :percentiles}).
* @return This builder.
*/
@Nullable
public Boolean isPercentileHistogram() {
return percentileHistogram;
}
/**
* Produces an additional time series for each requested percentile. This percentile
* is computed locally, and so can't be aggregated with percentiles computed across
* other dimensions (e.g. in a different instance). Use {@link #percentileHistogram}
* to publish a histogram that can be used to generate aggregable percentile
* approximations.
* @return Percentiles to compute and publish. The 95th percentile should be expressed
* as {@code 0.95}
*/
@Nullable
public double[] getPercentiles() {
return percentiles;
}
/**
* Determines the number of digits of precision to maintain on the dynamic range
* histogram used to compute percentile approximations. The higher the degrees of
* precision, the more accurate the approximation is at the cost of more memory.
* @return The digits of precision to maintain for percentile approximations.
*/
@Nullable
public Integer getPercentilePrecision() {
return percentilePrecision;
}
/**
* The minimum value that the meter is expected to observe. Sets a lower bound on
* histogram buckets that are shipped to monitoring systems that support aggregable
* percentile approximations.
* @return The minimum value that this distribution summary is expected to observe.
* @deprecated Use {@link #getMinimumExpectedValueAsDouble}. If you use this method,
* your code will not be compatible with code that uses Micrometer 1.3.x.
*/
@Deprecated
@Nullable
public Double getMinimumExpectedValue() {
return getMinimumExpectedValueAsDouble();
}
/**
* The minimum value that the meter is expected to observe. Sets a lower bound on
* histogram buckets that are shipped to monitoring systems that support aggregable
* percentile approximations.
* @return The minimum value that this distribution summary is expected to observe.
*/
@Nullable
public Double getMinimumExpectedValueAsDouble() {
return minimumExpectedValue;
}
/**
* The maximum value that the meter is expected to observe. Sets an upper bound on
* histogram buckets that are shipped to monitoring systems that support aggregable
* percentile approximations.
* @return The maximum value that the meter is expected to observe.
* @deprecated Use {@link #getMaximumExpectedValueAsDouble}. If you use this method,
* your code will not be compatible with code that uses Micrometer 1.3.x.
*/
@Deprecated
@Nullable
public Double getMaximumExpectedValue() {
return getMaximumExpectedValueAsDouble();
}
/**
* The maximum value that the meter is expected to observe. Sets an upper bound on
* histogram buckets that are shipped to monitoring systems that support aggregable
* percentile approximations.
* @return The maximum value that the meter is expected to observe.
*/
@Nullable
public Double getMaximumExpectedValueAsDouble() {
return maximumExpectedValue;
}
/**
* Statistics like max, percentiles, and histogram counts decay over time to give
* greater weight to recent samples (exception: histogram counts are cumulative for
* those systems that expect cumulative histogram buckets). Samples are accumulated to
* such statistics in ring buffers which rotate after this expiry, with a buffer
* length of {@link #bufferLength}.
* @return The amount of time samples are accumulated to a histogram before it is
* reset and rotated.
*/
@Nullable
public Duration getExpiry() {
return expiry;
}
/**
* Statistics like max, percentiles, and histogram counts decay over time to give
* greater weight to recent samples (exception: histogram counts are cumulative for
* those systems that expect cumulative histogram buckets). Samples are accumulated to
* such statistics in ring buffers which rotate after {@link #expiry}, with this
* buffer length.
* @return The number of histograms to keep in the ring buffer.
*/
@Nullable
public Integer getBufferLength() {
return bufferLength;
}
/**
* Publish at a minimum a histogram containing your defined SLA boundaries. When used
* in conjunction with {@link #percentileHistogram}, the boundaries defined here are
* included alongside other buckets used to generate aggregable percentile
* approximations. If the {@link DistributionStatisticConfig} is meant for use with a
* {@link io.micrometer.core.instrument.Timer}, the SLA unit is in nanoseconds.
* @return The SLA boundaries to include the set of histogram buckets shipped to the
* monitoring system.
* @deprecated Use {@link #getServiceLevelObjectiveBoundaries()}. If you use this
* method, your code will not be compatible with code that uses Micrometer 1.4.x and
* later.
*/
@Nullable
@Deprecated
public double[] getSlaBoundaries() {
return getServiceLevelObjectiveBoundaries();
}
/**
* Publish at a minimum a histogram containing your defined SLO boundaries. When used
* in conjunction with {@link #percentileHistogram}, the boundaries defined here are
* included alongside other buckets used to generate aggregable percentile
* approximations. If the {@link DistributionStatisticConfig} is meant for use with a
* {@link io.micrometer.core.instrument.Timer}, the SLO unit is in nanoseconds.
* @return The SLO boundaries to include the set of histogram buckets shipped to the
* monitoring system.
*/
@Nullable
public double[] getServiceLevelObjectiveBoundaries() {
return serviceLevelObjectives;
}
public static class Builder {
private final DistributionStatisticConfig config = new DistributionStatisticConfig();
public Builder percentilesHistogram(@Nullable Boolean enabled) {
config.percentileHistogram = enabled;
return this;
}
/**
* Produces an additional time series for each requested percentile. This
* percentile is computed locally, and so can't be aggregated with percentiles
* computed across other dimensions (e.g. in a different instance). Use
* {@link #percentileHistogram} to publish a histogram that can be used to
* generate aggregable percentile approximations.
* @param percentiles Percentiles to compute and publish. The 95th percentile
* should be expressed as {@code 0.95}.
* @return This builder.
*/
public Builder percentiles(@Nullable double... percentiles) {
config.percentiles = percentiles;
return this;
}
/**
* Determines the number of digits of precision to maintain on the dynamic range
* histogram used to compute percentile approximations. The higher the degrees of
* precision, the more accurate the approximation is at the cost of more memory.
* @param digitsOfPrecision The digits of precision to maintain for percentile
* approximations.
* @return This builder.
*/
public Builder percentilePrecision(@Nullable Integer digitsOfPrecision) {
config.percentilePrecision = digitsOfPrecision;
return this;
}
/**
* Publish at a minimum a histogram containing your defined Service Level
* Objective (SLO) boundaries. When used in conjunction with
* {@link #percentileHistogram}, the boundaries defined here are included
* alongside other buckets used to generate aggregable percentile approximations.
* If the {@link DistributionStatisticConfig} is meant for use with a
* {@link io.micrometer.core.instrument.Timer}, the SLO unit is in nanoseconds.
* @param slos The SLO boundaries to include the set of histogram buckets shipped
* to the monitoring system.
* @return This builder.
* @since 1.5.0
*/
public Builder serviceLevelObjectives(@Nullable double... slos) {
config.serviceLevelObjectives = slos;
return this;
}
/**
* Publish at a minimum a histogram containing your defined SLA boundaries. When
* used in conjunction with {@link #percentileHistogram}, the boundaries defined
* here are included alongside other buckets used to generate aggregable
* percentile approximations. If the {@link DistributionStatisticConfig} is meant
* for use with a {@link io.micrometer.core.instrument.Timer}, the SLA unit is in
* nanoseconds.
* @param sla The SLA boundaries to include the set of histogram buckets shipped
* to the monitoring system.
* @return This builder.
* @since 1.4.0
* @deprecated Use {@link #serviceLevelObjectives(double...)} instead. "Service
* Level Agreement" is more formally the agreement between an engineering
* organization and the business. Service Level Objectives are set more
* conservatively than the SLA to provide some wiggle room while still satisfying
* the business requirement. SLOs are the threshold we intend to measure against,
* then.
*/
@Deprecated
public Builder sla(@Nullable double... sla) {
return serviceLevelObjectives(sla);
}
/**
* Publish at a minimum a histogram containing your defined SLA boundaries. When
* used in conjunction with {@link #percentileHistogram}, the boundaries defined
* here are included alongside other buckets used to generate aggregable
* percentile approximations. If the {@link DistributionStatisticConfig} is meant
* for use with a {@link io.micrometer.core.instrument.Timer}, the SLA unit is in
* nanoseconds.
* @param sla The SLA boundaries to include the set of histogram buckets shipped
* to the monitoring system.
* @return This builder.
* @deprecated Use {@link #serviceLevelObjectives(double...)} instead. "Service
* Level Agreement" is more formally the agreement between an engineering
* organization and the business. Service Level Objectives are set more
* conservatively than the SLA to provide some wiggle room while still satisfying
* the business requirement. SLOs are the threshold we intend to measure against,
* then.
*/
@Deprecated
public Builder sla(@Nullable long... sla) {
return sla == null ? this : serviceLevelObjectives(LongStream.of(sla).asDoubleStream().toArray());
}
/**
* The minimum value that the meter is expected to observe. Sets a lower bound on
* histogram buckets that are shipped to monitoring systems that support
* aggregable percentile approximations.
* @deprecated Use {@link #minimumExpectedValue(Double)} instead since 1.4.0.
* @param min The minimum value that this distribution summary is expected to
* observe.
* @return This builder.
*/
@Deprecated
public Builder minimumExpectedValue(@Nullable Long min) {
return min == null ? this : minimumExpectedValue((double) min);
}
/**
* The minimum value that the meter is expected to observe. Sets a lower bound on
* histogram buckets that are shipped to monitoring systems that support
* aggregable percentile approximations.
* @param min The minimum value that this distribution summary is expected to
* observe.
* @return This builder.
* @since 1.3.10
*/
public Builder minimumExpectedValue(@Nullable Double min) {
config.minimumExpectedValue = min;
return this;
}
/**
* The maximum value that the meter is expected to observe. Sets an upper bound on
* histogram buckets that are shipped to monitoring systems that support
* aggregable percentile approximations.
* @deprecated Use {@link #maximumExpectedValue(Double)} instead since 1.4.0.
* @param max The maximum value that the meter is expected to observe.
* @return This builder.
*/
@Deprecated
public Builder maximumExpectedValue(@Nullable Long max) {
return max == null ? this : maximumExpectedValue((double) max);
}
/**
* The maximum value that the meter is expected to observe. Sets an upper bound on
* histogram buckets that are shipped to monitoring systems that support
* aggregable percentile approximations.
* @param max The maximum value that the meter is expected to observe.
* @return This builder.
* @since 1.3.10
*/
public Builder maximumExpectedValue(@Nullable Double max) {
config.maximumExpectedValue = max;
return this;
}
/**
* Statistics like max, percentiles, and histogram counts decay over time to give
* greater weight to recent samples (exception: histogram counts are cumulative
* for those systems that expect cumulative histogram buckets). Samples are
* accumulated to such statistics in ring buffers which rotate after this expiry,
* with a buffer length of {@link #bufferLength}.
* @param expiry The amount of time samples are accumulated to decaying
* distribution statistics before they are reset and rotated.
* @return This builder.
*/
public Builder expiry(@Nullable Duration expiry) {
config.expiry = expiry;
return this;
}
/**
* Statistics like max, percentiles, and histogram counts decay over time to give
* greater weight to recent samples (exception: histogram counts are cumulative
* for those systems that expect cumulative histogram buckets). Samples are
* accumulated to such statistics in ring buffers which rotate after
* {@link #expiry}, with this buffer length.
* @param bufferLength The number of histograms to keep in the ring buffer.
* @return This builder.
*/
public Builder bufferLength(@Nullable Integer bufferLength) {
config.bufferLength = bufferLength;
return this;
}
/**
* @return A new immutable distribution configuration.
*/
public DistributionStatisticConfig build() {
return config;
}
}
public boolean isPublishingPercentiles() {
return percentiles != null && percentiles.length > 0;
}
public boolean isPublishingHistogram() {
return (percentileHistogram != null && percentileHistogram)
|| (serviceLevelObjectives != null && serviceLevelObjectives.length > 0);
}
}