From fe90b2a2514b30174595213bb8567666b55dc30a Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Thu, 23 Apr 2020 10:13:17 +0200 Subject: [PATCH] Rename Service Level Agreement to Service Level Objective This commit harmonizes our integration of Micrometer's Service Level objectives. Closes gh-21076 --- .../metrics/MetricsProperties.java | 21 ++++-- .../metrics/PropertiesMeterFilter.java | 9 +-- .../ServiceLevelAgreementBoundary.java | 15 ++-- .../ServiceLevelObjectiveBoundary.java | 70 +++++++++++++++++++ .../metrics/PropertiesMeterFilterTests.java | 40 ++++++----- ...> ServiceLevelObjectiveBoundaryTests.java} | 28 ++++---- .../asciidoc/production-ready-features.adoc | 4 +- 7 files changed, 137 insertions(+), 50 deletions(-) create mode 100644 spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundary.java rename spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/{ServiceLevelAgreementBoundaryTests.java => ServiceLevelObjectiveBoundaryTests.java} (64%) 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 04ea5c467e..0598c16620 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 @@ -20,6 +20,7 @@ import java.util.LinkedHashMap; import java.util.Map; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.DeprecatedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty; /** @@ -232,12 +233,12 @@ public class MetricsProperties { private final Map percentiles = new LinkedHashMap<>(); /** - * Specific SLA boundaries for meter IDs starting-with the specified name. The - * longest match wins. Counters will be published for each specified boundary. - * Values can be specified as a long or as a Duration value (for timer meters, - * defaulting to ms if no unit specified). + * Specific Service Level Objectives boundaries for meter IDs starting-with the + * specified name. The longest match wins. Counters will be published for each + * specified boundary. Values can be specified as a long or as a Duration value + * (for timer meters, defaulting to ms if no unit specified). */ - private final Map sla = new LinkedHashMap<>(); + private final Map slo = new LinkedHashMap<>(); /** * Minimum value that meter IDs starting-with the specified name are expected to @@ -261,8 +262,14 @@ public class MetricsProperties { return this.percentiles; } - public Map getSla() { - return this.sla; + @Deprecated + @DeprecatedConfigurationProperty(replacement = "management.metrics.distribution.slo") + public Map getSla() { + return this.slo; + } + + public Map getSlo() { + return this.slo; } public Map getMinimumExpectedValue() { 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 b5bce1a418..7689480c7e 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 @@ -83,7 +83,8 @@ public class PropertiesMeterFilter implements MeterFilter { return DistributionStatisticConfig.builder() .percentilesHistogram(lookupWithFallbackToAll(distribution.getPercentilesHistogram(), id, null)) .percentiles(lookupWithFallbackToAll(distribution.getPercentiles(), id, null)) - .sla(convertSla(id.getType(), lookup(distribution.getSla(), id, null))) + .serviceLevelObjectives( + convertServiceLevelObjectives(id.getType(), lookup(distribution.getSlo(), id, null))) .minimumExpectedValue( convertMeterValue(id.getType(), lookup(distribution.getMinimumExpectedValue(), id, null))) .maximumExpectedValue( @@ -91,11 +92,11 @@ public class PropertiesMeterFilter implements MeterFilter { .build().merge(config); } - private double[] convertSla(Meter.Type meterType, ServiceLevelAgreementBoundary[] sla) { - if (sla == null) { + private double[] convertServiceLevelObjectives(Meter.Type meterType, ServiceLevelObjectiveBoundary[] slo) { + if (slo == null) { return null; } - double[] converted = Arrays.stream(sla).map((candidate) -> candidate.getValue(meterType)) + double[] converted = Arrays.stream(slo).map((candidate) -> candidate.getValue(meterType)) .filter(Objects::nonNull).mapToDouble(Double::doubleValue).toArray(); return (converted.length != 0) ? converted : null; } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundary.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundary.java index dd0f80f770..17a92394e2 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundary.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundary.java @@ -22,12 +22,14 @@ import io.micrometer.core.instrument.Meter; /** * A service level agreement boundary for use when configuring Micrometer. Can be - * specified as either a {@link Double} (applicable to timers and distribution summaries) - * or a {@link Duration} (applicable to only timers). + * specified as either a {@link Long} (applicable to timers and distribution summaries) or + * a {@link Duration} (applicable to only timers). * * @author Phillip Webb * @since 2.0.0 + * @deprecated as of 2.3.0 in favor of {@link ServiceLevelObjectiveBoundary} */ +@Deprecated public final class ServiceLevelAgreementBoundary { private final MeterValue value; @@ -42,17 +44,18 @@ public final class ServiceLevelAgreementBoundary { * @param meterType the meter type * @return the value or {@code null} if the value cannot be applied */ - public Double getValue(Meter.Type meterType) { - return this.value.getValue(meterType); + public Long getValue(Meter.Type meterType) { + Double value = this.value.getValue(meterType); + return (value != null) ? value.longValue() : null; } /** - * Return a new {@link ServiceLevelAgreementBoundary} instance for the given double + * Return a new {@link ServiceLevelAgreementBoundary} instance for the given long * value. * @param value the source value * @return a {@link ServiceLevelAgreementBoundary} instance */ - public static ServiceLevelAgreementBoundary valueOf(double value) { + public static ServiceLevelAgreementBoundary valueOf(long value) { return new ServiceLevelAgreementBoundary(MeterValue.valueOf(value)); } diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundary.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundary.java new file mode 100644 index 0000000000..8f858ff35d --- /dev/null +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundary.java @@ -0,0 +1,70 @@ +/* + * Copyright 2012-2020 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. + * 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 org.springframework.boot.actuate.autoconfigure.metrics; + +import java.time.Duration; + +import io.micrometer.core.instrument.Meter; + +/** + * A service level objective boundary for use when configuring Micrometer. Can be + * specified as either a {@link Double} (applicable to timers and distribution summaries) + * or a {@link Duration} (applicable to only timers). + * + * @author Phillip Webb + * @author Stephane Nicoll + * @since 2.3.0 + */ +public final class ServiceLevelObjectiveBoundary { + + private final MeterValue value; + + ServiceLevelObjectiveBoundary(MeterValue value) { + this.value = value; + } + + /** + * Return the underlying value of the SLO in form suitable to apply to the given meter + * type. + * @param meterType the meter type + * @return the value or {@code null} if the value cannot be applied + */ + public Double getValue(Meter.Type meterType) { + return this.value.getValue(meterType); + } + + /** + * Return a new {@link ServiceLevelObjectiveBoundary} instance for the given double + * value. + * @param value the source value + * @return a {@link ServiceLevelObjectiveBoundary} instance + */ + public static ServiceLevelObjectiveBoundary valueOf(double value) { + return new ServiceLevelObjectiveBoundary(MeterValue.valueOf(value)); + } + + /** + * Return a new {@link ServiceLevelObjectiveBoundary} instance for the given String + * value. + * @param value the source value + * @return a {@link ServiceLevelObjectiveBoundary} instance + */ + public static ServiceLevelObjectiveBoundary valueOf(String value) { + return new ServiceLevelObjectiveBoundary(MeterValue.valueOf(value)); + } + +} 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 d865e2e07d..bd4d442ccf 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 @@ -204,29 +204,35 @@ class PropertiesMeterFilterTests { } @Test - void configureWhenHasSlaShouldSetSlaToValue() { + @Deprecated + void configureWhenHasDeprecatedSlaShouldSetSlaToValue() { PropertiesMeterFilter filter = new PropertiesMeterFilter( createProperties("distribution.sla.spring.boot=1,2,3")); - assertThat( - filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getSlaBoundaries()) - .containsExactly(1000000, 2000000, 3000000); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT) + .getServiceLevelObjectiveBoundaries()).containsExactly(1000000, 2000000, 3000000); } @Test - void configureWhenHasHigherSlaShouldSetPercentilesToValue() { - PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.sla.spring=1,2,3")); - assertThat( - filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getSlaBoundaries()) - .containsExactly(1000000, 2000000, 3000000); - } - - @Test - void configureWhenHasHigherSlaAndLowerShouldSetSlaToHigher() { + void configureWhenHasSloShouldSetSloToValue() { PropertiesMeterFilter filter = new PropertiesMeterFilter( - createProperties("distribution.sla.spring=1,2,3", "distribution.sla.spring.boot=4,5,6")); - assertThat( - filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT).getSlaBoundaries()) - .containsExactly(4000000, 5000000, 6000000); + createProperties("distribution.slo.spring.boot=1,2,3")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT) + .getServiceLevelObjectiveBoundaries()).containsExactly(1000000, 2000000, 3000000); + } + + @Test + void configureWhenHasHigherSloShouldSetPercentilesToValue() { + PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties("distribution.slo.spring=1,2,3")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT) + .getServiceLevelObjectiveBoundaries()).containsExactly(1000000, 2000000, 3000000); + } + + @Test + void configureWhenHasHigherSloAndLowerShouldSetSloToHigher() { + PropertiesMeterFilter filter = new PropertiesMeterFilter( + createProperties("distribution.slo.spring=1,2,3", "distribution.slo.spring.boot=4,5,6")); + assertThat(filter.configure(createMeterId("spring.boot"), DistributionStatisticConfig.DEFAULT) + .getServiceLevelObjectiveBoundaries()).containsExactly(4000000, 5000000, 6000000); } @Test diff --git a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundaryTests.java b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundaryTests.java similarity index 64% rename from spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundaryTests.java rename to spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundaryTests.java index f3250c13ac..d5511edf3b 100644 --- a/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelAgreementBoundaryTests.java +++ b/spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/metrics/ServiceLevelObjectiveBoundaryTests.java @@ -22,47 +22,47 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; /** - * Tests for {@link ServiceLevelAgreementBoundary}. + * Tests for {@link ServiceLevelObjectiveBoundary}. * * @author Phillip Webb * @author Stephane Nicoll */ -class ServiceLevelAgreementBoundaryTests { +class ServiceLevelObjectiveBoundaryTests { @Test void getValueForTimerWhenFromLongShouldReturnMsToNanosValue() { - ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf(123L); - assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000); + ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf(123L); + assertThat(slo.getValue(Type.TIMER)).isEqualTo(123000000); } @Test void getValueForTimerWhenFromNumberStringShouldMsToNanosValue() { - ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123"); - assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000); + ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("123"); + assertThat(slo.getValue(Type.TIMER)).isEqualTo(123000000); } @Test void getValueForTimerWhenFromDurationStringShouldReturnDurationNanos() { - ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123ms"); - assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000); + ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("123ms"); + assertThat(slo.getValue(Type.TIMER)).isEqualTo(123000000); } @Test void getValueForDistributionSummaryWhenFromDoubleShouldReturnDoubleValue() { - ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf(123.42); - assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42); + ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf(123.42); + assertThat(slo.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42); } @Test void getValueForDistributionSummaryWhenFromStringShouldReturnDoubleValue() { - ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123.42"); - assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42); + ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("123.42"); + assertThat(slo.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123.42); } @Test void getValueForDistributionSummaryWhenFromDurationShouldReturnNull() { - ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123ms"); - assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isNull(); + ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("123ms"); + assertThat(slo.getValue(Type.DISTRIBUTION_SUMMARY)).isNull(); } } diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc index a95e69d3cc..fa152d71c3 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/production-ready-features.adoc @@ -2131,8 +2131,8 @@ The following properties allow per-meter customization: | configprop:management.metrics.distribution.percentiles[] | Publish percentile values computed in your application -| configprop:management.metrics.distribution.sla[] -| Publish a cumulative histogram with buckets defined by your SLAs. +| configprop:management.metrics.distribution.slo[] +| Publish a cumulative histogram with buckets defined by your Service Level Objectives. |=== For more details on concepts behind `percentiles-histogram`, `percentiles` and `sla` refer to the {micrometer-concepts-docs}#_histograms_and_percentiles["Histograms and percentiles" section] of the micrometer documentation.