diff --git a/instrumentation/micrometer/micrometer-1.5/README.md b/instrumentation/micrometer/micrometer-1.5/README.md new file mode 100644 index 000000000000..2181acafa118 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/README.md @@ -0,0 +1,5 @@ +# Settings for the Micrometer bridge instrumentation + +| System property | Type | Default | Description | +|---|---|---|---| +| `otel.instrumentation.micrometer.base-time-unit` | String | `ms` | Set the base time unit for the OpenTelemetry `MeterRegistry` implementation.
Valid values`ns`, `nanoseconds`, `us`, `microseconds`, `ms`, `microseconds`, `s`, `seconds`, `min`, `minutes`, `h`, `hours`, `d`, `days`
| \ No newline at end of file diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts index d23987d1dfe8..7898ca334ade 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts @@ -18,3 +18,24 @@ dependencies { testImplementation(project(":instrumentation:micrometer:micrometer-1.5:testing")) } + +tasks { + val testBaseTimeUnit by registering(Test::class) { + filter { + includeTestsMatching("*TimerSecondsTest") + includeTestsMatching("*LongTimerSecondsTest") + isFailOnNoMatchingTests = false + } + include("**/*TimerSecondsTest.*", "**/*LongTaskTimerSecondsTest.*") + jvmArgs("-Dotel.instrumentation.micrometer.base-time-unit=seconds") + } + + test { + dependsOn(testBaseTimeUnit) + filter { + excludeTestsMatching("*TimerSecondsTest") + excludeTestsMatching("*LongTimerSecondsTest") + isFailOnNoMatchingTests = false + } + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java new file mode 100644 index 000000000000..d3d6db88acc9 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java @@ -0,0 +1,22 @@ +/* + * 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.AbstractLongTaskTimerSecondsTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java new file mode 100644 index 000000000000..3782935de591 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerSecondsTest.java @@ -0,0 +1,22 @@ +/* + * 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.AbstractTimerSecondsTest; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TimerSecondsTest extends AbstractTimerSecondsTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java index 7ead9a6d2470..e1a1e596afd4 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryLongTaskTimer.java @@ -8,12 +8,14 @@ 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.Clock; import io.micrometer.core.instrument.Measurement; import io.micrometer.core.instrument.Statistic; 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; @@ -23,8 +25,7 @@ final class OpenTelemetryLongTaskTimer extends DefaultLongTaskTimer implements RemovableMeter { - private static final double NANOS_PER_MS = TimeUnit.MILLISECONDS.toNanos(1); - + private final TimeUnit baseTimeUnit; private final DistributionStatisticConfig distributionStatisticConfig; // TODO: use bound instruments when they're available private final DoubleHistogram otelHistogram; @@ -36,16 +37,19 @@ final class OpenTelemetryLongTaskTimer extends DefaultLongTaskTimer implements R OpenTelemetryLongTaskTimer( Id id, Clock clock, + TimeUnit baseTimeUnit, DistributionStatisticConfig distributionStatisticConfig, Meter otelMeter) { - super(id, clock, TimeUnit.MILLISECONDS, distributionStatisticConfig, false); + super(id, clock, baseTimeUnit, distributionStatisticConfig, false); + + this.baseTimeUnit = baseTimeUnit; this.distributionStatisticConfig = distributionStatisticConfig; this.otelHistogram = otelMeter .histogramBuilder(id.getName()) .setDescription(description(id)) - .setUnit("ms") + .setUnit(getUnitString(baseTimeUnit)) .build(); this.otelActiveTasksCounter = otelMeter @@ -101,7 +105,7 @@ public long stop() { long durationNanos = original.stop(); if (!removed) { otelActiveTasksCounter.add(-1, attributes); - double time = durationNanos / NANOS_PER_MS; + double time = TimeUtils.nanosToUnit(durationNanos, baseTimeUnit); otelHistogram.record(time, attributes); } return durationNanos; diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java index 725a64d31eb1..a7781b2db866 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistry.java @@ -49,11 +49,14 @@ public static OpenTelemetryMeterRegistryBuilder builder(OpenTelemetry openTeleme return new OpenTelemetryMeterRegistryBuilder(openTelemetry); } + private final TimeUnit baseTimeUnit; private final io.opentelemetry.api.metrics.Meter otelMeter; private final AsyncInstrumentRegistry asyncInstrumentRegistry; - OpenTelemetryMeterRegistry(Clock clock, io.opentelemetry.api.metrics.Meter otelMeter) { + OpenTelemetryMeterRegistry( + Clock clock, TimeUnit baseTimeUnit, io.opentelemetry.api.metrics.Meter otelMeter) { super(clock); + this.baseTimeUnit = baseTimeUnit; this.otelMeter = otelMeter; this.asyncInstrumentRegistry = AsyncInstrumentRegistry.getOrCreate(otelMeter); this.config().onMeterRemoved(OpenTelemetryMeterRegistry::onMeterRemoved); @@ -73,7 +76,8 @@ protected Counter newCounter(Meter.Id id) { protected LongTaskTimer newLongTaskTimer( Meter.Id id, DistributionStatisticConfig distributionStatisticConfig) { OpenTelemetryLongTaskTimer timer = - new OpenTelemetryLongTaskTimer(id, clock, distributionStatisticConfig, otelMeter); + new OpenTelemetryLongTaskTimer( + id, clock, getBaseTimeUnit(), distributionStatisticConfig, otelMeter); if (timer.isUsingMicrometerHistograms()) { HistogramGauges.registerWithCommonFormat(timer, this); } @@ -91,6 +95,7 @@ protected Timer newTimer( clock, distributionStatisticConfig, pauseDetector, + getBaseTimeUnit(), otelMeter, asyncInstrumentRegistry); if (timer.isUsingMicrometerHistograms()) { @@ -135,7 +140,7 @@ protected FunctionCounter newFunctionCounter( @Override protected TimeUnit getBaseTimeUnit() { - return TimeUnit.MILLISECONDS; + return baseTimeUnit; } @Override diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java index 53cbc6380c8d..157549d9f695 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryMeterRegistryBuilder.java @@ -8,6 +8,8 @@ import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.MeterRegistry; import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.config.Config; +import java.util.concurrent.TimeUnit; /** A builder of {@link OpenTelemetryMeterRegistry}. */ public final class OpenTelemetryMeterRegistryBuilder { @@ -16,6 +18,9 @@ public final class OpenTelemetryMeterRegistryBuilder { private final OpenTelemetry openTelemetry; private Clock clock = Clock.SYSTEM; + private TimeUnit baseTimeUnit = + TimeUnitHelper.parseConfigValue( + Config.get().getString("otel.instrumentation.micrometer.base-time-unit")); OpenTelemetryMeterRegistryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -27,12 +32,18 @@ public OpenTelemetryMeterRegistryBuilder setClock(Clock clock) { return this; } + /** Sets the base time unit. */ + public OpenTelemetryMeterRegistryBuilder setBaseTimeUnit(TimeUnit baseTimeUnit) { + this.baseTimeUnit = baseTimeUnit; + return this; + } + /** * Returns a new {@link OpenTelemetryMeterRegistry} with the settings of this {@link * OpenTelemetryMeterRegistryBuilder}. */ public MeterRegistry build() { return new OpenTelemetryMeterRegistry( - clock, openTelemetry.getMeterProvider().get(INSTRUMENTATION_NAME)); + clock, baseTimeUnit, openTelemetry.getMeterProvider().get(INSTRUMENTATION_NAME)); } } diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java index ae01bdb24a7b..e046a2839e4c 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryTimer.java @@ -8,6 +8,7 @@ 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.AbstractTimer; import io.micrometer.core.instrument.Clock; @@ -29,10 +30,9 @@ final class OpenTelemetryTimer extends AbstractTimer implements RemovableMeter { - private static final double NANOS_PER_MS = TimeUnit.MILLISECONDS.toNanos(1); - private final Measurements measurements; private final TimeWindowMax max; + private final TimeUnit baseTimeUnit; // TODO: use bound instruments when they're available private final DoubleHistogram otelHistogram; private final Attributes attributes; @@ -45,6 +45,7 @@ final class OpenTelemetryTimer extends AbstractTimer implements RemovableMeter { Clock clock, DistributionStatisticConfig distributionStatisticConfig, PauseDetector pauseDetector, + TimeUnit baseTimeUnit, Meter otelMeter, AsyncInstrumentRegistry asyncInstrumentRegistry) { super(id, clock, distributionStatisticConfig, pauseDetector, TimeUnit.MILLISECONDS, false); @@ -56,12 +57,13 @@ final class OpenTelemetryTimer extends AbstractTimer implements RemovableMeter { } max = new TimeWindowMax(clock, distributionStatisticConfig); + this.baseTimeUnit = baseTimeUnit; this.attributes = tagsAsAttributes(id); this.otelHistogram = otelMeter .histogramBuilder(id.getName()) .setDescription(description(id)) - .setUnit("ms") + .setUnit(getUnitString(baseTimeUnit)) .build(); this.maxHandle = asyncInstrumentRegistry.buildGauge( @@ -81,7 +83,7 @@ boolean isUsingMicrometerHistograms() { protected void recordNonNegative(long amount, TimeUnit unit) { if (amount >= 0 && !removed) { long nanos = unit.toNanos(amount); - double time = nanos / NANOS_PER_MS; + double time = TimeUtils.nanosToUnit(nanos, baseTimeUnit); otelHistogram.record(time, attributes); measurements.record(nanos); max.record(nanos, TimeUnit.NANOSECONDS); diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimeUnitHelper.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimeUnitHelper.java new file mode 100644 index 000000000000..29af910c8827 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimeUnitHelper.java @@ -0,0 +1,75 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +final class TimeUnitHelper { + + private static final Logger logger = LoggerFactory.getLogger(OpenTelemetryMeterRegistry.class); + + static TimeUnit parseConfigValue(@Nullable String value) { + if (value == null) { + return TimeUnit.MILLISECONDS; + } + // short names are UCUM names + // long names are just TimeUnit values lowercased + switch (value.toLowerCase(Locale.ROOT)) { + case "ns": + case "nanoseconds": + return TimeUnit.NANOSECONDS; + case "us": + case "microseconds": + return TimeUnit.MICROSECONDS; + case "ms": + case "milliseconds": + return TimeUnit.MILLISECONDS; + case "s": + case "seconds": + return TimeUnit.SECONDS; + case "min": + case "minutes": + return TimeUnit.MINUTES; + case "h": + case "hours": + return TimeUnit.HOURS; + case "d": + case "days": + return TimeUnit.DAYS; + default: + logger.warn( + "Invalid base time unit: '{}'; using microseconds as the base time unit instead", + value); + return TimeUnit.MILLISECONDS; + } + } + + static String getUnitString(TimeUnit unit) { + switch (unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "us"; + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + case MINUTES: + return "min"; + case HOURS: + return "h"; + case DAYS: + return "d"; + } + throw new IllegalStateException("Should not ever happen"); + } + + private TimeUnitHelper() {} +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java new file mode 100644 index 000000000000..b340b652a830 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/LongTaskTimerSecondsTest.java @@ -0,0 +1,42 @@ +/* + * 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 LongTaskTimerSecondsTest extends AbstractLongTaskTimerSecondsTest { + + @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; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerSecondsTest.java new file mode 100644 index 000000000000..cd800b3d2bc0 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerSecondsTest.java @@ -0,0 +1,42 @@ +/* + * 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 TimerSecondsTest extends AbstractTimerSecondsTest { + + @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; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerSecondsTest.java new file mode 100644 index 000000000000..658de0ab2e89 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractLongTaskTimerSecondsTest.java @@ -0,0 +1,144 @@ +/* + * 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.LongTaskTimer; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractLongTaskTimerSecondsTest { + + static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer-1.5"; + + protected abstract InstrumentationExtension testing(); + + @BeforeEach + void cleanupMeters() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testLongTaskTimerWithBaseUnitSeconds() throws InterruptedException { + // given + LongTaskTimer timer = + LongTaskTimer.builder("testLongTaskTimerSeconds") + .description("This is a test long task timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + LongTaskTimer.Sample sample = timer.start(); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test long task timer") + .hasUnit("tasks") + .hasLongSum() + .isNotMonotonic() + .points() + .satisfiesExactly( + point -> + assertThat(point) + .hasValue(1) + .attributes() + .containsOnly(attributeEntry("tag", "value"))))); + + // 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, + "testLongTaskTimerSeconds.active", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSum() + .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(); + + // 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 + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testLongTaskTimerSeconds", + metrics -> + metrics.allSatisfy( + metric -> + assertThat(metric) + .hasDoubleHistogram() + .points() + .noneSatisfy( + point -> assertThat(point).hasSumGreaterThan(0.2).hasCount(2)))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerSecondsTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerSecondsTest.java new file mode 100644 index 000000000000..86110bedaa3b --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerSecondsTest.java @@ -0,0 +1,83 @@ +/* + * 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.Metrics; +import io.micrometer.core.instrument.Timer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("PreferJavaTimeOverload") +public abstract class AbstractTimerSecondsTest { + + static final String INSTRUMENTATION_NAME = "io.opentelemetry.micrometer-1.5"; + + protected abstract InstrumentationExtension testing(); + + @BeforeEach + void cleanupMeters() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testTimerWithBaseUnitSeconds() { + // given + Timer timer = + Timer.builder("testTimerSeconds") + .description("This is a test timer") + .tags("tag", "value") + .register(Metrics.globalRegistry); + + // when + timer.record(1, TimeUnit.SECONDS); + timer.record(10, TimeUnit.SECONDS); + timer.record(12_345, TimeUnit.MILLISECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimerSeconds", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("s") + .hasDoubleHistogram() + .points() + .satisfiesExactly( + point -> + assertThat(point) + .hasSum(23.345) + .hasCount(3) + .attributes() + .containsOnly(attributeEntry("tag", "value"))))); + testing().clearData(); + + // when + Metrics.globalRegistry.remove(timer); + timer.record(12, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimerSeconds", + metrics -> + metrics.allSatisfy( + metric -> + assertThat(metric) + .hasDoubleHistogram() + .points() + .noneSatisfy(point -> assertThat(point).hasSum(35.345).hasCount(4)))); + } +}