hanahmily commented on code in PR #1153:
URL: 
https://github.com/apache/skywalking-banyandb/pull/1153#discussion_r3353428273


##########
fodc/proxy/internal/api/server.go:
##########
@@ -354,31 +409,71 @@ func (s *Server) formatPrometheusText(aggregatedMetrics 
[]*metrics.AggregatedMet
                regularNames = append(regularNames, name)
        }
        sort.Strings(regularNames)
-
        for _, name := range regularNames {
-               group := regularMetrics[name]
-               if group.description != "" {
-                       builder.WriteString(fmt.Sprintf("# HELP %s %s\n", 
group.name, group.description))
+               grp := regularMetrics[name]
+               if grp.description != "" {
+                       builder.WriteString(fmt.Sprintf("# HELP %s %s\n", 
grp.name, grp.description))
+               }
+               builder.WriteString(fmt.Sprintf("# TYPE %s gauge\n", grp.name))
+               for _, m := range grp.metrics {
+                       builder.WriteString(formatMetricLine(m))
                }
-               builder.WriteString(fmt.Sprintf("# TYPE %s gauge\n", 
group.name))
+       }
+}
 
-               for _, metric := range group.metrics {
-                       labelParts := make([]string, 0, len(metric.Labels))
-                       for key, value := range metric.Labels {
-                               labelParts = append(labelParts, 
fmt.Sprintf(`%s="%s"`, key, value))
+// matchTypedFamily returns the typed family an untyped series belongs to, or 
nil if none.
+// It checks the exact name first (counter/gauge/untyped series and bare 
summary quantile
+// series), then the histogram/summary component base after trimming a trailing
+// _bucket/_sum/_count suffix. Used to fold pre-upgrade (untyped) samples into 
the
+// authoritative typed family so the proxy never emits two TYPE lines for one 
name.
+func matchTypedFamily(typedFamilies map[string]*metricGroup, name string) 
*metricGroup {
+       if grp, ok := typedFamilies[name]; ok {
+               return grp
+       }
+       for _, suffix := range []string{"_bucket", "_sum", "_count"} {
+               if strings.HasSuffix(name, suffix) {
+                       base := strings.TrimSuffix(name, suffix)
+                       if base != "" {
+                               if grp, ok := typedFamilies[base]; ok {
+                                       return grp
+                               }
                        }
-                       sort.Strings(labelParts)
+                       break
+               }
+       }
+       return nil
+}
 
-                       labelStr := ""
-                       if len(labelParts) > 0 {
-                               labelStr = "{" + strings.Join(labelParts, ",") 
+ "}"
+// typedFamilyBase returns the Prometheus family base name for a typed series.
+// For histogram/summary component series the trailing _bucket/_sum/_count is 
stripped.
+// For all other types (counter, gauge, untyped) the name is used as-is.
+func typedFamilyBase(name, metricType string) string {
+       switch metricType {
+       case "histogram", "summary":
+               for _, suffix := range []string{"_bucket", "_sum", "_count"} {
+                       if strings.HasSuffix(name, suffix) {
+                               base := strings.TrimSuffix(name, suffix)
+                               if base != "" {
+                                       return base
+                               }
                        }
-
-                       builder.WriteString(fmt.Sprintf("%s%s %s\n", 
group.name, labelStr, formatFloat(metric.Value)))
                }
        }
+       return name
+}
 
-       return builder.String()
+// formatMetricLine renders a single metric sample as a Prometheus text line.
+func formatMetricLine(m *metrics.AggregatedMetric) string {
+       labelParts := make([]string, 0, len(m.Labels))
+       for k, v := range m.Labels {
+               labelParts = append(labelParts, fmt.Sprintf(`%s="%s"`, k, v))
+       }
+       sort.Strings(labelParts)
+       labelStr := ""
+       if len(labelParts) > 0 {
+               labelStr = "{" + strings.Join(labelParts, ",") + "}"
+       }
+       return fmt.Sprintf("%s%s %s\n", m.Name, labelStr, formatFloat(m.Value))
 }

Review Comment:
   Good catch — fixed in 0773ab81. `formatMetricLine` now escapes label values 
via a `strings.NewReplacer` (`\` → `\\`, `"` → `\"`, newline → `\n`) in a 
single pass, and `TestFormatPrometheusText_EscapesLabelValues` covers a value 
containing all three special characters.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to