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.
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
}

View File

@ -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))

View File

@ -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)
}

View File

@ -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.
}

View File

@ -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())

View File

@ -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 {