Fix base time unit in Micrometer FunctionTimer
MODIFY
instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts
instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts
@@ -23,10 +23,9 @@ tasks {
val testBaseTimeUnit by registering(Test::class) {
filter {
includeTestsMatching("*TimerSecondsTest")
- includeTestsMatching("*LongTimerSecondsTest")
isFailOnNoMatchingTests = false
}
- include("**/*TimerSecondsTest.*", "**/*LongTaskTimerSecondsTest.*")
+ include("**/*TimerSecondsTest.*")
jvmArgs("-Dotel.instrumentation.micrometer.base-time-unit=seconds")
}
@@ -34,7 +33,6 @@ tasks {
dependsOn(testBaseTimeUnit)
filter {
excludeTestsMatching("*TimerSecondsTest")
- excludeTestsMatching("*LongTimerSecondsTest")
isFailOnNoMatchingTests = false
}
}

ADD
instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5;
+
+import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractFunctionTimerSecondsTest;
+import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest {
+
+ @RegisterExtension
+ static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
+
+ @Override
+ protected InstrumentationExtension testing() {
+ return testing;
+ }
+}

MODIFY
instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionTimer.java
instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryFunctionTimer.java
@@ -8,6 +8,7 @@ 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.tagsAsAttributes;
+import static io.opentelemetry.instrumentation.micrometer.v1_5.TimeUnitHelper.getUnitString;
import io.micrometer.core.instrument.FunctionTimer;
import io.micrometer.core.instrument.Measurement;
@@ -27,6 +28,7 @@ import javax.annotation.Nullable;
final class OpenTelemetryFunctionTimer<T> implements FunctionTimer, RemovableMeter {
private final Id id;
+ private final TimeUnit baseTimeUnit;
private final AsyncMeasurementHandle countMeasurementHandle;
private final AsyncMeasurementHandle totalTimeMeasurementHandle;
@@ -36,8 +38,11 @@ final class OpenTelemetryFunctionTimer<T> implements FunctionTimer, RemovableMet
ToLongFunction<T> countFunction,
ToDoubleFunction<T> totalTimeFunction,
TimeUnit totalTimeFunctionUnit,
+ TimeUnit baseTimeUnit,
AsyncInstrumentRegistry asyncInstrumentRegistry) {
+
this.id = id;
+ this.baseTimeUnit = baseTimeUnit;
String countMeterName = statisticInstrumentName(id, Statistic.COUNT);
String totalTimeMeterName = statisticInstrumentName(id, Statistic.TOTAL_TIME);
@@ -51,14 +56,12 @@ final class OpenTelemetryFunctionTimer<T> implements FunctionTimer, RemovableMet
asyncInstrumentRegistry.buildDoubleCounter(
totalTimeMeterName,
description(id),
- /* baseUnit = */ "ms",
+ getUnitString(baseTimeUnit),
attributes,
obj,
val ->
TimeUtils.convert(
- totalTimeFunction.applyAsDouble(val),
- totalTimeFunctionUnit,
- TimeUnit.MILLISECONDS));
+ totalTimeFunction.applyAsDouble(val), totalTimeFunctionUnit, baseTimeUnit));
}
@Override
@@ -81,7 +84,7 @@ final class OpenTelemetryFunctionTimer<T> implements FunctionTimer, RemovableMet
@Override
public TimeUnit baseTimeUnit() {
- return TimeUnit.MILLISECONDS;
+ return baseTimeUnit;
}
@Override

MODIFY
instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java
instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java
@@ -129,7 +129,13 @@ public final class OpenTelemetryMeterRegistry extends MeterRegistry {
ToDoubleFunction<T> totalTimeFunction,
TimeUnit totalTimeFunctionUnit) {
return new OpenTelemetryFunctionTimer<>(
- id, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit, asyncInstrumentRegistry);
+ id,
+ obj,
+ countFunction,
+ totalTimeFunction,
+ totalTimeFunctionUnit,
+ getBaseTimeUnit(),
+ asyncInstrumentRegistry);
}
@Override

ADD
instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/FunctionTimerSecondsTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.micrometer.v1_5;
+
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Metrics;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
+import java.util.concurrent.TimeUnit;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+class FunctionTimerSecondsTest extends AbstractFunctionTimerSecondsTest {
+
+ @RegisterExtension
+ static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
+
+ static MeterRegistry otelMeterRegistry;
+
+ @BeforeAll
+ public static void setUpRegistry() {
+ otelMeterRegistry =
+ OpenTelemetryMeterRegistry.builder(testing.getOpenTelemetry())
+ .setBaseTimeUnit(TimeUnit.SECONDS)
+ .build();
+ Metrics.addRegistry(otelMeterRegistry);
+ }
+
+ @AfterAll
+ public static void tearDownRegistry() {
+ Metrics.removeRegistry(otelMeterRegistry);
+ }
+
+ @Override
+ protected InstrumentationExtension testing() {
+ return testing;
+ }
+}

