Change the way Micrometer LongTaskTimer is bridged (#5338)
This commit is contained in:
parent
cc60ffb6e0
commit
6774ce5791
|
@ -64,6 +64,8 @@ public final class AsyncInstrumentRegistry {
|
|||
private final Map<String, LongMeasurementsRecorder> longCounters = new ConcurrentHashMap<>();
|
||||
private final Map<String, DoubleMeasurementsRecorder> upDownDoubleCounters =
|
||||
new ConcurrentHashMap<>();
|
||||
private final Map<String, LongMeasurementsRecorder> upDownLongCounters =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
AsyncInstrumentRegistry(Meter meter) {
|
||||
this.meter = new WeakReference<>(meter);
|
||||
|
@ -150,7 +152,7 @@ public final class AsyncInstrumentRegistry {
|
|||
String description,
|
||||
String baseUnit,
|
||||
Attributes attributes,
|
||||
T obj,
|
||||
@Nullable T obj,
|
||||
ToDoubleFunction<T> objMetric) {
|
||||
|
||||
DoubleMeasurementsRecorder recorder =
|
||||
|
@ -171,6 +173,31 @@ public final class AsyncInstrumentRegistry {
|
|||
return new AsyncMeasurementHandle(recorder, attributes);
|
||||
}
|
||||
|
||||
public <T> AsyncMeasurementHandle buildUpDownLongCounter(
|
||||
String name,
|
||||
String description,
|
||||
String baseUnit,
|
||||
Attributes attributes,
|
||||
@Nullable T obj,
|
||||
ToLongFunction<T> objMetric) {
|
||||
|
||||
LongMeasurementsRecorder recorder =
|
||||
upDownLongCounters.computeIfAbsent(
|
||||
name,
|
||||
n -> {
|
||||
LongMeasurementsRecorder recorderCallback = new LongMeasurementsRecorder();
|
||||
otelMeter()
|
||||
.upDownCounterBuilder(name)
|
||||
.setDescription(description)
|
||||
.setUnit(baseUnit)
|
||||
.buildWithCallback(recorderCallback);
|
||||
return recorderCallback;
|
||||
});
|
||||
recorder.addMeasurement(attributes, new LongMeasurementSource<>(obj, objMetric));
|
||||
|
||||
return new AsyncMeasurementHandle(recorder, attributes);
|
||||
}
|
||||
|
||||
private Meter otelMeter() {
|
||||
Meter otelMeter = meter.get();
|
||||
if (otelMeter == null) {
|
||||
|
|
|
@ -33,11 +33,7 @@ final class Bridging {
|
|||
}
|
||||
|
||||
static String name(Meter.Id id, NamingConvention namingConvention) {
|
||||
return name(id.getName(), id, namingConvention);
|
||||
}
|
||||
|
||||
private static String name(String name, Meter.Id id, NamingConvention namingConvention) {
|
||||
return namingConvention.name(name, id.getType(), id.getBaseUnit());
|
||||
return namingConvention.name(id.getName(), id.getType(), id.getBaseUnit());
|
||||
}
|
||||
|
||||
static String description(Meter.Id id) {
|
||||
|
@ -56,7 +52,7 @@ final class Bridging {
|
|||
// use "total_time" instead of "total" to avoid clashing with Statistic.TOTAL
|
||||
String statisticStr =
|
||||
statistic == Statistic.TOTAL_TIME ? "total_time" : statistic.getTagValueRepresentation();
|
||||
return name(prefix + statisticStr, id, namingConvention);
|
||||
return namingConvention.name(prefix + statisticStr, id.getType(), id.getBaseUnit());
|
||||
}
|
||||
|
||||
private Bridging() {}
|
||||
|
|
|
@ -8,14 +8,12 @@ package io.opentelemetry.instrumentation.micrometer.v1_5;
|
|||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.statisticInstrumentName;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes;
|
||||
|
||||
import io.micrometer.core.instrument.AbstractDistributionSummary;
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.core.instrument.DistributionSummary;
|
||||
import io.micrometer.core.instrument.Measurement;
|
||||
import io.micrometer.core.instrument.Statistic;
|
||||
import io.micrometer.core.instrument.config.NamingConvention;
|
||||
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
|
||||
import io.micrometer.core.instrument.distribution.NoopHistogram;
|
||||
|
@ -59,15 +57,17 @@ final class OpenTelemetryDistributionSummary extends AbstractDistributionSummary
|
|||
max = new TimeWindowMax(clock, distributionStatisticConfig);
|
||||
|
||||
this.attributes = tagsAsAttributes(id, namingConvention);
|
||||
|
||||
String conventionName = name(id, namingConvention);
|
||||
this.otelHistogram =
|
||||
otelMeter
|
||||
.histogramBuilder(name(id, namingConvention))
|
||||
.histogramBuilder(conventionName)
|
||||
.setDescription(description(id))
|
||||
.setUnit(baseUnit(id))
|
||||
.build();
|
||||
this.maxHandle =
|
||||
asyncInstrumentRegistry.buildGauge(
|
||||
statisticInstrumentName(id, Statistic.MAX, namingConvention),
|
||||
conventionName + ".max",
|
||||
description(id),
|
||||
baseUnit(id),
|
||||
attributes,
|
||||
|
|
|
@ -6,13 +6,12 @@
|
|||
package io.opentelemetry.instrumentation.micrometer.v1_5;
|
||||
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.statisticInstrumentName;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.TimeUnitHelper.getUnitString;
|
||||
|
||||
import io.micrometer.core.instrument.FunctionTimer;
|
||||
import io.micrometer.core.instrument.Measurement;
|
||||
import io.micrometer.core.instrument.Statistic;
|
||||
import io.micrometer.core.instrument.config.NamingConvention;
|
||||
import io.micrometer.core.instrument.util.MeterEquivalence;
|
||||
import io.micrometer.core.instrument.util.TimeUtils;
|
||||
|
@ -46,8 +45,8 @@ final class OpenTelemetryFunctionTimer<T> implements FunctionTimer, RemovableMet
|
|||
this.id = id;
|
||||
this.baseTimeUnit = baseTimeUnit;
|
||||
|
||||
String countMeterName = statisticInstrumentName(id, Statistic.COUNT, namingConvention);
|
||||
String totalTimeMeterName = statisticInstrumentName(id, Statistic.TOTAL_TIME, namingConvention);
|
||||
String countMeterName = name(id, namingConvention) + ".count";
|
||||
String totalTimeMeterName = name(id, namingConvention) + ".sum";
|
||||
Attributes attributes = tagsAsAttributes(id, namingConvention);
|
||||
|
||||
countMeasurementHandle =
|
||||
|
|
|
@ -7,34 +7,25 @@ package io.opentelemetry.instrumentation.micrometer.v1_5;
|
|||
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.statisticInstrumentName;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.TimeUnitHelper.getUnitString;
|
||||
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.core.instrument.Measurement;
|
||||
import io.micrometer.core.instrument.Statistic;
|
||||
import io.micrometer.core.instrument.config.NamingConvention;
|
||||
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
|
||||
import io.micrometer.core.instrument.internal.DefaultLongTaskTimer;
|
||||
import io.micrometer.core.instrument.util.TimeUtils;
|
||||
import io.opentelemetry.api.common.Attributes;
|
||||
import io.opentelemetry.api.metrics.DoubleHistogram;
|
||||
import io.opentelemetry.api.metrics.LongUpDownCounter;
|
||||
import io.opentelemetry.api.metrics.Meter;
|
||||
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry;
|
||||
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry.AsyncMeasurementHandle;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
final class OpenTelemetryLongTaskTimer extends DefaultLongTaskTimer implements RemovableMeter {
|
||||
|
||||
private final TimeUnit baseTimeUnit;
|
||||
private final DistributionStatisticConfig distributionStatisticConfig;
|
||||
// TODO: use bound instruments when they're available
|
||||
private final DoubleHistogram otelHistogram;
|
||||
private final LongUpDownCounter otelActiveTasksCounter;
|
||||
private final Attributes attributes;
|
||||
|
||||
private volatile boolean removed = false;
|
||||
private final AsyncMeasurementHandle activeTasksHandle;
|
||||
private final AsyncMeasurementHandle durationHandle;
|
||||
|
||||
OpenTelemetryLongTaskTimer(
|
||||
Id id,
|
||||
|
@ -42,37 +33,29 @@ final class OpenTelemetryLongTaskTimer extends DefaultLongTaskTimer implements R
|
|||
Clock clock,
|
||||
TimeUnit baseTimeUnit,
|
||||
DistributionStatisticConfig distributionStatisticConfig,
|
||||
Meter otelMeter) {
|
||||
AsyncInstrumentRegistry asyncInstrumentRegistry) {
|
||||
super(id, clock, baseTimeUnit, distributionStatisticConfig, false);
|
||||
|
||||
this.baseTimeUnit = baseTimeUnit;
|
||||
this.distributionStatisticConfig = distributionStatisticConfig;
|
||||
|
||||
this.otelHistogram =
|
||||
otelMeter
|
||||
.histogramBuilder(name(id, namingConvention))
|
||||
.setDescription(description(id))
|
||||
.setUnit(getUnitString(baseTimeUnit))
|
||||
.build();
|
||||
this.otelActiveTasksCounter =
|
||||
otelMeter
|
||||
.upDownCounterBuilder(
|
||||
statisticInstrumentName(id, Statistic.ACTIVE_TASKS, namingConvention))
|
||||
.setDescription(description(id))
|
||||
.setUnit("tasks")
|
||||
.build();
|
||||
this.attributes = tagsAsAttributes(id, namingConvention);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sample start() {
|
||||
Sample original = super.start();
|
||||
if (removed) {
|
||||
return original;
|
||||
}
|
||||
|
||||
otelActiveTasksCounter.add(1, attributes);
|
||||
return new OpenTelemetrySample(original);
|
||||
String conventionName = name(id, namingConvention);
|
||||
Attributes attributes = tagsAsAttributes(id, namingConvention);
|
||||
this.activeTasksHandle =
|
||||
asyncInstrumentRegistry.buildUpDownLongCounter(
|
||||
conventionName + ".active",
|
||||
description(id),
|
||||
"tasks",
|
||||
attributes,
|
||||
this,
|
||||
DefaultLongTaskTimer::activeTasks);
|
||||
this.durationHandle =
|
||||
asyncInstrumentRegistry.buildUpDownDoubleCounter(
|
||||
conventionName + ".duration",
|
||||
description(id),
|
||||
getUnitString(baseTimeUnit),
|
||||
attributes,
|
||||
this,
|
||||
t -> t.duration(baseTimeUnit));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -83,41 +66,12 @@ final class OpenTelemetryLongTaskTimer extends DefaultLongTaskTimer implements R
|
|||
|
||||
@Override
|
||||
public void onRemove() {
|
||||
removed = true;
|
||||
activeTasksHandle.remove();
|
||||
durationHandle.remove();
|
||||
}
|
||||
|
||||
boolean isUsingMicrometerHistograms() {
|
||||
return distributionStatisticConfig.isPublishingPercentiles()
|
||||
|| distributionStatisticConfig.isPublishingHistogram();
|
||||
}
|
||||
|
||||
private final class OpenTelemetrySample extends Sample {
|
||||
|
||||
private final Sample original;
|
||||
private volatile boolean stopped = false;
|
||||
|
||||
private OpenTelemetrySample(Sample original) {
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long stop() {
|
||||
if (stopped) {
|
||||
return -1;
|
||||
}
|
||||
stopped = true;
|
||||
long durationNanos = original.stop();
|
||||
if (!removed) {
|
||||
otelActiveTasksCounter.add(-1, attributes);
|
||||
double time = TimeUtils.nanosToUnit(durationNanos, baseTimeUnit);
|
||||
otelHistogram.record(time, attributes);
|
||||
}
|
||||
return durationNanos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double duration(TimeUnit unit) {
|
||||
return stopped ? -1 : original.duration(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ public final class OpenTelemetryMeterRegistry extends MeterRegistry {
|
|||
clock,
|
||||
getBaseTimeUnit(),
|
||||
distributionStatisticConfig,
|
||||
otelMeter);
|
||||
asyncInstrumentRegistry);
|
||||
if (timer.isUsingMicrometerHistograms()) {
|
||||
HistogramGauges.registerWithCommonFormat(timer, this);
|
||||
}
|
||||
|
|
|
@ -7,14 +7,12 @@ package io.opentelemetry.instrumentation.micrometer.v1_5;
|
|||
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.name;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.statisticInstrumentName;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes;
|
||||
import static io.opentelemetry.instrumentation.micrometer.v1_5.TimeUnitHelper.getUnitString;
|
||||
|
||||
import io.micrometer.core.instrument.AbstractTimer;
|
||||
import io.micrometer.core.instrument.Clock;
|
||||
import io.micrometer.core.instrument.Measurement;
|
||||
import io.micrometer.core.instrument.Statistic;
|
||||
import io.micrometer.core.instrument.config.NamingConvention;
|
||||
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
|
||||
import io.micrometer.core.instrument.distribution.NoopHistogram;
|
||||
|
@ -62,15 +60,17 @@ final class OpenTelemetryTimer extends AbstractTimer implements RemovableMeter {
|
|||
|
||||
this.baseTimeUnit = baseTimeUnit;
|
||||
this.attributes = tagsAsAttributes(id, namingConvention);
|
||||
|
||||
String conventionName = name(id, namingConvention);
|
||||
this.otelHistogram =
|
||||
otelMeter
|
||||
.histogramBuilder(name(id, namingConvention))
|
||||
.histogramBuilder(conventionName)
|
||||
.setDescription(description(id))
|
||||
.setUnit(getUnitString(baseTimeUnit))
|
||||
.build();
|
||||
this.maxHandle =
|
||||
asyncInstrumentRegistry.buildGauge(
|
||||
statisticInstrumentName(id, Statistic.MAX, namingConvention),
|
||||
conventionName + ".max",
|
||||
description(id),
|
||||
getUnitString(baseTimeUnit),
|
||||
attributes,
|
||||
|
|
|
@ -10,6 +10,7 @@ import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attri
|
|||
|
||||
import io.micrometer.core.instrument.Counter;
|
||||
import io.micrometer.core.instrument.DistributionSummary;
|
||||
import io.micrometer.core.instrument.LongTaskTimer;
|
||||
import io.micrometer.core.instrument.Meter;
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.Metrics;
|
||||
|
@ -178,7 +179,7 @@ class NamingConventionTest {
|
|||
.containsOnly(attributeEntry("test.tag", "test.value")))));
|
||||
testing.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"test.renamedFunctionTimer.total_time",
|
||||
"test.renamedFunctionTimer.sum",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
|
@ -214,6 +215,43 @@ class NamingConventionTest {
|
|||
.containsOnly(attributeEntry("test.tag", "test.value")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void renameLongTaskTimer() {
|
||||
// when
|
||||
LongTaskTimer timer = Metrics.more().longTaskTimer("renamedLongTaskTimer", "tag", "value");
|
||||
timer.start().stop();
|
||||
|
||||
// then
|
||||
testing.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"test.renamedLongTaskTimer.active",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("test.tag", "test.value")))));
|
||||
testing.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"test.renamedLongTaskTimer.duration",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("test.tag", "test.value")))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void renameTimer() {
|
||||
// when
|
||||
|
|
|
@ -31,7 +31,7 @@ public abstract class AbstractFunctionTimerSecondsTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testFunctionCounterWithBaseUnitSeconds() throws InterruptedException {
|
||||
void testFunctionTimerWithBaseUnitSeconds() throws InterruptedException {
|
||||
// given
|
||||
FunctionTimer functionTimer =
|
||||
FunctionTimer.builder(
|
||||
|
@ -70,7 +70,7 @@ public abstract class AbstractFunctionTimerSecondsTest {
|
|||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testFunctionTimerSeconds.total_time",
|
||||
"testFunctionTimerSeconds.sum",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
|
@ -100,8 +100,6 @@ public abstract class AbstractFunctionTimerSecondsTest {
|
|||
AbstractIterableAssert::isEmpty);
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testFunctionTimerSeconds.total_time",
|
||||
AbstractIterableAssert::isEmpty);
|
||||
INSTRUMENTATION_NAME, "testFunctionTimerSeconds.sum", AbstractIterableAssert::isEmpty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ public abstract class AbstractFunctionTimerTest {
|
|||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testFunctionTimer.total_time",
|
||||
"testFunctionTimer.sum",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
|
@ -100,7 +100,7 @@ public abstract class AbstractFunctionTimerTest {
|
|||
INSTRUMENTATION_NAME, "testFunctionTimer.count", AbstractIterableAssert::isEmpty);
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME, "testFunctionTimer.total_time", AbstractIterableAssert::isEmpty);
|
||||
INSTRUMENTATION_NAME, "testFunctionTimer.sum", AbstractIterableAssert::isEmpty);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -121,7 +121,7 @@ public abstract class AbstractFunctionTimerTest {
|
|||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testNanoFunctionTimer.total_time",
|
||||
"testNanoFunctionTimer.sum",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
|
@ -162,7 +162,7 @@ public abstract class AbstractFunctionTimerTest {
|
|||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testFunctionTimerWithTags.total_time",
|
||||
"testFunctionTimerWithTags.sum",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.micrometer.core.instrument.LongTaskTimer;
|
|||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.assertj.core.api.AbstractIterableAssert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -58,31 +59,33 @@ public abstract class AbstractLongTaskTimerSecondsTest {
|
|||
.hasValue(1)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimerSeconds.duration",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDescription("This is a test long task timer")
|
||||
.hasUnit("s")
|
||||
.hasDoubleSum()
|
||||
.isNotMonotonic()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point -> {
|
||||
assertThat(point)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value"));
|
||||
// any value >0 - duration of currently running tasks
|
||||
assertThat(point.getValue()).isPositive();
|
||||
})));
|
||||
|
||||
// when
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
sample.stop();
|
||||
|
||||
// then
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimerSeconds",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDescription("This is a test long task timer")
|
||||
.hasUnit("s")
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSumGreaterThan(0.1)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
|
@ -99,46 +102,38 @@ public abstract class AbstractLongTaskTimerSecondsTest {
|
|||
.hasValue(0)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimerSeconds.duration",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(0)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing().clearData();
|
||||
|
||||
// when timer is removed from the registry
|
||||
Metrics.globalRegistry.remove(timer);
|
||||
sample = timer.start();
|
||||
timer.start();
|
||||
|
||||
// then no tasks are active after starting a new sample
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimerSeconds.active",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(0)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
|
||||
// when
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
sample.stop();
|
||||
|
||||
// then sample of a removed timer does not record any data
|
||||
AbstractIterableAssert::isEmpty);
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimerSeconds",
|
||||
metrics ->
|
||||
metrics.allSatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.noneSatisfy(
|
||||
point -> assertThat(point).hasSumGreaterThan(0.2).hasCount(2))));
|
||||
"testLongTaskTimerSeconds.duration",
|
||||
AbstractIterableAssert::isEmpty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import io.micrometer.core.instrument.LongTaskTimer;
|
|||
import io.micrometer.core.instrument.Metrics;
|
||||
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.assertj.core.api.AbstractIterableAssert;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -58,31 +59,33 @@ public abstract class AbstractLongTaskTimerTest {
|
|||
.hasValue(1)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimer.duration",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDescription("This is a test long task timer")
|
||||
.hasUnit("ms")
|
||||
.hasDoubleSum()
|
||||
.isNotMonotonic()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point -> {
|
||||
assertThat(point)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value"));
|
||||
// any value >0 - duration of currently running tasks
|
||||
assertThat(point.getValue()).isPositive();
|
||||
})));
|
||||
|
||||
// when
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
sample.stop();
|
||||
|
||||
// then
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimer",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDescription("This is a test long task timer")
|
||||
.hasUnit("ms")
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasSumGreaterThan(100)
|
||||
.hasCount(1)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
|
@ -99,76 +102,34 @@ public abstract class AbstractLongTaskTimerTest {
|
|||
.hasValue(0)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimer.duration",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleSum()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(0)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
testing().clearData();
|
||||
|
||||
// when timer is removed from the registry
|
||||
Metrics.globalRegistry.remove(timer);
|
||||
sample = timer.start();
|
||||
timer.start();
|
||||
|
||||
// then no tasks are active after starting a new sample
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimer.active",
|
||||
metrics ->
|
||||
metrics.anySatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasLongSum()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point ->
|
||||
assertThat(point)
|
||||
.hasValue(0)
|
||||
.attributes()
|
||||
.containsOnly(attributeEntry("tag", "value")))));
|
||||
|
||||
// when
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
sample.stop();
|
||||
|
||||
// then sample of a removed timer does not record any data
|
||||
INSTRUMENTATION_NAME, "testLongTaskTimer.active", AbstractIterableAssert::isEmpty);
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimer",
|
||||
metrics ->
|
||||
metrics.allSatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.noneSatisfy(
|
||||
point -> assertThat(point).hasSumGreaterThan(200).hasCount(2))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleSampleStopCalls() throws InterruptedException {
|
||||
// given
|
||||
LongTaskTimer timer =
|
||||
LongTaskTimer.builder("testLongTaskTimerSampleStop").register(Metrics.globalRegistry);
|
||||
|
||||
// when stop() is called multiple times
|
||||
LongTaskTimer.Sample sample = timer.start();
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
|
||||
sample.stop();
|
||||
sample.stop();
|
||||
sample.stop();
|
||||
|
||||
// then only the first time is recorded
|
||||
testing()
|
||||
.waitAndAssertMetrics(
|
||||
INSTRUMENTATION_NAME,
|
||||
"testLongTaskTimerSampleStop",
|
||||
metrics ->
|
||||
metrics.allSatisfy(
|
||||
metric ->
|
||||
assertThat(metric)
|
||||
.hasDoubleHistogram()
|
||||
.points()
|
||||
.satisfiesExactly(
|
||||
point -> assertThat(point).hasSumGreaterThan(100).hasCount(1))));
|
||||
INSTRUMENTATION_NAME, "testLongTaskTimer.duration", AbstractIterableAssert::isEmpty);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue