From 888acb94fc27fba8b2d2eb7a07d36b4d9d3e7da4 Mon Sep 17 00:00:00 2001 From: Leo Li <269739606@qq.com> Date: Fri, 6 Aug 2021 17:18:25 +0800 Subject: [PATCH 1/2] Add expiry and bufferLength configuration properties See gh-27584 --- .../metrics/MetricsProperties.java | 25 +++++++ .../metrics/PropertiesMeterFilter.java | 3 + .../metrics/PropertiesMeterFilterTests.java | 66 +++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java index c4c0b2c2f4..f3ac7fb53f 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -16,6 +16,7 @@ package org.springframework.boot.actuate.autoconfigure.metrics; +import java.time.Duration; import java.util.LinkedHashMap; import java.util.Map; @@ -29,6 +30,7 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; * @author Jon Schneider * @author Alexander Abramov * @author Tadaya Tsuyukubo + * @author Leo Li * @since 2.0.0 */ @ConfigurationProperties("management.metrics") @@ -296,6 +298,21 @@ public class MetricsProperties { */ private final Map maximumExpectedValue = new LinkedHashMap<>(); + /** + * Specific statistic's expiry for meter IDs starting-with the specified name. + * Values should be a Duration value, the key `all` can also be used to configure + * all meters. + */ + private final Map expiry = new LinkedHashMap<>(); + + /** + * Specific statistic's bufferLength for meter IDs starting-with the specified + * name. Samples are accumulated to statistics in ring buffers, and bufferLength + * is the number to keep in the ring buffer, the key `all` can also be used to + * configure all meters. + */ + private final Map bufferLength = new LinkedHashMap<>(); + public Map getPercentilesHistogram() { return this.percentilesHistogram; } @@ -316,6 +333,14 @@ public class MetricsProperties { return this.maximumExpectedValue; } + public Map getExpiry() { + return this.expiry; + } + + public Map getBufferLength() { + return this.bufferLength; + } + } } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java index 7689480c7e..528352404d 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java @@ -42,6 +42,7 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @author Artsiom Yudovin * @author Alexander Abramov + * @author Leo Li * @since 2.0.0 */ public class PropertiesMeterFilter implements MeterFilter { @@ -83,6 +84,8 @@ public class PropertiesMeterFilter implements MeterFilter { return DistributionStatisticConfig.builder() .percentilesHistogram(lookupWithFallbackToAll(distribution.getPercentilesHistogram(), id, null)) .percentiles(lookupWithFallbackToAll(distribution.getPercentiles(), id, null)) + .expiry(lookupWithFallbackToAll(distribution.getExpiry(), id, null)) + .bufferLength(lookupWithFallbackToAll(distribution.getBufferLength(), id, null)) .serviceLevelObjectives( convertServiceLevelObjectives(id.getType(), lookup(distribution.getSlo(), id, null))) .minimumExpectedValue( diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java index 7d6611df10..0e375e29eb 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java @@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Phillip Webb * @author Jon Schneider * @author Artsiom Yudovin + * @author Leo Li */ class PropertiesMeterFilterTests { @@ -275,6 +276,71 @@ class PropertiesMeterFilterTests { .getMaximumExpectedValueAsDouble()).isEqualTo(Duration.ofMillis(10000).toNanos()); } + @Test + void configureWhenHasExpiryShouldSetExpiryToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter( + createProperties("distribution.expiry[spring.boot]=5ms")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getExpiry()) + .isEqualTo(Duration.ofMillis(5)); + } + + @Test + void configureWhenHasHigherExpiryShouldSetExpiryToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.expiry.spring=5ms")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getExpiry()) + .isEqualTo(Duration.ofMillis(5)); + } + + @Test + void configureWhenHasHigherExpiryAndLowerShouldSetExpiryToHigher() { + PropertiesMeterFilter filter = new PropertiesMeterFilter( + createProperties("distribution.expiry.spring=5ms", "distribution.expiry[spring.boot]=10ms")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getExpiry()) + .isEqualTo(Duration.ofMillis(10)); + } + + @Test + void configureWhenAllExpirySetShouldSetExpiryToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.expiry.all=5ms")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getExpiry()) + .isEqualTo(Duration.ofMillis(5)); + } + + @Test + void configureWhenHasBufferLengthShouldSetBufferLengthToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter( + createProperties("distribution.bufferLength[spring.boot]=3")); + assertThat( + filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) + .isEqualTo(3); + } + + @Test + void configureWhenHasHigherBufferLengthShouldSetBufferLengthToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter( + createProperties("distribution.bufferLength.spring=3")); + assertThat( + filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) + .isEqualTo(3); + } + + @Test + void configureWhenHasHigherBufferLengthAndLowerShouldSetBufferLengthToHigher() { + PropertiesMeterFilter filter = new PropertiesMeterFilter( + createProperties("distribution.bufferLength.spring=2", "distribution.bufferLength[spring.boot]=3")); + assertThat( + filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) + .isEqualTo(3); + } + + @Test + void configureWhenAllBufferLengthSetShouldSetBufferLengthToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.bufferLength.all=3")); + assertThat( + filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) + .isEqualTo(3); + } + private Id createMeterId(String name) { Meter.Type meterType = Type.TIMER; return createMeterId(name, meterType); From 1475309b4dee2f17f3187602cd67411123fb7435 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 16 Aug 2021 10:05:47 +0200 Subject: [PATCH 2/2] Polish "Add expiry and bufferLength configuration properties" See gh-27584 --- .../autoconfigure/metrics/MetricsProperties.java | 13 ++++++------- .../metrics/PropertiesMeterFilter.java | 8 +++----- .../metrics/PropertiesMeterFilterTests.java | 8 ++++---- .../src/docs/asciidoc/actuator/metrics.adoc | 4 ++++ 4 files changed, 17 insertions(+), 16 deletions(-) diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java index f3ac7fb53f..61583ff424 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.java @@ -30,7 +30,6 @@ import org.springframework.boot.context.properties.NestedConfigurationProperty; * @author Jon Schneider * @author Alexander Abramov * @author Tadaya Tsuyukubo - * @author Leo Li * @since 2.0.0 */ @ConfigurationProperties("management.metrics") @@ -299,16 +298,16 @@ public class MetricsProperties { private final Map maximumExpectedValue = new LinkedHashMap<>(); /** - * Specific statistic's expiry for meter IDs starting-with the specified name. - * Values should be a Duration value, the key `all` can also be used to configure - * all meters. + * Maximum amount of time that samples for meter IDs starting with the specified + * name are accumulated to decaying distribution statistics before they are reset + * and rotated. The longest match wins, the key `all` can also be used to + * configure all meters. */ private final Map expiry = new LinkedHashMap<>(); /** - * Specific statistic's bufferLength for meter IDs starting-with the specified - * name. Samples are accumulated to statistics in ring buffers, and bufferLength - * is the number to keep in the ring buffer, the key `all` can also be used to + * Number of histograms for meter IDs starting with the specified name to keep in + * the ring buffer. The longest match wins, the key `all` can also be used to * configure all meters. */ private final Map bufferLength = new LinkedHashMap<>(); diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java index 528352404d..88e7d8dece 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,7 +42,6 @@ import org.springframework.util.StringUtils; * @author Stephane Nicoll * @author Artsiom Yudovin * @author Alexander Abramov - * @author Leo Li * @since 2.0.0 */ public class PropertiesMeterFilter implements MeterFilter { @@ -84,15 +83,14 @@ public class PropertiesMeterFilter implements MeterFilter { return DistributionStatisticConfig.builder() .percentilesHistogram(lookupWithFallbackToAll(distribution.getPercentilesHistogram(), id, null)) .percentiles(lookupWithFallbackToAll(distribution.getPercentiles(), id, null)) - .expiry(lookupWithFallbackToAll(distribution.getExpiry(), id, null)) - .bufferLength(lookupWithFallbackToAll(distribution.getBufferLength(), id, null)) .serviceLevelObjectives( convertServiceLevelObjectives(id.getType(), lookup(distribution.getSlo(), id, null))) .minimumExpectedValue( convertMeterValue(id.getType(), lookup(distribution.getMinimumExpectedValue(), id, null))) .maximumExpectedValue( convertMeterValue(id.getType(), lookup(distribution.getMaximumExpectedValue(), id, null))) - .build().merge(config); + .expiry(lookupWithFallbackToAll(distribution.getExpiry(), id, null)) + .bufferLength(lookupWithFallbackToAll(distribution.getBufferLength(), id, null)).build().merge(config); } private double[] convertServiceLevelObjectives(Meter.Type meterType, ServiceLevelObjectiveBoundary[] slo) { diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java index 0e375e29eb..b8a748b966 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilterTests.java @@ -309,7 +309,7 @@ class PropertiesMeterFilterTests { @Test void configureWhenHasBufferLengthShouldSetBufferLengthToValue() { PropertiesMeterFilter filter = new PropertiesMeterFilter( - createProperties("distribution.bufferLength[spring.boot]=3")); + createProperties("distribution.buffer-length.spring.boot=3")); assertThat( filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) .isEqualTo(3); @@ -318,7 +318,7 @@ class PropertiesMeterFilterTests { @Test void configureWhenHasHigherBufferLengthShouldSetBufferLengthToValue() { PropertiesMeterFilter filter = new PropertiesMeterFilter( - createProperties("distribution.bufferLength.spring=3")); + createProperties("distribution.buffer-length.spring=3")); assertThat( filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) .isEqualTo(3); @@ -327,7 +327,7 @@ class PropertiesMeterFilterTests { @Test void configureWhenHasHigherBufferLengthAndLowerShouldSetBufferLengthToHigher() { PropertiesMeterFilter filter = new PropertiesMeterFilter( - createProperties("distribution.bufferLength.spring=2", "distribution.bufferLength[spring.boot]=3")); + createProperties("distribution.buffer-length.spring=2", "distribution.buffer-length.spring.boot=3")); assertThat( filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) .isEqualTo(3); @@ -335,7 +335,7 @@ class PropertiesMeterFilterTests { @Test void configureWhenAllBufferLengthSetShouldSetBufferLengthToValue() { - PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.bufferLength.all=3")); + PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.buffer-length.all=3")); assertThat( filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getBufferLength()) .isEqualTo(3); diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc index a9e6da0e3a..bac1fbd914 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc @@ -1153,6 +1153,10 @@ The following properties allow per-meter customization: | configprop:management.metrics.distribution.percentiles[] | Publish percentile values computed in your application +| configprop:management.metrics.distribution.expiry[], configprop:management.metrics.distribution.buffer-length[] +| Give greater weight to recent samples by accumulating them in ring buffers which rotate after a configurable expiry, with a +configurable buffer length. + | configprop:management.metrics.distribution.slo[] | Publish a cumulative histogram with buckets defined by your service-level objectives. |===