Fix flaky micrometer retries in javaagent test (#5168)

* Fix flaky micrometer retries in javaagent test

* remove comment

* add clarifying comment
This commit is contained in:
Mateusz Rzeszutek 2022-01-19 10:43:50 +01:00 committed by GitHub
parent f6c520a062
commit 1ecf493332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 48 additions and 45 deletions

View File

@ -3,15 +3,13 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.micrometer.v1_5;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
package io.opentelemetry.instrumentation.api.internal;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
import io.opentelemetry.api.metrics.ObservableLongMeasurement;
import io.opentelemetry.instrumentation.api.cache.Cache;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@ -20,15 +18,35 @@ import java.util.function.ToDoubleFunction;
import java.util.function.ToLongFunction;
import javax.annotation.Nullable;
// this class must live in the bootstrap classloader - in case different metrics instrumentations
// (micrometer) get applied to classes in different classloaders, we want them to share the same
// async instrument registry - because the underlying OTel Meter is shared too. There is only one
// OTel SDK in the agent, and therefore there must be only one AsyncInstrumentRegistry too -
// otherwise some async metrics would get lost because of duplicate instrument registrations.
// TODO: refactor this class, there's too much copy-paste here
final class AsyncInstrumentRegistry {
public final class AsyncInstrumentRegistry {
// we need to re-use instrument registries per OpenTelemetry instance so that async instruments
// that were created by other OpenTelemetryMeterRegistries can be reused; otherwise the SDK will
// start logging errors and async measurements will not be recorded
private static final Cache<Meter, AsyncInstrumentRegistry> asyncInstrumentRegistries =
Cache.weak();
/**
* Returns the {@link AsyncInstrumentRegistry} for the passed {@link Meter}. There is at most one
* {@link AsyncInstrumentRegistry} created for each OpenTelemetry {@link Meter}.
*/
public static AsyncInstrumentRegistry getOrCreate(Meter meter) {
return asyncInstrumentRegistries.computeIfAbsent(meter, AsyncInstrumentRegistry::new);
}
// using a weak ref so that the AsyncInstrumentRegistry (which is stored in a static maps) does
// not hold strong references to Meter (and thus make it impossible to collect Meter garbage).
// in practice this should never return null - OpenTelemetryMeterRegistry maintains a strong
// reference to both Meter and AsyncInstrumentRegistry; if the meter registry is GC'd then its
// corresponding AsyncInstrumentRegistry cannot possibly be used; and Meter cannot be GC'd until
// OpentelemetryMeterRegistry is GC'd
// OpenTelemetryMeterRegistry is GC'd
private final WeakReference<Meter> meter;
// values from the maps below are never removed - that is because the underlying OpenTelemetry
@ -47,16 +65,7 @@ final class AsyncInstrumentRegistry {
this.meter = new WeakReference<>(meter);
}
<T> AsyncMeasurementHandle buildGauge(
io.micrometer.core.instrument.Meter.Id meterId,
Attributes attributes,
@Nullable T obj,
ToDoubleFunction<T> objMetric) {
return buildGauge(
meterId.getName(), description(meterId), baseUnit(meterId), attributes, obj, objMetric);
}
<T> AsyncMeasurementHandle buildGauge(
public <T> AsyncMeasurementHandle buildGauge(
String name,
String description,
String baseUnit,
@ -81,16 +90,7 @@ final class AsyncInstrumentRegistry {
return new AsyncMeasurementHandle(recorder, attributes);
}
<T> AsyncMeasurementHandle buildDoubleCounter(
io.micrometer.core.instrument.Meter.Id meterId,
Attributes attributes,
T obj,
ToDoubleFunction<T> objMetric) {
return buildDoubleCounter(
meterId.getName(), description(meterId), baseUnit(meterId), attributes, obj, objMetric);
}
<T> AsyncMeasurementHandle buildDoubleCounter(
public <T> AsyncMeasurementHandle buildDoubleCounter(
String name,
String description,
String baseUnit,
@ -116,7 +116,7 @@ final class AsyncInstrumentRegistry {
return new AsyncMeasurementHandle(recorder, attributes);
}
<T> AsyncMeasurementHandle buildLongCounter(
public <T> AsyncMeasurementHandle buildLongCounter(
String name,
String description,
String baseUnit,
@ -141,7 +141,7 @@ final class AsyncInstrumentRegistry {
return new AsyncMeasurementHandle(recorder, attributes);
}
<T> AsyncMeasurementHandle buildUpDownDoubleCounter(
public <T> AsyncMeasurementHandle buildUpDownDoubleCounter(
String name,
String description,
String baseUnit,
@ -251,7 +251,7 @@ final class AsyncInstrumentRegistry {
}
}
static final class AsyncMeasurementHandle {
public static final class AsyncMeasurementHandle {
private final MeasurementsRecorder<?> measurementsRecorder;
private final Attributes attributes;
@ -261,7 +261,7 @@ final class AsyncInstrumentRegistry {
this.attributes = attributes;
}
void remove() {
public void remove() {
measurementsRecorder.removeMeasurement(attributes);
}
}

View File

@ -5,12 +5,15 @@
package io.opentelemetry.instrumentation.micrometer.v1_5;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes;
import io.micrometer.core.instrument.FunctionCounter;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.util.MeterEquivalence;
import io.opentelemetry.instrumentation.micrometer.v1_5.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import java.util.Collections;
import java.util.function.ToDoubleFunction;
import javax.annotation.Nullable;
@ -29,7 +32,8 @@ final class OpenTelemetryFunctionCounter<T> implements FunctionCounter, Removabl
this.id = id;
countMeasurementHandle =
asyncInstrumentRegistry.buildDoubleCounter(id, tagsAsAttributes(id), obj, countFunction);
asyncInstrumentRegistry.buildDoubleCounter(
id.getName(), description(id), baseUnit(id), tagsAsAttributes(id), obj, countFunction);
}
@Override

View File

@ -15,7 +15,8 @@ import io.micrometer.core.instrument.Statistic;
import io.micrometer.core.instrument.util.MeterEquivalence;
import io.micrometer.core.instrument.util.TimeUtils;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.instrumentation.micrometer.v1_5.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;

View File

@ -5,12 +5,15 @@
package io.opentelemetry.instrumentation.micrometer.v1_5;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.baseUnit;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.description;
import static io.opentelemetry.instrumentation.micrometer.v1_5.Bridging.tagsAsAttributes;
import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.util.MeterEquivalence;
import io.opentelemetry.instrumentation.micrometer.v1_5.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import java.util.Collections;
import java.util.function.ToDoubleFunction;
import javax.annotation.Nullable;
@ -29,7 +32,8 @@ final class OpenTelemetryGauge<T> implements Gauge, RemovableMeter {
this.id = id;
gaugeMeasurementHandle =
asyncInstrumentRegistry.buildGauge(id, tagsAsAttributes(id), obj, objMetric);
asyncInstrumentRegistry.buildGauge(
id.getName(), description(id), baseUnit(id), tagsAsAttributes(id), obj, objMetric);
}
@Override

View File

@ -14,7 +14,8 @@ import io.micrometer.core.instrument.Measurement;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.util.MeterEquivalence;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.instrumentation.micrometer.v1_5.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry.AsyncMeasurementHandle;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

View File

@ -20,7 +20,7 @@ import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
import io.micrometer.core.instrument.distribution.HistogramGauges;
import io.micrometer.core.instrument.distribution.pause.PauseDetector;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.cache.Cache;
import io.opentelemetry.instrumentation.api.internal.AsyncInstrumentRegistry;
import java.util.concurrent.TimeUnit;
import java.util.function.ToDoubleFunction;
import java.util.function.ToLongFunction;
@ -49,20 +49,13 @@ public final class OpenTelemetryMeterRegistry extends MeterRegistry {
return new OpenTelemetryMeterRegistryBuilder(openTelemetry);
}
// we need to re-use instrument registries per OpenTelemetry instance so that async instruments
// that were created by other OpenTelemetryMeterRegistries can be reused; otherwise the SDK will
// start logging errors and async measurements will not be recorded
private static final Cache<io.opentelemetry.api.metrics.Meter, AsyncInstrumentRegistry>
asyncInstrumentRegistries = Cache.weak();
private final io.opentelemetry.api.metrics.Meter otelMeter;
private final AsyncInstrumentRegistry asyncInstrumentRegistry;
OpenTelemetryMeterRegistry(Clock clock, io.opentelemetry.api.metrics.Meter otelMeter) {
super(clock);
this.otelMeter = otelMeter;
this.asyncInstrumentRegistry =
asyncInstrumentRegistries.computeIfAbsent(otelMeter, AsyncInstrumentRegistry::new);
this.asyncInstrumentRegistry = AsyncInstrumentRegistry.getOrCreate(otelMeter);
this.config().onMeterRemoved(OpenTelemetryMeterRegistry::onMeterRemoved);
}