diff --git a/pkg/labels/labels.go b/pkg/labels/labels.go index 8025feb67..f29fb290a 100644 --- a/pkg/labels/labels.go +++ b/pkg/labels/labels.go @@ -13,8 +13,9 @@ const sep = '\xff' // Well-known label names used by Prometheus components. const ( - MetricName = "__name__" - AlertName = "alertname" + MetricName = "__name__" + AlertName = "alertname" + BucketLabel = "le" ) // Label is a key/value pair of strings. @@ -148,6 +149,72 @@ func Compare(a, b Labels) int { return len(a) - len(b) } -type LabelsBuilder struct { +// LabelsBuilder allows modifiying Labels. +type Builder struct { base Labels + del []string + add []Label +} + +// NewBuilder returns a new LabelsBuilder +func NewBuilder(base Labels) *Builder { + return &Builder{ + base: base, + del: make([]string, 0, 5), + add: make([]Label, 0, 5), + } +} + +// Del deletes the label of the given name. +func (b *Builder) Del(ns ...string) *Builder { + for _, n := range ns { + for i, a := range b.add { + if a.Name == n { + b.add = append(b.add[:i], b.add[i+1:]...) + } + } + b.del = append(b.del, n) + } + return b +} + +// Set the name/value pair as a label. +func (b *Builder) Set(n, v string) *Builder { + for i, a := range b.add { + if a.Name == n { + b.add[i].Value = v + return b + } + } + b.add = append(b.add, Label{Name: n, Value: v}) + + return b +} + +// Labels returns the labels from the builder. If no modifications +// were made, the originl labels are returned. +func (b *Builder) Labels() Labels { + if len(b.del) == 0 && len(b.add) == 0 { + return b.base + } + + res := make(Labels, 0, len(b.base)+len(b.add)-len(b.del)) +Outer: + for _, l := range b.base { + for _, n := range b.del { + if l.Name == n { + continue Outer + } + } + for _, la := range b.add { + if l.Name == la.Name { + continue Outer + } + } + res = append(res, l) + } + res = append(res, b.add...) + sort.Sort(res) + + return res } diff --git a/promql/engine.go b/promql/engine.go index 14bec5799..cc2739e3e 100644 --- a/promql/engine.go +++ b/promql/engine.go @@ -36,9 +36,6 @@ const ( maxInt64 = 9223372036854774784 // The smallest SampleValue that can be converted to an int64 without underflow. minInt64 = -9223372036854775808 - - // MetricNameLabel is the name of the label containing the metric name. - MetricNameLabel = "__name__" ) // convertibleToInt64 returns true if v does not over-/underflow an int64. @@ -1021,7 +1018,7 @@ Outer: continue Outer } } - if l.Name == MetricNameLabel { + if l.Name == labels.MetricName { continue } cm = append(cm, l) @@ -1059,13 +1056,10 @@ func signatureFunc(on bool, names ...string) func(labels.Labels) uint64 { // resultMetric returns the metric for the given sample(s) based on the Vector // binary operation and the matching options. func resultMetric(lhs, rhs labels.Labels, op itemType, matching *VectorMatching) labels.Labels { - // del and add hold modifications to the LHS input metric. - // Deletions are applied first. - del := make([]string, 0, 16) - add := make([]labels.Label, 0, 16) + lb := labels.NewBuilder(lhs) if shouldDropMetricName(op) { - del = append(del, MetricNameLabel) + lb.Del(labels.MetricName) } if matching.Card == CardOneToOne { @@ -1077,40 +1071,22 @@ func resultMetric(lhs, rhs labels.Labels, op itemType, matching *VectorMatching) continue Outer } } - del = append(del, l.Name) + lb.Del(l.Name) } } else { - del = append(del, matching.MatchingLabels...) + lb.Del(matching.MatchingLabels...) } } for _, ln := range matching.Include { - // We always want to delete the include label on the LHS - // before adding an included one or not. - del = append(del, ln) // Included labels from the `group_x` modifier are taken from the "one"-side. if v := rhs.Get(ln); v != "" { - add = append(add, labels.Label{Name: ln, Value: v}) + lb.Set(ln, v) + } else { + lb.Del(ln) } } - return modifiedLabels(lhs, del, add) -} - -func modifiedLabels(lhs labels.Labels, del []string, add []labels.Label) labels.Labels { - res := make(labels.Labels, 0, len(lhs)+len(add)-len(del)) -Outer: - for _, l := range lhs { - for _, n := range del { - if l.Name == n { - continue Outer - } - } - res = append(res, l) - } - res = append(res, add...) - sort.Sort(res) - - return res + return lb.Labels() } // VectorscalarBinop evaluates a binary operation between a Vector and a Scalar. @@ -1135,27 +1111,17 @@ func (ev *evaluator) VectorscalarBinop(op itemType, lhs Vector, rhs Scalar, swap } if keep { lhsSample.V = value - lhsSample.Metric = copyLabels(lhsSample.Metric, shouldDropMetricName(op)) - + if shouldDropMetricName(op) { + lhsSample.Metric = dropMetricName(lhsSample.Metric) + } vec = append(vec, lhsSample) } } return vec } -func copyLabels(metric labels.Labels, withName bool) labels.Labels { - if withName { - cm := make(labels.Labels, len(metric)) - copy(cm, metric) - return cm - } - cm := make(labels.Labels, 0, len(metric)-1) - for _, l := range metric { - if l.Name != MetricNameLabel { - cm = append(cm, l) - } - } - return cm +func dropMetricName(l labels.Labels) labels.Labels { + return labels.NewBuilder(l).Del(labels.MetricName).Labels() } // scalarBinop evaluates a binary operation between two Scalars. @@ -1268,29 +1234,24 @@ func (ev *evaluator) aggregation(op itemType, grouping []string, without bool, k } for _, s := range vec { - var ( - del []string - add []labels.Label - ) - if without { - del = append(grouping, MetricNameLabel) + lb := labels.NewBuilder(s.Metric) + + if without || keepCommon { + lb.Del(labels.MetricName) } if op == itemCountValues { - del = append(del, valueLabel) - add = append(add, labels.Label{Name: valueLabel, Value: fmt.Sprintf("%f", s.V)}) + lb.Set(valueLabel, fmt.Sprintf("%f", s.V)) // TODO(fabxc): use correct printing. } var ( - metric = modifiedLabels(s.Metric, del, add) + metric = lb.Labels() groupingKey = metric.Hash() ) group, ok := result[groupingKey] // Add a new group if it doesn't exist. if !ok { var m labels.Labels - if keepCommon { - m = copyLabels(metric, false) - } else if without { + if keepCommon || without { m = metric } else { m = make(labels.Labels, 0, len(grouping)) diff --git a/promql/functions.go b/promql/functions.go index 97d4a520c..0afcfa145 100644 --- a/promql/functions.go +++ b/promql/functions.go @@ -117,7 +117,7 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Valu } resultVector = append(resultVector, Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: resultValue, T: ev.Timestamp}, }) } @@ -180,7 +180,7 @@ func instantValue(ev *evaluator, arg Expr, isRate bool) Value { } resultVector = append(resultVector, Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: resultValue, T: ev.Timestamp}, }) } @@ -273,7 +273,7 @@ func funcHoltWinters(ev *evaluator, args Expressions) Value { } resultVector = append(resultVector, Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: s[len(s)-1], T: ev.Timestamp}, // The last value in the Vector is the smoothed result. }) } @@ -304,7 +304,7 @@ func funcClampMax(ev *evaluator, args Expressions) Value { vec := ev.evalVector(args[0]) max := ev.evalFloat(args[1]) for _, el := range vec { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Min(max, float64(el.V)) } return vec @@ -315,7 +315,7 @@ func funcClampMin(ev *evaluator, args Expressions) Value { vec := ev.evalVector(args[0]) min := ev.evalFloat(args[1]) for _, el := range vec { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Max(min, float64(el.V)) } return vec @@ -331,7 +331,7 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value { for _, l := range vec[0].Metric { // TODO(julius): Should we also drop common metric names? - if l.Name == MetricNameLabel { + if l.Name == labels.MetricName { continue } common[l.Name] = l.Value @@ -357,7 +357,7 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value { } for _, el := range vec { - el.Metric = modifiedLabels(el.Metric, cnames, nil) + el.Metric = labels.NewBuilder(el.Metric).Del(cnames...).Labels() } return vec } @@ -375,7 +375,7 @@ func funcRound(ev *evaluator, args Expressions) Value { vec := ev.evalVector(args[0]) for _, el := range vec { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Floor(float64(el.V)*toNearestInverse+0.5) / toNearestInverse } return vec @@ -414,7 +414,7 @@ func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]Point) float64) } resultVector = append(resultVector, Sample{ - Metric: copyLabels(el.Metric, false), + Metric: dropMetricName(el.Metric), Point: Point{V: aggrFn(el.Points), T: ev.Timestamp}, }) } @@ -443,7 +443,7 @@ func funcCountOverTime(ev *evaluator, args Expressions) Value { func funcFloor(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Floor(float64(el.V)) } return Vector @@ -493,7 +493,7 @@ func funcQuantileOverTime(ev *evaluator, args Expressions) Value { continue } - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) values := make(vectorByValueHeap, 0, len(el.Points)) for _, v := range el.Points { values = append(values, Sample{Point: Point{V: v.V}}) @@ -538,7 +538,7 @@ func funcStdvarOverTime(ev *evaluator, args Expressions) Value { func funcAbs(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Abs(float64(el.V)) } return Vector @@ -553,7 +553,7 @@ func funcAbsent(ev *evaluator, args Expressions) Value { if vs, ok := args[0].(*VectorSelector); ok { for _, ma := range vs.LabelMatchers { - if ma.Type == MatchEqual && ma.Name != MetricNameLabel { + if ma.Type == MatchEqual && ma.Name != labels.MetricName { m = append(m, labels.Label{Name: ma.Name, Value: ma.Value}) } } @@ -570,7 +570,7 @@ func funcAbsent(ev *evaluator, args Expressions) Value { func funcCeil(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Ceil(float64(el.V)) } return Vector @@ -580,7 +580,7 @@ func funcCeil(ev *evaluator, args Expressions) Value { func funcExp(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Exp(float64(el.V)) } return Vector @@ -590,7 +590,7 @@ func funcExp(ev *evaluator, args Expressions) Value { func funcSqrt(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Sqrt(float64(el.V)) } return Vector @@ -600,7 +600,7 @@ func funcSqrt(ev *evaluator, args Expressions) Value { func funcLn(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Log(float64(el.V)) } return Vector @@ -610,7 +610,7 @@ func funcLn(ev *evaluator, args Expressions) Value { func funcLog2(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Log2(float64(el.V)) } return Vector @@ -620,7 +620,7 @@ func funcLog2(ev *evaluator, args Expressions) Value { func funcLog10(ev *evaluator, args Expressions) Value { Vector := ev.evalVector(args[0]) for _, el := range Vector { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) el.V = math.Log10(float64(el.V)) } return Vector @@ -664,7 +664,7 @@ func funcDeriv(ev *evaluator, args Expressions) Value { } slope, _ := linearRegression(samples.Points, 0) resultSample := Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: slope, T: ev.Timestamp}, } @@ -688,7 +688,7 @@ func funcPredictLinear(ev *evaluator, args Expressions) Value { slope, intercept := linearRegression(samples.Points, ev.Timestamp) resultVector = append(resultVector, Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: slope*duration + intercept, T: ev.Timestamp}, }) } @@ -715,10 +715,9 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) Value { mb, ok := signatureToMetricWithBuckets[hash] if !ok { - el.Metric = modifiedLabels(el.Metric, []string{ - string(model.BucketLabel), - MetricNameLabel, - }, nil) + el.Metric = labels.NewBuilder(el.Metric). + Del(labels.BucketLabel, labels.MetricName). + Labels() mb = &metricWithBuckets{el.Metric, nil} signatureToMetricWithBuckets[hash] = mb @@ -753,7 +752,7 @@ func funcResets(ev *evaluator, args Expressions) Value { } out = append(out, Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: float64(resets), T: ev.Timestamp}, }) } @@ -777,7 +776,7 @@ func funcChanges(ev *evaluator, args Expressions) Value { } out = append(out, Sample{ - Metric: copyLabels(samples.Metric, false), + Metric: dropMetricName(samples.Metric), Point: Point{V: float64(changes), T: ev.Timestamp}, }) } @@ -811,12 +810,12 @@ func funcLabelReplace(ev *evaluator, args Expressions) Value { continue } res := regex.ExpandString([]byte{}, repl, srcVal, indexes) - del := []string{dst} - add := []labels.Label{} + + lb := labels.NewBuilder(el.Metric).Del(dst) if len(res) > 0 { - add = append(add, labels.Label{Name: dst, Value: string(res)}) + lb.Set(dst, string(res)) } - el.Metric = modifiedLabels(el.Metric, del, add) + el.Metric = lb.Labels() h := el.Metric.Hash() if _, ok := outSet[h]; ok { @@ -853,7 +852,7 @@ func dateWrapper(ev *evaluator, args Expressions, f func(time.Time) float64) Val v = ev.evalVector(args[0]) } for _, el := range v { - el.Metric = copyLabels(el.Metric, false) + el.Metric = dropMetricName(el.Metric) t := time.Unix(int64(el.V), 0).UTC() el.V = f(t) } diff --git a/promql/parse.go b/promql/parse.go index 525efd2ec..039f2da5e 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -911,7 +911,7 @@ func (p *parser) metric() labels.Labels { m = p.labelSet() } if name != "" { - m = append(m, labels.Label{Name: MetricNameLabel, Value: name}) + m = append(m, labels.Label{Name: labels.MetricName, Value: name}) sort.Sort(m) } return m @@ -949,12 +949,12 @@ func (p *parser) VectorSelector(name string) *VectorSelector { // Metric name must not be set in the label matchers and before at the same time. if name != "" { for _, m := range matchers { - if m.Name == MetricNameLabel { + if m.Name == labels.MetricName { p.errorf("metric name must not be set twice: %q or %q", name, m.Value) } } // Set name label matching. - m, err := NewLabelMatcher(MatchEqual, MetricNameLabel, name) + m, err := NewLabelMatcher(MatchEqual, labels.MetricName, name) if err != nil { panic(err) // Must not happen with metric.Equal. } diff --git a/promql/printer.go b/promql/printer.go index 56fab7a9c..969043c95 100644 --- a/promql/printer.go +++ b/promql/printer.go @@ -20,6 +20,7 @@ import ( "time" "github.com/prometheus/common/model" + "github.com/prometheus/prometheus/pkg/labels" ) // Tree returns a string of the tree structure of the given node. @@ -216,7 +217,7 @@ func (node *VectorSelector) String() string { labelStrings := make([]string, 0, len(node.LabelMatchers)-1) for _, matcher := range node.LabelMatchers { // Only include the __name__ label if its no equality matching. - if matcher.Name == MetricNameLabel && matcher.Type == MatchEqual { + if matcher.Name == labels.MetricName && matcher.Type == MatchEqual { continue } labelStrings = append(labelStrings, matcher.String()) diff --git a/promql/quantile.go b/promql/quantile.go index c7c905460..91f41566d 100644 --- a/promql/quantile.go +++ b/promql/quantile.go @@ -17,7 +17,6 @@ import ( "math" "sort" - "github.com/prometheus/common/model" "github.com/prometheus/prometheus/pkg/labels" ) @@ -26,8 +25,8 @@ import ( // excludedLabels are the labels to exclude from signature calculation for // quantiles. var excludedLabels = []string{ - model.MetricNameLabel, - model.BucketLabel, + labels.MetricName, + labels.BucketLabel, } type bucket struct {