Improve Micrometer histogram properties support
This commit adds configuration properties for Micrometer histogram settings: "minimumExpectedValue" and "maximumExpectedValue". See gh-14139
This commit is contained in:
parent
5607fcae85
commit
c1c79ab1c2
|
@ -25,6 +25,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||
* {@link ConfigurationProperties} for configuring Micrometer-based metrics.
|
||||
*
|
||||
* @author Jon Schneider
|
||||
* @author Alexander Abramov
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@ConfigurationProperties("management.metrics")
|
||||
|
@ -198,6 +199,22 @@ public class MetricsProperties {
|
|||
*/
|
||||
private final Map<String, ServiceLevelAgreementBoundary[]> sla = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* The minimum value that this distribution summary is expected to observe.
|
||||
* Controls the number of buckets shipped by percentilesHistogram. Can be
|
||||
* specified as a long or as a Duration value (for timer meters, defaulting to ms
|
||||
* if no unit specified).
|
||||
*/
|
||||
private final Map<String, ServiceLevelAgreementBoundary> minimumExpectedValue = new LinkedHashMap<>();
|
||||
|
||||
/**
|
||||
* The maximum value that this distribution summary is expected to observe.
|
||||
* Controls the number of buckets shipped by percentilesHistogram. Can be
|
||||
* specified as a long or as a Duration value (for timer meters, defaulting to ms
|
||||
* if no unit specified).
|
||||
*/
|
||||
private final Map<String, ServiceLevelAgreementBoundary> maximumExpectedValue = new LinkedHashMap<>();
|
||||
|
||||
public Map<String, Boolean> getPercentilesHistogram() {
|
||||
return this.percentilesHistogram;
|
||||
}
|
||||
|
@ -210,6 +227,14 @@ public class MetricsProperties {
|
|||
return this.sla;
|
||||
}
|
||||
|
||||
public Map<String, ServiceLevelAgreementBoundary> getMinimumExpectedValue() {
|
||||
return this.minimumExpectedValue;
|
||||
}
|
||||
|
||||
public Map<String, ServiceLevelAgreementBoundary> getMaximumExpectedValue() {
|
||||
return this.maximumExpectedValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.springframework.util.StringUtils;
|
|||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Artsiom Yudovin
|
||||
* @author Alexander Abramov
|
||||
* @since 2.0.0
|
||||
*/
|
||||
public class PropertiesMeterFilter implements MeterFilter {
|
||||
|
@ -87,6 +88,10 @@ public class PropertiesMeterFilter implements MeterFilter {
|
|||
.percentiles(
|
||||
lookupWithFallbackToAll(distribution.getPercentiles(), id, null))
|
||||
.sla(convertSla(id.getType(), lookup(distribution.getSla(), id, null)))
|
||||
.minimumExpectedValue(convertSla(id.getType(),
|
||||
lookup(distribution.getMinimumExpectedValue(), id, null)))
|
||||
.maximumExpectedValue(convertSla(id.getType(),
|
||||
lookup(distribution.getMaximumExpectedValue(), id, null)))
|
||||
.build().merge(config);
|
||||
}
|
||||
|
||||
|
@ -100,6 +105,10 @@ public class PropertiesMeterFilter implements MeterFilter {
|
|||
return (converted.length != 0) ? converted : null;
|
||||
}
|
||||
|
||||
private Long convertSla(Meter.Type meterType, ServiceLevelAgreementBoundary sla) {
|
||||
return (sla != null) ? sla.getValue(meterType) : null;
|
||||
}
|
||||
|
||||
private <T> T lookup(Map<String, T> values, Id id, T defaultValue) {
|
||||
if (values.isEmpty()) {
|
||||
return defaultValue;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.actuate.autoconfigure.metrics;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
|
||||
import io.micrometer.core.instrument.Meter;
|
||||
|
@ -251,6 +252,110 @@ public class PropertiesMeterFilterTests {
|
|||
.containsExactly(4000000, 5000000, 6000000);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenHasMinimumExpectedValueShouldSetMinimumExpectedToValue() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.minimum-expected-value.[spring.boot]=10"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMinimumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(10).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenHasHigherMinimumExpectedValueShouldSetMinimumExpectedValueToValue() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.minimum-expected-value.spring=10"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMinimumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(10).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenHasHigherMinimumExpectedValueAndLowerShouldSetMinimumExpectedValueToHigher() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.minimum-expected-value.spring=10",
|
||||
"distribution.minimum-expected-value.[spring.boot]=50"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMinimumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(50).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAllMinimumExpectedValueSetShouldSetMinimumExpectedValueToValue() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.minimum-expected-value.all=10"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMinimumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(10).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenMinimumExpectedValueDurationShouldOnlyApplyToTimer() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.minimum-expected-value.all=10ms"));
|
||||
Meter.Id timer = createMeterId("spring.boot", Meter.Type.TIMER);
|
||||
Meter.Id summary = createMeterId("spring.boot", Meter.Type.DISTRIBUTION_SUMMARY);
|
||||
Meter.Id counter = createMeterId("spring.boot", Meter.Type.COUNTER);
|
||||
assertThat(filter.configure(timer, DistributionStatisticConfig.DEFAULT)
|
||||
.getMinimumExpectedValue()).isEqualTo(Duration.ofMillis(10).toNanos());
|
||||
assertThat(filter.configure(summary, DistributionStatisticConfig.DEFAULT)
|
||||
.getMinimumExpectedValue()).isEqualTo(1L);
|
||||
assertThat(filter.configure(counter, DistributionStatisticConfig.DEFAULT)
|
||||
.getMinimumExpectedValue()).isEqualTo(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenHasMaximumExpectedValueShouldSetMaximumExpectedToValue() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties(
|
||||
"distribution.maximum-expected-value.[spring.boot]=5000"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMaximumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(5000).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenHasHigherMaximumExpectedValueShouldSetMaximumExpectedValueToValue() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.maximum-expected-value.spring=5000"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMaximumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(5000).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenHasHigherMaximumExpectedValueAndLowerShouldSetMaximumExpectedValueToHigher() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.maximum-expected-value.spring=5000",
|
||||
"distribution.maximum-expected-value.[spring.boot]=10000"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMaximumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(10000).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenAllMaximumExpectedValueSetShouldSetMaximumExpectedValueToValue() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.maximum-expected-value.all=5000"));
|
||||
assertThat(filter.configure(createMeterId("spring.boot"),
|
||||
DistributionStatisticConfig.DEFAULT).getMaximumExpectedValue())
|
||||
.isEqualTo(Duration.ofMillis(5000).toNanos());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configureWhenMaximumExpectedValueDurationShouldOnlyApplyToTimer() {
|
||||
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||
createProperties("distribution.maximum-expected-value.all=15s"));
|
||||
Meter.Id timer = createMeterId("spring.boot", Meter.Type.TIMER);
|
||||
Meter.Id summary = createMeterId("spring.boot", Meter.Type.DISTRIBUTION_SUMMARY);
|
||||
Meter.Id counter = createMeterId("spring.boot", Meter.Type.COUNTER);
|
||||
assertThat(filter.configure(timer, DistributionStatisticConfig.DEFAULT)
|
||||
.getMaximumExpectedValue()).isEqualTo(Duration.ofMillis(15000).toNanos());
|
||||
assertThat(filter.configure(summary, DistributionStatisticConfig.DEFAULT)
|
||||
.getMaximumExpectedValue()).isEqualTo(Long.MAX_VALUE);
|
||||
assertThat(filter.configure(counter, DistributionStatisticConfig.DEFAULT)
|
||||
.getMaximumExpectedValue()).isEqualTo(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
private Id createMeterId(String name) {
|
||||
Meter.Type meterType = Type.TIMER;
|
||||
return createMeterId(name, meterType);
|
||||
|
|
|
@ -1380,6 +1380,8 @@ content into your application. Rather, pick only the properties that you need.
|
|||
|
||||
# METRICS
|
||||
management.metrics.distribution.percentiles-histogram.*= # Whether meter IDs starting with the specified name should publish percentile histograms.
|
||||
management.metrics.distribution.minimum-expected-value.*= # Minimum limit on the histogram buckets for IDs starting with the specified name. The longest match wins, the key `all` can also be used to configure all meters.
|
||||
management.metrics.distribution.maximum-expected-value.*= # Maximum limit on the histogram buckets for IDs starting with the specified name. The longest match wins, the key `all` can also be used to configure all meters.
|
||||
management.metrics.distribution.percentiles.*= # Specific computed non-aggregable percentiles to ship to the backend for meter IDs starting-with the specified name.
|
||||
management.metrics.distribution.sla.*= # Specific SLA boundaries for meter IDs starting-with the specified name. The longest match wins.
|
||||
management.metrics.enable.*= # Whether meter IDs starting-with the specified name should be enabled. The longest match wins, the key `all` can also be used to configure all meters.
|
||||
|
|
|
@ -2051,6 +2051,10 @@ The following properties allow per-meter customization:
|
|||
| Whether to publish a histogram suitable for computing aggregable (across dimension)
|
||||
percentile approximations.
|
||||
|
||||
| `management.metrics.distribution.minimum-expected-value`
|
||||
| `management.metrics.distribution.maximum-expected-value`
|
||||
| Publish less histogram buckets by clamping the range of expected values.
|
||||
|
||||
| `management.metrics.distribution.percentiles`
|
||||
| Publish percentile values computed in your application
|
||||
|
||||
|
|
Loading…
Reference in New Issue