Skip to content

Commit f0be1c5

Browse files
committed
Introduce MetricVecOpts and add constraints to VariableLabels
Signed-off-by: Quentin Devos <[email protected]>
1 parent 5b62db2 commit f0be1c5

File tree

13 files changed

+207
-161
lines changed

13 files changed

+207
-161
lines changed

prometheus/counter.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,18 @@ type ExemplarAdder interface {
5959
// CounterOpts is an alias for Opts. See there for doc comments.
6060
type CounterOpts Opts
6161

62+
// CounterVecOpts bundles the options to create a CounterVec metric.
63+
// It is mandatory to set CounterOpts, see there for mandatory fields. VariableLabels
64+
// is optional and can safely be left to its default value.
65+
type CounterVecOpts struct {
66+
CounterOpts
67+
68+
// VariableLabels are used to partition the metric vector by the given set
69+
// of labels. Each label value will be constrained with the optional Contraint
70+
// function, if provided.
71+
VariableLabels ConstrainableLabels
72+
}
73+
6274
// NewCounter creates a new Counter based on the provided CounterOpts.
6375
//
6476
// The returned implementation also implements ExemplarAdder. It is safe to
@@ -174,28 +186,24 @@ type CounterVec struct {
174186
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
175187
// partitioned by the given label names.
176188
func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
177-
return NewConstrainedCounterVec(opts, labelNames, nil)
189+
return V2.NewCounterVec(CounterVecOpts{
190+
CounterOpts: opts,
191+
VariableLabels: UnconstrainedLabels(labelNames),
192+
})
178193
}
179194

180-
// NewConstrainedCounterVec creates a new CounterVec based on the provided CounterOpts
181-
// and partitioned by the given label names, for which the values will be normalized
182-
// through the constrainedLabels functions, when provided.
183-
func NewConstrainedCounterVec(
184-
opts CounterOpts,
185-
labelNames []string,
186-
constrainedLabels ConstrainedLabels,
187-
) *CounterVec {
188-
desc := NewConstrainedDesc(
195+
// NewCounterVec creates a new CounterVec based on the provided CounterVecOpts.
196+
func (v2) NewCounterVec(opts CounterVecOpts) *CounterVec {
197+
desc := V2.NewDesc(
189198
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
190199
opts.Help,
191-
labelNames,
200+
opts.VariableLabels,
192201
opts.ConstLabels,
193-
constrainedLabels,
194202
)
195203
return &CounterVec{
196204
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
197205
if len(lvs) != len(desc.variableLabels) {
198-
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
206+
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
199207
}
200208
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
201209
result.init(result) // Init self-collection.

prometheus/counter_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func TestCounterVecGetMetricWithInvalidLabelValues(t *testing.T) {
102102
Name: "test",
103103
}, []string{"a"})
104104

105-
labelValues := make([]string, len(test.labels))
105+
labelValues := make([]string, 0, len(test.labels))
106106
for _, val := range test.labels {
107107
labelValues = append(labelValues, val)
108108
}

prometheus/desc.go

Lines changed: 24 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,9 @@ type Desc struct {
5454
// constLabelPairs contains precalculated DTO label pairs based on
5555
// the constant labels.
5656
constLabelPairs []*dto.LabelPair
57-
// variableLabels contains names of labels for which the metric
58-
// maintains variable values.
59-
variableLabels []string
60-
// labelConstraints contains the constraints applied on the label values.
61-
labelConstraints []func(string) string
57+
// variableLabels contains names of labels and normalization function for
58+
// which the metric maintains variable values.
59+
variableLabels ConstrainedLabels
6260
// id is a hash of the values of the ConstLabels and fqName. This
6361
// must be unique among all registered descriptors and can therefore be
6462
// used as an identifier of the descriptor.
@@ -82,10 +80,24 @@ type Desc struct {
8280
// For constLabels, the label values are constant. Therefore, they are fully
8381
// specified in the Desc. See the Collector example for a usage pattern.
8482
func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *Desc {
83+
return V2.NewDesc(fqName, help, UnconstrainedLabels(variableLabels), constLabels)
84+
}
85+
86+
// NewDesc allocates and initializes a new Desc. Errors are recorded in the Desc
87+
// and will be reported on registration time. variableLabels and constLabels can
88+
// be nil if no such labels should be set. fqName must not be empty.
89+
//
90+
// variableLabels only contain the label names and normalization functions. Their
91+
// label values are variable and therefore not part of the Desc. (They are managed
92+
// within the Metric.)
93+
//
94+
// For constLabels, the label values are constant. Therefore, they are fully
95+
// specified in the Desc. See the Collector example for a usage pattern.
96+
func (v2) NewDesc(fqName, help string, variableLabels ConstrainableLabels, constLabels Labels) *Desc {
8597
d := &Desc{
8698
fqName: fqName,
8799
help: help,
88-
variableLabels: variableLabels,
100+
variableLabels: variableLabels.constrainedLabels(),
89101
}
90102
if !model.IsValidMetricName(model.LabelValue(fqName)) {
91103
d.err = fmt.Errorf("%q is not a valid metric name", fqName)
@@ -95,7 +107,7 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
95107
// their sorted label names) plus the fqName (at position 0).
96108
labelValues := make([]string, 1, len(constLabels)+1)
97109
labelValues[0] = fqName
98-
labelNames := make([]string, 0, len(constLabels)+len(variableLabels))
110+
labelNames := make([]string, 0, len(constLabels)+len(d.variableLabels))
99111
labelNameSet := map[string]struct{}{}
100112
// First add only the const label names and sort them...
101113
for labelName := range constLabels {
@@ -120,13 +132,13 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
120132
// Now add the variable label names, but prefix them with something that
121133
// cannot be in a regular label name. That prevents matching the label
122134
// dimension with a different mix between preset and variable labels.
123-
for _, labelName := range variableLabels {
124-
if !checkLabelName(labelName) {
125-
d.err = fmt.Errorf("%q is not a valid label name for metric %q", labelName, fqName)
135+
for _, label := range d.variableLabels {
136+
if !checkLabelName(label.Name) {
137+
d.err = fmt.Errorf("%q is not a valid label name for metric %q", label.Name, fqName)
126138
return d
127139
}
128-
labelNames = append(labelNames, "$"+labelName)
129-
labelNameSet[labelName] = struct{}{}
140+
labelNames = append(labelNames, "$"+label.Name)
141+
labelNameSet[label.Name] = struct{}{}
130142
}
131143
if len(labelNames) != len(labelNameSet) {
132144
d.err = errors.New("duplicate label names")
@@ -163,62 +175,6 @@ func NewDesc(fqName, help string, variableLabels []string, constLabels Labels) *
163175
return d
164176
}
165177

166-
// NewConstrainedDesc allocates and initializes a new Desc with constraints on
167-
// the variable labels. Errors are recorded in the Desc and will be reported on
168-
// registration time. variableLabels, constLabels and constrainedLabels can be
169-
// nil if no such labels should be set. fqName must not be empty.
170-
//
171-
// variableLabels only contain the label names. Their label values are variable
172-
// and therefore not part of the Desc. (They are managed within the Metric.)
173-
//
174-
// constrainedLabels contains the label names and functions to normalize them.
175-
// Label names in constrainedLabels are merged into variableLabels, so they can
176-
// be omitted from variableLabels. Order of variableLabels is preserved, and new
177-
// labels from constrainedLabels are appended to variableLabels.
178-
//
179-
// For constLabels, the label values are constant. Therefore, they are fully
180-
// specified in the Desc. See the Collector example for a usage pattern.
181-
func NewConstrainedDesc(
182-
fqName, help string,
183-
variableLabels []string,
184-
constLabels Labels,
185-
constrainedLabels ConstrainedLabels,
186-
) *Desc {
187-
if len(constrainedLabels) > 0 {
188-
// Merge constrainedLabels into variableLabels as a source for variable
189-
// labels in the Desc
190-
labelSet := map[string]bool{}
191-
for label := range constrainedLabels {
192-
labelSet[label] = true
193-
}
194-
for _, label := range variableLabels {
195-
labelSet[label] = false
196-
}
197-
for label := range constrainedLabels {
198-
if labelSet[label] {
199-
variableLabels = append(variableLabels, label)
200-
}
201-
}
202-
}
203-
204-
d := NewDesc(fqName, help, variableLabels, constLabels)
205-
if len(constrainedLabels) > 0 {
206-
d.labelConstraints = make([]func(string) string, len(d.variableLabels))
207-
for i, label := range d.variableLabels {
208-
if fn, ok := constrainedLabels[label]; ok {
209-
d.labelConstraints[i] = fn
210-
} else {
211-
d.labelConstraints[i] = identityConstraint
212-
}
213-
}
214-
}
215-
return d
216-
}
217-
218-
func identityConstraint(lv string) string {
219-
return lv
220-
}
221-
222178
// NewInvalidDesc returns an invalid descriptor, i.e. a descriptor with the
223179
// provided error set. If a collector returning such a descriptor is registered,
224180
// registration will fail with the provided error. NewInvalidDesc can be used by

prometheus/examples_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,9 @@ func ExampleRegister() {
294294

295295
// Output:
296296
// taskCounter registered.
297-
// 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
297+
// 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
298298
// taskCounter unregistered.
299-
// 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
299+
// 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
300300
// taskCounterVec registered.
301301
// Worker initialization failed: inconsistent label cardinality: expected 1 label values but got 2 in []string{"42", "spurious arg"}
302302
// notMyCounter is nil.

prometheus/gauge.go

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ type Gauge interface {
5555
// GaugeOpts is an alias for Opts. See there for doc comments.
5656
type GaugeOpts Opts
5757

58+
// GaugeVecOpts bundles the options to create a GaugeVec metric.
59+
// It is mandatory to set GaugeOpts, see there for mandatory fields. VariableLabels
60+
// is optional and can safely be left to its default value.
61+
type GaugeVecOpts struct {
62+
GaugeOpts
63+
64+
// VariableLabels are used to partition the metric vector by the given set
65+
// of labels. Each label value will be constrained with the optional Contraint
66+
// function, if provided.
67+
VariableLabels ConstrainableLabels
68+
}
69+
5870
// NewGauge creates a new Gauge based on the provided GaugeOpts.
5971
//
6072
// The returned implementation is optimized for a fast Set method. If you have a
@@ -138,28 +150,24 @@ type GaugeVec struct {
138150
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
139151
// partitioned by the given label names.
140152
func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
141-
return NewConstrainedGaugeVec(opts, labelNames, nil)
153+
return V2.NewGaugeVec(GaugeVecOpts{
154+
GaugeOpts: opts,
155+
VariableLabels: UnconstrainedLabels(labelNames),
156+
})
142157
}
143158

144-
// NewConstrainedGaugeVec creates a new GaugeVec based on the provided GaugeOpts
145-
// and partitioned by the given label names, for which the values will be normalized
146-
// through the constrainedLabels functions, when provided.
147-
func NewConstrainedGaugeVec(
148-
opts GaugeOpts,
149-
labelNames []string,
150-
constrainedLabels ConstrainedLabels,
151-
) *GaugeVec {
152-
desc := NewConstrainedDesc(
159+
// NewGaugeVec creates a new GaugeVec based on the provided GaugeVecOpts.
160+
func (v2) NewGaugeVec(opts GaugeVecOpts) *GaugeVec {
161+
desc := V2.NewDesc(
153162
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
154163
opts.Help,
155-
labelNames,
164+
opts.VariableLabels,
156165
opts.ConstLabels,
157-
constrainedLabels,
158166
)
159167
return &GaugeVec{
160168
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
161169
if len(lvs) != len(desc.variableLabels) {
162-
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
170+
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), lvs))
163171
}
164172
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
165173
result.init(result) // Init self-collection.

prometheus/histogram.go

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,18 @@ type HistogramOpts struct {
469469
NativeHistogramMaxZeroThreshold float64
470470
}
471471

472+
// HistogramVecOpts bundles the options to create a HistogramVec metric.
473+
// It is mandatory to set HistogramOpts, see there for mandatory fields. VariableLabels
474+
// is optional and can safely be left to its default value.
475+
type HistogramVecOpts struct {
476+
HistogramOpts
477+
478+
// VariableLabels are used to partition the metric vector by the given set
479+
// of labels. Each label value will be constrained with the optional Contraint
480+
// function, if provided.
481+
VariableLabels ConstrainableLabels
482+
}
483+
472484
// NewHistogram creates a new Histogram based on the provided HistogramOpts. It
473485
// panics if the buckets in HistogramOpts are not in strictly increasing order.
474486
//
@@ -489,11 +501,11 @@ func NewHistogram(opts HistogramOpts) Histogram {
489501

490502
func newHistogram(desc *Desc, opts HistogramOpts, labelValues ...string) Histogram {
491503
if len(desc.variableLabels) != len(labelValues) {
492-
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, labelValues))
504+
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels.labelNames(), labelValues))
493505
}
494506

495507
for _, n := range desc.variableLabels {
496-
if n == bucketLabel {
508+
if n.Name == bucketLabel {
497509
panic(errBucketLabelNotAllowed)
498510
}
499511
}
@@ -1034,27 +1046,23 @@ type HistogramVec struct {
10341046
// NewHistogramVec creates a new HistogramVec based on the provided HistogramOpts and
10351047
// partitioned by the given label names.
10361048
func NewHistogramVec(opts HistogramOpts, labelNames []string) *HistogramVec {
1037-
return NewConstrainedHistogramVec(opts, labelNames, nil)
1049+
return V2.NewHistogramVec(HistogramVecOpts{
1050+
HistogramOpts: opts,
1051+
VariableLabels: UnconstrainedLabels(labelNames),
1052+
})
10381053
}
10391054

1040-
// NewConstrainedHistogramVec creates a new HistogramVec based on the provided HistogramOpts
1041-
// and partitioned by the given label names, for which the values will be normalized
1042-
// through the constrainedLabels functions, when provided.
1043-
func NewConstrainedHistogramVec(
1044-
opts HistogramOpts,
1045-
labelNames []string,
1046-
constrainedLabels ConstrainedLabels,
1047-
) *HistogramVec {
1048-
desc := NewConstrainedDesc(
1055+
// NewHistogramVec creates a new HistogramVec based on the provided HistogramVecOpts.
1056+
func (v2) NewHistogramVec(opts HistogramVecOpts) *HistogramVec {
1057+
desc := V2.NewDesc(
10491058
BuildFQName(opts.Namespace, opts.Subsystem, opts.Name),
10501059
opts.Help,
1051-
labelNames,
1060+
opts.VariableLabels,
10521061
opts.ConstLabels,
1053-
constrainedLabels,
10541062
)
10551063
return &HistogramVec{
10561064
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
1057-
return newHistogram(desc, opts, lvs...)
1065+
return newHistogram(desc, opts.HistogramOpts, lvs...)
10581066
}),
10591067
}
10601068
}

0 commit comments

Comments
 (0)