Use seconds by default in the Micrometer bridge (#8490)

Co-authored-by: Lauri Tulmin <tulmin@gmail.com>
This commit is contained in:
Mateusz Rzeszutek 2023-05-15 15:54:37 +02:00 committed by GitHub
parent 3dd8ad7669
commit 0b774af1ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 90 additions and 82 deletions

View File

@ -2,6 +2,6 @@
| System property | Type | Default | Description |
|------------------------------------------------------------|---------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `otel.instrumentation.micrometer.base-time-unit` | String | `ms` | Set the base time unit for the OpenTelemetry `MeterRegistry` implementation. <details><summary>Valid values</summary>`ns`, `nanoseconds`, `us`, `microseconds`, `ms`, `microseconds`, `s`, `seconds`, `min`, `minutes`, `h`, `hours`, `d`, `days`</details> |
| `otel.instrumentation.micrometer.base-time-unit` | String | `s` | Set the base time unit for the OpenTelemetry `MeterRegistry` implementation. <details><summary>Valid values</summary>`ns`, `nanoseconds`, `us`, `microseconds`, `ms`, `milliseconds`, `s`, `seconds`, `min`, `minutes`, `h`, `hours`, `d`, `days`</details> |
| `otel.instrumentation.micrometer.prometheus-mode.enabled` | boolean | false | Enable the "Prometheus mode" this will simulate the behavior of Micrometer's PrometheusMeterRegistry. The instruments will be renamed to match Micrometer instrument naming, and the base time unit will be set to seconds. |
| `otel.instrumentation.micrometer.histogram-gauges.enabled` | boolean | false | Enables the generation of gauge-based Micrometer histograms for `DistributionSummary` and `Timer` instruments. |

View File

@ -30,10 +30,10 @@ tasks {
val testBaseTimeUnit by registering(Test::class) {
filter {
includeTestsMatching("*TimerSecondsTest")
includeTestsMatching("*TimerMillisecondsTest")
}
include("**/*TimerSecondsTest.*")
jvmArgs("-Dotel.instrumentation.micrometer.base-time-unit=seconds")
include("**/*TimerMillisecondsTest.*")
jvmArgs("-Dotel.instrumentation.micrometer.base-time-unit=milliseconds")
}
val testHistogramGauges by registering(Test::class) {
@ -46,7 +46,7 @@ tasks {
test {
filter {
excludeTestsMatching("*TimerSecondsTest")
excludeTestsMatching("*TimerMillisecondsTest")
excludeTestsMatching("*PrometheusModeTest")
excludeTestsMatching("*HistogramGaugesTest")
}

View File

@ -19,7 +19,7 @@ final class TimeUnitParser {
static TimeUnit parseConfigValue(@Nullable String value) {
if (value == null) {
return TimeUnit.MILLISECONDS;
return TimeUnit.SECONDS;
}
// short names are UCUM names
// long names are just TimeUnit values lowercased
@ -49,10 +49,10 @@ final class TimeUnitParser {
if (logger.isLoggable(WARNING)) {
logger.log(
WARNING,
"Invalid base time unit: \"{0}\"; using 'ms' as the base time unit instead",
"Invalid base time unit: \"{0}\"; using \"s\" as the base time unit instead",
value);
}
return TimeUnit.MILLISECONDS;
return TimeUnit.SECONDS;
}
}

View File

@ -6,13 +6,13 @@
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5;
import io.micrometer.core.instrument.Metrics;
import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractFunctionTimerSecondsTest;
import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractFunctionTimerMillisecondsTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.extension.RegisterExtension;
class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest {
class FunctionTimerMillisecondsTest extends AbstractFunctionTimerMillisecondsTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();

View File

@ -6,13 +6,13 @@
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5;
import io.micrometer.core.instrument.Metrics;
import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractLongTaskTimerSecondsTest;
import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractLongTaskTimerMillisecondsTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.extension.RegisterExtension;
class LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest {
class LongTaskTimerMillisecondsTest extends AbstractLongTaskTimerMillisecondsTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();

View File

@ -6,13 +6,13 @@
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5;
import io.micrometer.core.instrument.Metrics;
import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractTimerSecondsTest;
import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractTimerMillisecondsTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.extension.RegisterExtension;
class TimerSecondsTest extends AbstractTimerSecondsTest {
class TimerMillisecondsTest extends AbstractTimerMillisecondsTest {
@RegisterExtension
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();

View File

@ -23,7 +23,7 @@ public final class OpenTelemetryMeterRegistryBuilder {
private final OpenTelemetry openTelemetry;
private Clock clock = Clock.SYSTEM;
private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS;
private TimeUnit baseTimeUnit = TimeUnit.SECONDS;
private boolean prometheusMode = false;
private boolean histogramGaugesEnabled = false;

View File

@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExte
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.extension.RegisterExtension;
class LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest {
class FunctionTimerMillisecondsTest extends AbstractFunctionTimerMillisecondsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@ -21,7 +21,7 @@ class LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest {
@Override
OpenTelemetryMeterRegistryBuilder configureOtelRegistry(
OpenTelemetryMeterRegistryBuilder registry) {
return registry.setBaseTimeUnit(TimeUnit.SECONDS);
return registry.setBaseTimeUnit(TimeUnit.MILLISECONDS);
}
};

View File

@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExte
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.extension.RegisterExtension;
class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest {
class LongTaskTimerMillisecondsTest extends AbstractLongTaskTimerMillisecondsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@ -21,7 +21,7 @@ class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest {
@Override
OpenTelemetryMeterRegistryBuilder configureOtelRegistry(
OpenTelemetryMeterRegistryBuilder registry) {
return registry.setBaseTimeUnit(TimeUnit.SECONDS);
return registry.setBaseTimeUnit(TimeUnit.MILLISECONDS);
}
};

View File

@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExte
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.extension.RegisterExtension;
class TimerSecondsTest extends AbstractTimerSecondsTest {
class TimerMillisecondsTest extends AbstractTimerMillisecondsTest {
@RegisterExtension
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
@ -21,7 +21,7 @@ class TimerSecondsTest extends AbstractTimerSecondsTest {
@Override
OpenTelemetryMeterRegistryBuilder configureOtelRegistry(
OpenTelemetryMeterRegistryBuilder registry) {
return registry.setBaseTimeUnit(TimeUnit.SECONDS);
return registry.setBaseTimeUnit(TimeUnit.MILLISECONDS);
}
};

View File

@ -17,7 +17,7 @@ import org.assertj.core.api.AbstractIterableAssert;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public abstract class AbstractFunctionTimerSecondsTest {
public abstract class AbstractFunctionTimerMillisecondsTest {
protected abstract InstrumentationExtension testing();
@ -30,11 +30,11 @@ public abstract class AbstractFunctionTimerSecondsTest {
}
@Test
void testFunctionTimerWithBaseUnitSeconds() {
void testFunctionTimerWithBaseUnitMilliseconds() {
// given
FunctionTimer functionTimer =
FunctionTimer.builder(
"testFunctionTimerSeconds",
"testFunctionTimerMilliseconds",
timerObj,
TestTimer::getCount,
TestTimer::getTotalTimeNanos,
@ -50,7 +50,7 @@ public abstract class AbstractFunctionTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testFunctionTimerSeconds.count",
"testFunctionTimerMilliseconds.count",
metrics ->
metrics.anySatisfy(
metric ->
@ -69,19 +69,19 @@ public abstract class AbstractFunctionTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testFunctionTimerSeconds.sum",
"testFunctionTimerMilliseconds.sum",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasDescription("This is a test function timer")
.hasUnit("s")
.hasUnit("ms")
.hasDoubleSumSatisfying(
sum ->
sum.hasPointsSatisfying(
point ->
point
.hasValue(42)
.hasValue(42_000)
.hasAttributes(attributeEntry("tag", "value"))))));
// when
@ -92,7 +92,7 @@ public abstract class AbstractFunctionTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testFunctionTimerSeconds.count",
"testFunctionTimerMilliseconds.count",
AbstractIterableAssert::isEmpty);
}
}

View File

@ -78,13 +78,13 @@ public abstract class AbstractFunctionTimerTest {
metric ->
assertThat(metric)
.hasDescription("This is a test function timer")
.hasUnit("ms")
.hasUnit("s")
.hasDoubleSumSatisfying(
sum ->
sum.hasPointsSatisfying(
point ->
point
.hasValue(42_000)
.hasValue(42)
.hasAttributes(attributeEntry("tag", "value"))))));
// when
@ -120,13 +120,13 @@ public abstract class AbstractFunctionTimerTest {
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("ms")
.hasUnit("s")
.hasDoubleSumSatisfying(
sum ->
sum.hasPointsSatisfying(
point ->
point
.hasValue(1.234)
.hasValue(0.001234)
.hasAttributes(Attributes.empty())))));
}
@ -163,17 +163,17 @@ public abstract class AbstractFunctionTimerTest {
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("ms")
.hasUnit("s")
.hasDoubleSumSatisfying(
sum ->
sum.hasPointsSatisfying(
point ->
point
.hasValue(12_000)
.hasValue(12)
.hasAttributes(attributeEntry("tag", "1")),
point ->
point
.hasValue(42_000)
.hasValue(42)
.hasAttributes(attributeEntry("tag", "2"))))));
}
}

View File

@ -70,7 +70,7 @@ public abstract class AbstractLongTaskTimerHistogramTest {
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("ms")
.hasUnit("s")
.hasDoubleSumSatisfying(
sum ->
sum.isNotMonotonic()
@ -95,11 +95,11 @@ public abstract class AbstractLongTaskTimerHistogramTest {
gauge.hasPointsSatisfying(
point ->
point
.hasAttributes(attributeEntry("le", "100"))
.hasAttributes(attributeEntry("le", "0.1"))
.hasValue(2),
point ->
point
.hasAttributes(attributeEntry("le", "1000"))
.hasAttributes(attributeEntry("le", "1"))
.hasValue(3)))));
// when
@ -136,7 +136,7 @@ public abstract class AbstractLongTaskTimerHistogramTest {
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("ms")
.hasUnit("s")
.hasDoubleSumSatisfying(
sum ->
sum.isNotMonotonic()
@ -159,10 +159,10 @@ public abstract class AbstractLongTaskTimerHistogramTest {
point ->
point
.hasValue(0)
.hasAttributes(attributeEntry("le", "100")),
.hasAttributes(attributeEntry("le", "0.1")),
point ->
point
.hasValue(0)
.hasAttributes(attributeEntry("le", "1000"))))));
.hasAttributes(attributeEntry("le", "1"))))));
}
}

View File

@ -16,15 +16,15 @@ import java.util.concurrent.TimeUnit;
import org.assertj.core.api.AbstractIterableAssert;
import org.junit.jupiter.api.Test;
public abstract class AbstractLongTaskTimerSecondsTest {
public abstract class AbstractLongTaskTimerMillisecondsTest {
protected abstract InstrumentationExtension testing();
@Test
void testLongTaskTimerWithBaseUnitSeconds() throws InterruptedException {
void testLongTaskTimerWithBaseUnitMilliseconds() throws InterruptedException {
// given
LongTaskTimer timer =
LongTaskTimer.builder("testLongTaskTimerSeconds")
LongTaskTimer.builder("testLongTaskTimerMilliseconds")
.description("This is a test long task timer")
.tags("tag", "value")
.register(Metrics.globalRegistry);
@ -36,7 +36,7 @@ public abstract class AbstractLongTaskTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testLongTaskTimerSeconds.active",
"testLongTaskTimerMilliseconds.active",
metrics ->
metrics.anySatisfy(
metric ->
@ -55,13 +55,13 @@ public abstract class AbstractLongTaskTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testLongTaskTimerSeconds.duration",
"testLongTaskTimerMilliseconds.duration",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasDescription("This is a test long task timer")
.hasUnit("s")
.hasUnit("ms")
.hasDoubleSumSatisfying(
sum ->
sum.isNotMonotonic()
@ -82,7 +82,7 @@ public abstract class AbstractLongTaskTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testLongTaskTimerSeconds.active",
"testLongTaskTimerMilliseconds.active",
metrics ->
metrics.anySatisfy(
metric ->
@ -97,7 +97,7 @@ public abstract class AbstractLongTaskTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testLongTaskTimerSeconds.duration",
"testLongTaskTimerMilliseconds.duration",
metrics ->
metrics.anySatisfy(
metric ->
@ -119,7 +119,7 @@ public abstract class AbstractLongTaskTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testLongTaskTimerSeconds.active",
"testLongTaskTimerMilliseconds.active",
AbstractIterableAssert::isEmpty);
}
}

View File

@ -61,7 +61,7 @@ public abstract class AbstractLongTaskTimerTest {
metric ->
assertThat(metric)
.hasDescription("This is a test long task timer")
.hasUnit("ms")
.hasUnit("s")
.hasDoubleSumSatisfying(
sum ->
sum.isNotMonotonic()

View File

@ -52,13 +52,13 @@ public abstract class AbstractTimerHistogramGaugesTest {
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("ms")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasSum(555500)
.hasSum(555.5)
.hasCount(4)
.hasAttributes(attributeEntry("tag", "value"))))));
testing()
@ -75,7 +75,7 @@ public abstract class AbstractTimerHistogramGaugesTest {
gauge.hasPointsSatisfying(
point ->
point
.hasValue(500000)
.hasValue(500)
.hasAttributes(attributeEntry("tag", "value"))))));
testing()
.waitAndAssertMetrics(
@ -92,25 +92,25 @@ public abstract class AbstractTimerHistogramGaugesTest {
point
.hasValue(1)
.hasAttributes(
attributeEntry("le", "1000"),
attributeEntry("le", "1"),
attributeEntry("tag", "value")),
point ->
point
.hasValue(2)
.hasAttributes(
attributeEntry("le", "10000"),
attributeEntry("le", "10"),
attributeEntry("tag", "value")),
point ->
point
.hasValue(3)
.hasAttributes(
attributeEntry("le", "100000"),
attributeEntry("le", "100"),
attributeEntry("tag", "value")),
point ->
point
.hasValue(4)
.hasAttributes(
attributeEntry("le", "1000000"),
attributeEntry("le", "1000"),
attributeEntry("tag", "value"))))));
}
@ -126,7 +126,7 @@ public abstract class AbstractTimerHistogramGaugesTest {
// when
timer.record(50, TimeUnit.MILLISECONDS);
timer.record(100, TimeUnit.MILLISECONDS);
timer.record(500, TimeUnit.MILLISECONDS);
// then
testing()
@ -138,13 +138,13 @@ public abstract class AbstractTimerHistogramGaugesTest {
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("ms")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasSum(150)
.hasSum(0.55)
.hasCount(2)
.hasAttributes(attributeEntry("tag", "value"))))));
testing()
@ -161,7 +161,7 @@ public abstract class AbstractTimerHistogramGaugesTest {
gauge.hasPointsSatisfying(
point ->
point
.hasValue(100)
.hasValue(0.5)
.hasAttributes(attributeEntry("tag", "value"))))));
testing()
.waitAndAssertMetrics(

View File

@ -17,15 +17,15 @@ import org.assertj.core.api.AbstractIterableAssert;
import org.junit.jupiter.api.Test;
@SuppressWarnings("PreferJavaTimeOverload")
public abstract class AbstractTimerSecondsTest {
public abstract class AbstractTimerMillisecondsTest {
protected abstract InstrumentationExtension testing();
@Test
void testTimerWithBaseUnitSeconds() {
void testTimerWithBaseUnitMilliseconds() {
// given
Timer timer =
Timer.builder("testTimerSeconds")
Timer.builder("testTimerMilliseconds")
.description("This is a test timer")
.tags("tag", "value")
.register(Metrics.globalRegistry);
@ -39,37 +39,37 @@ public abstract class AbstractTimerSecondsTest {
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testTimerSeconds",
"testTimerMilliseconds",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("s")
.hasUnit("ms")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasSum(23.345)
.hasSum(23_345)
.hasCount(3)
.hasAttributes(attributeEntry("tag", "value"))))));
testing()
.waitAndAssertMetrics(
INSTRUMENTATION_NAME,
"testTimerSeconds.max",
"testTimerMilliseconds.max",
metrics ->
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("s")
.hasUnit("ms")
.hasDoubleGaugeSatisfying(
gauge ->
gauge.hasPointsSatisfying(
point ->
point
.hasValue(12.345)
.hasValue(12_345)
.hasAttributes(attributeEntry("tag", "value"))))));
// when

View File

@ -8,15 +8,18 @@ package io.opentelemetry.instrumentation.micrometer.v1_5;
import static io.opentelemetry.instrumentation.micrometer.v1_5.AbstractCounterTest.INSTRUMENTATION_NAME;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
import static org.assertj.core.api.Assertions.within;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Timer;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.metrics.data.HistogramPointData;
import io.opentelemetry.sdk.metrics.internal.aggregator.ExplicitBucketHistogramUtils;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
import org.assertj.core.api.AbstractIterableAssert;
import org.assertj.core.api.ThrowingConsumer;
import org.junit.jupiter.api.Test;
@SuppressWarnings("PreferJavaTimeOverload")
@ -51,13 +54,13 @@ public abstract class AbstractTimerTest {
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("ms")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasSum(42_000)
.hasSum(42)
.hasCount(1)
.hasAttributes(attributeEntry("tag", "value"))
.hasBucketBoundaries(DEFAULT_BUCKETS)))));
@ -75,7 +78,7 @@ public abstract class AbstractTimerTest {
gauge.hasPointsSatisfying(
point ->
point
.hasValue(42_000)
.hasValue(42)
.hasAttributes(attributeEntry("tag", "value"))))));
// micrometer gauge histogram is not emitted
@ -110,13 +113,13 @@ public abstract class AbstractTimerTest {
metrics.anySatisfy(
metric ->
assertThat(metric)
.hasUnit("ms")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasSum(1.234)
.hasSum(0.001234)
.hasCount(1)
.hasAttributes(Attributes.empty())))));
testing()
@ -132,7 +135,7 @@ public abstract class AbstractTimerTest {
gauge.hasPointsSatisfying(
point ->
point
.hasValue(1.234)
.hasValue(0.001234)
.hasAttributes(Attributes.empty())))));
}
@ -167,17 +170,22 @@ public abstract class AbstractTimerTest {
metric ->
assertThat(metric)
.hasDescription("This is a test timer")
.hasUnit("ms")
.hasUnit("s")
.hasHistogramSatisfying(
histogram ->
histogram.hasPointsSatisfying(
point ->
point
.hasSum(555500)
.hasSum(555.5)
.hasCount(4)
.hasAttributes(attributeEntry("tag", "value"))
.hasBucketBoundaries(
1_000, 10_000, 100_000, 1_000_000)
.satisfies(hasBucketBoundaries(1, 10, 100, 1_000))
.hasBucketCounts(1, 1, 1, 1, 0)))));
}
private static ThrowingConsumer<HistogramPointData> hasBucketBoundaries(double... buckets) {
return pointData ->
assertThat(pointData.getBoundaries().stream().mapToDouble(d -> d).toArray())
.contains(buckets, within(0.00001));
}
}