promql: use labels.Builder to modify labels
This commit is contained in:
parent
c6cd998905
commit
9ea10d5265
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue