Browse Source

Metrics Tests - Recording Otel Meter (#101281)

Adds an implementation of Otel's Meter that records all instrument calls made through the open telemetry interface.

This allows the registry to avoid mocking out otel in testing.

Updates the GaugeAdapterTests to use the recording meter.
Stuart Tettemer 2 years ago
parent
commit
5b2c25f80b

+ 648 - 0
modules/apm/src/test/java/org/elasticsearch/telemetry/apm/RecordingOtelMeter.java

@@ -0,0 +1,648 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0 and the Server Side Public License, v 1; you may not use this file except
+ * in compliance with, at your election, the Elastic License 2.0 or the Server
+ * Side Public License, v 1.
+ */
+
+package org.elasticsearch.telemetry.apm;
+
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.metrics.DoubleCounter;
+import io.opentelemetry.api.metrics.DoubleCounterBuilder;
+import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
+import io.opentelemetry.api.metrics.DoubleHistogram;
+import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
+import io.opentelemetry.api.metrics.DoubleUpDownCounter;
+import io.opentelemetry.api.metrics.DoubleUpDownCounterBuilder;
+import io.opentelemetry.api.metrics.LongCounter;
+import io.opentelemetry.api.metrics.LongCounterBuilder;
+import io.opentelemetry.api.metrics.LongGaugeBuilder;
+import io.opentelemetry.api.metrics.LongHistogram;
+import io.opentelemetry.api.metrics.LongHistogramBuilder;
+import io.opentelemetry.api.metrics.LongUpDownCounter;
+import io.opentelemetry.api.metrics.LongUpDownCounterBuilder;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.ObservableDoubleCounter;
+import io.opentelemetry.api.metrics.ObservableDoubleGauge;
+import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
+import io.opentelemetry.api.metrics.ObservableDoubleUpDownCounter;
+import io.opentelemetry.api.metrics.ObservableLongCounter;
+import io.opentelemetry.api.metrics.ObservableLongGauge;
+import io.opentelemetry.api.metrics.ObservableLongMeasurement;
+import io.opentelemetry.api.metrics.ObservableLongUpDownCounter;
+import io.opentelemetry.context.Context;
+
+import org.elasticsearch.telemetry.InstrumentType;
+import org.elasticsearch.telemetry.MetricRecorder;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.function.Consumer;
+
+public class RecordingOtelMeter implements Meter {
+
+    Queue<Callback> callbacks = new ConcurrentLinkedQueue<>();
+
+    public void collectMetrics() {
+        callbacks.forEach(Callback::doCall);
+    }
+
+    public MetricRecorder<OtelInstrument> getRecorder() {
+        return recorder;
+    }
+
+    private final MetricRecorder<OtelInstrument> recorder = new MetricRecorder<>();
+
+    @Override
+    public LongCounterBuilder counterBuilder(String name) {
+        return new RecordingLongCounterBuilder(name);
+    }
+
+    @Override
+    public LongUpDownCounterBuilder upDownCounterBuilder(String name) {
+        return new RecordingLongUpDownBuilder(name);
+    }
+
+    @Override
+    public DoubleHistogramBuilder histogramBuilder(String name) {
+        return new RecordingDoubleHistogramBuilder(name);
+    }
+
+    @Override
+    public DoubleGaugeBuilder gaugeBuilder(String name) {
+        return new RecordingDoubleGaugeBuilder(name);
+    }
+
+    // Counter
+    private class RecordingLongCounterBuilder extends AbstractBuilder implements LongCounterBuilder {
+        RecordingLongCounterBuilder(String name) {
+            super(name);
+        }
+
+        @Override
+        public LongCounterBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public LongCounterBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public DoubleCounterBuilder ofDoubles() {
+            return new RecordingDoubleCounterBuilder(this);
+        }
+
+        @Override
+        public LongCounter build() {
+            LongRecorder counter = new LongRecorder(name);
+            recorder.register(counter, counter.getInstrument(), name, description, unit);
+            return counter;
+        }
+
+        @Override
+        public ObservableLongCounter buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
+            unimplemented();
+            return null;
+        }
+
+        @Override
+        public ObservableLongMeasurement buildObserver() {
+            unimplemented();
+            return null;
+        }
+    }
+
+    private class LongRecorder extends LongUpDownRecorder implements LongCounter, OtelInstrument {
+        LongRecorder(String name) {
+            super(name, InstrumentType.LONG_COUNTER);
+        }
+
+        @Override
+        public void add(long value) {
+            assert value >= 0;
+            super.add(value);
+        }
+
+        @Override
+        public void add(long value, Attributes attributes) {
+            assert value >= 0;
+            super.add(value, attributes);
+        }
+
+        @Override
+        public void add(long value, Attributes attributes, Context context) {
+            assert value >= 0;
+            super.add(value, attributes, context);
+        }
+    }
+
+    private class RecordingDoubleCounterBuilder extends AbstractBuilder implements DoubleCounterBuilder {
+
+        RecordingDoubleCounterBuilder(AbstractBuilder other) {
+            super(other);
+        }
+
+        @Override
+        public DoubleCounterBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public DoubleCounterBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public DoubleCounter build() {
+            DoubleRecorder counter = new DoubleRecorder(name);
+            recorder.register(counter, counter.getInstrument(), name, description, unit);
+            return counter;
+        }
+
+        @Override
+        public ObservableDoubleCounter buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
+            unimplemented();
+            return null;
+        }
+
+        @Override
+        public ObservableDoubleMeasurement buildObserver() {
+            unimplemented();
+            return null;
+        }
+    }
+
+    private class DoubleRecorder extends DoubleUpDownRecorder implements DoubleCounter, OtelInstrument {
+        DoubleRecorder(String name) {
+            super(name, InstrumentType.DOUBLE_COUNTER);
+        }
+
+        @Override
+        public void add(double value) {
+            assert value >= 0;
+            super.add(value);
+        }
+
+        @Override
+        public void add(double value, Attributes attributes) {
+            assert value >= 0;
+            super.add(value, attributes);
+        }
+
+        @Override
+        public void add(double value, Attributes attributes, Context context) {
+            assert value >= 0;
+            super.add(value, attributes, context);
+        }
+    }
+
+    private class RecordingLongUpDownBuilder extends AbstractBuilder implements LongUpDownCounterBuilder {
+        RecordingLongUpDownBuilder(String name) {
+            super(name);
+        }
+
+        @Override
+        public LongUpDownCounterBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public LongUpDownCounterBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public DoubleUpDownCounterBuilder ofDoubles() {
+            return new RecordingDoubleUpDownBuilder(this);
+        }
+
+        @Override
+        public LongUpDownCounter build() {
+            LongUpDownRecorder counter = new LongUpDownRecorder(name);
+            recorder.register(counter, counter.getInstrument(), name, description, unit);
+            return counter;
+        }
+
+        @Override
+        public ObservableLongUpDownCounter buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
+            unimplemented();
+            return null;
+        }
+
+        @Override
+        public ObservableLongMeasurement buildObserver() {
+            unimplemented();
+            return null;
+        }
+    }
+
+    private class LongUpDownRecorder extends AbstractInstrument implements LongUpDownCounter, OtelInstrument {
+        LongUpDownRecorder(String name) {
+            super(name, InstrumentType.LONG_UP_DOWN_COUNTER);
+        }
+
+        protected LongUpDownRecorder(String name, InstrumentType instrument) {
+            // used by LongRecorder
+            super(name, instrument);
+        }
+
+        @Override
+        public void add(long value) {
+            recorder.call(instrument, name, value, null);
+        }
+
+        @Override
+        public void add(long value, Attributes attributes) {
+            recorder.call(instrument, name, value, toMap(attributes));
+        }
+
+        @Override
+        public void add(long value, Attributes attributes, Context context) {
+            unimplemented();
+        }
+    }
+
+    private class RecordingDoubleUpDownBuilder extends AbstractBuilder implements DoubleUpDownCounterBuilder {
+
+        RecordingDoubleUpDownBuilder(AbstractBuilder other) {
+            super(other);
+        }
+
+        @Override
+        public DoubleUpDownCounterBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public DoubleUpDownCounterBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public DoubleUpDownCounter build() {
+            DoubleUpDownRecorder counter = new DoubleUpDownRecorder(name);
+            recorder.register(counter, counter.getInstrument(), name, description, unit);
+            return counter;
+        }
+
+        @Override
+        public ObservableDoubleUpDownCounter buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
+            unimplemented();
+            return null;
+        }
+
+        @Override
+        public ObservableDoubleMeasurement buildObserver() {
+            unimplemented();
+            return null;
+        }
+    }
+
+    private class DoubleUpDownRecorder extends AbstractInstrument implements DoubleUpDownCounter, OtelInstrument {
+        DoubleUpDownRecorder(String name) {
+            super(name, InstrumentType.LONG_UP_DOWN_COUNTER);
+        }
+
+        protected DoubleUpDownRecorder(String name, InstrumentType instrument) {
+            // used by DoubleRecorder
+            super(name, instrument);
+        }
+
+        @Override
+        public void add(double value) {
+            recorder.call(instrument, name, value, null);
+        }
+
+        @Override
+        public void add(double value, Attributes attributes) {
+            recorder.call(instrument, name, value, toMap(attributes));
+        }
+
+        @Override
+        public void add(double value, Attributes attributes, Context context) {
+            unimplemented();
+        }
+    }
+
+    interface Callback {
+        void doCall();
+    }
+
+    abstract static class AbstractInstrument {
+        protected final String name;
+        protected final InstrumentType instrument;
+
+        AbstractInstrument(String name, InstrumentType instrument) {
+            this.name = name;
+            this.instrument = instrument;
+        }
+
+        public InstrumentType getInstrument() {
+            return instrument;
+        }
+
+        protected void unimplemented() {
+            throw new UnsupportedOperationException("unimplemented");
+        }
+
+        Map<String, Object> toMap(Attributes attributes) {
+            if (attributes == null) {
+                return null;
+            }
+            if (attributes.isEmpty()) {
+                return Collections.emptyMap();
+            }
+            Map<String, Object> map = new HashMap<>(attributes.size());
+            attributes.forEach((k, v) -> map.put(k.getKey(), v));
+            return map;
+        }
+    }
+
+    abstract static class AbstractBuilder {
+        protected final String name;
+        protected String description;
+        protected String unit;
+
+        AbstractBuilder(String name) {
+            this.name = name;
+        }
+
+        AbstractBuilder(AbstractBuilder other) {
+            this.name = other.name;
+            this.description = other.description;
+            this.unit = other.unit;
+        }
+
+        void innerSetDescription(String description) {
+            this.description = description;
+        }
+
+        void innerSetUnit(String unit) {
+            this.unit = unit;
+        }
+
+        protected void unimplemented() {
+            throw new UnsupportedOperationException("unimplemented");
+        }
+    }
+
+    interface OtelInstrument {}
+
+    // Gauges
+    private class RecordingDoubleGaugeBuilder extends AbstractBuilder implements DoubleGaugeBuilder {
+        RecordingDoubleGaugeBuilder(String name) {
+            super(name);
+        }
+
+        @Override
+        public DoubleGaugeBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public DoubleGaugeBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public LongGaugeBuilder ofLongs() {
+            return new RecordingLongGaugeBuilder(this);
+        }
+
+        @Override
+        public ObservableDoubleGauge buildWithCallback(Consumer<ObservableDoubleMeasurement> callback) {
+            DoubleGaugeRecorder gauge = new DoubleGaugeRecorder(name, callback);
+            recorder.register(gauge, gauge.getInstrument(), name, description, unit);
+            callbacks.add(gauge);
+            return gauge;
+        }
+
+        @Override
+        public ObservableDoubleMeasurement buildObserver() {
+            DoubleMeasurementRecorder measurement = new DoubleMeasurementRecorder(name);
+            recorder.register(measurement, measurement.getInstrument(), name, description, unit);
+            return measurement;
+        }
+    }
+
+    private class DoubleGaugeRecorder extends AbstractInstrument implements ObservableDoubleGauge, Callback, OtelInstrument {
+        final Consumer<ObservableDoubleMeasurement> callback;
+
+        DoubleGaugeRecorder(String name, Consumer<ObservableDoubleMeasurement> callback) {
+            super(name, InstrumentType.DOUBLE_GAUGE_OBSERVER);
+            this.callback = callback;
+        }
+
+        @Override
+        public void close() {
+            callbacks.remove(this);
+        }
+
+        public void doCall() {
+            callback.accept(new DoubleMeasurementRecorder(name, instrument));
+        }
+    }
+
+    private class DoubleMeasurementRecorder extends AbstractInstrument implements ObservableDoubleMeasurement, OtelInstrument {
+        DoubleMeasurementRecorder(String name, InstrumentType instrument) {
+            super(name, instrument);
+        }
+
+        DoubleMeasurementRecorder(String name) {
+            super(name, InstrumentType.DOUBLE_GAUGE);
+        }
+
+        @Override
+        public void record(double value) {
+            recorder.call(instrument, name, value, null);
+        }
+
+        @Override
+        public void record(double value, Attributes attributes) {
+            recorder.call(instrument, name, value, toMap(attributes));
+        }
+    }
+
+    private class RecordingLongGaugeBuilder extends AbstractBuilder implements LongGaugeBuilder {
+        RecordingLongGaugeBuilder(AbstractBuilder other) {
+            super(other);
+        }
+
+        @Override
+        public LongGaugeBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public LongGaugeBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public ObservableLongGauge buildWithCallback(Consumer<ObservableLongMeasurement> callback) {
+            LongGaugeRecorder gauge = new LongGaugeRecorder(name, callback);
+            recorder.register(gauge, gauge.getInstrument(), name, description, unit);
+            callbacks.add(gauge);
+            return gauge;
+        }
+
+        @Override
+        public ObservableLongMeasurement buildObserver() {
+            LongMeasurementRecorder measurement = new LongMeasurementRecorder(name);
+            recorder.register(measurement, measurement.getInstrument(), name, description, unit);
+            return measurement;
+        }
+    }
+
+    private class LongGaugeRecorder extends AbstractInstrument implements ObservableLongGauge, Callback, OtelInstrument {
+        final Consumer<ObservableLongMeasurement> callback;
+
+        LongGaugeRecorder(String name, Consumer<ObservableLongMeasurement> callback) {
+            super(name, InstrumentType.LONG_GAUGE_OBSERVER);
+            this.callback = callback;
+        }
+
+        @Override
+        public void close() {
+            callbacks.remove(this);
+        }
+
+        public void doCall() {
+            callback.accept(new LongMeasurementRecorder(name, instrument));
+        }
+    }
+
+    private class LongMeasurementRecorder extends AbstractInstrument implements ObservableLongMeasurement, OtelInstrument {
+        LongMeasurementRecorder(String name, InstrumentType instrument) {
+            super(name, instrument);
+        }
+
+        LongMeasurementRecorder(String name) {
+            super(name, InstrumentType.LONG_GAUGE);
+        }
+
+        @Override
+        public void record(long value) {
+            recorder.call(instrument, name, value, null);
+        }
+
+        @Override
+        public void record(long value, Attributes attributes) {
+            recorder.call(instrument, name, value, toMap(attributes));
+        }
+    }
+
+    // Histograms
+    private class RecordingDoubleHistogramBuilder extends AbstractBuilder implements DoubleHistogramBuilder {
+        RecordingDoubleHistogramBuilder(String name) {
+            super(name);
+        }
+
+        @Override
+        public DoubleHistogramBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public DoubleHistogramBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public LongHistogramBuilder ofLongs() {
+            return new RecordingLongHistogramBuilder(this);
+        }
+
+        @Override
+        public DoubleHistogram build() {
+            return new DoubleHistogramRecorder(name);
+        }
+    }
+
+    private class DoubleHistogramRecorder extends AbstractInstrument implements DoubleHistogram, OtelInstrument {
+        DoubleHistogramRecorder(String name) {
+            super(name, InstrumentType.DOUBLE_HISTOGRAM);
+        }
+
+        @Override
+        public void record(double value) {
+            recorder.call(getInstrument(), name, value, null);
+        }
+
+        @Override
+        public void record(double value, Attributes attributes) {
+            recorder.call(getInstrument(), name, value, toMap(attributes));
+        }
+
+        @Override
+        public void record(double value, Attributes attributes, Context context) {
+            unimplemented();
+        }
+    }
+
+    private class RecordingLongHistogramBuilder extends AbstractBuilder implements LongHistogramBuilder {
+
+        RecordingLongHistogramBuilder(AbstractBuilder other) {
+            super(other);
+        }
+
+        @Override
+        public LongHistogramBuilder setDescription(String description) {
+            innerSetDescription(description);
+            return this;
+        }
+
+        @Override
+        public LongHistogramBuilder setUnit(String unit) {
+            innerSetUnit(unit);
+            return this;
+        }
+
+        @Override
+        public LongHistogram build() {
+            return new LongHistogramRecorder(name);
+        }
+    }
+
+    private class LongHistogramRecorder extends AbstractInstrument implements LongHistogram, OtelInstrument {
+        LongHistogramRecorder(String name) {
+            super(name, InstrumentType.LONG_HISTOGRAM);
+        }
+
+        @Override
+        public void record(long value) {
+            recorder.call(getInstrument(), name, value, null);
+        }
+
+        @Override
+        public void record(long value, Attributes attributes) {
+            recorder.call(getInstrument(), name, value, toMap(attributes));
+        }
+
+        @Override
+        public void record(long value, Attributes attributes, Context context) {
+            unimplemented();
+        }
+    }
+}

+ 28 - 85
modules/apm/src/test/java/org/elasticsearch/telemetry/apm/internal/metrics/GaugeAdapterTests.java

@@ -8,116 +8,59 @@
 
 package org.elasticsearch.telemetry.apm.internal.metrics;
 
-import io.opentelemetry.api.common.Attributes;
-import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
-import io.opentelemetry.api.metrics.LongGaugeBuilder;
-import io.opentelemetry.api.metrics.Meter;
-import io.opentelemetry.api.metrics.ObservableDoubleMeasurement;
-import io.opentelemetry.api.metrics.ObservableLongMeasurement;
-
+import org.elasticsearch.telemetry.Measurement;
+import org.elasticsearch.telemetry.apm.APMMeterRegistry;
+import org.elasticsearch.telemetry.apm.RecordingOtelMeter;
+import org.elasticsearch.telemetry.metric.DoubleGauge;
+import org.elasticsearch.telemetry.metric.LongGauge;
 import org.elasticsearch.test.ESTestCase;
-import org.hamcrest.Matchers;
 import org.junit.Before;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mockito;
 
+import java.util.List;
 import java.util.Map;
-import java.util.function.Consumer;
 
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasSize;
 
 public class GaugeAdapterTests extends ESTestCase {
-    Meter testMeter = Mockito.mock(Meter.class);
-    LongGaugeBuilder longGaugeBuilder = Mockito.mock(LongGaugeBuilder.class);
-    DoubleGaugeBuilder mockDoubleGaugeBuilder = Mockito.mock(DoubleGaugeBuilder.class);
+    RecordingOtelMeter otelMeter;
+    APMMeterRegistry registry;
 
     @Before
     public void init() {
-        when(longGaugeBuilder.setDescription(Mockito.anyString())).thenReturn(longGaugeBuilder);
-        when(longGaugeBuilder.setUnit(Mockito.anyString())).thenReturn(longGaugeBuilder);
-
-
-        when(mockDoubleGaugeBuilder.ofLongs()).thenReturn(longGaugeBuilder);
-        when(mockDoubleGaugeBuilder.setUnit(Mockito.anyString())).thenReturn(mockDoubleGaugeBuilder);
-        when(mockDoubleGaugeBuilder.setDescription(Mockito.anyString())).thenReturn(mockDoubleGaugeBuilder);
-        when(testMeter.gaugeBuilder(anyString())).thenReturn(mockDoubleGaugeBuilder);
+        otelMeter = new RecordingOtelMeter();
+        registry = new APMMeterRegistry(otelMeter);
     }
 
     // testing that a value reported is then used in a callback
     @SuppressWarnings("unchecked")
     public void testLongGaugeRecord() {
-        LongGaugeAdapter longGaugeAdapter = new LongGaugeAdapter(testMeter, "name", "desc", "unit");
+        LongGauge longGauge = registry.registerLongGauge("name", "desc", "unit");
 
         // recording a value
-        longGaugeAdapter.record(1L, Map.of("k", 1L));
+        Map<String, Object> attributes = Map.of("k", 1L);
+        longGauge.record(1L, attributes);
 
-        // upon metric export, the consumer will be called
-        ArgumentCaptor<Consumer<ObservableLongMeasurement>> captor = ArgumentCaptor.forClass(Consumer.class);
-        verify(longGaugeBuilder).buildWithCallback(captor.capture());
+        otelMeter.collectMetrics();
 
-        Consumer<ObservableLongMeasurement> value = captor.getValue();
-        // making sure that a consumer will fetch the value passed down upon recording of a value
-        TestLongMeasurement testLongMeasurement = new TestLongMeasurement();
-        value.accept(testLongMeasurement);
-
-        assertThat(testLongMeasurement.value, Matchers.equalTo(1L));
-        assertThat(testLongMeasurement.attributes, Matchers.equalTo(Attributes.builder().put("k", 1).build()));
+        List<Measurement> metrics = otelMeter.getRecorder().getMeasurements(longGauge);
+        assertThat(metrics, hasSize(1));
+        assertThat(metrics.get(0).attributes(), equalTo(attributes));
+        assertThat(metrics.get(0).getLong(), equalTo(1L));
     }
 
     // testing that a value reported is then used in a callback
     @SuppressWarnings("unchecked")
     public void testDoubleGaugeRecord() {
-        DoubleGaugeAdapter doubleGaugeAdapter = new DoubleGaugeAdapter(testMeter, "name", "desc", "unit");
-
-        // recording a value
-        doubleGaugeAdapter.record(1.0, Map.of("k", 1.0));
-
-        // upon metric export, the consumer will be called
-        ArgumentCaptor<Consumer<ObservableDoubleMeasurement>> captor = ArgumentCaptor.forClass(Consumer.class);
-        verify(mockDoubleGaugeBuilder).buildWithCallback(captor.capture());
-
-        Consumer<ObservableDoubleMeasurement> value = captor.getValue();
-        // making sure that a consumer will fetch the value passed down upon recording of a value
-        TestDoubleMeasurement testLongMeasurement = new TestDoubleMeasurement();
-        value.accept(testLongMeasurement);
-
-        assertThat(testLongMeasurement.value, Matchers.equalTo(1.0));
-        assertThat(testLongMeasurement.attributes, Matchers.equalTo(Attributes.builder().put("k", 1.0).build()));
-    }
-
-    private static class TestDoubleMeasurement implements ObservableDoubleMeasurement {
-        double value;
-        Attributes attributes;
-
-        @Override
-        public void record(double value) {
-            this.value = value;
-        }
-
-        @Override
-        public void record(double value, Attributes attributes) {
-            this.value = value;
-            this.attributes = attributes;
-
-        }
-    }
-
-    private static class TestLongMeasurement implements ObservableLongMeasurement {
-        long value;
-        Attributes attributes;
-
-        @Override
-        public void record(long value) {
-            this.value = value;
-        }
+        DoubleGauge doubleGauge = registry.registerDoubleGauge("name", "desc", "unit");
+        Map<String, Object> attributes = Map.of("k", 1L);
+        doubleGauge.record(1.0, attributes);
 
-        @Override
-        public void record(long value, Attributes attributes) {
-            this.value = value;
-            this.attributes = attributes;
+        otelMeter.collectMetrics();
 
-        }
+        List<Measurement> metrics = otelMeter.getRecorder().getMeasurements(doubleGauge);
+        assertThat(metrics, hasSize(1));
+        assertThat(metrics.get(0).attributes(), equalTo(attributes));
+        assertThat(metrics.get(0).getDouble(), equalTo(1.0));
     }
 }

