io.opentelemetry.contrib.metrics.micrometer.internal.PollingMeterCallbackRegistrar Maven / Gradle / Ivy
Show all versions of opentelemetry-micrometer-meter-provider Show documentation
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.contrib.metrics.micrometer.internal;
import io.micrometer.core.instrument.Meter;
import io.micrometer.core.instrument.MeterRegistry;
import io.opentelemetry.contrib.metrics.micrometer.CallbackRegistrar;
import io.opentelemetry.contrib.metrics.micrometer.CallbackRegistration;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Supplier;
import javax.annotation.Nullable;
/**
* Implementation of a {@link CallbackRegistrar} that uses a Micrometer {@link Meter} to invoke the
* callbacks when the meter is measured.
*
* This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public final class PollingMeterCallbackRegistrar implements CallbackRegistrar {
static final String OTEL_POLLING_METER_NAME = "otel_polling_meter";
private final Supplier meterRegistrySupplier;
private final List callbacks;
@Nullable private volatile Meter pollingMeter;
public PollingMeterCallbackRegistrar(Supplier meterRegistrySupplier) {
this.meterRegistrySupplier = meterRegistrySupplier;
this.callbacks = new CopyOnWriteArrayList<>();
}
private void poll() {
for (Runnable callback : this.callbacks) {
callback.run();
}
}
@Override
public CallbackRegistration registerCallback(Runnable callback) {
if (callback != null) {
ensurePollingMeterCreated();
callbacks.add(callback);
return () -> callbacks.remove(callback);
} else {
return () -> {};
}
}
private synchronized void ensurePollingMeterCreated() {
if (pollingMeter == null) {
pollingMeter = createPollingMeter(meterRegistrySupplier.get());
}
}
/**
* Creates a dummy {@link Meter} which will be used to intercept when measurements are being
* enumerated so that observable instruments can be polled to record their metrics.
*/
private Meter createPollingMeter(MeterRegistry meterRegistry) {
return Meter.builder(OTEL_POLLING_METER_NAME, Meter.Type.OTHER, PollingIterable.of(this::poll))
.register(meterRegistry);
}
@Override
public synchronized void close() {
if (pollingMeter != null) {
meterRegistrySupplier.get().remove(pollingMeter);
pollingMeter = null;
}
}
/**
* An implementation of an {@link Iterable Iterable} that will invoke a {@link Runnable} when
* it is enumerated.
*/
@SuppressWarnings("IterableAndIterator")
private static class PollingIterable implements Iterable, Iterator {
static Iterable of(Runnable callback) {
return new PollingIterable<>(callback);
}
private final Runnable callback;
public PollingIterable(Runnable callback) {
this.callback = callback;
}
@Override
public Iterator iterator() {
return this;
}
@Override
public boolean hasNext() {
callback.run();
return false;
}
@Override
public T next() {
throw new NoSuchElementException();
}
}
}