Rename Service Level Agreement to Service Level Objective
This commit harmonizes our integration of Micrometer's Service Level objectives. Closes gh-21076
This commit is contained in:
parent
489f9b0e58
commit
fe90b2a251
|
@ -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<String, double[]> 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<String, ServiceLevelAgreementBoundary[]> sla = new LinkedHashMap<>();
|
||||
private final Map<String, ServiceLevelObjectiveBoundary[]> 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<String, ServiceLevelAgreementBoundary[]> getSla() {
|
||||
return this.sla;
|
||||
@Deprecated
|
||||
@DeprecatedConfigurationProperty(replacement = "management.metrics.distribution.slo")
|
||||
public Map<String, ServiceLevelObjectiveBoundary[]> getSla() {
|
||||
return this.slo;
|
||||
}
|
||||
|
||||
public Map<String, ServiceLevelObjectiveBoundary[]> getSlo() {
|
||||
return this.slo;
|
||||
}
|
||||
|
||||
public Map<String, String> getMinimumExpectedValue() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue