Skip to content

Commit dfa203a

Browse files
GaganjunejaGagan Juneja
and
Gagan Juneja
authored
Adds support for asynchronous gauge metric type (#12642)
* Adds support for asynchronous gauge metric type Signed-off-by: Gagan Juneja <gjjuneja@amazon.com> * Adds change log Signed-off-by: Gagan Juneja <gjjuneja@amazon.com> * incorporate pr comments Signed-off-by: Gagan Juneja <gjjuneja@amazon.com> * fixes build errors Signed-off-by: Gagan Juneja <gjjuneja@amazon.com> --------- Signed-off-by: Gagan Juneja <gjjuneja@amazon.com> Signed-off-by: Gagan Juneja <gagandeepjuneja@gmail.com> Co-authored-by: Gagan Juneja <gjjuneja@amazon.com>
1 parent 18e5816 commit dfa203a

File tree

9 files changed

+150
-0
lines changed

9 files changed

+150
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
112112
- [Tiered caching] Make IndicesRequestCache implementation configurable [EXPERIMENTAL] ([#12533](https://github.com/opensearch-project/OpenSearch/pull/12533))
113113
- Add kuromoji_completion analyzer and filter ([#4835](https://github.com/opensearch-project/OpenSearch/issues/4835))
114114
- The org.opensearch.bootstrap.Security should support codebase for JAR files with classifiers ([#12586](https://github.com/opensearch-project/OpenSearch/issues/12586))
115+
- [Metrics Framework] Adds support for asynchronous gauge metric type. ([#12642](https://github.com/opensearch-project/OpenSearch/issues/12642))
115116
- Make search query counters dynamic to support all query types ([#12601](https://github.com/opensearch-project/OpenSearch/pull/12601))
116117

117118
### Dependencies

libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistry.java

+9
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88

99
package org.opensearch.telemetry.metrics;
1010

11+
import org.opensearch.telemetry.metrics.tags.Tags;
12+
13+
import java.io.Closeable;
1114
import java.io.IOException;
15+
import java.util.function.Supplier;
1216

1317
/**
1418
* Default implementation for {@link MetricsRegistry}
@@ -39,6 +43,11 @@ public Histogram createHistogram(String name, String description, String unit) {
3943
return metricsTelemetry.createHistogram(name, description, unit);
4044
}
4145

46+
@Override
47+
public Closeable createGauge(String name, String description, String unit, Supplier<Double> valueProvider, Tags tags) {
48+
return metricsTelemetry.createGauge(name, description, unit, valueProvider, tags);
49+
}
50+
4251
@Override
4352
public void close() throws IOException {
4453
metricsTelemetry.close();

libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/MetricsRegistry.java

+16
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
package org.opensearch.telemetry.metrics;
1010

1111
import org.opensearch.common.annotation.ExperimentalApi;
12+
import org.opensearch.telemetry.metrics.tags.Tags;
1213

1314
import java.io.Closeable;
15+
import java.util.function.Supplier;
1416

1517
/**
1618
* MetricsRegistry helps in creating the metric instruments.
@@ -47,4 +49,18 @@ public interface MetricsRegistry extends Closeable {
4749
* @return histogram.
4850
*/
4951
Histogram createHistogram(String name, String description, String unit);
52+
53+
/**
54+
* Creates the Observable Gauge type of Metric. Where the value provider will be called at a certain frequency
55+
* to capture the value.
56+
*
57+
* @param name name of the observable gauge.
58+
* @param description any description about the metric.
59+
* @param unit unit of the metric.
60+
* @param valueProvider value provider.
61+
* @param tags attributes/dimensions of the metric.
62+
* @return closeable to dispose/close the Gauge metric.
63+
*/
64+
Closeable createGauge(String name, String description, String unit, Supplier<Double> valueProvider, Tags tags);
65+
5066
}

libs/telemetry/src/main/java/org/opensearch/telemetry/metrics/noop/NoopMetricsRegistry.java

+8
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
import org.opensearch.telemetry.metrics.Counter;
1313
import org.opensearch.telemetry.metrics.Histogram;
1414
import org.opensearch.telemetry.metrics.MetricsRegistry;
15+
import org.opensearch.telemetry.metrics.tags.Tags;
1516

17+
import java.io.Closeable;
1618
import java.io.IOException;
19+
import java.util.function.Supplier;
1720

1821
/**
1922
*No-op {@link MetricsRegistry}
@@ -44,6 +47,11 @@ public Histogram createHistogram(String name, String description, String unit) {
4447
return NoopHistogram.INSTANCE;
4548
}
4649

50+
@Override
51+
public Closeable createGauge(String name, String description, String unit, Supplier<Double> valueProvider, Tags tags) {
52+
return () -> {};
53+
}
54+
4755
@Override
4856
public void close() throws IOException {
4957

libs/telemetry/src/test/java/org/opensearch/telemetry/metrics/DefaultMetricsRegistryTests.java

+20
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88

99
package org.opensearch.telemetry.metrics;
1010

11+
import org.opensearch.telemetry.metrics.tags.Tags;
1112
import org.opensearch.test.OpenSearchTestCase;
1213

14+
import java.io.Closeable;
15+
import java.util.function.Supplier;
16+
1317
import static org.mockito.ArgumentMatchers.any;
1418
import static org.mockito.Mockito.mock;
1519
import static org.mockito.Mockito.when;
@@ -59,4 +63,20 @@ public void testHistogram() {
5963
assertSame(mockHistogram, histogram);
6064
}
6165

66+
@SuppressWarnings("unchecked")
67+
public void testGauge() {
68+
Closeable mockCloseable = mock(Closeable.class);
69+
when(
70+
defaultMeterRegistry.createGauge(any(String.class), any(String.class), any(String.class), any(Supplier.class), any(Tags.class))
71+
).thenReturn(mockCloseable);
72+
Closeable closeable = defaultMeterRegistry.createGauge(
73+
"org.opensearch.telemetry.metrics.DefaultMeterRegistryTests.testObservableGauge",
74+
"test observable gauge",
75+
"ms",
76+
() -> 1.0,
77+
Tags.EMPTY
78+
);
79+
assertSame(mockCloseable, closeable);
80+
}
81+
6282
}

plugins/telemetry-otel/src/internalClusterTest/java/org/opensearch/telemetry/metrics/TelemetryMetricsEnabledSanityIT.java

+41
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,21 @@
1414
import org.opensearch.telemetry.IntegrationTestOTelTelemetryPlugin;
1515
import org.opensearch.telemetry.OTelTelemetrySettings;
1616
import org.opensearch.telemetry.TelemetrySettings;
17+
import org.opensearch.telemetry.metrics.tags.Tags;
1718
import org.opensearch.test.OpenSearchIntegTestCase;
1819
import org.junit.After;
1920

21+
import java.io.Closeable;
2022
import java.util.ArrayList;
2123
import java.util.Arrays;
2224
import java.util.Collection;
25+
import java.util.List;
26+
import java.util.concurrent.atomic.AtomicInteger;
27+
import java.util.function.Supplier;
2328
import java.util.stream.Collectors;
2429

2530
import io.opentelemetry.sdk.metrics.data.DoublePointData;
31+
import io.opentelemetry.sdk.metrics.data.MetricData;
2632
import io.opentelemetry.sdk.metrics.internal.data.ImmutableExponentialHistogramPointData;
2733

2834
@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE, minNumDataNodes = 1)
@@ -118,6 +124,41 @@ public void testHistogram() throws Exception {
118124
assertEquals(1.0, histogramPointData.getMin(), 1.0);
119125
}
120126

127+
public void testObservableGauge() throws Exception {
128+
String metricName = "test-observable-gauge";
129+
MetricsRegistry metricsRegistry = internalCluster().getInstance(MetricsRegistry.class);
130+
InMemorySingletonMetricsExporter.INSTANCE.reset();
131+
Tags tags = Tags.create().addTag("test", "integ-test");
132+
final AtomicInteger testValue = new AtomicInteger(0);
133+
Supplier<Double> valueProvider = () -> { return Double.valueOf(testValue.incrementAndGet()); };
134+
Closeable gaugeCloseable = metricsRegistry.createGauge(metricName, "test", "ms", valueProvider, tags);
135+
// Sleep for about 2.2s to wait for metrics to be published.
136+
Thread.sleep(2200);
137+
138+
InMemorySingletonMetricsExporter exporter = InMemorySingletonMetricsExporter.INSTANCE;
139+
140+
assertEquals(2.0, getMaxObservableGaugeValue(exporter, metricName), 0.0);
141+
gaugeCloseable.close();
142+
double observableGaugeValueAfterStop = getMaxObservableGaugeValue(exporter, metricName);
143+
144+
// Sleep for about 1.2s to wait for metrics to see that closed observableGauge shouldn't execute the callable.
145+
Thread.sleep(1200);
146+
assertEquals(observableGaugeValueAfterStop, getMaxObservableGaugeValue(exporter, metricName), 0.0);
147+
148+
}
149+
150+
private static double getMaxObservableGaugeValue(InMemorySingletonMetricsExporter exporter, String metricName) {
151+
List<MetricData> dataPoints = exporter.getFinishedMetricItems()
152+
.stream()
153+
.filter(a -> a.getName().contains(metricName))
154+
.collect(Collectors.toList());
155+
double totalValue = 0;
156+
for (MetricData metricData : dataPoints) {
157+
totalValue = Math.max(totalValue, ((DoublePointData) metricData.getDoubleGaugeData().getPoints().toArray()[0]).getValue());
158+
}
159+
return totalValue;
160+
}
161+
121162
@After
122163
public void reset() {
123164
InMemorySingletonMetricsExporter.INSTANCE.reset();

plugins/telemetry-otel/src/main/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetry.java

+15
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,22 @@
99
package org.opensearch.telemetry.metrics;
1010

1111
import org.opensearch.common.concurrent.RefCountedReleasable;
12+
import org.opensearch.telemetry.OTelAttributesConverter;
1213
import org.opensearch.telemetry.OTelTelemetryPlugin;
14+
import org.opensearch.telemetry.metrics.tags.Tags;
1315

1416
import java.io.Closeable;
1517
import java.io.IOException;
1618
import java.security.AccessController;
1719
import java.security.PrivilegedAction;
20+
import java.util.function.Supplier;
1821

1922
import io.opentelemetry.api.metrics.DoubleCounter;
2023
import io.opentelemetry.api.metrics.DoubleHistogram;
2124
import io.opentelemetry.api.metrics.DoubleUpDownCounter;
2225
import io.opentelemetry.api.metrics.Meter;
2326
import io.opentelemetry.api.metrics.MeterProvider;
27+
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
2428
import io.opentelemetry.sdk.OpenTelemetrySdk;
2529

2630
/**
@@ -86,6 +90,17 @@ public Histogram createHistogram(String name, String description, String unit) {
8690
return new OTelHistogram(doubleHistogram);
8791
}
8892

93+
@Override
94+
public Closeable createGauge(String name, String description, String unit, Supplier<Double> valueProvider, Tags tags) {
95+
ObservableDoubleGauge doubleObservableGauge = AccessController.doPrivileged(
96+
(PrivilegedAction<ObservableDoubleGauge>) () -> otelMeter.gaugeBuilder(name)
97+
.setUnit(unit)
98+
.setDescription(description)
99+
.buildWithCallback(record -> record.record(valueProvider.get(), OTelAttributesConverter.convert(tags)))
100+
);
101+
return () -> doubleObservableGauge.close();
102+
}
103+
89104
@Override
90105
public void close() throws IOException {
91106
meterProvider.close();

plugins/telemetry-otel/src/test/java/org/opensearch/telemetry/metrics/OTelMetricsTelemetryTests.java

+31
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,13 @@
1414
import org.opensearch.telemetry.metrics.tags.Tags;
1515
import org.opensearch.test.OpenSearchTestCase;
1616

17+
import java.io.Closeable;
18+
import java.util.function.Consumer;
19+
1720
import io.opentelemetry.api.OpenTelemetry;
1821
import io.opentelemetry.api.metrics.DoubleCounter;
1922
import io.opentelemetry.api.metrics.DoubleCounterBuilder;
23+
import io.opentelemetry.api.metrics.DoubleGaugeBuilder;
2024
import io.opentelemetry.api.metrics.DoubleHistogram;
2125
import io.opentelemetry.api.metrics.DoubleHistogramBuilder;
2226
import io.opentelemetry.api.metrics.DoubleUpDownCounter;
@@ -25,8 +29,10 @@
2529
import io.opentelemetry.api.metrics.LongUpDownCounterBuilder;
2630
import io.opentelemetry.api.metrics.Meter;
2731
import io.opentelemetry.api.metrics.MeterProvider;
32+
import io.opentelemetry.api.metrics.ObservableDoubleGauge;
2833
import org.mockito.Mockito;
2934

35+
import static org.mockito.ArgumentMatchers.any;
3036
import static org.mockito.Mockito.mock;
3137
import static org.mockito.Mockito.verify;
3238
import static org.mockito.Mockito.when;
@@ -149,4 +155,29 @@ public void testHistogram() {
149155
histogram.record(2.0, tags);
150156
verify(mockOTelDoubleHistogram).record(2.0, OTelAttributesConverter.convert(tags));
151157
}
158+
159+
@SuppressWarnings({ "rawtypes", "unchecked" })
160+
public void testGauge() throws Exception {
161+
String observableGaugeName = "test-gauge";
162+
String description = "test";
163+
String unit = "1";
164+
Meter mockMeter = mock(Meter.class);
165+
OpenTelemetry mockOpenTelemetry = mock(OpenTelemetry.class);
166+
ObservableDoubleGauge observableDoubleGauge = mock(ObservableDoubleGauge.class);
167+
DoubleGaugeBuilder mockOTelDoubleGaugeBuilder = mock(DoubleGaugeBuilder.class);
168+
MeterProvider meterProvider = mock(MeterProvider.class);
169+
when(meterProvider.get(OTelTelemetryPlugin.INSTRUMENTATION_SCOPE_NAME)).thenReturn(mockMeter);
170+
MetricsTelemetry metricsTelemetry = new OTelMetricsTelemetry(
171+
new RefCountedReleasable("telemetry", mockOpenTelemetry, () -> {}),
172+
meterProvider
173+
);
174+
when(mockMeter.gaugeBuilder(Mockito.contains(observableGaugeName))).thenReturn(mockOTelDoubleGaugeBuilder);
175+
when(mockOTelDoubleGaugeBuilder.setDescription(description)).thenReturn(mockOTelDoubleGaugeBuilder);
176+
when(mockOTelDoubleGaugeBuilder.setUnit(unit)).thenReturn(mockOTelDoubleGaugeBuilder);
177+
when(mockOTelDoubleGaugeBuilder.buildWithCallback(any(Consumer.class))).thenReturn(observableDoubleGauge);
178+
179+
Closeable closeable = metricsTelemetry.createGauge(observableGaugeName, description, unit, () -> 1.0, Tags.EMPTY);
180+
closeable.close();
181+
verify(observableDoubleGauge).close();
182+
}
152183
}

test/framework/src/main/java/org/opensearch/test/telemetry/MockTelemetry.java

+9
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,13 @@
1515
import org.opensearch.telemetry.metrics.MetricsTelemetry;
1616
import org.opensearch.telemetry.metrics.noop.NoopCounter;
1717
import org.opensearch.telemetry.metrics.noop.NoopHistogram;
18+
import org.opensearch.telemetry.metrics.tags.Tags;
1819
import org.opensearch.telemetry.tracing.TracingTelemetry;
1920
import org.opensearch.test.telemetry.tracing.MockTracingTelemetry;
2021

22+
import java.io.Closeable;
23+
import java.util.function.Supplier;
24+
2125
/**
2226
* Mock {@link Telemetry} implementation for testing.
2327
*/
@@ -53,6 +57,11 @@ public Histogram createHistogram(String name, String description, String unit) {
5357
return NoopHistogram.INSTANCE;
5458
}
5559

60+
@Override
61+
public Closeable createGauge(String name, String description, String unit, Supplier<Double> valueProvider, Tags tags) {
62+
return () -> {};
63+
}
64+
5665
@Override
5766
public void close() {
5867

0 commit comments

Comments
 (0)