From 46194cdac5ae548f1e171e448d545e425a0d9cc8 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Fri, 21 Aug 2026 18:38:02 +0000 Subject: [PATCH] fix: support exemplars with empty label sets in OpenMetrics 2.0 OpenMetrics 2.0 specification allows exemplars with an empty label set ({}). Previously, exemplars without labels were dropped. Signed-off-by: David Ashpole --- expfmt/openmetrics_2_0_create.go | 10 +++++++--- expfmt/openmetrics_2_0_create_test.go | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/expfmt/openmetrics_2_0_create.go b/expfmt/openmetrics_2_0_create.go index 9f35aced..96990cf8 100644 --- a/expfmt/openmetrics_2_0_create.go +++ b/expfmt/openmetrics_2_0_create.go @@ -260,7 +260,7 @@ func writeOpenMetrics20Sample(w enhancedWriter, name string, metric *dto.Metric, } } - if exemplar != nil && len(exemplar.Label) > 0 && exemplar.Timestamp != nil { + if exemplar != nil && exemplar.Timestamp != nil { n, err = writeExemplar20(w, exemplar) written += n if err != nil { @@ -279,7 +279,7 @@ func writeOpenMetrics20Sample(w enhancedWriter, name string, metric *dto.Metric, // writeExemplar20 writes the provided exemplar in OpenMetrics 2.0 format to w. // In OpenMetrics 2.0, exemplars without a timestamp are dropped. func writeExemplar20(w enhancedWriter, e *dto.Exemplar) (int, error) { - if e == nil || len(e.Label) == 0 || e.Timestamp == nil { + if e == nil || e.Timestamp == nil { return 0, nil } if err := validateExemplar20(e); err != nil { @@ -291,7 +291,11 @@ func writeExemplar20(w enhancedWriter, e *dto.Exemplar) (int, error) { if err != nil { return written, err } - n, err = writeOpenMetricsNameAndLabelPairs(w, "", e.Label, "", 0) + if len(e.Label) == 0 { + n, err = w.WriteString("{}") + } else { + n, err = writeOpenMetricsNameAndLabelPairs(w, "", e.Label, "", 0) + } written += n if err != nil { return written, err diff --git a/expfmt/openmetrics_2_0_create_test.go b/expfmt/openmetrics_2_0_create_test.go index 6de60358..c5ee03dd 100644 --- a/expfmt/openmetrics_2_0_create_test.go +++ b/expfmt/openmetrics_2_0_create_test.go @@ -161,6 +161,27 @@ node_memory_active_bytes 1.2345e+09 1234567890 }, out: `# TYPE http_requests_total counter http_requests_total 1027 1234567891 st@1234567890 # {trace_id="1234"} 1 1234567890.5 +`, + }, + { + name: "CounterWithExemplarWithoutLabels", + in: &dto.MetricFamily{ + Name: proto.String("http_requests_total"), + Type: dto.MetricType_COUNTER.Enum(), + Metric: []*dto.Metric{ + { + Counter: &dto.Counter{ + Value: proto.Float64(1027), + Exemplar: &dto.Exemplar{ + Value: proto.Float64(1), + Timestamp: ×tamppb.Timestamp{Seconds: 1234567890, Nanos: 500000000}, + }, + }, + }, + }, + }, + out: `# TYPE http_requests_total counter +http_requests_total 1027 # {} 1 1234567890.5 `, }, {