Skip to content

Commit 85aa957

Browse files
committed
Export MetricVec (again)
MetricVec was already exported in early versions of this library, but nobody really used it to implement vectors of custom Metric implementations. Now #796 has shown up with a fairly special use case for which I'd prefer a custom implementation of a special "auto-sampling histogram" outside of this library. Therefore, I'd like to reinstate support for creating vectors of custom Metric implementations. I played around for quite some while with the option of a separate package providing the tools one would need to create vectors of custom Metric implementations. However, with the current structure of the prometheus/client_golang/prometheus package, this leads to a lot of complications with circular dependencies. (The new package would need the primitives from the prometheus package, while the existing metric vectors like GaugeVec need to import the new vector package to not duplicate the implementation. Separating vector types from the main prometheus package is out of the question at this point because that would be a breaking change.) Signed-off-by: beorn7 <[email protected]>
1 parent 65c5578 commit 85aa957

File tree

8 files changed

+306
-68
lines changed

8 files changed

+306
-68
lines changed

prometheus/counter.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func (c *counter) updateExemplar(v float64, l Labels) {
163163
// (e.g. number of HTTP requests, partitioned by response code and
164164
// method). Create instances with NewCounterVec.
165165
type CounterVec struct {
166-
*metricVec
166+
*MetricVec
167167
}
168168

169169
// NewCounterVec creates a new CounterVec based on the provided CounterOpts and
@@ -176,19 +176,19 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
176176
opts.ConstLabels,
177177
)
178178
return &CounterVec{
179-
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
179+
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
180180
if len(lvs) != len(desc.variableLabels) {
181181
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
182182
}
183-
result := &counter{desc: desc, labelPairs: makeLabelPairs(desc, lvs), now: time.Now}
183+
result := &counter{desc: desc, labelPairs: MakeLabelPairs(desc, lvs), now: time.Now}
184184
result.init(result) // Init self-collection.
185185
return result
186186
}),
187187
}
188188
}
189189

190190
// GetMetricWithLabelValues returns the Counter for the given slice of label
191-
// values (same order as the VariableLabels in Desc). If that combination of
191+
// values (same order as the variable labels in Desc). If that combination of
192192
// label values is accessed for the first time, a new Counter is created.
193193
//
194194
// It is possible to call this method without using the returned Counter to only
@@ -202,7 +202,7 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
202202
// Counter with the same label values is created later.
203203
//
204204
// An error is returned if the number of label values is not the same as the
205-
// number of VariableLabels in Desc (minus any curried labels).
205+
// number of variable labels in Desc (minus any curried labels).
206206
//
207207
// Note that for more than one label value, this method is prone to mistakes
208208
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
@@ -211,27 +211,27 @@ func NewCounterVec(opts CounterOpts, labelNames []string) *CounterVec {
211211
// with a performance overhead (for creating and processing the Labels map).
212212
// See also the GaugeVec example.
213213
func (v *CounterVec) GetMetricWithLabelValues(lvs ...string) (Counter, error) {
214-
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
214+
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
215215
if metric != nil {
216216
return metric.(Counter), err
217217
}
218218
return nil, err
219219
}
220220

221221
// GetMetricWith returns the Counter for the given Labels map (the label names
222-
// must match those of the VariableLabels in Desc). If that label map is
222+
// must match those of the variable labels in Desc). If that label map is
223223
// accessed for the first time, a new Counter is created. Implications of
224224
// creating a Counter without using it and keeping the Counter for later use are
225225
// the same as for GetMetricWithLabelValues.
226226
//
227227
// An error is returned if the number and names of the Labels are inconsistent
228-
// with those of the VariableLabels in Desc (minus any curried labels).
228+
// with those of the variable labels in Desc (minus any curried labels).
229229
//
230230
// This method is used for the same purpose as
231231
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
232232
// methods.
233233
func (v *CounterVec) GetMetricWith(labels Labels) (Counter, error) {
234-
metric, err := v.metricVec.getMetricWith(labels)
234+
metric, err := v.MetricVec.GetMetricWith(labels)
235235
if metric != nil {
236236
return metric.(Counter), err
237237
}
@@ -275,7 +275,7 @@ func (v *CounterVec) With(labels Labels) Counter {
275275
// registered with a given registry (usually the uncurried version). The Reset
276276
// method deletes all metrics, even if called on a curried vector.
277277
func (v *CounterVec) CurryWith(labels Labels) (*CounterVec, error) {
278-
vec, err := v.curryWith(labels)
278+
vec, err := v.MetricVec.CurryWith(labels)
279279
if vec != nil {
280280
return &CounterVec{vec}, err
281281
}

prometheus/desc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ type Desc struct {
5151
// constLabelPairs contains precalculated DTO label pairs based on
5252
// the constant labels.
5353
constLabelPairs []*dto.LabelPair
54-
// VariableLabels contains names of labels for which the metric
54+
// variableLabels contains names of labels for which the metric
5555
// maintains variable values.
5656
variableLabels []string
5757
// id is a hash of the values of the ConstLabels and fqName. This

prometheus/example_metricvec_test.go

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
// Copyright 2020 The Prometheus Authors
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package prometheus_test
15+
16+
import (
17+
//lint:ignore SA1019 Need to keep deprecated package for compatibility.
18+
"fmt"
19+
20+
"github.com/golang/protobuf/proto"
21+
22+
dto "github.com/prometheus/client_model/go"
23+
24+
"github.com/prometheus/client_golang/prometheus"
25+
)
26+
27+
// Info implements an info pseudo-metric, which is modeled as a Gauge that
28+
// always has a value of 1. In practice, you would just use a Gauge directly,
29+
// but for this example, we pretend it would be useful to have a “native”
30+
// implementation.
31+
type Info struct {
32+
desc *prometheus.Desc
33+
labelPairs []*dto.LabelPair
34+
}
35+
36+
func (i Info) Desc() *prometheus.Desc {
37+
return i.desc
38+
}
39+
40+
func (i Info) Write(out *dto.Metric) error {
41+
out.Label = i.labelPairs
42+
out.Gauge = &dto.Gauge{Value: proto.Float64(1)}
43+
return nil
44+
}
45+
46+
// InfoVec is the vector version for Info. As an info metric never changes, we
47+
// wouldn't really need to wrap GetMetricWithLabelValues and GetMetricWith
48+
// because Info has no additional methods compared to the vanilla Metric that
49+
// the unwrapped MetricVec methods return. However, to demonstrate all there is
50+
// to do to fully implement a vector for a custom Metric implementation, we do
51+
// it in this example anyway.
52+
type InfoVec struct {
53+
*prometheus.MetricVec
54+
}
55+
56+
func NewInfoVec(name, help string, labelNames []string) *InfoVec {
57+
desc := prometheus.NewDesc(name, help, labelNames, nil)
58+
return &InfoVec{
59+
MetricVec: prometheus.NewMetricVec(desc, func(lvs ...string) prometheus.Metric {
60+
if len(lvs) != len(labelNames) {
61+
panic("inconsistent label cardinality")
62+
}
63+
return Info{desc: desc, labelPairs: prometheus.MakeLabelPairs(desc, lvs)}
64+
}),
65+
}
66+
}
67+
68+
func (v *InfoVec) GetMetricWithLabelValues(lvs ...string) (Info, error) {
69+
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
70+
return metric.(Info), err
71+
}
72+
73+
func (v *InfoVec) GetMetricWith(labels prometheus.Labels) (Info, error) {
74+
metric, err := v.MetricVec.GetMetricWith(labels)
75+
return metric.(Info), err
76+
}
77+
78+
func (v *InfoVec) WithLabelValues(lvs ...string) Info {
79+
i, err := v.GetMetricWithLabelValues(lvs...)
80+
if err != nil {
81+
panic(err)
82+
}
83+
return i
84+
}
85+
86+
func (v *InfoVec) With(labels prometheus.Labels) Info {
87+
i, err := v.GetMetricWith(labels)
88+
if err != nil {
89+
panic(err)
90+
}
91+
return i
92+
}
93+
94+
func (v *InfoVec) CurryWith(labels prometheus.Labels) (*InfoVec, error) {
95+
vec, err := v.MetricVec.CurryWith(labels)
96+
if vec != nil {
97+
return &InfoVec{vec}, err
98+
}
99+
return nil, err
100+
}
101+
102+
func (v *InfoVec) MustCurryWith(labels prometheus.Labels) *InfoVec {
103+
vec, err := v.CurryWith(labels)
104+
if err != nil {
105+
panic(err)
106+
}
107+
return vec
108+
}
109+
110+
func ExampleMetricVec() {
111+
112+
infoVec := NewInfoVec(
113+
"library_version_info",
114+
"Versions of the libraries used in this binary.",
115+
[]string{"library", "version"},
116+
)
117+
118+
infoVec.WithLabelValues("prometheus/client_golang", "1.7.1")
119+
infoVec.WithLabelValues("k8s.io/client-go", "0.18.8")
120+
121+
// Just for demonstration, let's check the state of the InfoVec by
122+
// registering it with a custom registry and then let it collect the
123+
// metrics.
124+
reg := prometheus.NewRegistry()
125+
reg.MustRegister(infoVec)
126+
127+
metricFamilies, err := reg.Gather()
128+
if err != nil || len(metricFamilies) != 1 {
129+
panic("unexpected behavior of custom test registry")
130+
}
131+
fmt.Println(proto.MarshalTextString(metricFamilies[0]))
132+
133+
// Output:
134+
// name: "library_version_info"
135+
// help: "Versions of the libraries used in this binary."
136+
// type: GAUGE
137+
// metric: <
138+
// label: <
139+
// name: "library"
140+
// value: "k8s.io/client-go"
141+
// >
142+
// label: <
143+
// name: "version"
144+
// value: "0.18.8"
145+
// >
146+
// gauge: <
147+
// value: 1
148+
// >
149+
// >
150+
// metric: <
151+
// label: <
152+
// name: "library"
153+
// value: "prometheus/client_golang"
154+
// >
155+
// label: <
156+
// name: "version"
157+
// value: "1.7.1"
158+
// >
159+
// gauge: <
160+
// value: 1
161+
// >
162+
// >
163+
}

prometheus/gauge.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ func (g *gauge) Write(out *dto.Metric) error {
132132
// (e.g. number of operations queued, partitioned by user and operation
133133
// type). Create instances with NewGaugeVec.
134134
type GaugeVec struct {
135-
*metricVec
135+
*MetricVec
136136
}
137137

138138
// NewGaugeVec creates a new GaugeVec based on the provided GaugeOpts and
@@ -145,19 +145,19 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
145145
opts.ConstLabels,
146146
)
147147
return &GaugeVec{
148-
metricVec: newMetricVec(desc, func(lvs ...string) Metric {
148+
MetricVec: NewMetricVec(desc, func(lvs ...string) Metric {
149149
if len(lvs) != len(desc.variableLabels) {
150150
panic(makeInconsistentCardinalityError(desc.fqName, desc.variableLabels, lvs))
151151
}
152-
result := &gauge{desc: desc, labelPairs: makeLabelPairs(desc, lvs)}
152+
result := &gauge{desc: desc, labelPairs: MakeLabelPairs(desc, lvs)}
153153
result.init(result) // Init self-collection.
154154
return result
155155
}),
156156
}
157157
}
158158

159159
// GetMetricWithLabelValues returns the Gauge for the given slice of label
160-
// values (same order as the VariableLabels in Desc). If that combination of
160+
// values (same order as the variable labels in Desc). If that combination of
161161
// label values is accessed for the first time, a new Gauge is created.
162162
//
163163
// It is possible to call this method without using the returned Gauge to only
@@ -172,35 +172,35 @@ func NewGaugeVec(opts GaugeOpts, labelNames []string) *GaugeVec {
172172
// example.
173173
//
174174
// An error is returned if the number of label values is not the same as the
175-
// number of VariableLabels in Desc (minus any curried labels).
175+
// number of variable labels in Desc (minus any curried labels).
176176
//
177177
// Note that for more than one label value, this method is prone to mistakes
178178
// caused by an incorrect order of arguments. Consider GetMetricWith(Labels) as
179179
// an alternative to avoid that type of mistake. For higher label numbers, the
180180
// latter has a much more readable (albeit more verbose) syntax, but it comes
181181
// with a performance overhead (for creating and processing the Labels map).
182182
func (v *GaugeVec) GetMetricWithLabelValues(lvs ...string) (Gauge, error) {
183-
metric, err := v.metricVec.getMetricWithLabelValues(lvs...)
183+
metric, err := v.MetricVec.GetMetricWithLabelValues(lvs...)
184184
if metric != nil {
185185
return metric.(Gauge), err
186186
}
187187
return nil, err
188188
}
189189

190190
// GetMetricWith returns the Gauge for the given Labels map (the label names
191-
// must match those of the VariableLabels in Desc). If that label map is
191+
// must match those of the variable labels in Desc). If that label map is
192192
// accessed for the first time, a new Gauge is created. Implications of
193193
// creating a Gauge without using it and keeping the Gauge for later use are
194194
// the same as for GetMetricWithLabelValues.
195195
//
196196
// An error is returned if the number and names of the Labels are inconsistent
197-
// with those of the VariableLabels in Desc (minus any curried labels).
197+
// with those of the variable labels in Desc (minus any curried labels).
198198
//
199199
// This method is used for the same purpose as
200200
// GetMetricWithLabelValues(...string). See there for pros and cons of the two
201201
// methods.
202202
func (v *GaugeVec) GetMetricWith(labels Labels) (Gauge, error) {
203-
metric, err := v.metricVec.getMetricWith(labels)
203+
metric, err := v.MetricVec.GetMetricWith(labels)
204204
if metric != nil {
205205
return metric.(Gauge), err
206206
}
@@ -244,7 +244,7 @@ func (v *GaugeVec) With(labels Labels) Gauge {
244244
// registered with a given registry (usually the uncurried version). The Reset
245245
// method deletes all metrics, even if called on a curried vector.
246246
func (v *GaugeVec) CurryWith(labels Labels) (*GaugeVec, error) {
247-
vec, err := v.curryWith(labels)
247+
vec, err := v.MetricVec.CurryWith(labels)
248248
if vec != nil {
249249
return &GaugeVec{vec}, err
250250
}

0 commit comments

Comments
 (0)