diff --git a/instrumentation/micrometer/micrometer-1.5/README.md b/instrumentation/micrometer/micrometer-1.5/README.md index 1bb868a05f81..e6fe4c15f060 100644 --- a/instrumentation/micrometer/micrometer-1.5/README.md +++ b/instrumentation/micrometer/micrometer-1.5/README.md @@ -1,6 +1,7 @@ # 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`
| -| `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. | +| 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`
| +| `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. | diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts index cc2ba4246dfe..e3606c0a7b4a 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts @@ -36,15 +36,25 @@ tasks { jvmArgs("-Dotel.instrumentation.micrometer.base-time-unit=seconds") } + val testHistogramGauges by registering(Test::class) { + filter { + includeTestsMatching("*HistogramGaugesTest") + } + include("**/*HistogramGaugesTest.*") + jvmArgs("-Dotel.instrumentation.micrometer.histogram-gauges.enabled=true") + } + test { filter { excludeTestsMatching("*TimerSecondsTest") excludeTestsMatching("*PrometheusModeTest") + excludeTestsMatching("*HistogramGaugesTest") } } check { dependsOn(testBaseTimeUnit) dependsOn(testPrometheusMode) + dependsOn(testHistogramGauges) } } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java index bc4e2b714607..8416c35fe9f2 100644 --- a/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java @@ -23,6 +23,9 @@ public final class MicrometerSingletons { .setBaseTimeUnit( TimeUnitParser.parseConfigValue( config.getString("otel.instrumentation.micrometer.base-time-unit"))) + .setMicrometerHistogramGaugesEnabled( + config.getBoolean( + "otel.instrumentation.micrometer.histogram-gauges.enabled", false)) .build(); } diff --git a/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryHistogramGaugesTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryHistogramGaugesTest.java new file mode 100644 index 000000000000..7b12050a13ba --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/DistributionSummaryHistogramGaugesTest.java @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractDistributionSummaryHistogramGaugesTest; +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 DistributionSummaryHistogramGaugesTest + extends AbstractDistributionSummaryHistogramGaugesTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @AfterEach + public void cleanup() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @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/TimerHistogramGaugesTest.java b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerHistogramGaugesTest.java new file mode 100644 index 000000000000..7bd08f70478d --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/TimerHistogramGaugesTest.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.micrometer.v1_5.AbstractTimerHistogramGaugesTest; +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 TimerHistogramGaugesTest extends AbstractTimerHistogramGaugesTest { + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @AfterEach + public void cleanup() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionStatisticConfigModifier.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionStatisticConfigModifier.java new file mode 100644 index 000000000000..24e21829cec4 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionStatisticConfigModifier.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; + +enum DistributionStatisticConfigModifier { + DISABLE_HISTOGRAM_GAUGES { + @Override + DistributionStatisticConfig modify(DistributionStatisticConfig config) { + return DistributionStatisticConfig.builder() + // disable all percentile and slo related options + .percentilesHistogram(null) + .percentiles((double[]) null) + .serviceLevelObjectives((double[]) null) + .percentilePrecision(null) + // and keep the rest + .minimumExpectedValue(config.getMinimumExpectedValueAsDouble()) + .maximumExpectedValue(config.getMaximumExpectedValueAsDouble()) + .expiry(config.getExpiry()) + .bufferLength(config.getBufferLength()) + .build(); + } + }, + IDENTITY { + @Override + DistributionStatisticConfig modify(DistributionStatisticConfig config) { + return config; + } + }; + + abstract DistributionStatisticConfig modify(DistributionStatisticConfig config); +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java index 5b58713b8670..dc817368bf01 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java +++ b/instrumentation/micrometer/micrometer-1.5/library/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/OpenTelemetryDistributionSummary.java @@ -41,9 +41,10 @@ final class OpenTelemetryDistributionSummary extends AbstractDistributionSummary NamingConvention namingConvention, Clock clock, DistributionStatisticConfig distributionStatisticConfig, + DistributionStatisticConfigModifier modifier, double scale, Meter otelMeter) { - super(id, clock, distributionStatisticConfig, scale, false); + super(id, clock, modifier.modify(distributionStatisticConfig), scale, false); if (isUsingMicrometerHistograms()) { measurements = new MicrometerHistogramMeasurements(); 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 5c4e5e342cb8..1c23ef581242 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 @@ -50,15 +50,18 @@ public static OpenTelemetryMeterRegistryBuilder builder(OpenTelemetry openTeleme } private final TimeUnit baseTimeUnit; + private final DistributionStatisticConfigModifier distributionStatisticConfigModifier; private final io.opentelemetry.api.metrics.Meter otelMeter; OpenTelemetryMeterRegistry( Clock clock, TimeUnit baseTimeUnit, NamingConvention namingConvention, + DistributionStatisticConfigModifier distributionStatisticConfigModifier, io.opentelemetry.api.metrics.Meter otelMeter) { super(clock); this.baseTimeUnit = baseTimeUnit; + this.distributionStatisticConfigModifier = distributionStatisticConfigModifier; this.otelMeter = otelMeter; this.config() @@ -104,6 +107,7 @@ protected Timer newTimer( config().namingConvention(), clock, distributionStatisticConfig, + distributionStatisticConfigModifier, pauseDetector, getBaseTimeUnit(), otelMeter); @@ -118,7 +122,13 @@ protected DistributionSummary newDistributionSummary( Meter.Id id, DistributionStatisticConfig distributionStatisticConfig, double scale) { OpenTelemetryDistributionSummary distributionSummary = new OpenTelemetryDistributionSummary( - id, config().namingConvention(), clock, distributionStatisticConfig, scale, otelMeter); + id, + config().namingConvention(), + clock, + distributionStatisticConfig, + distributionStatisticConfigModifier, + scale, + otelMeter); if (distributionSummary.isUsingMicrometerHistograms()) { HistogramGauges.registerWithCommonFormat(distributionSummary, this); } 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 7cb896f977e6..adccc7f0c4d2 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 @@ -7,7 +7,10 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; import io.micrometer.core.instrument.Clock; +import io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.LongTaskTimer; import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.Timer; import io.micrometer.core.instrument.config.NamingConvention; import io.opentelemetry.api.OpenTelemetry; import java.util.concurrent.TimeUnit; @@ -22,6 +25,7 @@ public final class OpenTelemetryMeterRegistryBuilder { private Clock clock = Clock.SYSTEM; private TimeUnit baseTimeUnit = TimeUnit.MILLISECONDS; private boolean prometheusMode = false; + private boolean histogramGaugesEnabled = false; OpenTelemetryMeterRegistryBuilder(OpenTelemetry openTelemetry) { this.openTelemetry = openTelemetry; @@ -54,6 +58,27 @@ public OpenTelemetryMeterRegistryBuilder setPrometheusMode(boolean prometheusMod return this; } + /** + * Enables the generation of gauge-based Micrometer histograms. While the Micrometer bridge is + * able to map Micrometer's {@link DistributionSummary} and {@link Timer} service level objectives + * to OpenTelemetry histogram buckets, it might not cover all cases that are normally supported by + * Micrometer (e.g. the bridge is not able to translate percentiles). With this setting enabled, + * the Micrometer bridge will additionally emit Micrometer service level objectives and + * percentiles as separate gauges. + * + *

Note that this setting does not concern the {@link LongTaskTimer}, as it is not bridged to + * an OpenTelemetry histogram. + * + *

This is disabled by default, set this to {@code true} to enable gauge-based Micrometer + * histograms. + */ + @CanIgnoreReturnValue + public OpenTelemetryMeterRegistryBuilder setMicrometerHistogramGaugesEnabled( + boolean histogramGaugesEnabled) { + this.histogramGaugesEnabled = histogramGaugesEnabled; + return this; + } + /** * Returns a new {@link OpenTelemetryMeterRegistry} with the settings of this {@link * OpenTelemetryMeterRegistryBuilder}. @@ -63,11 +88,16 @@ public MeterRegistry build() { TimeUnit baseTimeUnit = prometheusMode ? TimeUnit.SECONDS : this.baseTimeUnit; NamingConvention namingConvention = prometheusMode ? PrometheusModeNamingConvention.INSTANCE : NamingConvention.identity; + DistributionStatisticConfigModifier modifier = + histogramGaugesEnabled + ? DistributionStatisticConfigModifier.IDENTITY + : DistributionStatisticConfigModifier.DISABLE_HISTOGRAM_GAUGES; return new OpenTelemetryMeterRegistry( clock, baseTimeUnit, namingConvention, + modifier, 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 86df5008a8f9..a81531e52d40 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 @@ -43,10 +43,17 @@ final class OpenTelemetryTimer extends AbstractTimer implements RemovableMeter { NamingConvention namingConvention, Clock clock, DistributionStatisticConfig distributionStatisticConfig, + DistributionStatisticConfigModifier modifier, PauseDetector pauseDetector, TimeUnit baseTimeUnit, Meter otelMeter) { - super(id, clock, distributionStatisticConfig, pauseDetector, TimeUnit.MILLISECONDS, false); + super( + id, + clock, + modifier.modify(distributionStatisticConfig), + pauseDetector, + baseTimeUnit, + false); if (isUsingMicrometerHistograms()) { measurements = new MicrometerHistogramMeasurements(); diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionSummaryHistogramGaugesTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionSummaryHistogramGaugesTest.java new file mode 100644 index 000000000000..853aa3ffb85a --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/DistributionSummaryHistogramGaugesTest.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class DistributionSummaryHistogramGaugesTest + extends AbstractDistributionSummaryHistogramGaugesTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setMicrometerHistogramGaugesEnabled(true); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java index 0487167ad9d1..2de72631d7cc 100644 --- a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/NamingConventionTest.java @@ -10,7 +10,7 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; import org.junit.jupiter.api.extension.RegisterExtension; -public class NamingConventionTest extends AbstractNamingConventionTest { +class NamingConventionTest extends AbstractNamingConventionTest { @RegisterExtension static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); diff --git a/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerHistogramGaugesTest.java b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerHistogramGaugesTest.java new file mode 100644 index 000000000000..95176d3a3cb3 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/library/src/test/java/io/opentelemetry/instrumentation/micrometer/v1_5/TimerHistogramGaugesTest.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.micrometer.v1_5; + +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import org.junit.jupiter.api.extension.RegisterExtension; + +class TimerHistogramGaugesTest extends AbstractTimerHistogramGaugesTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @RegisterExtension + static final MicrometerTestingExtension micrometerExtension = + new MicrometerTestingExtension(testing) { + @Override + OpenTelemetryMeterRegistryBuilder configureOtelRegistry( + OpenTelemetryMeterRegistryBuilder registry) { + return registry.setMicrometerHistogramGaugesEnabled(true); + } + }; + + @Override + protected InstrumentationExtension testing() { + return testing; + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryHistogramGaugesTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryHistogramGaugesTest.java new file mode 100644 index 000000000000..dbaa2ddbb39a --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryHistogramGaugesTest.java @@ -0,0 +1,191 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +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 io.micrometer.core.instrument.DistributionSummary; +import io.micrometer.core.instrument.Metrics; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class AbstractDistributionSummaryHistogramGaugesTest { + + protected abstract InstrumentationExtension testing(); + + @BeforeEach + void cleanupMeters() { + Metrics.globalRegistry.forEachMeter(Metrics.globalRegistry::remove); + } + + @Test + void testMicrometerHistogram() { + // given + DistributionSummary summary = + DistributionSummary.builder("testSummary") + .description("This is a test distribution summary") + .baseUnit("things") + .tags("tag", "value") + .serviceLevelObjectives(1, 10, 100, 1000) + .distributionStatisticBufferLength(10) + .register(Metrics.globalRegistry); + + // when + summary.record(0.5); + summary.record(5); + summary.record(50); + summary.record(500); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasUnit("things") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + points -> + points + .hasSum(555.5) + .hasCount(4) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(500) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("le", "1"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(2) + .hasAttributes( + attributeEntry("le", "10"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(3) + .hasAttributes( + attributeEntry("le", "100"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(4) + .hasAttributes( + attributeEntry("le", "1000"), + attributeEntry("tag", "value")))))); + } + + @Test + void testMicrometerPercentiles() { + // given + DistributionSummary summary = + DistributionSummary.builder("testSummary") + .description("This is a test distribution summary") + .baseUnit("things") + .tags("tag", "value") + .publishPercentiles(0.5, 0.95, 0.99) + .register(Metrics.globalRegistry); + + // when + summary.record(50); + summary.record(100); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasUnit("things") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150) + .hasCount(2) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test distribution summary") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(100) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testSummary.percentile", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("phi", "0.5"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.95"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.99"), + attributeEntry("tag", "value")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java index bf64e0d632ee..67589bf55bec 100644 --- a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractDistributionSummaryTest.java @@ -12,11 +12,18 @@ import io.micrometer.core.instrument.DistributionSummary; import io.micrometer.core.instrument.Metrics; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.sdk.metrics.internal.aggregator.ExplicitBucketHistogramUtils; +import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public abstract class AbstractDistributionSummaryTest { + static final double[] DEFAULT_BUCKETS = + ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES.stream() + .mapToDouble(d -> d) + .toArray(); + protected abstract InstrumentationExtension testing(); @BeforeEach @@ -57,7 +64,8 @@ void testMicrometerDistributionSummary() { point .hasSum(7) .hasCount(3) - .hasAttributes(attributeEntry("tag", "value")))))); + .hasAttributes(attributeEntry("tag", "value")) + .hasBucketBoundaries(DEFAULT_BUCKETS))))); testing() .waitAndAssertMetrics( INSTRUMENTATION_NAME, @@ -75,6 +83,11 @@ void testMicrometerDistributionSummary() { .hasValue(4) .hasAttributes(attributeEntry("tag", "value")))))); + // micrometer gauge histogram is not emitted + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testSummary.histogram", AbstractIterableAssert::isEmpty); + // when Metrics.globalRegistry.remove(summary); @@ -98,169 +111,4 @@ void testMicrometerDistributionSummary() { .hasCount(3) .hasAttributes(attributeEntry("tag", "value")))))); } - - @Test - void testMicrometerHistogram() { - // given - DistributionSummary summary = - DistributionSummary.builder("testSummary") - .description("This is a test distribution summary") - .baseUnit("things") - .tags("tag", "value") - .serviceLevelObjectives(1, 10, 100, 1000) - .distributionStatisticBufferLength(10) - .register(Metrics.globalRegistry); - - // when - summary.record(0.5); - summary.record(5); - summary.record(50); - summary.record(500); - - // then - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test distribution summary") - .hasUnit("things") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - points -> - points - .hasSum(555.5) - .hasCount(4) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test distribution summary") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(500) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary.histogram", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributes( - attributeEntry("le", "1"), - attributeEntry("tag", "value")), - point -> - point - .hasValue(2) - .hasAttributes( - attributeEntry("le", "10"), - attributeEntry("tag", "value")), - point -> - point - .hasValue(3) - .hasAttributes( - attributeEntry("le", "100"), - attributeEntry("tag", "value")), - point -> - point - .hasValue(4) - .hasAttributes( - attributeEntry("le", "1000"), - attributeEntry("tag", "value")))))); - } - - @Test - void testMicrometerPercentiles() { - // given - DistributionSummary summary = - DistributionSummary.builder("testSummary") - .description("This is a test distribution summary") - .baseUnit("things") - .tags("tag", "value") - .publishPercentiles(0.5, 0.95, 0.99) - .register(Metrics.globalRegistry); - - // when - summary.record(50); - summary.record(100); - - // then - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test distribution summary") - .hasUnit("things") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(150) - .hasCount(2) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test distribution summary") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(100) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testSummary.percentile", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point.hasAttributes( - attributeEntry("phi", "0.5"), - attributeEntry("tag", "value")), - point -> - point.hasAttributes( - attributeEntry("phi", "0.95"), - attributeEntry("tag", "value")), - point -> - point.hasAttributes( - attributeEntry("phi", "0.99"), - attributeEntry("tag", "value")))))); - } } diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerHistogramGaugesTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerHistogramGaugesTest.java new file mode 100644 index 000000000000..7f8ceaf4a173 --- /dev/null +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerHistogramGaugesTest.java @@ -0,0 +1,190 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +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 io.micrometer.core.instrument.Metrics; +import io.micrometer.core.instrument.Timer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; + +@SuppressWarnings("PreferJavaTimeOverload") +public abstract class AbstractTimerHistogramGaugesTest { + + protected abstract InstrumentationExtension testing(); + + @Test + void testMicrometerHistogram() { + // given + Timer timer = + Timer.builder("testTimer") + .description("This is a test timer") + .tags("tag", "value") + .serviceLevelObjectives( + Duration.ofSeconds(1), + Duration.ofSeconds(10), + Duration.ofSeconds(100), + Duration.ofSeconds(1000)) + .distributionStatisticBufferLength(10) + .register(Metrics.globalRegistry); + + // when + timer.record(500, TimeUnit.MILLISECONDS); + timer.record(5, TimeUnit.SECONDS); + timer.record(50, TimeUnit.SECONDS); + timer.record(500, TimeUnit.SECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(555500) + .hasCount(4) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(500000) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(1) + .hasAttributes( + attributeEntry("le", "1000"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(2) + .hasAttributes( + attributeEntry("le", "10000"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(3) + .hasAttributes( + attributeEntry("le", "100000"), + attributeEntry("tag", "value")), + point -> + point + .hasValue(4) + .hasAttributes( + attributeEntry("le", "1000000"), + attributeEntry("tag", "value")))))); + } + + @Test + void testMicrometerPercentiles() { + // given + Timer timer = + Timer.builder("testTimer") + .description("This is a test timer") + .tags("tag", "value") + .publishPercentiles(0.5, 0.95, 0.99) + .register(Metrics.globalRegistry); + + // when + timer.record(50, TimeUnit.MILLISECONDS); + timer.record(100, TimeUnit.MILLISECONDS); + + // then + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> + point + .hasSum(150) + .hasCount(2) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.max", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDescription("This is a test timer") + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point + .hasValue(100) + .hasAttributes(attributeEntry("tag", "value")))))); + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "testTimer.percentile", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + gauge -> + gauge.hasPointsSatisfying( + point -> + point.hasAttributes( + attributeEntry("phi", "0.5"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.95"), + attributeEntry("tag", "value")), + point -> + point.hasAttributes( + attributeEntry("phi", "0.99"), + attributeEntry("tag", "value")))))); + } +} diff --git a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java index 84d4b07f142b..b21eb73b4597 100644 --- a/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java +++ b/instrumentation/micrometer/micrometer-1.5/testing/src/main/java/io/opentelemetry/instrumentation/micrometer/v1_5/AbstractTimerTest.java @@ -13,7 +13,7 @@ import io.micrometer.core.instrument.Timer; import io.opentelemetry.api.common.Attributes; import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; -import java.time.Duration; +import io.opentelemetry.sdk.metrics.internal.aggregator.ExplicitBucketHistogramUtils; import java.util.concurrent.TimeUnit; import org.assertj.core.api.AbstractIterableAssert; import org.junit.jupiter.api.Test; @@ -21,6 +21,11 @@ @SuppressWarnings("PreferJavaTimeOverload") public abstract class AbstractTimerTest { + static final double[] DEFAULT_BUCKETS = + ExplicitBucketHistogramUtils.DEFAULT_HISTOGRAM_BUCKET_BOUNDARIES.stream() + .mapToDouble(d -> d) + .toArray(); + protected abstract InstrumentationExtension testing(); @Test @@ -53,7 +58,8 @@ void testTimer() { point .hasSum(42_000) .hasCount(1) - .hasAttributes(attributeEntry("tag", "value")))))); + .hasAttributes(attributeEntry("tag", "value")) + .hasBucketBoundaries(DEFAULT_BUCKETS))))); testing() .waitAndAssertMetrics( INSTRUMENTATION_NAME, @@ -71,6 +77,11 @@ void testTimer() { .hasValue(42_000) .hasAttributes(attributeEntry("tag", "value")))))); + // micrometer gauge histogram is not emitted + testing() + .waitAndAssertMetrics( + INSTRUMENTATION_NAME, "testTimer.histogram", AbstractIterableAssert::isEmpty); + // when Metrics.globalRegistry.remove(timer); testing().clearData(); @@ -123,171 +134,4 @@ void testNanoPrecision() { .hasValue(1.234) .hasAttributes(Attributes.empty()))))); } - - @Test - void testMicrometerHistogram() { - // given - Timer timer = - Timer.builder("testTimer") - .description("This is a test timer") - .tags("tag", "value") - .serviceLevelObjectives( - Duration.ofSeconds(1), - Duration.ofSeconds(10), - Duration.ofSeconds(100), - Duration.ofSeconds(1000)) - .distributionStatisticBufferLength(10) - .register(Metrics.globalRegistry); - - // when - timer.record(500, TimeUnit.MILLISECONDS); - timer.record(5, TimeUnit.SECONDS); - timer.record(50, TimeUnit.SECONDS); - timer.record(500, TimeUnit.SECONDS); - - // then - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(555500) - .hasCount(4) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(500000) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer.histogram", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(1) - .hasAttributes( - attributeEntry("le", "1000"), - attributeEntry("tag", "value")), - point -> - point - .hasValue(2) - .hasAttributes( - attributeEntry("le", "10000"), - attributeEntry("tag", "value")), - point -> - point - .hasValue(3) - .hasAttributes( - attributeEntry("le", "100000"), - attributeEntry("tag", "value")), - point -> - point - .hasValue(4) - .hasAttributes( - attributeEntry("le", "1000000"), - attributeEntry("tag", "value")))))); - } - - @Test - void testMicrometerPercentiles() { - // given - Timer timer = - Timer.builder("testTimer") - .description("This is a test timer") - .tags("tag", "value") - .publishPercentiles(0.5, 0.95, 0.99) - .register(Metrics.globalRegistry); - - // when - timer.record(50, TimeUnit.MILLISECONDS); - timer.record(100, TimeUnit.MILLISECONDS); - - // then - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasUnit("ms") - .hasHistogramSatisfying( - histogram -> - histogram.hasPointsSatisfying( - point -> - point - .hasSum(150) - .hasCount(2) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer.max", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDescription("This is a test timer") - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point - .hasValue(100) - .hasAttributes(attributeEntry("tag", "value")))))); - testing() - .waitAndAssertMetrics( - INSTRUMENTATION_NAME, - "testTimer.percentile", - metrics -> - metrics.anySatisfy( - metric -> - assertThat(metric) - .hasDoubleGaugeSatisfying( - gauge -> - gauge.hasPointsSatisfying( - point -> - point.hasAttributes( - attributeEntry("phi", "0.5"), - attributeEntry("tag", "value")), - point -> - point.hasAttributes( - attributeEntry("phi", "0.95"), - attributeEntry("tag", "value")), - point -> - point.hasAttributes( - attributeEntry("phi", "0.99"), - attributeEntry("tag", "value")))))); - } }