+ 1 - 1
test/framework/src/main/java/org/elasticsearch/telemetry/InstrumentType.java

@@ -24,7 +24,7 @@ import java.util.Objects;
  * Enum with the different types for use as keys.  This enum acts a bridge between the Otel and Elasticsearch versions of each
  * of the instruments.
  */
-enum InstrumentType {
+public enum InstrumentType {
     DOUBLE_COUNTER(true),
     LONG_COUNTER(false),
     DOUBLE_UP_DOWN_COUNTER(true),

+ 8 - 8
test/framework/src/main/java/org/elasticsearch/telemetry/MetricRecorder.java

@@ -23,7 +23,7 @@ import java.util.Objects;
  * Records invocations of the Instruments as {@link Measurement}s.
  * @param <I> The supertype of the registered instrument.
  */
-class MetricRecorder<I> {
+public class MetricRecorder<I> {
 
     /**
      * Container for Instrument of a given type, such as DoubleGauge, LongHistogram, etc.
@@ -54,7 +54,7 @@ class MetricRecorder<I> {
      */
     private final Map<InstrumentType, RegisteredMetric<I>> metrics;
 
-    MetricRecorder() {
+    public MetricRecorder() {
         metrics = new HashMap<>(InstrumentType.values().length);
         for (var instrument : InstrumentType.values()) {
             metrics.put(instrument, new RegisteredMetric<>(new HashMap<>(), new HashMap<>(), new HashMap<>()));
@@ -64,21 +64,21 @@ class MetricRecorder<I> {
     /**
      * Register an instrument.  Instruments must be registered before they are used.
      */
-    void register(I instrument, InstrumentType instrumentType, String name, String description, String unit) {
+    public void register(I instrument, InstrumentType instrumentType, String name, String description, String unit) {
         metrics.get(instrumentType).register(name, description, unit, instrument);
     }
 
     /**
      * Record a call made to a registered Elasticsearch {@link Instrument}.
      */
-    void call(Instrument instrument, Number value, Map<String, Object> attributes) {
+    public void call(Instrument instrument, Number value, Map<String, Object> attributes) {
         call(InstrumentType.fromInstrument(instrument), instrument.getName(), value, attributes);
     }
 
     /**
      * Record a call made to the registered instrument represented by the {@link InstrumentType} enum.
      */
-    void call(InstrumentType instrumentType, String name, Number value, Map<String, Object> attributes) {
+    public void call(InstrumentType instrumentType, String name, Number value, Map<String, Object> attributes) {
         metrics.get(instrumentType).call(name, new Measurement(value, attributes, instrumentType.isDouble));
     }
 
@@ -89,21 +89,21 @@ class MetricRecorder<I> {
         return getMeasurements(InstrumentType.fromInstrument(instrument), instrument.getName());
     }
 
-    List<Measurement> getMeasurements(InstrumentType instrumentType, String name) {
+    public List<Measurement> getMeasurements(InstrumentType instrumentType, String name) {
         return metrics.get(instrumentType).called.getOrDefault(Objects.requireNonNull(name), Collections.emptyList());
     }
 
     /**
      * Get the {@link Registration} for a given elasticsearch {@link Instrument}.
      */
-    Registration getRegistration(Instrument instrument) {
+    public Registration getRegistration(Instrument instrument) {
         return metrics.get(InstrumentType.fromInstrument(instrument)).registered().get(instrument.getName());
     }
 
     /**
      * Fetch the instrument instance given the type and registered name.
      */
-    I getInstrument(InstrumentType instrumentType, String name) {
+    public I getInstrument(InstrumentType instrumentType, String name) {
         return metrics.get(instrumentType).instruments.get(name);
     }
 }