Support property based MeterFilters
Add per-meter property support for `enabled`, `percentiles-histogram`, `percentiles` and `sla`. Fixes gh-11800pull/11932/merge
parent
6b70c96e37
commit
6889ad59b8
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2018 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
|
||||||
|
*
|
||||||
|
* http://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.util.Arrays;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Meter;
|
||||||
|
import io.micrometer.core.instrument.Meter.Id;
|
||||||
|
import io.micrometer.core.instrument.config.MeterFilter;
|
||||||
|
import io.micrometer.core.instrument.config.MeterFilterReply;
|
||||||
|
import io.micrometer.core.instrument.histogram.HistogramConfig;
|
||||||
|
|
||||||
|
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties.Distribution;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MeterFilter} to apply settings from {@link MetricsProperties}.
|
||||||
|
*
|
||||||
|
* @author Jon Schneider
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public class PropertiesMeterFilter implements MeterFilter {
|
||||||
|
|
||||||
|
private static final ServiceLevelAgreementBoundary[] EMPTY_SLA = {};
|
||||||
|
|
||||||
|
private MetricsProperties properties;
|
||||||
|
|
||||||
|
public PropertiesMeterFilter(MetricsProperties properties) {
|
||||||
|
Assert.notNull(properties, "Properties must not be null");
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MeterFilterReply accept(Meter.Id id) {
|
||||||
|
boolean enabled = lookup(this.properties.getEnable(), id, true);
|
||||||
|
return (enabled ? MeterFilterReply.NEUTRAL : MeterFilterReply.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HistogramConfig configure(Meter.Id id, HistogramConfig config) {
|
||||||
|
HistogramConfig.Builder builder = HistogramConfig.builder();
|
||||||
|
Distribution distribution = this.properties.getDistribution();
|
||||||
|
builder.percentilesHistogram(lookup(distribution.getPercentilesHistogram(), id, null));
|
||||||
|
builder.percentiles(lookup(distribution.getPercentiles(), id, null));
|
||||||
|
builder.sla(convertSla(id.getType(), lookup(distribution.getSla(), id, null)));
|
||||||
|
return builder.build().merge(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long[] convertSla(Meter.Type meterType, ServiceLevelAgreementBoundary[] sla) {
|
||||||
|
long[] converted = Arrays.stream(sla == null ? EMPTY_SLA : sla)
|
||||||
|
.map((candidate) -> candidate.getValue(meterType))
|
||||||
|
.filter(Objects::nonNull).mapToLong(Long::longValue).toArray();
|
||||||
|
return (converted.length == 0 ? null : converted);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T lookup(Map<String, T> values, Id id, T defaultValue) {
|
||||||
|
String name = id.getName();
|
||||||
|
while (StringUtils.hasLength(name)) {
|
||||||
|
T result = values.get(name);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
int lastDot = name.lastIndexOf('.');
|
||||||
|
name = lastDot == -1 ? "" : name.substring(0, lastDot);
|
||||||
|
}
|
||||||
|
return values.getOrDefault("all", defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2018 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
|
||||||
|
*
|
||||||
|
* http://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 java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Meter;
|
||||||
|
import io.micrometer.core.instrument.Meter.Type;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.bind.convert.DurationConverter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A service level agreement boundary for use when configuring micrometer. Can be
|
||||||
|
* specified as either a {@link Long} (applicable to timers and distribution summaries) or
|
||||||
|
* a {@link Long} (applicable to only timers).
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
public final class ServiceLevelAgreementBoundary {
|
||||||
|
|
||||||
|
private final Object value;
|
||||||
|
|
||||||
|
ServiceLevelAgreementBoundary(long value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ServiceLevelAgreementBoundary(Duration value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying value of the SLA 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 Long getValue(Meter.Type meterType) {
|
||||||
|
if (meterType == Type.DISTRIBUTION_SUMMARY) {
|
||||||
|
return getDistributionSummaryValue();
|
||||||
|
}
|
||||||
|
if (meterType == Type.TIMER) {
|
||||||
|
return getTimerValue();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long getDistributionSummaryValue() {
|
||||||
|
if (this.value instanceof Long) {
|
||||||
|
return (Long) this.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Long getTimerValue() {
|
||||||
|
if (this.value instanceof Long) {
|
||||||
|
return TimeUnit.MILLISECONDS.toNanos((long) this.value);
|
||||||
|
}
|
||||||
|
if (this.value instanceof Duration) {
|
||||||
|
return ((Duration) this.value).toNanos();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServiceLevelAgreementBoundary valueOf(String value) {
|
||||||
|
if (isNumber(value)) {
|
||||||
|
return new ServiceLevelAgreementBoundary(Long.parseLong(value));
|
||||||
|
}
|
||||||
|
return new ServiceLevelAgreementBoundary(
|
||||||
|
DurationConverter.toDuration(value, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(long value) {
|
||||||
|
return new ServiceLevelAgreementBoundary(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new {@link ServiceLevelAgreementBoundary} instance for the given String
|
||||||
|
* value. The value may contain a simple number, or a {@link DurationConverter
|
||||||
|
* duration formatted value}
|
||||||
|
* @param value the source value
|
||||||
|
* @return a {@link ServiceLevelAgreementBoundary} instance
|
||||||
|
*/
|
||||||
|
private static boolean isNumber(String value) {
|
||||||
|
return value.chars().allMatch(Character::isDigit);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,314 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2018 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
|
||||||
|
*
|
||||||
|
* http://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.util.Collections;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.Meter;
|
||||||
|
import io.micrometer.core.instrument.Meter.Id;
|
||||||
|
import io.micrometer.core.instrument.Meter.Type;
|
||||||
|
import io.micrometer.core.instrument.config.MeterFilterReply;
|
||||||
|
import io.micrometer.core.instrument.histogram.HistogramConfig;
|
||||||
|
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.ExpectedException;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.bind.Bindable;
|
||||||
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
|
import org.springframework.boot.test.util.TestPropertyValues;
|
||||||
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests fir {@link PropertiesMeterFilter}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @author Jon Schneider
|
||||||
|
*/
|
||||||
|
public class PropertiesMeterFilterTests {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public ExpectedException thrown = ExpectedException.none();
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private HistogramConfig config;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createWhenPropertiesIsNullShouldThrowException() {
|
||||||
|
this.thrown.expect(IllegalArgumentException.class);
|
||||||
|
this.thrown.expectMessage("Properties must not be null");
|
||||||
|
new PropertiesMeterFilter(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasNoEnabledPropertiesShouldReturnNeutral() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties());
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.NEUTRAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasEnableFalseShouldReturnDeny() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.spring.boot=false"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasEnableTrueShouldReturnNeutral() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.spring.boot=true"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.NEUTRAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasHigherEnableFalseShouldReturnDeny() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.spring=false"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasHigherEnableTrueShouldReturnNeutral() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.spring=true"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.NEUTRAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasHigherEnableFalseExactEnableTrueShouldReturnNeutral() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.spring=false", "enable.spring.boot=true"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.NEUTRAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasHigherEnableTrueExactEnableFalseShouldReturnDeny() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.spring=true", "enable.spring.boot=false"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasAllEnableFalseShouldReturnDeny() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.all=false"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.DENY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptWhenHasAllEnableFalseButHigherEnableTrueShouldReturnNeutral() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("enable.all=false", "enable.spring=true"));
|
||||||
|
assertThat(filter.accept(createMeterId("spring.boot")))
|
||||||
|
.isEqualTo(MeterFilterReply.NEUTRAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHistogramTrueShouldSetPercentilesHistogramToTrue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.spring.boot=true"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHistogramFalseShouldSetPercentilesHistogramToFalse() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.spring.boot=false"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherHistogramTrueShouldSetPercentilesHistogramToTrue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.spring=true"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherHistogramFalseShouldSetPercentilesHistogramToFalse() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.spring=false"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherHistogramTrueAndLowerFalseShouldSetPercentilesHistogramToFalse() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.spring=true",
|
||||||
|
"distribution.percentiles-histogram.spring.boot=false"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherHistogramFalseAndLowerTrueShouldSetPercentilesHistogramToFalse() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.spring=false",
|
||||||
|
"distribution.percentiles-histogram.spring.boot=true"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAllHistogramTrueSetPercentilesHistogramToTrue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles-histogram.all=true"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.isPercentileHistogram()).isTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasPercentilesShouldSetPercentilesToValue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles.spring.boot=1,1.5,2"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getPercentiles()).containsExactly(1, 1.5, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherPercentilesShouldSetPercentilesToValue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles.spring=1,1.5,2"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getPercentiles()).containsExactly(1, 1.5, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherPercentilesAndLowerShouldSetPercentilesToHigher() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles.spring=1,1.5,2",
|
||||||
|
"distribution.percentiles.spring.boot=3,3.5,4"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getPercentiles()).containsExactly(3, 3.5, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAllPercentilesSetShouldSetPercentilesToValue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.percentiles.all=1,1.5,2"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getPercentiles()).containsExactly(1, 1.5, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasSlaShouldSetSlaToValue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.sla.spring.boot=1,2,3"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getSlaBoundaries()).containsExactly(1000000, 2000000, 3000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherSlaShouldSetPercentilesToValue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.sla.spring=1,2,3"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getSlaBoundaries()).containsExactly(1000000, 2000000, 3000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenHasHigherSlaAndLowerShouldSetSlaToHigher() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(createProperties(
|
||||||
|
"distribution.sla.spring=1,2,3", "distribution.sla.spring.boot=4,5,6"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getSlaBoundaries()).containsExactly(4000000, 5000000, 6000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAllSlaSetShouldSetSlaToValue() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.sla.all=1,2,3"));
|
||||||
|
assertThat(filter.configure(createMeterId("spring.boot"), HistogramConfig.DEFAULT)
|
||||||
|
.getSlaBoundaries()).containsExactly(1000000, 2000000, 3000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenSlaDurationShouldOnlyApplyToTimer() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.sla.all=1ms,2ms,3ms"));
|
||||||
|
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, HistogramConfig.DEFAULT).getSlaBoundaries())
|
||||||
|
.containsExactly(1000000, 2000000, 3000000);
|
||||||
|
assertThat(filter.configure(summary, HistogramConfig.DEFAULT).getSlaBoundaries())
|
||||||
|
.isEmpty();
|
||||||
|
assertThat(filter.configure(counter, HistogramConfig.DEFAULT).getSlaBoundaries())
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenSlaLongShouldOnlyApplyToTimerAndDistributionSummary() {
|
||||||
|
PropertiesMeterFilter filter = new PropertiesMeterFilter(
|
||||||
|
createProperties("distribution.sla.all=1,2,3"));
|
||||||
|
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, HistogramConfig.DEFAULT).getSlaBoundaries())
|
||||||
|
.containsExactly(1000000, 2000000, 3000000);
|
||||||
|
assertThat(filter.configure(summary, HistogramConfig.DEFAULT).getSlaBoundaries())
|
||||||
|
.containsExactly(1, 2, 3);
|
||||||
|
assertThat(filter.configure(counter, HistogramConfig.DEFAULT).getSlaBoundaries())
|
||||||
|
.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Id createMeterId(String name) {
|
||||||
|
Meter.Type meterType = Type.TIMER;
|
||||||
|
return createMeterId(name, meterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Id createMeterId(String name, Meter.Type meterType) {
|
||||||
|
TestMeterRegistry registry = new TestMeterRegistry();
|
||||||
|
return Meter.builder(name, meterType, Collections.emptyList()).register(registry)
|
||||||
|
.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MetricsProperties createProperties(String... properties) {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
TestPropertyValues.of(properties).applyTo(environment);
|
||||||
|
Binder binder = Binder.get(environment);
|
||||||
|
return binder.bind("", Bindable.of(MetricsProperties.class))
|
||||||
|
.orElseGet(MetricsProperties::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestMeterRegistry extends SimpleMeterRegistry {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2018 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
|
||||||
|
*
|
||||||
|
* http://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 io.micrometer.core.instrument.Meter.Type;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.bind.Bindable;
|
||||||
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
|
import org.springframework.boot.test.util.TestPropertyValues;
|
||||||
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link ServiceLevelAgreementBoundary}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class ServiceLevelAgreementBoundaryTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForDistributionSummaryWhenFromLongShouldReturnLongValue() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf(123L);
|
||||||
|
assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForDistributionSummaryWhenFromNumberStringShouldReturnLongValue() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123");
|
||||||
|
assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isEqualTo(123);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForDistributionSummaryWhenFromDurationStringShouldReturnNull() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary
|
||||||
|
.valueOf("123ms");
|
||||||
|
assertThat(sla.getValue(Type.DISTRIBUTION_SUMMARY)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForTimerWhenFromLongShouldReturnMsToNanosValue() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf(123L);
|
||||||
|
assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForTimerWhenFromNumberStringShouldMsToNanosValue() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123");
|
||||||
|
assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForTimerWhenFromDurationStringShouldReturnDrationNanos() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary
|
||||||
|
.valueOf("123ms");
|
||||||
|
assertThat(sla.getValue(Type.TIMER)).isEqualTo(123000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getValueForOthersShouldReturnNull() {
|
||||||
|
ServiceLevelAgreementBoundary sla = ServiceLevelAgreementBoundary.valueOf("123");
|
||||||
|
assertThat(sla.getValue(Type.COUNTER)).isNull();
|
||||||
|
assertThat(sla.getValue(Type.GAUGE)).isNull();
|
||||||
|
assertThat(sla.getValue(Type.LONG_TASK_TIMER)).isNull();
|
||||||
|
assertThat(sla.getValue(Type.OTHER)).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void valueOfShouldWorkInBinder() {
|
||||||
|
MockEnvironment environment = new MockEnvironment();
|
||||||
|
TestPropertyValues.of("duration=10ms", "long=20").applyTo(environment);
|
||||||
|
assertThat(Binder.get(environment)
|
||||||
|
.bind("duration", Bindable.of(ServiceLevelAgreementBoundary.class)).get()
|
||||||
|
.getValue(Type.TIMER)).isEqualTo(10000000);
|
||||||
|
assertThat(Binder.get(environment)
|
||||||
|
.bind("long", Bindable.of(ServiceLevelAgreementBoundary.class)).get()
|
||||||
|
.getValue(Type.TIMER)).isEqualTo(20000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2012-2018 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
|
||||||
|
*
|
||||||
|
* http://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.metrics;
|
||||||
|
|
||||||
|
import io.micrometer.core.instrument.config.MeterFilter;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example to show a {@link MeterFilter}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class MetricsFilterBeanExample {
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class MetricsFilterExampleConfiguration {
|
||||||
|
|
||||||
|
// tag::configuration[]
|
||||||
|
@Bean
|
||||||
|
public MeterFilter renameRegionTagMeterFilter() {
|
||||||
|
return MeterFilter.renameTag("com.example", "mytag.region", "mytag.area");
|
||||||
|
}
|
||||||
|
// end::configuration[]
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue