Do not allocate memory when there's no constraints (#1296)
Signed-off-by: Quentin Devos <4972091+Okhoshi@users.noreply.github.com>
This commit is contained in:
parent
553eb4c7a8
commit
644c80d136
|
@ -18,18 +18,94 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkCounterWithLabelValues(b *testing.B) {
|
func BenchmarkCounter(b *testing.B) {
|
||||||
m := NewCounterVec(
|
type fns []func(*CounterVec) Counter
|
||||||
CounterOpts{
|
|
||||||
Name: "benchmark_counter",
|
twoConstraint := func(_ string) string {
|
||||||
Help: "A counter to benchmark it.",
|
return "two"
|
||||||
},
|
}
|
||||||
[]string{"one", "two", "three"},
|
|
||||||
)
|
deLV := func(m *CounterVec) Counter {
|
||||||
b.ReportAllocs()
|
return m.WithLabelValues("eins", "zwei", "drei")
|
||||||
b.ResetTimer()
|
}
|
||||||
for i := 0; i < b.N; i++ {
|
frLV := func(m *CounterVec) Counter {
|
||||||
m.WithLabelValues("eins", "zwei", "drei").Inc()
|
return m.WithLabelValues("une", "deux", "trois")
|
||||||
|
}
|
||||||
|
nlLV := func(m *CounterVec) Counter {
|
||||||
|
return m.WithLabelValues("een", "twee", "drie")
|
||||||
|
}
|
||||||
|
|
||||||
|
deML := func(m *CounterVec) Counter {
|
||||||
|
return m.With(Labels{"two": "zwei", "one": "eins", "three": "drei"})
|
||||||
|
}
|
||||||
|
frML := func(m *CounterVec) Counter {
|
||||||
|
return m.With(Labels{"two": "deux", "one": "une", "three": "trois"})
|
||||||
|
}
|
||||||
|
nlML := func(m *CounterVec) Counter {
|
||||||
|
return m.With(Labels{"two": "twee", "one": "een", "three": "drie"})
|
||||||
|
}
|
||||||
|
|
||||||
|
deLabels := Labels{"two": "zwei", "one": "eins", "three": "drei"}
|
||||||
|
dePML := func(m *CounterVec) Counter {
|
||||||
|
return m.With(deLabels)
|
||||||
|
}
|
||||||
|
frLabels := Labels{"two": "deux", "one": "une", "three": "trois"}
|
||||||
|
frPML := func(m *CounterVec) Counter {
|
||||||
|
return m.With(frLabels)
|
||||||
|
}
|
||||||
|
nlLabels := Labels{"two": "twee", "one": "een", "three": "drie"}
|
||||||
|
nlPML := func(m *CounterVec) Counter {
|
||||||
|
return m.With(nlLabels)
|
||||||
|
}
|
||||||
|
|
||||||
|
table := []struct {
|
||||||
|
name string
|
||||||
|
constraint LabelConstraint
|
||||||
|
counters fns
|
||||||
|
}{
|
||||||
|
{"With Label Values", nil, fns{deLV}},
|
||||||
|
{"With Label Values and Constraint", twoConstraint, fns{deLV}},
|
||||||
|
{"With triple Label Values", nil, fns{deLV, frLV, nlLV}},
|
||||||
|
{"With triple Label Values and Constraint", twoConstraint, fns{deLV, frLV, nlLV}},
|
||||||
|
{"With repeated Label Values", nil, fns{deLV, deLV}},
|
||||||
|
{"With repeated Label Values and Constraint", twoConstraint, fns{deLV, deLV}},
|
||||||
|
{"With Mapped Labels", nil, fns{deML}},
|
||||||
|
{"With Mapped Labels and Constraint", twoConstraint, fns{deML}},
|
||||||
|
{"With triple Mapped Labels", nil, fns{deML, frML, nlML}},
|
||||||
|
{"With triple Mapped Labels and Constraint", twoConstraint, fns{deML, frML, nlML}},
|
||||||
|
{"With repeated Mapped Labels", nil, fns{deML, deML}},
|
||||||
|
{"With repeated Mapped Labels and Constraint", twoConstraint, fns{deML, deML}},
|
||||||
|
{"With Prepared Mapped Labels", nil, fns{dePML}},
|
||||||
|
{"With Prepared Mapped Labels and Constraint", twoConstraint, fns{dePML}},
|
||||||
|
{"With triple Prepared Mapped Labels", nil, fns{dePML, frPML, nlPML}},
|
||||||
|
{"With triple Prepared Mapped Labels and Constraint", twoConstraint, fns{dePML, frPML, nlPML}},
|
||||||
|
{"With repeated Prepared Mapped Labels", nil, fns{dePML, dePML}},
|
||||||
|
{"With repeated Prepared Mapped Labels and Constraint", twoConstraint, fns{dePML, dePML}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, t := range table {
|
||||||
|
b.Run(t.name, func(b *testing.B) {
|
||||||
|
m := V2.NewCounterVec(
|
||||||
|
CounterVecOpts{
|
||||||
|
CounterOpts: CounterOpts{
|
||||||
|
Name: "benchmark_counter",
|
||||||
|
Help: "A counter to benchmark it.",
|
||||||
|
},
|
||||||
|
VariableLabels: ConstrainedLabels{
|
||||||
|
ConstrainedLabel{Name: "one"},
|
||||||
|
ConstrainedLabel{Name: "two", Constraint: t.constraint},
|
||||||
|
ConstrainedLabel{Name: "three"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for _, fn := range t.counters {
|
||||||
|
fn(m).Inc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,37 +132,6 @@ func BenchmarkCounterWithLabelValuesConcurrent(b *testing.B) {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkCounterWithMappedLabels(b *testing.B) {
|
|
||||||
m := NewCounterVec(
|
|
||||||
CounterOpts{
|
|
||||||
Name: "benchmark_counter",
|
|
||||||
Help: "A counter to benchmark it.",
|
|
||||||
},
|
|
||||||
[]string{"one", "two", "three"},
|
|
||||||
)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
m.With(Labels{"two": "zwei", "one": "eins", "three": "drei"}).Inc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCounterWithPreparedMappedLabels(b *testing.B) {
|
|
||||||
m := NewCounterVec(
|
|
||||||
CounterOpts{
|
|
||||||
Name: "benchmark_counter",
|
|
||||||
Help: "A counter to benchmark it.",
|
|
||||||
},
|
|
||||||
[]string{"one", "two", "three"},
|
|
||||||
)
|
|
||||||
b.ReportAllocs()
|
|
||||||
b.ResetTimer()
|
|
||||||
labels := Labels{"two": "zwei", "one": "eins", "three": "drei"}
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
m.With(labels).Inc()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkCounterNoLabels(b *testing.B) {
|
func BenchmarkCounterNoLabels(b *testing.B) {
|
||||||
m := NewCounter(CounterOpts{
|
m := NewCounter(CounterOpts{
|
||||||
Name: "benchmark_counter",
|
Name: "benchmark_counter",
|
||||||
|
|
|
@ -202,8 +202,8 @@ func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
|
||||||
)
|
)
|
||||||
return &CounterVec{
|
return &CounterVec{
|
||||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||||
if len(lvs) != len(desc.variableLabels) {
|
if len(lvs) != len(desc.variableLabels.names) {
|
||||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||||
}
|
}
|
||||||
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
|
||||||
result.init(result) // Init self-collection.
|
result.init(result) // Init self-collection.
|
||||||
|
|
|
@ -52,7 +52,7 @@ type Desc struct {
|
||||||
constLabelPairs []*dto.LabelPair
|
constLabelPairs []*dto.LabelPair
|
||||||
// variableLabels contains names of labels and normalization function for
|
// variableLabels contains names of labels and normalization function for
|
||||||
// which the metric maintains variable values.
|
// which the metric maintains variable values.
|
||||||
variableLabels ConstrainedLabels
|
variableLabels *compiledLabels
|
||||||
// id is a hash of the values of the ConstLabels and fqName. This
|
// id is a hash of the values of the ConstLabels and fqName. This
|
||||||
// must be unique among all registered descriptors and can therefore be
|
// must be unique among all registered descriptors and can therefore be
|
||||||
// used as an identifier of the descriptor.
|
// used as an identifier of the descriptor.
|
||||||
|
@ -93,7 +93,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||||
d := &Desc{
|
d := &Desc{
|
||||||
fqName: fqName,
|
fqName: fqName,
|
||||||
help: help,
|
help: help,
|
||||||
variableLabels: variableLabels.constrainedLabels(),
|
variableLabels: variableLabels.compile(),
|
||||||
}
|
}
|
||||||
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
if !model.IsValidMetricName(model.LabelValue(fqName)) {
|
||||||
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
|
||||||
|
@ -103,7 +103,7 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||||
// their sorted label names) plus the fqName (at position 0).
|
// their sorted label names) plus the fqName (at position 0).
|
||||||
labelValues := make([]string, 1, len(constLabels)+1)
|
labelValues := make([]string, 1, len(constLabels)+1)
|
||||||
labelValues[0] = fqName
|
labelValues[0] = fqName
|
||||||
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels))
|
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels.names))
|
||||||
labelNameSet := map[string]struct{}{}
|
labelNameSet := map[string]struct{}{}
|
||||||
// First add only the const label names and sort them...
|
// First add only the const label names and sort them...
|
||||||
for labelName := range constLabels {
|
for labelName := range constLabels {
|
||||||
|
@ -128,13 +128,13 @@ func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, const
|
||||||
// Now add the variable label names, but prefix them with something that
|
// Now add the variable label names, but prefix them with something that
|
||||||
// cannot be in a regular label name. That prevents matching the label
|
// cannot be in a regular label name. That prevents matching the label
|
||||||
// dimension with a different mix between preset and variable labels.
|
// dimension with a different mix between preset and variable labels.
|
||||||
for _, label := range d.variableLabels {
|
for _, label := range d.variableLabels.names {
|
||||||
if !checkLabelName(label.Name) {
|
if !checkLabelName(label) {
|
||||||
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label.Name, fqName)
|
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label, fqName)
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
labelNames = append(labelNames, "$"+label.Name)
|
labelNames = append(labelNames, "$"+label)
|
||||||
labelNameSet[label.Name] = struct{}{}
|
labelNameSet[label] = struct{}{}
|
||||||
}
|
}
|
||||||
if len(labelNames) != len(labelNameSet) {
|
if len(labelNames) != len(labelNameSet) {
|
||||||
d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName)
|
d.err = fmt.Errorf("duplicate label names in constant and variable labels for metric %q", fqName)
|
||||||
|
@ -189,11 +189,19 @@ func (d *Desc) String() string {
|
||||||
fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
|
fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
vlStrings := make([]string, 0, len(d.variableLabels.names))
|
||||||
|
for _, vl := range d.variableLabels.names {
|
||||||
|
if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil {
|
||||||
|
vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl))
|
||||||
|
} else {
|
||||||
|
vlStrings = append(vlStrings, vl)
|
||||||
|
}
|
||||||
|
}
|
||||||
return fmt.Sprintf(
|
return fmt.Sprintf(
|
||||||
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: %v}",
|
"Desc{fqName: %q, help: %q, constLabels: {%s}, variableLabels: {%s}}",
|
||||||
d.fqName,
|
d.fqName,
|
||||||
d.help,
|
d.help,
|
||||||
strings.Join(lpStrings, ","),
|
strings.Join(lpStrings, ","),
|
||||||
d.variableLabels,
|
strings.Join(vlStrings, ","),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,9 +292,9 @@ func ExampleRegister() {
|
||||||
|
|
||||||
// Output:
|
// Output:
|
||||||
// taskCounter registered.
|
// taskCounter registered.
|
||||||
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [{worker_id <nil>}]} has different label names or a different help string
|
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: {worker_id}} has different label names or a different help string
|
||||||
// taskCounter unregistered.
|
// taskCounter unregistered.
|
||||||
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: [{worker_id <nil>}]} has different label names or a different help string
|
// taskCounterVec not registered: a previously registered descriptor with the same fully-qualified name as Desc{fqName: "worker_pool_completed_tasks_total", help: "Total number of tasks completed.", constLabels: {}, variableLabels: {worker_id}} has different label names or a different help string
|
||||||
// taskCounterVec registered.
|
// taskCounterVec registered.
|
||||||
// Worker initialization failed: inconsistent label cardinality: expected 1 label values but got 2 in []string{"42", "spurious arg"}
|
// Worker initialization failed: inconsistent label cardinality: expected 1 label values but got 2 in []string{"42", "spurious arg"}
|
||||||
// notMyCounter is nil.
|
// notMyCounter is nil.
|
||||||
|
|
|
@ -48,7 +48,7 @@ func (e *expvarCollector) Collect(ch chan<- Metric) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var v interface{}
|
var v interface{}
|
||||||
labels := make([]string, len(desc.variableLabels))
|
labels := make([]string, len(desc.variableLabels.names))
|
||||||
if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
|
if err := json.Unmarshal([]byte(expVar.String()), &v); err != nil {
|
||||||
ch <- NewInvalidMetric(desc, err)
|
ch <- NewInvalidMetric(desc, err)
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -166,8 +166,8 @@ func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec {
|
||||||
)
|
)
|
||||||
return &GaugeVec{
|
return &GaugeVec{
|
||||||
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
|
||||||
if len(lvs) != len(desc.variableLabels) {
|
if len(lvs) != len(desc.variableLabels.names) {
|
||||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, lvs))
|
||||||
}
|
}
|
||||||
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
|
||||||
result.init(result) // Init self-collection.
|
result.init(result) // Init self-collection.
|
||||||
|
|
|
@ -170,7 +170,7 @@ func TestGaugeFunc(t *testing.T) {
|
||||||
func() float64 { return 3.1415 },
|
func() float64 { return 3.1415 },
|
||||||
)
|
)
|
||||||
|
|
||||||
if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: []}`, gf.Desc().String(); expected != got {
|
if expected, got := `Desc{fqName: "test_name", help: "test help", constLabels: {a="1",b="2"}, variableLabels: {}}`, gf.Desc().String(); expected != got {
|
||||||
t.Errorf("expected %q, got %q", expected, got)
|
t.Errorf("expected %q, got %q", expected, got)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -499,12 +499,12 @@ func NewHistogram(opts HistogramOpts) Histogram {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if len(desc.variableLabels.names) != len(labelValues) {
|
||||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range desc.variableLabels {
|
for _, n := range desc.variableLabels.names {
|
||||||
if n.Name == bucketLabel {
|
if n == bucketLabel {
|
||||||
panic(errBucketLabelNotAllowed)
|
panic(errBucketLabelNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1230,7 +1230,7 @@ func NewConstHistogram(
|
||||||
if desc.err != nil {
|
if desc.err != nil {
|
||||||
return nil, desc.err
|
return nil, desc.err
|
||||||
}
|
}
|
||||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &constHistogram{
|
return &constHistogram{
|
||||||
|
|
|
@ -32,19 +32,15 @@ import (
|
||||||
// create a Desc.
|
// create a Desc.
|
||||||
type Labels map[string]string
|
type Labels map[string]string
|
||||||
|
|
||||||
|
// LabelConstraint normalizes label values.
|
||||||
|
type LabelConstraint func(string) string
|
||||||
|
|
||||||
// ConstrainedLabels represents a label name and its constrain function
|
// ConstrainedLabels represents a label name and its constrain function
|
||||||
// to normalize label values. This type is commonly used when constructing
|
// to normalize label values. This type is commonly used when constructing
|
||||||
// metric vector Collectors.
|
// metric vector Collectors.
|
||||||
type ConstrainedLabel struct {
|
type ConstrainedLabel struct {
|
||||||
Name string
|
Name string
|
||||||
Constraint func(string) string
|
Constraint LabelConstraint
|
||||||
}
|
|
||||||
|
|
||||||
func (cl ConstrainedLabel) Constrain(v string) string {
|
|
||||||
if cl.Constraint == nil {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return cl.Constraint(v)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConstrainableLabels is an interface that allows creating of labels that can
|
// ConstrainableLabels is an interface that allows creating of labels that can
|
||||||
|
@ -58,7 +54,7 @@ func (cl ConstrainedLabel) Constrain(v string) string {
|
||||||
// },
|
// },
|
||||||
// })
|
// })
|
||||||
type ConstrainableLabels interface {
|
type ConstrainableLabels interface {
|
||||||
constrainedLabels() ConstrainedLabels
|
compile() *compiledLabels
|
||||||
labelNames() []string
|
labelNames() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,8 +63,20 @@ type ConstrainableLabels interface {
|
||||||
// metric vector Collectors.
|
// metric vector Collectors.
|
||||||
type ConstrainedLabels []ConstrainedLabel
|
type ConstrainedLabels []ConstrainedLabel
|
||||||
|
|
||||||
func (cls ConstrainedLabels) constrainedLabels() ConstrainedLabels {
|
func (cls ConstrainedLabels) compile() *compiledLabels {
|
||||||
return cls
|
compiled := &compiledLabels{
|
||||||
|
names: make([]string, len(cls)),
|
||||||
|
labelConstraints: map[string]LabelConstraint{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, label := range cls {
|
||||||
|
compiled.names[i] = label.Name
|
||||||
|
if label.Constraint != nil {
|
||||||
|
compiled.labelConstraints[label.Name] = label.Constraint
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return compiled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cls ConstrainedLabels) labelNames() []string {
|
func (cls ConstrainedLabels) labelNames() []string {
|
||||||
|
@ -92,18 +100,36 @@ func (cls ConstrainedLabels) labelNames() []string {
|
||||||
// }
|
// }
|
||||||
type UnconstrainedLabels []string
|
type UnconstrainedLabels []string
|
||||||
|
|
||||||
func (uls UnconstrainedLabels) constrainedLabels() ConstrainedLabels {
|
func (uls UnconstrainedLabels) compile() *compiledLabels {
|
||||||
constrainedLabels := make([]ConstrainedLabel, len(uls))
|
return &compiledLabels{
|
||||||
for i, l := range uls {
|
names: uls,
|
||||||
constrainedLabels[i] = ConstrainedLabel{Name: l}
|
|
||||||
}
|
}
|
||||||
return constrainedLabels
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uls UnconstrainedLabels) labelNames() []string {
|
func (uls UnconstrainedLabels) labelNames() []string {
|
||||||
return uls
|
return uls
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type compiledLabels struct {
|
||||||
|
names []string
|
||||||
|
labelConstraints map[string]LabelConstraint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cls *compiledLabels) compile() *compiledLabels {
|
||||||
|
return cls
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cls *compiledLabels) labelNames() []string {
|
||||||
|
return cls.names
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cls *compiledLabels) constrain(labelName, value string) string {
|
||||||
|
if fn, ok := cls.labelConstraints[labelName]; ok && fn != nil {
|
||||||
|
return fn(value)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
// reservedLabelPrefix is a prefix which is not legal in user-supplied
|
||||||
// label names.
|
// label names.
|
||||||
const reservedLabelPrefix = "__"
|
const reservedLabelPrefix = "__"
|
||||||
|
|
|
@ -128,11 +128,11 @@ func TestHandlerErrorHandling(t *testing.T) {
|
||||||
t.Fatalf("unexpected number of done invokes, want 0, got %d", got)
|
t.Fatalf("unexpected number of done invokes, want 0, got %d", got)
|
||||||
}
|
}
|
||||||
|
|
||||||
wantMsg := `error gathering metrics: error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: []}: collect error
|
wantMsg := `error gathering metrics: error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: {}}: collect error
|
||||||
`
|
`
|
||||||
wantErrorBody := `An error has occurred while serving metrics:
|
wantErrorBody := `An error has occurred while serving metrics:
|
||||||
|
|
||||||
error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: []}: collect error
|
error collecting metric Desc{fqName: "invalid_metric", help: "not helpful", constLabels: {}, variableLabels: {}}: collect error
|
||||||
`
|
`
|
||||||
wantOKBody1 := `# HELP name docstring
|
wantOKBody1 := `# HELP name docstring
|
||||||
# TYPE name counter
|
# TYPE name counter
|
||||||
|
|
|
@ -963,9 +963,9 @@ func checkDescConsistency(
|
||||||
// Is the desc consistent with the content of the metric?
|
// Is the desc consistent with the content of the metric?
|
||||||
lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
|
lpsFromDesc := make([]*dto.LabelPair, len(desc.constLabelPairs), len(dtoMetric.Label))
|
||||||
copy(lpsFromDesc, desc.constLabelPairs)
|
copy(lpsFromDesc, desc.constLabelPairs)
|
||||||
for _, l := range desc.variableLabels {
|
for _, l := range desc.variableLabels.names {
|
||||||
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
lpsFromDesc = append(lpsFromDesc, &dto.LabelPair{
|
||||||
Name: proto.String(l.Name),
|
Name: proto.String(l),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if len(lpsFromDesc) != len(dtoMetric.Label) {
|
if len(lpsFromDesc) != len(dtoMetric.Label) {
|
||||||
|
|
|
@ -188,12 +188,12 @@ func NewSummary(opts SummaryOpts) Summary {
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary {
|
||||||
if len(desc.variableLabels) != len(labelValues) {
|
if len(desc.variableLabels.names) != len(labelValues) {
|
||||||
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
|
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.names, labelValues))
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range desc.variableLabels {
|
for _, n := range desc.variableLabels.names {
|
||||||
if n.Name == quantileLabel {
|
if n == quantileLabel {
|
||||||
panic(errQuantileLabelNotAllowed)
|
panic(errQuantileLabelNotAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -737,7 +737,7 @@ func NewConstSummary(
|
||||||
if desc.err != nil {
|
if desc.err != nil {
|
||||||
return nil, desc.err
|
return nil, desc.err
|
||||||
}
|
}
|
||||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &constSummary{
|
return &constSummary{
|
||||||
|
|
|
@ -105,7 +105,7 @@ func NewConstMetric(desc *Desc, valueType ValueType, value float64, labelValues
|
||||||
if desc.err != nil {
|
if desc.err != nil {
|
||||||
return nil, desc.err
|
return nil, desc.err
|
||||||
}
|
}
|
||||||
if err := validateLabelValues(labelValues, len(desc.variableLabels)); err != nil {
|
if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,19 +176,19 @@ func populateMetric(
|
||||||
// This function is only needed for custom Metric implementations. See MetricVec
|
// This function is only needed for custom Metric implementations. See MetricVec
|
||||||
// example.
|
// example.
|
||||||
func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
func MakeLabelPairs(desc *Desc, labelValues []string) []*dto.LabelPair {
|
||||||
totalLen := len(desc.variableLabels) + len(desc.constLabelPairs)
|
totalLen := len(desc.variableLabels.names) + len(desc.constLabelPairs)
|
||||||
if totalLen == 0 {
|
if totalLen == 0 {
|
||||||
// Super fast path.
|
// Super fast path.
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if len(desc.variableLabels) == 0 {
|
if len(desc.variableLabels.names) == 0 {
|
||||||
// Moderately fast path.
|
// Moderately fast path.
|
||||||
return desc.constLabelPairs
|
return desc.constLabelPairs
|
||||||
}
|
}
|
||||||
labelPairs := make([]*dto.LabelPair, 0, totalLen)
|
labelPairs := make([]*dto.LabelPair, 0, totalLen)
|
||||||
for i, l := range desc.variableLabels {
|
for i, l := range desc.variableLabels.names {
|
||||||
labelPairs = append(labelPairs, &dto.LabelPair{
|
labelPairs = append(labelPairs, &dto.LabelPair{
|
||||||
Name: proto.String(l.Name),
|
Name: proto.String(l),
|
||||||
Value: proto.String(labelValues[i]),
|
Value: proto.String(labelValues[i]),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,24 +20,6 @@ import (
|
||||||
"github.com/prometheus/common/model"
|
"github.com/prometheus/common/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
var labelsPool = &sync.Pool{
|
|
||||||
New: func() interface{} {
|
|
||||||
return make(Labels)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLabelsFromPool() Labels {
|
|
||||||
return labelsPool.Get().(Labels)
|
|
||||||
}
|
|
||||||
|
|
||||||
func putLabelsToPool(labels Labels) {
|
|
||||||
for k := range labels {
|
|
||||||
delete(labels, k)
|
|
||||||
}
|
|
||||||
|
|
||||||
labelsPool.Put(labels)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MetricVec is a Collector to bundle metrics of the same name that differ in
|
// MetricVec is a Collector to bundle metrics of the same name that differ in
|
||||||
// their label values. MetricVec is not used directly but as a building block
|
// their label values. MetricVec is not used directly but as a building block
|
||||||
// for implementations of vectors of a given metric type, like GaugeVec,
|
// for implementations of vectors of a given metric type, like GaugeVec,
|
||||||
|
@ -91,6 +73,7 @@ func NewMetricVec(desc *Desc, newMetric func(lvs ...string) Metric) *MetricVec {
|
||||||
// See also the CounterVec example.
|
// See also the CounterVec example.
|
||||||
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||||
lvs = constrainLabelValues(m.desc, lvs, m.curry)
|
lvs = constrainLabelValues(m.desc, lvs, m.curry)
|
||||||
|
|
||||||
h, err := m.hashLabelValues(lvs)
|
h, err := m.hashLabelValues(lvs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
@ -110,8 +93,8 @@ func (m *MetricVec) DeleteLabelValues(lvs ...string) bool {
|
||||||
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
// This method is used for the same purpose as DeleteLabelValues(...string). See
|
||||||
// there for pros and cons of the two methods.
|
// there for pros and cons of the two methods.
|
||||||
func (m *MetricVec) Delete(labels Labels) bool {
|
func (m *MetricVec) Delete(labels Labels) bool {
|
||||||
labels = constrainLabels(m.desc, labels)
|
labels, closer := constrainLabels(m.desc, labels)
|
||||||
defer putLabelsToPool(labels)
|
defer closer()
|
||||||
|
|
||||||
h, err := m.hashLabels(labels)
|
h, err := m.hashLabels(labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -128,8 +111,8 @@ func (m *MetricVec) Delete(labels Labels) bool {
|
||||||
// Note that curried labels will never be matched if deleting from the curried vector.
|
// Note that curried labels will never be matched if deleting from the curried vector.
|
||||||
// To match curried labels with DeletePartialMatch, it must be called on the base vector.
|
// To match curried labels with DeletePartialMatch, it must be called on the base vector.
|
||||||
func (m *MetricVec) DeletePartialMatch(labels Labels) int {
|
func (m *MetricVec) DeletePartialMatch(labels Labels) int {
|
||||||
labels = constrainLabels(m.desc, labels)
|
labels, closer := constrainLabels(m.desc, labels)
|
||||||
defer putLabelsToPool(labels)
|
defer closer()
|
||||||
|
|
||||||
return m.metricMap.deleteByLabels(labels, m.curry)
|
return m.metricMap.deleteByLabels(labels, m.curry)
|
||||||
}
|
}
|
||||||
|
@ -169,11 +152,11 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||||
oldCurry = m.curry
|
oldCurry = m.curry
|
||||||
iCurry int
|
iCurry int
|
||||||
)
|
)
|
||||||
for i, label := range m.desc.variableLabels {
|
for i, labelName := range m.desc.variableLabels.names {
|
||||||
val, ok := labels[label.Name]
|
val, ok := labels[labelName]
|
||||||
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
|
if iCurry < len(oldCurry) && oldCurry[iCurry].index == i {
|
||||||
if ok {
|
if ok {
|
||||||
return nil, fmt.Errorf("label name %q is already curried", label.Name)
|
return nil, fmt.Errorf("label name %q is already curried", labelName)
|
||||||
}
|
}
|
||||||
newCurry = append(newCurry, oldCurry[iCurry])
|
newCurry = append(newCurry, oldCurry[iCurry])
|
||||||
iCurry++
|
iCurry++
|
||||||
|
@ -181,7 +164,10 @@ func (m *MetricVec) CurryWith(labels Labels) (*MetricVec, error) {
|
||||||
if !ok {
|
if !ok {
|
||||||
continue // Label stays uncurried.
|
continue // Label stays uncurried.
|
||||||
}
|
}
|
||||||
newCurry = append(newCurry, curriedLabelValue{i, label.Constrain(val)})
|
newCurry = append(newCurry, curriedLabelValue{
|
||||||
|
i,
|
||||||
|
m.desc.variableLabels.constrain(labelName, val),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
|
if l := len(oldCurry) + len(labels) - len(newCurry); l > 0 {
|
||||||
|
@ -250,8 +236,8 @@ func (m *MetricVec) GetMetricWithLabelValues(lvs ...string) (Metric, error) {
|
||||||
// around MetricVec, implementing a vector for a specific Metric implementation,
|
// around MetricVec, implementing a vector for a specific Metric implementation,
|
||||||
// for example GaugeVec.
|
// for example GaugeVec.
|
||||||
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||||
labels = constrainLabels(m.desc, labels)
|
labels, closer := constrainLabels(m.desc, labels)
|
||||||
defer putLabelsToPool(labels)
|
defer closer()
|
||||||
|
|
||||||
h, err := m.hashLabels(labels)
|
h, err := m.hashLabels(labels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -262,7 +248,7 @@ func (m *MetricVec) GetMetricWith(labels Labels) (Metric, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||||
if err := validateLabelValues(vals, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
if err := validateLabelValues(vals, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,7 +257,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||||
curry = m.curry
|
curry = m.curry
|
||||||
iVals, iCurry int
|
iVals, iCurry int
|
||||||
)
|
)
|
||||||
for i := 0; i < len(m.desc.variableLabels); i++ {
|
for i := 0; i < len(m.desc.variableLabels.names); i++ {
|
||||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
h = m.hashAdd(h, curry[iCurry].value)
|
h = m.hashAdd(h, curry[iCurry].value)
|
||||||
iCurry++
|
iCurry++
|
||||||
|
@ -285,7 +271,7 @@ func (m *MetricVec) hashLabelValues(vals []string) (uint64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||||
if err := validateValuesInLabels(labels, len(m.desc.variableLabels)-len(m.curry)); err != nil {
|
if err := validateValuesInLabels(labels, len(m.desc.variableLabels.names)-len(m.curry)); err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -294,17 +280,17 @@ func (m *MetricVec) hashLabels(labels Labels) (uint64, error) {
|
||||||
curry = m.curry
|
curry = m.curry
|
||||||
iCurry int
|
iCurry int
|
||||||
)
|
)
|
||||||
for i, label := range m.desc.variableLabels {
|
for i, labelName := range m.desc.variableLabels.names {
|
||||||
val, ok := labels[label.Name]
|
val, ok := labels[labelName]
|
||||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
if ok {
|
if ok {
|
||||||
return 0, fmt.Errorf("label name %q is already curried", label.Name)
|
return 0, fmt.Errorf("label name %q is already curried", labelName)
|
||||||
}
|
}
|
||||||
h = m.hashAdd(h, curry[iCurry].value)
|
h = m.hashAdd(h, curry[iCurry].value)
|
||||||
iCurry++
|
iCurry++
|
||||||
} else {
|
} else {
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("label name %q missing in label map", label.Name)
|
return 0, fmt.Errorf("label name %q missing in label map", labelName)
|
||||||
}
|
}
|
||||||
h = m.hashAdd(h, val)
|
h = m.hashAdd(h, val)
|
||||||
}
|
}
|
||||||
|
@ -482,7 +468,7 @@ func valueMatchesVariableOrCurriedValue(targetValue string, index int, values []
|
||||||
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
|
func matchPartialLabels(desc *Desc, values []string, labels Labels, curry []curriedLabelValue) bool {
|
||||||
for l, v := range labels {
|
for l, v := range labels {
|
||||||
// Check if the target label exists in our metrics and get the index.
|
// Check if the target label exists in our metrics and get the index.
|
||||||
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.labelNames())
|
varLabelIndex, validLabel := indexOf(l, desc.variableLabels.names)
|
||||||
if validLabel {
|
if validLabel {
|
||||||
// Check the value of that label against the target value.
|
// Check the value of that label against the target value.
|
||||||
// We don't consider curried values in partial matches.
|
// We don't consider curried values in partial matches.
|
||||||
|
@ -626,7 +612,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
iCurry := 0
|
iCurry := 0
|
||||||
for i, k := range desc.variableLabels {
|
for i, k := range desc.variableLabels.names {
|
||||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
if values[i] != curry[iCurry].value {
|
if values[i] != curry[iCurry].value {
|
||||||
return false
|
return false
|
||||||
|
@ -634,7 +620,7 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||||
iCurry++
|
iCurry++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if values[i] != labels[k.Name] {
|
if values[i] != labels[k] {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -644,13 +630,13 @@ func matchLabels(desc *Desc, values []string, labels Labels, curry []curriedLabe
|
||||||
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
|
func extractLabelValues(desc *Desc, labels Labels, curry []curriedLabelValue) []string {
|
||||||
labelValues := make([]string, len(labels)+len(curry))
|
labelValues := make([]string, len(labels)+len(curry))
|
||||||
iCurry := 0
|
iCurry := 0
|
||||||
for i, k := range desc.variableLabels {
|
for i, k := range desc.variableLabels.names {
|
||||||
if iCurry < len(curry) && curry[iCurry].index == i {
|
if iCurry < len(curry) && curry[iCurry].index == i {
|
||||||
labelValues[i] = curry[iCurry].value
|
labelValues[i] = curry[iCurry].value
|
||||||
iCurry++
|
iCurry++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
labelValues[i] = labels[k.Name]
|
labelValues[i] = labels[k]
|
||||||
}
|
}
|
||||||
return labelValues
|
return labelValues
|
||||||
}
|
}
|
||||||
|
@ -670,20 +656,37 @@ func inlineLabelValues(lvs []string, curry []curriedLabelValue) []string {
|
||||||
return labelValues
|
return labelValues
|
||||||
}
|
}
|
||||||
|
|
||||||
func constrainLabels(desc *Desc, labels Labels) Labels {
|
var labelsPool = &sync.Pool{
|
||||||
constrainedLabels := getLabelsFromPool()
|
New: func() interface{} {
|
||||||
for l, v := range labels {
|
return make(Labels)
|
||||||
if i, ok := indexOf(l, desc.variableLabels.labelNames()); ok {
|
},
|
||||||
v = desc.variableLabels[i].Constrain(v)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constrainedLabels[l] = v
|
func constrainLabels(desc *Desc, labels Labels) (Labels, func()) {
|
||||||
|
if len(desc.variableLabels.labelConstraints) == 0 {
|
||||||
|
// Fast path when there's no constraints
|
||||||
|
return labels, func() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
return constrainedLabels
|
constrainedLabels := labelsPool.Get().(Labels)
|
||||||
|
for l, v := range labels {
|
||||||
|
constrainedLabels[l] = desc.variableLabels.constrain(l, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return constrainedLabels, func() {
|
||||||
|
for k := range constrainedLabels {
|
||||||
|
delete(constrainedLabels, k)
|
||||||
|
}
|
||||||
|
labelsPool.Put(constrainedLabels)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {
|
func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) []string {
|
||||||
|
if len(desc.variableLabels.labelConstraints) == 0 {
|
||||||
|
// Fast path when there's no constraints
|
||||||
|
return lvs
|
||||||
|
}
|
||||||
|
|
||||||
constrainedValues := make([]string, len(lvs))
|
constrainedValues := make([]string, len(lvs))
|
||||||
var iCurry, iLVs int
|
var iCurry, iLVs int
|
||||||
for i := 0; i < len(lvs)+len(curry); i++ {
|
for i := 0; i < len(lvs)+len(curry); i++ {
|
||||||
|
@ -692,8 +695,11 @@ func constrainLabelValues(desc *Desc, lvs []string, curry []curriedLabelValue) [
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if i < len(desc.variableLabels) {
|
if i < len(desc.variableLabels.names) {
|
||||||
constrainedValues[iLVs] = desc.variableLabels[i].Constrain(lvs[iLVs])
|
constrainedValues[iLVs] = desc.variableLabels.constrain(
|
||||||
|
desc.variableLabels.names[i],
|
||||||
|
lvs[iLVs],
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
constrainedValues[iLVs] = lvs[iLVs]
|
constrainedValues[iLVs] = lvs[iLVs]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue