promql: use labels.Builder to modify labels

This commit is contained in:
Fabian Reinartz 2016-12-24 14:35:24 +01:00
parent c6cd998905
commit 9ea10d5265
6 changed files with 129 additions and 102 deletions

View File

@ -13,8 +13,9 @@ const sep = '\xff'
// Well-known label names used by Prometheus components. // Well-known label names used by Prometheus components.
const ( const (
MetricName = "__name__" MetricName = "__name__"
AlertName = "alertname" AlertName = "alertname"
BucketLabel = "le"
) )
// Label is a key/value pair of strings. // Label is a key/value pair of strings.
@ -148,6 +149,72 @@ func Compare(a, b Labels) int {
return len(a) - len(b) return len(a) - len(b)
} }
type LabelsBuilder struct { // LabelsBuilder allows modifiying Labels.
type Builder struct {
base Labels 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
} }

View File

@ -36,9 +36,6 @@ const (
maxInt64 = 9223372036854774784 maxInt64 = 9223372036854774784
// The smallest SampleValue that can be converted to an int64 without underflow. // The smallest SampleValue that can be converted to an int64 without underflow.
minInt64 = -9223372036854775808 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. // convertibleToInt64 returns true if v does not over-/underflow an int64.
@ -1021,7 +1018,7 @@ Outer:
continue Outer continue Outer
} }
} }
if l.Name == MetricNameLabel { if l.Name == labels.MetricName {
continue continue
} }
cm = append(cm, l) 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 // resultMetric returns the metric for the given sample(s) based on the Vector
// binary operation and the matching options. // binary operation and the matching options.
func resultMetric(lhs, rhs labels.Labels, op itemType, matching *VectorMatching) labels.Labels { func resultMetric(lhs, rhs labels.Labels, op itemType, matching *VectorMatching) labels.Labels {
// del and add hold modifications to the LHS input metric. lb := labels.NewBuilder(lhs)
// Deletions are applied first.
del := make([]string, 0, 16)
add := make([]labels.Label, 0, 16)
if shouldDropMetricName(op) { if shouldDropMetricName(op) {
del = append(del, MetricNameLabel) lb.Del(labels.MetricName)
} }
if matching.Card == CardOneToOne { if matching.Card == CardOneToOne {
@ -1077,40 +1071,22 @@ func resultMetric(lhs, rhs labels.Labels, op itemType, matching *VectorMatching)
continue Outer continue Outer
} }
} }
del = append(del, l.Name) lb.Del(l.Name)
} }
} else { } else {
del = append(del, matching.MatchingLabels...) lb.Del(matching.MatchingLabels...)
} }
} }
for _, ln := range matching.Include { 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. // Included labels from the `group_x` modifier are taken from the "one"-side.
if v := rhs.Get(ln); v != "" { 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) return lb.Labels()
}
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
} }
// VectorscalarBinop evaluates a binary operation between a Vector and a Scalar. // 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 { if keep {
lhsSample.V = value lhsSample.V = value
lhsSample.Metric = copyLabels(lhsSample.Metric, shouldDropMetricName(op)) if shouldDropMetricName(op) {
lhsSample.Metric = dropMetricName(lhsSample.Metric)
}
vec = append(vec, lhsSample) vec = append(vec, lhsSample)
} }
} }
return vec return vec
} }
func copyLabels(metric labels.Labels, withName bool) labels.Labels { func dropMetricName(l labels.Labels) labels.Labels {
if withName { return labels.NewBuilder(l).Del(labels.MetricName).Labels()
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
} }
// scalarBinop evaluates a binary operation between two Scalars. // 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 { for _, s := range vec {
var ( lb := labels.NewBuilder(s.Metric)
del []string
add []labels.Label if without || keepCommon {
) lb.Del(labels.MetricName)
if without {
del = append(grouping, MetricNameLabel)
} }
if op == itemCountValues { if op == itemCountValues {
del = append(del, valueLabel) lb.Set(valueLabel, fmt.Sprintf("%f", s.V)) // TODO(fabxc): use correct printing.
add = append(add, labels.Label{Name: valueLabel, Value: fmt.Sprintf("%f", s.V)})
} }
var ( var (
metric = modifiedLabels(s.Metric, del, add) metric = lb.Labels()
groupingKey = metric.Hash() groupingKey = metric.Hash()
) )
group, ok := result[groupingKey] group, ok := result[groupingKey]
// Add a new group if it doesn't exist. // Add a new group if it doesn't exist.
if !ok { if !ok {
var m labels.Labels var m labels.Labels
if keepCommon { if keepCommon || without {
m = copyLabels(metric, false)
} else if without {
m = metric m = metric
} else { } else {
m = make(labels.Labels, 0, len(grouping)) m = make(labels.Labels, 0, len(grouping))

View File

@ -117,7 +117,7 @@ func extrapolatedRate(ev *evaluator, arg Expr, isCounter bool, isRate bool) Valu
} }
resultVector = append(resultVector, Sample{ resultVector = append(resultVector, Sample{
Metric: copyLabels(samples.Metric, false), Metric: dropMetricName(samples.Metric),
Point: Point{V: resultValue, T: ev.Timestamp}, Point: Point{V: resultValue, T: ev.Timestamp},
}) })
} }
@ -180,7 +180,7 @@ func instantValue(ev *evaluator, arg Expr, isRate bool) Value {
} }
resultVector = append(resultVector, Sample{ resultVector = append(resultVector, Sample{
Metric: copyLabels(samples.Metric, false), Metric: dropMetricName(samples.Metric),
Point: Point{V: resultValue, T: ev.Timestamp}, Point: Point{V: resultValue, T: ev.Timestamp},
}) })
} }
@ -273,7 +273,7 @@ func funcHoltWinters(ev *evaluator, args Expressions) Value {
} }
resultVector = append(resultVector, Sample{ 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. 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]) vec := ev.evalVector(args[0])
max := ev.evalFloat(args[1]) max := ev.evalFloat(args[1])
for _, el := range vec { for _, el := range vec {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Min(max, float64(el.V)) el.V = math.Min(max, float64(el.V))
} }
return vec return vec
@ -315,7 +315,7 @@ func funcClampMin(ev *evaluator, args Expressions) Value {
vec := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
min := ev.evalFloat(args[1]) min := ev.evalFloat(args[1])
for _, el := range vec { for _, el := range vec {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Max(min, float64(el.V)) el.V = math.Max(min, float64(el.V))
} }
return vec return vec
@ -331,7 +331,7 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value {
for _, l := range vec[0].Metric { for _, l := range vec[0].Metric {
// TODO(julius): Should we also drop common metric names? // TODO(julius): Should we also drop common metric names?
if l.Name == MetricNameLabel { if l.Name == labels.MetricName {
continue continue
} }
common[l.Name] = l.Value common[l.Name] = l.Value
@ -357,7 +357,7 @@ func funcDropCommonLabels(ev *evaluator, args Expressions) Value {
} }
for _, el := range vec { for _, el := range vec {
el.Metric = modifiedLabels(el.Metric, cnames, nil) el.Metric = labels.NewBuilder(el.Metric).Del(cnames...).Labels()
} }
return vec return vec
} }
@ -375,7 +375,7 @@ func funcRound(ev *evaluator, args Expressions) Value {
vec := ev.evalVector(args[0]) vec := ev.evalVector(args[0])
for _, el := range vec { 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 el.V = math.Floor(float64(el.V)*toNearestInverse+0.5) / toNearestInverse
} }
return vec return vec
@ -414,7 +414,7 @@ func aggrOverTime(ev *evaluator, args Expressions, aggrFn func([]Point) float64)
} }
resultVector = append(resultVector, Sample{ resultVector = append(resultVector, Sample{
Metric: copyLabels(el.Metric, false), Metric: dropMetricName(el.Metric),
Point: Point{V: aggrFn(el.Points), T: ev.Timestamp}, 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 { func funcFloor(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Floor(float64(el.V)) el.V = math.Floor(float64(el.V))
} }
return Vector return Vector
@ -493,7 +493,7 @@ func funcQuantileOverTime(ev *evaluator, args Expressions) Value {
continue continue
} }
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
values := make(vectorByValueHeap, 0, len(el.Points)) values := make(vectorByValueHeap, 0, len(el.Points))
for _, v := range el.Points { for _, v := range el.Points {
values = append(values, Sample{Point: Point{V: v.V}}) 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 { func funcAbs(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Abs(float64(el.V)) el.V = math.Abs(float64(el.V))
} }
return Vector return Vector
@ -553,7 +553,7 @@ func funcAbsent(ev *evaluator, args Expressions) Value {
if vs, ok := args[0].(*VectorSelector); ok { if vs, ok := args[0].(*VectorSelector); ok {
for _, ma := range vs.LabelMatchers { 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}) 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 { func funcCeil(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Ceil(float64(el.V)) el.V = math.Ceil(float64(el.V))
} }
return Vector return Vector
@ -580,7 +580,7 @@ func funcCeil(ev *evaluator, args Expressions) Value {
func funcExp(ev *evaluator, args Expressions) Value { func funcExp(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Exp(float64(el.V)) el.V = math.Exp(float64(el.V))
} }
return Vector return Vector
@ -590,7 +590,7 @@ func funcExp(ev *evaluator, args Expressions) Value {
func funcSqrt(ev *evaluator, args Expressions) Value { func funcSqrt(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Sqrt(float64(el.V)) el.V = math.Sqrt(float64(el.V))
} }
return Vector return Vector
@ -600,7 +600,7 @@ func funcSqrt(ev *evaluator, args Expressions) Value {
func funcLn(ev *evaluator, args Expressions) Value { func funcLn(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Log(float64(el.V)) el.V = math.Log(float64(el.V))
} }
return Vector return Vector
@ -610,7 +610,7 @@ func funcLn(ev *evaluator, args Expressions) Value {
func funcLog2(ev *evaluator, args Expressions) Value { func funcLog2(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Log2(float64(el.V)) el.V = math.Log2(float64(el.V))
} }
return Vector return Vector
@ -620,7 +620,7 @@ func funcLog2(ev *evaluator, args Expressions) Value {
func funcLog10(ev *evaluator, args Expressions) Value { func funcLog10(ev *evaluator, args Expressions) Value {
Vector := ev.evalVector(args[0]) Vector := ev.evalVector(args[0])
for _, el := range Vector { for _, el := range Vector {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
el.V = math.Log10(float64(el.V)) el.V = math.Log10(float64(el.V))
} }
return Vector return Vector
@ -664,7 +664,7 @@ func funcDeriv(ev *evaluator, args Expressions) Value {
} }
slope, _ := linearRegression(samples.Points, 0) slope, _ := linearRegression(samples.Points, 0)
resultSample := Sample{ resultSample := Sample{
Metric: copyLabels(samples.Metric, false), Metric: dropMetricName(samples.Metric),
Point: Point{V: slope, T: ev.Timestamp}, 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) slope, intercept := linearRegression(samples.Points, ev.Timestamp)
resultVector = append(resultVector, Sample{ resultVector = append(resultVector, Sample{
Metric: copyLabels(samples.Metric, false), Metric: dropMetricName(samples.Metric),
Point: Point{V: slope*duration + intercept, T: ev.Timestamp}, Point: Point{V: slope*duration + intercept, T: ev.Timestamp},
}) })
} }
@ -715,10 +715,9 @@ func funcHistogramQuantile(ev *evaluator, args Expressions) Value {
mb, ok := signatureToMetricWithBuckets[hash] mb, ok := signatureToMetricWithBuckets[hash]
if !ok { if !ok {
el.Metric = modifiedLabels(el.Metric, []string{ el.Metric = labels.NewBuilder(el.Metric).
string(model.BucketLabel), Del(labels.BucketLabel, labels.MetricName).
MetricNameLabel, Labels()
}, nil)
mb = &metricWithBuckets{el.Metric, nil} mb = &metricWithBuckets{el.Metric, nil}
signatureToMetricWithBuckets[hash] = mb signatureToMetricWithBuckets[hash] = mb
@ -753,7 +752,7 @@ func funcResets(ev *evaluator, args Expressions) Value {
} }
out = append(out, Sample{ out = append(out, Sample{
Metric: copyLabels(samples.Metric, false), Metric: dropMetricName(samples.Metric),
Point: Point{V: float64(resets), T: ev.Timestamp}, Point: Point{V: float64(resets), T: ev.Timestamp},
}) })
} }
@ -777,7 +776,7 @@ func funcChanges(ev *evaluator, args Expressions) Value {
} }
out = append(out, Sample{ out = append(out, Sample{
Metric: copyLabels(samples.Metric, false), Metric: dropMetricName(samples.Metric),
Point: Point{V: float64(changes), T: ev.Timestamp}, Point: Point{V: float64(changes), T: ev.Timestamp},
}) })
} }
@ -811,12 +810,12 @@ func funcLabelReplace(ev *evaluator, args Expressions) Value {
continue continue
} }
res := regex.ExpandString([]byte{}, repl, srcVal, indexes) res := regex.ExpandString([]byte{}, repl, srcVal, indexes)
del := []string{dst}
add := []labels.Label{} lb := labels.NewBuilder(el.Metric).Del(dst)
if len(res) > 0 { 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() h := el.Metric.Hash()
if _, ok := outSet[h]; ok { 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]) v = ev.evalVector(args[0])
} }
for _, el := range v { for _, el := range v {
el.Metric = copyLabels(el.Metric, false) el.Metric = dropMetricName(el.Metric)
t := time.Unix(int64(el.V), 0).UTC() t := time.Unix(int64(el.V), 0).UTC()
el.V = f(t) el.V = f(t)
} }

View File

@ -911,7 +911,7 @@ func (p *parser) metric() labels.Labels {
m = p.labelSet() m = p.labelSet()
} }
if name != "" { if name != "" {
m = append(m, labels.Label{Name: MetricNameLabel, Value: name}) m = append(m, labels.Label{Name: labels.MetricName, Value: name})
sort.Sort(m) sort.Sort(m)
} }
return 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. // Metric name must not be set in the label matchers and before at the same time.
if name != "" { if name != "" {
for _, m := range matchers { 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) p.errorf("metric name must not be set twice: %q or %q", name, m.Value)
} }
} }
// Set name label matching. // Set name label matching.
m, err := NewLabelMatcher(MatchEqual, MetricNameLabel, name) m, err := NewLabelMatcher(MatchEqual, labels.MetricName, name)
if err != nil { if err != nil {
panic(err) // Must not happen with metric.Equal. panic(err) // Must not happen with metric.Equal.
} }

View File

@ -20,6 +20,7 @@ import (
"time" "time"
"github.com/prometheus/common/model" "github.com/prometheus/common/model"
"github.com/prometheus/prometheus/pkg/labels"
) )
// Tree returns a string of the tree structure of the given node. // 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) labelStrings := make([]string, 0, len(node.LabelMatchers)-1)
for _, matcher := range node.LabelMatchers { for _, matcher := range node.LabelMatchers {
// Only include the __name__ label if its no equality matching. // 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 continue
} }
labelStrings = append(labelStrings, matcher.String()) labelStrings = append(labelStrings, matcher.String())

View File

@ -17,7 +17,6 @@ import (
"math" "math"
"sort" "sort"
"github.com/prometheus/common/model"
"github.com/prometheus/prometheus/pkg/labels" "github.com/prometheus/prometheus/pkg/labels"
) )
@ -26,8 +25,8 @@ import (
// excludedLabels are the labels to exclude from signature calculation for // excludedLabels are the labels to exclude from signature calculation for
// quantiles. // quantiles.
var excludedLabels = []string{ var excludedLabels = []string{
model.MetricNameLabel, labels.MetricName,
model.BucketLabel, labels.BucketLabel,
} }
type bucket struct { type bucket struct {