Skip to content

Commit 59c8552

Browse files
authored
fix: Handle empty datapoints in otel exporter (#1898)
From @dhoard on slack, There was an error in the jmx-exporter project smoke tests: > time=2026-02-16T15:49:03.466Z level=ERROR source=write_handler.go:652 msg="Error appending remote write" component=web err="empty data points. jvm_memory_pool_allocated_bytes is dropped" > 2026-02-16 15:49:03.482 | OkHttp http://prometheus:9090/... | WARNING | e1723a08afd7bca35570fd31a7656f59.io.prometheus.metrics.shaded.io_opentelemetry_2_25_0_alpha.exporter.internal.http.HttpExporter | Failed to export metrics. Server responded with HTTP status code 500. Error message: Unable to parse response body, HTTP status message: Internal Server Error > Looking at the documentation for `jvm_memory_pool_allocated_bytes`... "Only updated after GC, not continuously." Most likely GC hasn't happened, yet the OTel exporter is trying to write the data. I updated the method to return null when the data points are empty, which is then handled via the [addUnlessNull](https://github.com/prometheus/client_java/blob/main/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/PrometheusMetricProducer.java#L136) method. After applying this change locally, I ran the [quick test](https://github.com/prometheus/jmx_exporter/blob/main/run-quick-test.sh) a few times and the error was no longer present. --------- Signed-off-by: Jay DeLuca <jaydeluca4@gmail.com>
1 parent 0d800d0 commit 59c8552

File tree

2 files changed

+45
-1
lines changed
  • prometheus-metrics-exporter-opentelemetry/src

2 files changed

+45
-1
lines changed

prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/MetricDataFactory.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,23 @@ public MetricDataFactory(
2727
this.currentTimeMillis = currentTimeMillis;
2828
}
2929

30+
@Nullable
3031
public MetricData create(CounterSnapshot snapshot) {
32+
if (snapshot.getDataPoints().isEmpty()) {
33+
return null;
34+
}
3135
return new PrometheusMetricData<>(
3236
snapshot.getMetadata(),
3337
new PrometheusCounter(snapshot, currentTimeMillis),
3438
instrumentationScopeInfo,
3539
resource);
3640
}
3741

42+
@Nullable
3843
public MetricData create(GaugeSnapshot snapshot) {
44+
if (snapshot.getDataPoints().isEmpty()) {
45+
return null;
46+
}
3947
return new PrometheusMetricData<>(
4048
snapshot.getMetadata(),
4149
new PrometheusGauge(snapshot, currentTimeMillis),
@@ -64,31 +72,47 @@ public MetricData create(HistogramSnapshot snapshot) {
6472
return null;
6573
}
6674

75+
@Nullable
6776
public MetricData create(SummarySnapshot snapshot) {
77+
if (snapshot.getDataPoints().isEmpty()) {
78+
return null;
79+
}
6880
return new PrometheusMetricData<>(
6981
snapshot.getMetadata(),
7082
new PrometheusSummary(snapshot, currentTimeMillis),
7183
instrumentationScopeInfo,
7284
resource);
7385
}
7486

87+
@Nullable
7588
public MetricData create(InfoSnapshot snapshot) {
89+
if (snapshot.getDataPoints().isEmpty()) {
90+
return null;
91+
}
7692
return new PrometheusMetricData<>(
7793
snapshot.getMetadata(),
7894
new PrometheusInfo(snapshot, currentTimeMillis),
7995
instrumentationScopeInfo,
8096
resource);
8197
}
8298

99+
@Nullable
83100
public MetricData create(StateSetSnapshot snapshot) {
101+
if (snapshot.getDataPoints().isEmpty()) {
102+
return null;
103+
}
84104
return new PrometheusMetricData<>(
85105
snapshot.getMetadata(),
86106
new PrometheusStateSet(snapshot, currentTimeMillis),
87107
instrumentationScopeInfo,
88108
resource);
89109
}
90110

111+
@Nullable
91112
public MetricData create(UnknownSnapshot snapshot) {
113+
if (snapshot.getDataPoints().isEmpty()) {
114+
return null;
115+
}
92116
return new PrometheusMetricData<>(
93117
snapshot.getMetadata(),
94118
new PrometheusUnknown(snapshot, currentTimeMillis),

prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ class ExportTest {
3333

3434
private static final Attributes ATTRIBUTES =
3535
Attributes.of(AttributeKey.stringKey("label"), "val", AttributeKey.stringKey("key"), "value");
36-
@RegisterExtension OpenTelemetryExtension testing = OpenTelemetryExtension.create();
36+
@RegisterExtension static OpenTelemetryExtension testing = OpenTelemetryExtension.create();
3737

3838
private final PrometheusRegistry registry = new PrometheusRegistry();
3939

@@ -304,6 +304,26 @@ void unknown() {
304304
.hasAttributes(Attributes.of(AttributeKey.stringKey("label"), "val"))));
305305
}
306306

307+
@Test
308+
void metricsWithoutDataPointsAreNotExported() {
309+
// Register metrics with labels but don't create any data points
310+
// This simulates the jvm_memory_pool_allocated_bytes scenario where a metric
311+
// is registered with label names, but no data points are created until GC happens
312+
Counter.builder().name("counter_no_data").labelNames("pool").register(registry);
313+
Gauge.builder().name("gauge_no_data").labelNames("pool").register(registry);
314+
Summary.builder().name("summary_no_data").labelNames("pool").register(registry);
315+
Histogram.builder().name("histogram_no_data").labelNames("pool").register(registry);
316+
StateSet.builder()
317+
.name("stateset_no_data")
318+
.states("state")
319+
.labelNames("pool")
320+
.register(registry);
321+
Info.builder().name("info_no_data").labelNames("pool").register(registry);
322+
323+
List<MetricData> metrics = testing.getMetrics();
324+
assertThat(metrics).isEmpty();
325+
}
326+
307327
private MetricAssert metricAssert() {
308328
List<MetricData> metrics = testing.getMetrics();
309329
assertThat(metrics).hasSize(1);

0 commit comments

Comments
 (0)