Fix parsing of day duration meter values

Switch `MeterValue` parsing logic so that we try `Duration` before
`double`. Prior to this commit, the value `1d` would result in `1.0`
rather than "1 day".

Fixes gh-28302
2.4.x
Andy Wilkinson 3 years ago committed by Phillip Webb
parent fc2c460512
commit 540468b2f0

@ -86,11 +86,11 @@ public final class MeterValue {
* @return a {@link MeterValue} instance * @return a {@link MeterValue} instance
*/ */
public static MeterValue valueOf(String value) { public static MeterValue valueOf(String value) {
Double number = safeParseDouble(value); Duration duration = safeParseDuration(value);
if (number != null) { if (duration != null) {
return new MeterValue(number); return new MeterValue(duration);
} }
return new MeterValue(DurationStyle.detectAndParse(value)); return new MeterValue(Double.valueOf(value));
} }
/** /**
@ -114,11 +114,11 @@ public final class MeterValue {
return new MeterValue(value); return new MeterValue(value);
} }
private static Double safeParseDouble(String value) { private static Duration safeParseDuration(String value) {
try { try {
return Double.valueOf(value); return DurationStyle.detectAndParse(value);
} }
catch (NumberFormatException ex) { catch (IllegalArgumentException ex) {
return null; return null;
} }
} }

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -235,21 +235,21 @@ public class MetricsProperties {
/** /**
* Specific service-level objective boundaries for meter IDs starting with the * Specific service-level objective boundaries for meter IDs starting with the
* specified name. The longest match wins. Counters will be published for each * 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 * specified boundary. Values can be specified as a double or as a Duration value
* (for timer meters, defaulting to ms if no unit specified). * (for timer meters, defaulting to ms if no unit specified).
*/ */
private final Map<String, ServiceLevelObjectiveBoundary[]> slo = new LinkedHashMap<>(); private final Map<String, ServiceLevelObjectiveBoundary[]> slo = new LinkedHashMap<>();
/** /**
* Minimum value that meter IDs starting with the specified name are expected to * Minimum value that meter IDs starting with the specified name are expected to
* observe. The longest match wins. Values can be specified as a long or as a * observe. The longest match wins. Values can be specified as a double or as a
* Duration value (for timer meters, defaulting to ms if no unit specified). * Duration value (for timer meters, defaulting to ms if no unit specified).
*/ */
private final Map<String, String> minimumExpectedValue = new LinkedHashMap<>(); private final Map<String, String> minimumExpectedValue = new LinkedHashMap<>();
/** /**
* Maximum value that meter IDs starting with the specified name are expected to * Maximum value that meter IDs starting with the specified name are expected to
* observe. The longest match wins. Values can be specified as a long or as a * observe. The longest match wins. Values can be specified as a double or as a
* Duration value (for timer meters, defaulting to ms if no unit specified). * Duration value (for timer meters, defaulting to ms if no unit specified).
*/ */
private final Map<String, String> maximumExpectedValue = new LinkedHashMap<>(); private final Map<String, String> maximumExpectedValue = new LinkedHashMap<>();

@ -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"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.boot.actuate.autoconfigure.metrics; package org.springframework.boot.actuate.autoconfigure.metrics;
import java.time.Duration;
import io.micrometer.core.instrument.Meter.Type; import io.micrometer.core.instrument.Meter.Type;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -42,11 +44,17 @@ class ServiceLevelObjectiveBoundaryTests {
} }
@Test @Test
void getValueForTimerWhenFromDurationStringShouldReturnDurationNanos() { void getValueForTimerWhenFromMillisecondDurationStringShouldReturnDurationNanos() {
ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("123ms"); ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("123ms");
assertThat(slo.getValue(Type.TIMER)).isEqualTo(123000000); assertThat(slo.getValue(Type.TIMER)).isEqualTo(123000000);
} }
@Test
void getValueForTimerWhenFromDaysDurationStringShouldReturnDurationNanos() {
ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf("1d");
assertThat(slo.getValue(Type.TIMER)).isEqualTo(Duration.ofDays(1).toNanos());
}
@Test @Test
void getValueForDistributionSummaryWhenFromDoubleShouldReturnDoubleValue() { void getValueForDistributionSummaryWhenFromDoubleShouldReturnDoubleValue() {
ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf(123.42); ServiceLevelObjectiveBoundary slo = ServiceLevelObjectiveBoundary.valueOf(123.42);

Loading…
Cancel
Save