-
Notifications
You must be signed in to change notification settings - Fork 873
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Micrometer bridge instrumentation (#4919)
* Micrometer bridge instrumentation * gauges with the same name and different attributes * weak ref gauge * one more test * disable by default + muzzle * code review comments * log one-time warning * make AsyncInstrumentRegistry actually thread safe * code review comments * one more minor fix
- Loading branch information
Mateusz Rzeszutek
authored
Jan 3, 2022
1 parent
606f39c
commit a022f0c
Showing
16 changed files
with
1,157 additions
and
0 deletions.
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
instrumentation/micrometer/micrometer-1.5/javaagent/build.gradle.kts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("io.micrometer") | ||
module.set("micrometer-core") | ||
versions.set("[1.5.0,)") | ||
assertInverse.set(true) | ||
} | ||
} | ||
|
||
dependencies { | ||
library("io.micrometer:micrometer-core:1.5.0") | ||
} | ||
|
||
// TODO: disabled by default, since not all instruments are implemented | ||
tasks.withType<Test>().configureEach { | ||
jvmArgs("-Dotel.instrumentation.micrometer.enabled=true") | ||
} |
120 changes: 120 additions & 0 deletions
120
...a/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/AsyncInstrumentRegistry.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; | ||
|
||
import static io.opentelemetry.javaagent.instrumentation.micrometer.v1_5.Bridging.baseUnit; | ||
import static io.opentelemetry.javaagent.instrumentation.micrometer.v1_5.Bridging.description; | ||
|
||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.metrics.Meter; | ||
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement; | ||
import io.opentelemetry.instrumentation.api.internal.GuardedBy; | ||
import java.lang.ref.WeakReference; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.function.Consumer; | ||
import java.util.function.ToDoubleFunction; | ||
import javax.annotation.Nullable; | ||
|
||
final class AsyncInstrumentRegistry { | ||
|
||
private final Meter meter; | ||
|
||
@GuardedBy("gauges") | ||
private final Map<String, GaugeMeasurementsRecorder> gauges = new HashMap<>(); | ||
|
||
AsyncInstrumentRegistry(Meter meter) { | ||
this.meter = meter; | ||
} | ||
|
||
<T> void buildGauge( | ||
io.micrometer.core.instrument.Meter.Id meterId, | ||
Attributes attributes, | ||
@Nullable T obj, | ||
ToDoubleFunction<T> objMetric) { | ||
|
||
synchronized (gauges) { | ||
GaugeMeasurementsRecorder recorder = | ||
gauges.computeIfAbsent( | ||
meterId.getName(), | ||
n -> { | ||
GaugeMeasurementsRecorder recorderCallback = new GaugeMeasurementsRecorder(); | ||
meter | ||
.gaugeBuilder(meterId.getName()) | ||
.setDescription(description(meterId)) | ||
.setUnit(baseUnit(meterId)) | ||
.buildWithCallback(recorderCallback); | ||
return recorderCallback; | ||
}); | ||
recorder.addGaugeMeasurement(attributes, obj, objMetric); | ||
} | ||
} | ||
|
||
void removeGauge(String name, Attributes attributes) { | ||
synchronized (gauges) { | ||
GaugeMeasurementsRecorder recorder = gauges.get(name); | ||
if (recorder != null) { | ||
recorder.removeGaugeMeasurement(attributes); | ||
// if this was the last measurement then let's remove the whole recorder | ||
if (recorder.isEmpty()) { | ||
gauges.remove(name); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private final class GaugeMeasurementsRecorder implements Consumer<ObservableDoubleMeasurement> { | ||
|
||
@GuardedBy("gauges") | ||
private final Map<Attributes, GaugeInfo> measurements = new HashMap<>(); | ||
|
||
@Override | ||
public void accept(ObservableDoubleMeasurement measurement) { | ||
Map<Attributes, GaugeInfo> measurementsCopy; | ||
synchronized (gauges) { | ||
measurementsCopy = new HashMap<>(measurements); | ||
} | ||
|
||
measurementsCopy.forEach( | ||
(attributes, gauge) -> { | ||
Object obj = gauge.objWeakRef.get(); | ||
if (obj != null) { | ||
measurement.record(gauge.metricFunction.applyAsDouble(obj), attributes); | ||
} | ||
}); | ||
} | ||
|
||
<T> void addGaugeMeasurement( | ||
Attributes attributes, @Nullable T obj, ToDoubleFunction<T> objMetric) { | ||
synchronized (gauges) { | ||
measurements.put(attributes, new GaugeInfo(obj, (ToDoubleFunction<Object>) objMetric)); | ||
} | ||
} | ||
|
||
void removeGaugeMeasurement(Attributes attributes) { | ||
synchronized (gauges) { | ||
measurements.remove(attributes); | ||
} | ||
} | ||
|
||
boolean isEmpty() { | ||
synchronized (gauges) { | ||
return measurements.isEmpty(); | ||
} | ||
} | ||
} | ||
|
||
private static final class GaugeInfo { | ||
|
||
private final WeakReference<Object> objWeakRef; | ||
private final ToDoubleFunction<Object> metricFunction; | ||
|
||
private GaugeInfo(@Nullable Object obj, ToDoubleFunction<Object> metricFunction) { | ||
this.objWeakRef = new WeakReference<>(obj); | ||
this.metricFunction = metricFunction; | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
...nt/src/main/java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/Bridging.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; | ||
|
||
import io.micrometer.core.instrument.Meter; | ||
import io.micrometer.core.instrument.Tag; | ||
import io.opentelemetry.api.common.AttributeKey; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.common.AttributesBuilder; | ||
import io.opentelemetry.instrumentation.api.cache.Cache; | ||
|
||
final class Bridging { | ||
|
||
private static final Cache<String, AttributeKey<String>> tagsCache = Cache.bounded(1024); | ||
|
||
static Attributes toAttributes(Iterable<Tag> tags) { | ||
if (!tags.iterator().hasNext()) { | ||
return Attributes.empty(); | ||
} | ||
AttributesBuilder builder = Attributes.builder(); | ||
for (Tag tag : tags) { | ||
builder.put(tagsCache.computeIfAbsent(tag.getKey(), AttributeKey::stringKey), tag.getValue()); | ||
} | ||
return builder.build(); | ||
} | ||
|
||
static String description(Meter.Id id) { | ||
String description = id.getDescription(); | ||
return description == null ? "" : description; | ||
} | ||
|
||
static String baseUnit(Meter.Id id) { | ||
String baseUnit = id.getBaseUnit(); | ||
return baseUnit == null ? "1" : baseUnit; | ||
} | ||
|
||
private Bridging() {} | ||
} |
38 changes: 38 additions & 0 deletions
38
...va/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MetricsInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; | ||
|
||
import static net.bytebuddy.matcher.ElementMatchers.isTypeInitializer; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
|
||
import io.micrometer.core.instrument.Metrics; | ||
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 MetricsInstrumentation implements TypeInstrumentation { | ||
|
||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return named("io.micrometer.core.instrument.Metrics"); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isTypeInitializer(), this.getClass().getName() + "$StaticInitializerAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class StaticInitializerAdvice { | ||
@Advice.OnMethodExit(suppress = Throwable.class) | ||
public static void onExit() { | ||
Metrics.addRegistry(MicrometerSingletons.meterRegistry()); | ||
} | ||
} | ||
} |
40 changes: 40 additions & 0 deletions
40
...ntelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerInstrumentationModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; | ||
|
||
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
@AutoService(InstrumentationModule.class) | ||
public class MicrometerInstrumentationModule extends InstrumentationModule { | ||
|
||
public MicrometerInstrumentationModule() { | ||
super("micrometer", "micrometer-1.5"); | ||
} | ||
|
||
@Override | ||
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() { | ||
// added in 1.5 | ||
return hasClassesNamed("io.micrometer.core.instrument.config.validate.Validated"); | ||
} | ||
|
||
@Override | ||
protected boolean defaultEnabled() { | ||
// TODO: disabled by default, since not all instruments are implemented | ||
return false; | ||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return Collections.singletonList(new MetricsInstrumentation()); | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
...java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/MicrometerSingletons.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; | ||
|
||
import io.micrometer.core.instrument.MeterRegistry; | ||
import io.opentelemetry.api.GlobalOpenTelemetry; | ||
|
||
public final class MicrometerSingletons { | ||
|
||
private static final MeterRegistry METER_REGISTRY = | ||
OpenTelemetryMeterRegistry.create(GlobalOpenTelemetry.get()); | ||
|
||
public static MeterRegistry meterRegistry() { | ||
return METER_REGISTRY; | ||
} | ||
|
||
private MicrometerSingletons() {} | ||
} |
69 changes: 69 additions & 0 deletions
69
...java/io/opentelemetry/javaagent/instrumentation/micrometer/v1_5/OpenTelemetryCounter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.micrometer.v1_5; | ||
|
||
import static io.opentelemetry.javaagent.instrumentation.micrometer.v1_5.Bridging.baseUnit; | ||
import static io.opentelemetry.javaagent.instrumentation.micrometer.v1_5.Bridging.description; | ||
import static io.opentelemetry.javaagent.instrumentation.micrometer.v1_5.Bridging.toAttributes; | ||
|
||
import io.micrometer.core.instrument.Counter; | ||
import io.micrometer.core.instrument.Measurement; | ||
import io.opentelemetry.api.common.Attributes; | ||
import io.opentelemetry.api.metrics.DoubleCounter; | ||
import io.opentelemetry.api.metrics.Meter; | ||
import java.util.Collections; | ||
|
||
final class OpenTelemetryCounter implements Counter, RemovableMeter { | ||
|
||
private final Id id; | ||
// TODO: use bound instruments when they're available | ||
private final DoubleCounter otelCounter; | ||
private final Attributes attributes; | ||
|
||
private volatile boolean removed = false; | ||
|
||
OpenTelemetryCounter(Id id, Meter otelMeter) { | ||
this.id = id; | ||
this.otelCounter = | ||
otelMeter | ||
.counterBuilder(id.getName()) | ||
.setDescription(description(id)) | ||
.setUnit(baseUnit(id)) | ||
.ofDoubles() | ||
.build(); | ||
this.attributes = toAttributes(id.getTags()); | ||
} | ||
|
||
@Override | ||
public void increment(double v) { | ||
if (removed) { | ||
return; | ||
} | ||
otelCounter.add(v, attributes); | ||
} | ||
|
||
@Override | ||
public double count() { | ||
UnsupportedReadLogger.logWarning(); | ||
return Double.NaN; | ||
} | ||
|
||
@Override | ||
public Iterable<Measurement> measure() { | ||
UnsupportedReadLogger.logWarning(); | ||
return Collections.emptyList(); | ||
} | ||
|
||
@Override | ||
public Id getId() { | ||
return id; | ||
} | ||
|
||
@Override | ||
public void onRemove() { | ||
removed = true; | ||
} | ||
} |
Oops, something went wrong.