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 (#5304)

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

* add readme
  • Loading branch information
Mateusz Rzeszutek authored Feb 8, 2022
1 parent 551418c commit 0b7f466
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 0b7f466

Please sign in to comment.