Skip to content

Commit

Permalink
Make it possible to configure base time unit used by the Micrometer b…
Browse files Browse the repository at this point in the history
…ridge (open-telemetry#5304)

* Make it possible to configure base time unit used by the Micrometer bridge

* add readme
  • Loading branch information
Mateusz Rzeszutek authored and RashmiRam committed May 23, 2022
1 parent 0de15a1 commit 9212a67
Show file tree
Hide file tree
Showing 13 changed files with 491 additions and 13 deletions.
5 changes: 5 additions & 0 deletions instrumentation/micrometer/micrometer-1.5/README.md
Original file line number Diff line number Diff line change
@@ -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. <details><summary>Valid values</summary>`ns`, `nanoseconds`, `us`, `microseconds`, `ms`, `microseconds`, `s`, `seconds`, `min`, `minutes`, `h`, `hours`, `d`, `days`</details> |
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -91,6 +95,7 @@ protected Timer newTimer(
clock,
distributionStatisticConfig,
pauseDetector,
getBaseTimeUnit(),
otelMeter,
asyncInstrumentRegistry);
if (timer.isUsingMicrometerHistograms()) {
Expand Down Expand Up @@ -135,7 +140,7 @@ protected <T> FunctionCounter newFunctionCounter(

@Override
protected TimeUnit getBaseTimeUnit() {
return TimeUnit.MILLISECONDS;
return baseTimeUnit;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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;
Expand All @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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(
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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() {}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Loading

0 comments on commit 9212a67

Please sign in to comment.