Normalise 'le' and 'quantile' labels in queries
Signed-off-by: Ganesh Vernekar <ganesh.vernekar@reddit.com>
This commit is contained in:
parent
11c49151b7
commit
8815b789b8
|
@ -253,6 +253,9 @@ func (c *flagConfig) setFeatureListOptions(logger *slog.Logger) error {
|
|||
case "promql-duration-expr":
|
||||
parser.ExperimentalDurationExpr = true
|
||||
logger.Info("Experimental duration expression parsing enabled.")
|
||||
case "promql-normalise-le-quantile-labels":
|
||||
parser.NormaliseLeAndQuantileMatchers = true
|
||||
logger.Info("Experimental normalising of 'le' and 'quantile' labels for PromQL enabled.")
|
||||
case "native-histograms":
|
||||
c.tsdb.EnableNativeHistograms = true
|
||||
c.scrape.EnableNativeHistogramsIngestion = true
|
||||
|
|
|
@ -773,7 +773,7 @@ func normalizeFloatsInLabelValues(t model.MetricType, l, v string) string {
|
|||
if (t == model.MetricTypeSummary && l == model.QuantileLabel) || (t == model.MetricTypeHistogram && l == model.BucketLabel) {
|
||||
f, err := strconv.ParseFloat(v, 64)
|
||||
if err == nil {
|
||||
return formatOpenMetricsFloat(f)
|
||||
return FormatOpenMetricsFloat(f)
|
||||
}
|
||||
}
|
||||
return v
|
||||
|
|
|
@ -34,7 +34,7 @@ import (
|
|||
"github.com/prometheus/prometheus/schema"
|
||||
)
|
||||
|
||||
// floatFormatBufPool is exclusively used in formatOpenMetricsFloat.
|
||||
// floatFormatBufPool is exclusively used in FormatOpenMetricsFloat.
|
||||
var floatFormatBufPool = sync.Pool{
|
||||
New: func() any {
|
||||
// To contain at most 17 digits and additional syntax for a float64.
|
||||
|
@ -632,7 +632,7 @@ func (p *ProtobufParser) getMagicLabel() (bool, string, string) {
|
|||
qq := p.dec.GetSummary().GetQuantile()
|
||||
q := qq[p.fieldPos]
|
||||
p.fieldsDone = p.fieldPos == len(qq)-1
|
||||
return true, model.QuantileLabel, formatOpenMetricsFloat(q.GetQuantile())
|
||||
return true, model.QuantileLabel, FormatOpenMetricsFloat(q.GetQuantile())
|
||||
case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM:
|
||||
bb := p.dec.GetHistogram().GetBucket()
|
||||
if p.fieldPos >= len(bb) {
|
||||
|
@ -641,15 +641,15 @@ func (p *ProtobufParser) getMagicLabel() (bool, string, string) {
|
|||
}
|
||||
b := bb[p.fieldPos]
|
||||
p.fieldsDone = math.IsInf(b.GetUpperBound(), +1)
|
||||
return true, model.BucketLabel, formatOpenMetricsFloat(b.GetUpperBound())
|
||||
return true, model.BucketLabel, FormatOpenMetricsFloat(b.GetUpperBound())
|
||||
}
|
||||
return false, "", ""
|
||||
}
|
||||
|
||||
// formatOpenMetricsFloat works like the usual Go string formatting of a float
|
||||
// FormatOpenMetricsFloat works like the usual Go string formatting of a float
|
||||
// but appends ".0" if the resulting number would otherwise contain neither a
|
||||
// "." nor an "e".
|
||||
func formatOpenMetricsFloat(f float64) string {
|
||||
func FormatOpenMetricsFloat(f float64) string {
|
||||
// A few common cases hardcoded.
|
||||
switch {
|
||||
case f == 1:
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
|
||||
"github.com/prometheus/prometheus/model/histogram"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/model/textparse"
|
||||
"github.com/prometheus/prometheus/model/timestamp"
|
||||
"github.com/prometheus/prometheus/promql/parser/posrange"
|
||||
"github.com/prometheus/prometheus/util/strutil"
|
||||
|
@ -42,6 +43,11 @@ var parserPool = sync.Pool{
|
|||
// ExperimentalDurationExpr is a flag to enable experimental duration expression parsing.
|
||||
var ExperimentalDurationExpr bool
|
||||
|
||||
// NormaliseLeAndQuantileMatchers is a flag to enable experimental conversion of matchers like
|
||||
// le="2" and quantile="2" to le=~"2|2.0" and quantile=~"2|2.0" to match the normalisation
|
||||
// of these labels during scrape time.
|
||||
var NormaliseLeAndQuantileMatchers bool
|
||||
|
||||
type Parser interface {
|
||||
ParseExpr() (Expr, error)
|
||||
Close()
|
||||
|
@ -839,6 +845,31 @@ func (p *parser) checkAST(node Node) (typ ValueType) {
|
|||
p.checkAST(n.VectorSelector)
|
||||
|
||||
case *VectorSelector:
|
||||
if NormaliseLeAndQuantileMatchers {
|
||||
for i, m := range n.LabelMatchers {
|
||||
// Rewrite the matchers of type le="2" and quantile="2".
|
||||
if m == nil || m.Type != labels.MatchEqual || (m.Name != model.QuantileLabel && m.Name != model.BucketLabel) {
|
||||
continue
|
||||
}
|
||||
f, err := strconv.ParseFloat(m.Value, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
omFloat := textparse.FormatOpenMetricsFloat(f)
|
||||
if omFloat == m.Value {
|
||||
// The label value is already in the OpenMetric format, example le="2.0".
|
||||
continue
|
||||
}
|
||||
// Example: changes the matcher from le="2" to le=~"2|2.0".
|
||||
mat, err := labels.NewMatcher(labels.MatchRegexp, m.Name, fmt.Sprintf("%s|%s", m.Value, omFloat))
|
||||
if err == nil {
|
||||
n.LabelMatchers[i] = mat
|
||||
} else {
|
||||
p.addParseErrf(n.PositionRange(), "rewrite of matcher failed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if n.Name != "" {
|
||||
// In this case the last LabelMatcher is checking for the metric name
|
||||
// set outside the braces. This checks if the name has already been set
|
||||
|
|
Loading…
Reference in New Issue