From 504d86be2671691e81ba882d551b318dac6a9cfe Mon Sep 17 00:00:00 2001 From: Mateusz Rzeszutek Date: Mon, 4 Jul 2022 11:41:02 +0200 Subject: [PATCH] Add a dropwizard-metrics -> OTel metrics bridge --- .../javaagent/build.gradle.kts | 16 ++ .../CounterInstrumentation.java | 51 +++++ .../DropwizardMetricsAdapter.java | 174 +++++++++++++++ ...ropwizardMetricsInstrumentationModule.java | 40 ++++ .../DropwizardSingletons.java | 20 ++ .../HistogramInstrumentation.java | 41 ++++ .../MeterInstrumentation.java | 40 ++++ .../MetricRegistryInstrumentation.java | 40 ++++ .../TimerInstrumentation.java | 41 ++++ .../DropwizardMetricsTest.java | 199 ++++++++++++++++++ ...ditionalLibraryIgnoredTypesConfigurer.java | 5 - settings.gradle.kts | 1 + 12 files changed, 663 insertions(+), 5 deletions(-) create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java create mode 100644 instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts new file mode 100644 index 000000000000..6da6b5e10af9 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/build.gradle.kts @@ -0,0 +1,16 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("io.dropwizard.metrics") + module.set("metrics-core") + versions.set("[4.0.0,)") + assertInverse.set(true) + } +} + +dependencies { + library("io.dropwizard.metrics:metrics-core:4.0.0") +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java new file mode 100644 index 000000000000..3db26d07e420 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/CounterInstrumentation.java @@ -0,0 +1,51 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Counter; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class CounterInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Counter"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("inc").and(takesArguments(long.class)), this.getClass().getName() + "$IncAdvice"); + transformer.applyAdviceToMethod( + named("dec").and(takesArguments(long.class)), this.getClass().getName() + "$DecAdvice"); + } + + @SuppressWarnings("unused") + public static class IncAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Counter counter, @Advice.Argument(0) long increment) { + metrics().counterAdd(counter, increment); + } + } + + @SuppressWarnings("unused") + public static class DecAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Counter counter, @Advice.Argument(0) long decrement) { + metrics().counterAdd(counter, -decrement); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java new file mode 100644 index 000000000000..61c01ef1c38f --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsAdapter.java @@ -0,0 +1,174 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Gauge; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistryListener; +import com.codahale.metrics.Timer; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.api.metrics.DoubleHistogram; +import io.opentelemetry.api.metrics.LongCounter; +import io.opentelemetry.api.metrics.LongHistogram; +import io.opentelemetry.api.metrics.LongUpDownCounter; +import io.opentelemetry.api.metrics.ObservableDoubleGauge; +import io.opentelemetry.instrumentation.api.util.VirtualField; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +public final class DropwizardMetricsAdapter implements MetricRegistryListener { + + private static final double NANOS_PER_MS = TimeUnit.MILLISECONDS.toNanos(1); + + private static final VirtualField otelUpDownCounterField = + VirtualField.find(Counter.class, LongUpDownCounter.class); + private static final VirtualField otelHistogramField = + VirtualField.find(Histogram.class, LongHistogram.class); + private static final VirtualField otelCounterField = + VirtualField.find(Meter.class, LongCounter.class); + private static final VirtualField otelDoubleHistogramField = + VirtualField.find(Timer.class, DoubleHistogram.class); + + private final io.opentelemetry.api.metrics.Meter otelMeter; + + private final Map otelDoubleHistograms = new ConcurrentHashMap<>(); + private final Map otelCounters = new ConcurrentHashMap<>(); + private final Map otelHistograms = new ConcurrentHashMap<>(); + private final Map otelUpDownCounters = new ConcurrentHashMap<>(); + private final Map otelGauges = new ConcurrentHashMap<>(); + + private final Map dropwizardCounters = new ConcurrentHashMap<>(); + private final Map dropwizardHistograms = new ConcurrentHashMap<>(); + private final Map dropwizardMeters = new ConcurrentHashMap<>(); + private final Map dropwizardTimers = new ConcurrentHashMap<>(); + + public DropwizardMetricsAdapter(OpenTelemetry openTelemetry) { + this.otelMeter = openTelemetry.getMeter("io.opentelemetry.dropwizard-metrics-4.0"); + } + + @Override + public void onGaugeAdded(String name, Gauge gauge) { + ObservableDoubleGauge otelGauge = + otelMeter + .gaugeBuilder(name) + .buildWithCallback( + measurement -> { + Object val = gauge.getValue(); + if (val instanceof Number) { + measurement.record(((Number) val).doubleValue()); + } + }); + otelGauges.put(name, otelGauge); + } + + @Override + public void onGaugeRemoved(String name) { + ObservableDoubleGauge otelGauge = otelGauges.remove(name); + if (otelGauge != null) { + otelGauge.close(); + } + } + + @Override + public void onCounterAdded(String name, Counter dropwizardCounter) { + dropwizardCounters.put(name, dropwizardCounter); + LongUpDownCounter otelCounter = + otelUpDownCounters.computeIfAbsent(name, n -> otelMeter.upDownCounterBuilder(n).build()); + otelUpDownCounterField.set(dropwizardCounter, otelCounter); + } + + @Override + public void onCounterRemoved(String name) { + Counter dropwizardCounter = dropwizardCounters.remove(name); + otelUpDownCounters.remove(name); + if (dropwizardCounter != null) { + otelUpDownCounterField.set(dropwizardCounter, null); + } + } + + public void counterAdd(Counter dropwizardCounter, long increment) { + LongUpDownCounter otelCounter = otelUpDownCounterField.get(dropwizardCounter); + if (otelCounter != null) { + otelCounter.add(increment); + } + } + + @Override + public void onHistogramAdded(String name, Histogram dropwizardHistogram) { + dropwizardHistograms.put(name, dropwizardHistogram); + LongHistogram otelHistogram = + otelHistograms.computeIfAbsent(name, n -> otelMeter.histogramBuilder(n).ofLongs().build()); + otelHistogramField.set(dropwizardHistogram, otelHistogram); + } + + @Override + public void onHistogramRemoved(String name) { + Histogram dropwizardHistogram = dropwizardHistograms.remove(name); + otelHistograms.remove(name); + if (dropwizardHistogram != null) { + otelHistogramField.set(dropwizardHistogram, null); + } + } + + public void histogramUpdate(Histogram dropwizardHistogram, long value) { + LongHistogram otelHistogram = otelHistogramField.get(dropwizardHistogram); + if (otelHistogram != null) { + otelHistogram.record(value); + } + } + + @Override + public void onMeterAdded(String name, Meter dropwizardMeter) { + dropwizardMeters.put(name, dropwizardMeter); + LongCounter otelCounter = + otelCounters.computeIfAbsent(name, n -> otelMeter.counterBuilder(n).build()); + otelCounterField.set(dropwizardMeter, otelCounter); + } + + @Override + public void onMeterRemoved(String name) { + Meter dropwizardMeter = dropwizardMeters.remove(name); + otelCounters.remove(name); + if (dropwizardMeter != null) { + otelCounterField.set(dropwizardMeter, null); + } + } + + public void meterMark(Meter dropwizardMeter, long increment) { + LongCounter otelCounter = otelCounterField.get(dropwizardMeter); + if (otelCounter != null) { + otelCounter.add(increment); + } + } + + @Override + public void onTimerAdded(String name, Timer dropwizardTimer) { + dropwizardTimers.put(name, dropwizardTimer); + DoubleHistogram otelHistogram = + otelDoubleHistograms.computeIfAbsent( + name, n -> otelMeter.histogramBuilder(n).setUnit("ms").build()); + otelDoubleHistogramField.set(dropwizardTimer, otelHistogram); + } + + @Override + public void onTimerRemoved(String name) { + Timer dropwizardTimer = dropwizardTimers.remove(name); + otelDoubleHistograms.remove(name); + if (dropwizardTimer != null) { + otelDoubleHistogramField.set(dropwizardTimer, null); + } + } + + public void timerUpdate(Timer dropwizardTimer, long nanos) { + DoubleHistogram otelHistogram = otelDoubleHistogramField.get(dropwizardTimer); + if (otelHistogram != null) { + otelHistogram.record(nanos / NANOS_PER_MS); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java new file mode 100644 index 000000000000..3d97cc07141d --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsInstrumentationModule.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Arrays.asList; +import static net.bytebuddy.matcher.ElementMatchers.not; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class DropwizardMetricsInstrumentationModule extends InstrumentationModule { + + public DropwizardMetricsInstrumentationModule() { + super("dropwizard-metrics", "dropwizard-metrics-4.0"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // removed in 4.0 + return not(hasClassesNamed("com.codahale.metrics.LongAdder")); + } + + @Override + public List typeInstrumentations() { + return asList( + new MetricRegistryInstrumentation(), + new CounterInstrumentation(), + new HistogramInstrumentation(), + new MeterInstrumentation(), + new TimerInstrumentation()); + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java new file mode 100644 index 000000000000..e8ea2ce5dd32 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardSingletons.java @@ -0,0 +1,20 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import io.opentelemetry.api.GlobalOpenTelemetry; + +public final class DropwizardSingletons { + + private static final DropwizardMetricsAdapter METRICS = + new DropwizardMetricsAdapter(GlobalOpenTelemetry.get()); + + public static DropwizardMetricsAdapter metrics() { + return METRICS; + } + + private DropwizardSingletons() {} +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java new file mode 100644 index 000000000000..6786a5cf41bd --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/HistogramInstrumentation.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Histogram; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class HistogramInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Histogram"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("update").and(takesArguments(long.class)), + this.getClass().getName() + "$UpdateAdvice"); + } + + @SuppressWarnings("unused") + public static class UpdateAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Histogram histogram, @Advice.Argument(0) long value) { + metrics().histogramUpdate(histogram, value); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java new file mode 100644 index 000000000000..2fbd48948644 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MeterInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Meter; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class MeterInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Meter"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("mark").and(takesArguments(long.class)), this.getClass().getName() + "$MarkAdvice"); + } + + @SuppressWarnings("unused") + public static class MarkAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Meter meter, @Advice.Argument(0) long increment) { + metrics().meterMark(meter, increment); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java new file mode 100644 index 000000000000..40decdfa1cb9 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/MetricRegistryInstrumentation.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.isDefaultConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import com.codahale.metrics.MetricRegistry; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class MetricRegistryInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.MetricRegistry"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isDefaultConstructor(), this.getClass().getName() + "$ConstructorAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit(@Advice.This MetricRegistry metricRegistry) { + metricRegistry.addListener(metrics()); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java new file mode 100644 index 000000000000..e7762069874c --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/TimerInstrumentation.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.javaagent.instrumentation.dropwizardmetrics.DropwizardSingletons.metrics; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.codahale.metrics.Timer; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +public class TimerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("com.codahale.metrics.Timer"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("update").and(takesArguments(long.class)), + this.getClass().getName() + "$UpdateAdvice"); + } + + @SuppressWarnings("unused") + public static class UpdateAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void onEnter(@Advice.This Timer timer, @Advice.Argument(0) long nanos) { + metrics().timerUpdate(timer, nanos); + } + } +} diff --git a/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java new file mode 100644 index 000000000000..193a9baf7848 --- /dev/null +++ b/instrumentation/dropwizard/dropwizard-metrics-4.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/dropwizardmetrics/DropwizardMetricsTest.java @@ -0,0 +1,199 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.dropwizardmetrics; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; + +import com.codahale.metrics.Counter; +import com.codahale.metrics.Histogram; +import com.codahale.metrics.Meter; +import com.codahale.metrics.MetricRegistry; +import com.codahale.metrics.Timer; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import org.assertj.core.api.AbstractIterableAssert; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class DropwizardMetricsTest { + + static final String INSTRUMENTATION_NAME = "io.opentelemetry.dropwizard-metrics-4.0"; + + @RegisterExtension + static final InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + @Test + void gauge() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + AtomicLong value = new AtomicLong(42); + + // when + metricRegistry.gauge("test.gauge", () -> value::get); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.gauge", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasDoubleGaugeSatisfying( + g -> g.hasPointsSatisfying(point -> point.hasValue(42))))); + + // when + metricRegistry.remove("test.gauge"); + Thread.sleep(100); // give time for any inflight metric export to be received + testing.clearData(); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.gauge", AbstractIterableAssert::isEmpty); + } + + @Test + void counter() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Counter counter = metricRegistry.counter("test.counter"); + counter.inc(); + counter.inc(11); + counter.dec(5); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.counter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.isNotMonotonic() + .hasPointsSatisfying(point -> point.hasValue(7))))); + testing.clearData(); + + // when + metricRegistry.remove("test.counter"); + counter.inc(123); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.counter", AbstractIterableAssert::isEmpty); + } + + @Test + void histogram() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Histogram histogram = metricRegistry.histogram("test.histogram"); + histogram.update(12); + histogram.update(30); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.histogram", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasHistogramSatisfying( + histogramMetric -> + histogramMetric.hasPointsSatisfying( + point -> point.hasSum(42).hasCount(2))))); + testing.clearData(); + + // when + metricRegistry.remove("test.histogram"); + histogram.update(100); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.histogram", AbstractIterableAssert::isEmpty); + } + + @Test + void meter() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Meter meter = metricRegistry.meter("test.meter"); + meter.mark(); + meter.mark(11); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.meter", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasLongSumSatisfying( + sum -> + sum.isMonotonic() + .hasPointsSatisfying(point -> point.hasValue(12))))); + testing.clearData(); + + // when + metricRegistry.remove("test.meter"); + meter.mark(); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.meter", AbstractIterableAssert::isEmpty); + } + + @Test + void timer() throws InterruptedException { + // given + MetricRegistry metricRegistry = new MetricRegistry(); + + // when + Timer timer = metricRegistry.timer("test.timer"); + timer.update(1, TimeUnit.MILLISECONDS); + timer.update(234_000, TimeUnit.NANOSECONDS); + + // then + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, + "test.timer", + metrics -> + metrics.anySatisfy( + metric -> + assertThat(metric) + .hasUnit("ms") + .hasHistogramSatisfying( + histogram -> + histogram.hasPointsSatisfying( + point -> point.hasSum(1.234).hasCount(2))))); + testing.clearData(); + + // when + metricRegistry.remove("test.timer"); + timer.update(12, TimeUnit.SECONDS); + + // then + Thread.sleep(100); // interval of the test metrics exporter + testing.waitAndAssertMetrics( + INSTRUMENTATION_NAME, "test.timer", AbstractIterableAssert::isEmpty); + } +} diff --git a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java index 3a64a32fdf57..a759b7629db9 100644 --- a/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java +++ b/javaagent-tooling/src/main/java/io/opentelemetry/javaagent/tooling/ignore/AdditionalLibraryIgnoredTypesConfigurer.java @@ -213,11 +213,6 @@ public void configure(IgnoredTypesBuilder builder) { .allowClass("ch.qos.logback.classic.spi.LoggingEvent") .allowClass("ch.qos.logback.classic.spi.LoggingEventVO"); - builder - .ignoreClass("com.codahale.metrics.") - // We instrument servlets - .allowClass("com.codahale.metrics.servlets."); - builder .ignoreClass("com.couchbase.client.deps.") // Couchbase library includes some packaged dependencies, unfortunately some of them are diff --git a/settings.gradle.kts b/settings.gradle.kts index 372306a2a48c..4a7d72ecb8d6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -201,6 +201,7 @@ include(":instrumentation:couchbase:couchbase-3.1.6:tracing-opentelemetry-shaded include(":instrumentation:couchbase:couchbase-3.2:javaagent") include(":instrumentation:couchbase:couchbase-3.2:tracing-opentelemetry-shaded") include(":instrumentation:couchbase:couchbase-common:testing") +include(":instrumentation:dropwizard:dropwizard-metrics-4.0:javaagent") include(":instrumentation:dropwizard:dropwizard-views-0.7:javaagent") include(":instrumentation:dropwizard:dropwizard-testing") include(":instrumentation:elasticsearch:elasticsearch-rest-common:javaagent")