ADD
instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerSecondsTest.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.micrometer.v1_5;
+
+import static io.opentelemetry.sdk.testing.assertj.MetricAssertions.assertThat;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.attributeEntry;
+
+import io.micrometer.core.instrument.FunctionTimer;
+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;
+
+public abstract class AbstractFunctionTimerSecondsTest {
+
+ static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer-1.5";
+
+ protected abstract InstrumentationExtension testing();
+
+ final TestTimer timerObj = new TestTimer();
+
+ @BeforeEach
+ void cleanupMeters() {
+ timerObj.reset();
+ Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove);
+ }
+
+ @Test
+ void testFunctionCounterWithBaseUnitSeconds() throws InterruptedException {
+ // given
+ FunctionTimer functionTimer =
+ FunctionTimer.builder(
+ "testFunctionTimerSeconds",
+ timerObj,
+ TestTimer::getCount,
+ TestTimer::getTotalTimeNanos,
+ TimeUnit.NANOSECONDS)
+ .description("This is a test function timer")
+ .tags("tag", "value")
+ .register(Metrics.globalRegistry);
+
+ // when
+ timerObj.add(42, TimeUnit.SECONDS);
+
+ // then
+ testing()
+ .waitAndAssertMetrics(
+ INSTRUMENTATION_NAME,
+ "testFunctionTimerSeconds.count",
+ metrics ->
+ metrics.anySatisfy(
+ metric ->
+ assertThat(metric)
+ .hasDescription("This is a test function timer")
+ .hasUnit("1")
+ .hasLongSum()
+ .isMonotonic()
+ .points()
+ .satisfiesExactly(
+ point ->
+ assertThat(point)
+ .hasValue(1)
+ .attributes()
+ .containsOnly(attributeEntry("tag", "value")))));
+ testing()
+ .waitAndAssertMetrics(
+ INSTRUMENTATION_NAME,
+ "testFunctionTimerSeconds.total_time",
+ metrics ->
+ metrics.anySatisfy(
+ metric ->
+ assertThat(metric)
+ .hasDescription("This is a test function timer")
+ .hasUnit("s")
+ .hasDoubleSum()
+ .points()
+ .satisfiesExactly(
+ point ->
+ assertThat(point)
+ .hasValue(42)
+ .attributes()
+ .containsOnly(attributeEntry("tag", "value")))));
+
+ // when
+ Metrics.globalRegistry.remove(functionTimer);
+ Thread.sleep(10); // give time for any inflight metric export to be received
+ testing().clearData();
+
+ // then
+ Thread.sleep(100); // interval of the test metrics exporter
+ testing()
+ .waitAndAssertMetrics(
+ INSTRUMENTATION_NAME,
+ "testFunctionTimerSeconds.count",
+ AbstractIterableAssert::isEmpty);
+ testing()
+ .waitAndAssertMetrics(
+ INSTRUMENTATION_NAME,
+ "testFunctionTimerSeconds.total_time",
+ AbstractIterableAssert::isEmpty);
+ }
+}

MODIFY
instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerTest.java
instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractFunctionTimerTest.java
@@ -22,8 +22,8 @@ public abstract class AbstractFunctionTimerTest {
protected abstract InstrumentationExtension testing();
- final MyTimer timerObj = new MyTimer();
- final MyTimer anotherTimerObj = new MyTimer();
+ final TestTimer timerObj = new TestTimer();
+ final TestTimer anotherTimerObj = new TestTimer();
@BeforeEach
void cleanupMeters() {
@@ -39,8 +39,8 @@ public abstract class AbstractFunctionTimerTest {
FunctionTimer.builder(
"testFunctionTimer",
timerObj,
- MyTimer::getCount,
- MyTimer::getTotalTimeNanos,
+ TestTimer::getCount,
+ TestTimer::getTotalTimeNanos,
TimeUnit.NANOSECONDS)
.description("This is a test function timer")
.tags("tag", "value")
@@ -109,8 +109,8 @@ public abstract class AbstractFunctionTimerTest {
FunctionTimer.builder(
"testNanoFunctionTimer",
timerObj,
- MyTimer::getCount,
- MyTimer::getTotalTimeNanos,
+ TestTimer::getCount,
+ TestTimer::getTotalTimeNanos,
TimeUnit.NANOSECONDS)
.register(Metrics.globalRegistry);
@@ -139,8 +139,8 @@ public abstract class AbstractFunctionTimerTest {
FunctionTimer.builder(
"testFunctionTimerWithTags",
timerObj,
- MyTimer::getCount,
- MyTimer::getTotalTimeNanos,
+ TestTimer::getCount,
+ TestTimer::getTotalTimeNanos,
TimeUnit.NANOSECONDS)
.tags("tag", "1")
.register(Metrics.globalRegistry);
@@ -148,8 +148,8 @@ public abstract class AbstractFunctionTimerTest {
FunctionTimer.builder(
"testFunctionTimerWithTags",
anotherTimerObj,
- MyTimer::getCount,
- MyTimer::getTotalTimeNanos,
+ TestTimer::getCount,
+ TestTimer::getTotalTimeNanos,
TimeUnit.NANOSECONDS)
.tags("tag", "2")
.register(Metrics.globalRegistry);
@@ -183,27 +183,4 @@ public abstract class AbstractFunctionTimerTest {
.attributes()
.containsOnly(attributeEntry("tag", "2")))));
}
-
- static class MyTimer {
- int count = 0;
- long totalTimeNanos = 0;
-
- void add(long time, TimeUnit unit) {
- count++;
- totalTimeNanos += unit.toNanos(time);
- }
-
- int getCount() {
- return count;
- }
-
- double getTotalTimeNanos() {
- return totalTimeNanos;
- }
-
- void reset() {
- count = 0;
- totalTimeNanos = 0;
- }
- }
}

ADD
instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TestTimer.java
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.micrometer.v1_5;
+
+import java.util.concurrent.TimeUnit;
+
+class TestTimer {
+ int count = 0;
+ long totalTimeNanos = 0;
+
+ void add(long time, TimeUnit unit) {
+ count++;
+ totalTimeNanos += unit.toNanos(time);
+ }
+
+ int getCount() {
+ return count;
+ }
+
+ double getTotalTimeNanos() {
+ return totalTimeNanos;
+ }
+
+ void reset() {
+ count = 0;
+ totalTimeNanos = 0;
+ }
+}

Fix base time unit in Micrometer FunctionTimer (#5321)