This is an automated email from the ASF dual-hosted git repository.

HTHou pushed a commit to branch codex/prometheus
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/codex/prometheus by this push:
     new d38c50e90d9 Fix metric scrape parser for query labels
d38c50e90d9 is described below

commit d38c50e90d988a27a3aac70a7b50734406e14765
Author: HTHou <[email protected]>
AuthorDate: Fri May 15 11:17:31 2026 +0800

    Fix metric scrape parser for query labels
---
 .../iotdb/metricscrape/PrometheusTextParser.java   | 52 +++++++++++++++++++++-
 .../metricscrape/PrometheusTextParserTest.java     | 22 +++++++++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git 
a/external-service-impl/metric-scrape/src/main/java/org/apache/iotdb/metricscrape/PrometheusTextParser.java
 
b/external-service-impl/metric-scrape/src/main/java/org/apache/iotdb/metricscrape/PrometheusTextParser.java
index 8b553a7ff25..334f07a3c7c 100644
--- 
a/external-service-impl/metric-scrape/src/main/java/org/apache/iotdb/metricscrape/PrometheusTextParser.java
+++ 
b/external-service-impl/metric-scrape/src/main/java/org/apache/iotdb/metricscrape/PrometheusTextParser.java
@@ -217,7 +217,11 @@ public class PrometheusTextParser {
       while (position < line.length()) {
         char c = line.charAt(position++);
         if (c == '"') {
-          return builder.toString();
+          if (isLabelValueTerminator()) {
+            return builder.toString();
+          }
+          builder.append(c);
+          continue;
         }
         if (c == '\\') {
           if (position >= line.length()) {
@@ -244,6 +248,45 @@ public class PrometheusTextParser {
       throw new IllegalArgumentException("Unclosed Prometheus label value at 
line " + lineNumber);
     }
 
+    private boolean isLabelValueTerminator() {
+      int next = skipWhitespace(position);
+      if (next >= line.length()) {
+        return true;
+      }
+      if (line.charAt(next) == '}') {
+        return next + 1 >= line.length() || 
Character.isWhitespace(line.charAt(next + 1));
+      }
+      if (line.charAt(next) != ',') {
+        return false;
+      }
+      int afterComma = skipWhitespace(next + 1);
+      return afterComma >= line.length()
+          || line.charAt(afterComma) == '}'
+          || isLabelAssignmentStart(afterComma);
+    }
+
+    private boolean isLabelAssignmentStart(int start) {
+      if (start >= line.length() || line.charAt(start) == '"') {
+        return false;
+      }
+      int position = start;
+      while (position < line.length()) {
+        char c = line.charAt(position);
+        if (c == '=') {
+          String labelName = line.substring(start, position).trim();
+          int valueStart = skipWhitespace(position + 1);
+          return !labelName.isEmpty()
+              && valueStart < line.length()
+              && line.charAt(valueStart) == '"';
+        }
+        if (c == ',' || c == '}') {
+          return false;
+        }
+        position++;
+      }
+      return false;
+    }
+
     private double parseValue() {
       String token = parseToken("sample value");
       return parsePrometheusDouble(token, lineNumber);
@@ -279,6 +322,13 @@ public class PrometheusTextParser {
       }
     }
 
+    private int skipWhitespace(int position) {
+      while (position < line.length() && 
Character.isWhitespace(line.charAt(position))) {
+        position++;
+      }
+      return position;
+    }
+
     private void expect(char expected) {
       if (position >= line.length() || line.charAt(position) != expected) {
         throw new IllegalArgumentException(
diff --git 
a/external-service-impl/metric-scrape/src/test/java/org/apache/iotdb/metricscrape/PrometheusTextParserTest.java
 
b/external-service-impl/metric-scrape/src/test/java/org/apache/iotdb/metricscrape/PrometheusTextParserTest.java
index 237d2d6495d..9a114584956 100644
--- 
a/external-service-impl/metric-scrape/src/test/java/org/apache/iotdb/metricscrape/PrometheusTextParserTest.java
+++ 
b/external-service-impl/metric-scrape/src/test/java/org/apache/iotdb/metricscrape/PrometheusTextParserTest.java
@@ -105,6 +105,28 @@ public class PrometheusTextParserTest {
     assertEquals("request_duration_seconds_count", 
samples.get(1).getMetricName());
   }
 
+  @Test
+  public void testParseQueryLabelValueWithUnescapedQuote() {
+    String text =
+        "# HELP performance_overview_seconds\n"
+            + "# TYPE performance_overview_seconds summary\n"
+            + 
"performance_overview_seconds{interface=\"executeQueryStatement\","
+            + "type=\"Query{selectItems=[\"s1\", \"s2\"], 
from=Optional[Table{d1}]}\","
+            + "quantile=\"0.5\",} 1\n";
+
+    List<PrometheusSample> samples = new PrometheusTextParser().parse(text, 
100);
+
+    assertEquals(1, samples.size());
+    assertEquals("performance_overview_seconds", 
samples.get(0).getMetricFamilyName());
+    assertEquals("performance_overview_seconds", 
samples.get(0).getMetricName());
+    assertEquals("executeQueryStatement", 
samples.get(0).getLabels().get("interface"));
+    assertEquals(
+        "Query{selectItems=[\"s1\", \"s2\"], from=Optional[Table{d1}]}",
+        samples.get(0).getLabels().get("type"));
+    assertEquals("0.5", samples.get(0).getLabels().get("quantile"));
+    assertEquals(1.0, samples.get(0).getValue(), 0.0);
+  }
+
   @Test(expected = IllegalArgumentException.class)
   public void testRejectDuplicateLabel() {
     new PrometheusTextParser().parse("up{job=\"a\",job=\"b\"} 1", 100);

Reply via email to