11
11
// See the License for the specific language governing permissions and
12
12
// limitations under the License.
13
13
14
- // Package promauto provides constructors for the usual Prometheus metrics that
15
- // return them already registered with the global registry
16
- // (prometheus.DefaultRegisterer). This allows very compact code, avoiding any
17
- // references to the registry altogether, but all the constructors in this
18
- // package will panic if the registration fails.
14
+ // Package promauto provides alternative constructors for the fundamental
15
+ // Prometheus metric types and their …Vec and …Func variants. The difference to
16
+ // their counterparts in the prometheus package is that the promauto
17
+ // constructors return Collectors that are already registered with a
18
+ // registry. There are two sets of constructors. The constructors in the first
19
+ // set are top-level functions, while the constructors in the other set are
20
+ // methods of the Factory type. The top-level function return Collectors
21
+ // registered with the global registry (prometheus.DefaultRegisterer), while the
22
+ // methods return Collectors registered with the registry the Factory was
23
+ // constructed with. All constructors panic if the registration fails.
19
24
//
20
25
// The following example is a complete program to create a histogram of normally
21
26
// distributed random numbers from the math/rand package:
79
84
// http.ListenAndServe(":1971", nil)
80
85
// }
81
86
//
87
+ // A Factory is created with the With(prometheus.Registerer) function, which
88
+ // enables two usage pattern. With(prometheus.Registerer) can be called once per
89
+ // line:
90
+ //
91
+ // var (
92
+ // reg = prometheus.NewRegistry()
93
+ // randomNumbers = promauto.With(reg).NewHistogram(prometheus.HistogramOpts{
94
+ // Name: "random_numbers",
95
+ // Help: "A histogram of normally distributed random numbers.",
96
+ // Buckets: prometheus.LinearBuckets(-3, .1, 61),
97
+ // })
98
+ // requestCount = promauto.With(reg).NewCounterVec(
99
+ // prometheus.CounterOpts{
100
+ // Name: "http_requests_total",
101
+ // Help: "Total number of HTTP requests by status code end method.",
102
+ // },
103
+ // []string{"code", "method"},
104
+ // )
105
+ // )
106
+ //
107
+ // Or it can be used to create a Factory once to be used multiple times:
108
+ //
109
+ // var (
110
+ // reg = prometheus.NewRegistry()
111
+ // factory = promauto.With(reg)
112
+ // randomNumbers = factory.NewHistogram(prometheus.HistogramOpts{
113
+ // Name: "random_numbers",
114
+ // Help: "A histogram of normally distributed random numbers.",
115
+ // Buckets: prometheus.LinearBuckets(-3, .1, 61),
116
+ // })
117
+ // requestCount = factory.NewCounterVec(
118
+ // prometheus.CounterOpts{
119
+ // Name: "http_requests_total",
120
+ // Help: "Total number of HTTP requests by status code end method.",
121
+ // },
122
+ // []string{"code", "method"},
123
+ // )
124
+ // )
125
+ //
82
126
// This appears very handy. So why are these constructors locked away in a
83
- // separate package? There are two caveats:
84
- //
85
- // First, in more complex programs, global state is often quite problematic.
86
- // That's the reason why the metrics constructors in the prometheus package do
87
- // not interact with the global prometheus.DefaultRegisterer on their own. You
88
- // are free to use the Register or MustRegister functions to register them with
89
- // the global prometheus.DefaultRegisterer, but you could as well choose a local
90
- // Registerer (usually created with prometheus.NewRegistry, but there are other
91
- // scenarios, e.g. testing).
92
- //
93
- // The second issue is that registration may fail, e.g. if a metric inconsistent
94
- // with the newly to be registered one is already registered. But how to signal
95
- // and handle a panic in the automatic registration with the default registry?
96
- // The only way is panicking. While panicking on invalid input provided by the
97
- // programmer is certainly fine, things are a bit more subtle in this case: You
98
- // might just add another package to the program, and that package (in its init
99
- // function) happens to register a metric with the same name as your code. Now,
100
- // all of a sudden, either your code or the code of the newly imported package
101
- // panics, depending on initialization order, without any opportunity to handle
102
- // the case gracefully. Even worse is a scenario where registration happens
103
- // later during the runtime (e.g. upon loading some kind of plugin), where the
104
- // panic could be triggered long after the code has been deployed to
105
- // production. A possibility to panic should be explicitly called out by the
106
- // Must… idiom, cf. prometheus.MustRegister. But adding a separate set of
107
- // constructors in the prometheus package called MustRegisterNewCounterVec or
108
- // similar would be quite unwieldy. Adding an extra MustRegister method to each
109
- // metric, returning the registered metric, would result in nice code for those
110
- // using the method, but would pollute every single metric interface for
111
- // everybody avoiding the global registry.
112
- //
113
- // To address both issues, the problematic auto-registering and possibly
114
- // panicking constructors are all in this package with a clear warning
115
- // ahead. And whoever cares about avoiding global state and possibly panicking
116
- // function calls can simply ignore the existence of the promauto package
117
- // altogether.
118
- //
119
- // A final note: There is a similar case in the net/http package of the standard
120
- // library. It has DefaultServeMux as a global instance of ServeMux, and the
121
- // Handle function acts on it, panicking if a handler for the same pattern has
122
- // already been registered. However, one might argue that the whole HTTP routing
123
- // is usually set up closely together in the same package or file, while
124
- // Prometheus metrics tend to be spread widely over the codebase, increasing the
125
- // chance of surprising registration failures. Furthermore, the use of global
126
- // state in net/http has been criticized widely, and some avoid it altogether.
127
+ // separate package?
128
+ //
129
+ // The main problem is that registration may fail, e.g. if a metric inconsistent
130
+ // with the newly to be registered one is already registered. Therefore, the
131
+ // Register method in the prometheus.Registerer interface returns an error, and
132
+ // the same is the case for the top-level prometheus.Register function that
133
+ // registers with the global registry. The prometheus package also provides
134
+ // MustRegister versions for both. They panic if the registration fails, and
135
+ // they clearly call this out by using the Must… idiom. Panicking is a bit
136
+ // problematic here because it doesn't just happen on input provided by the
137
+ // caller that is invalid on its own. Things are a bit more subtle here: Metric
138
+ // creation and registration tend to be spread widely over the codebase. It can
139
+ // easily happen that an incompatible metric is added to an unrelated part of
140
+ // the code, and suddenly code that used to work perfectly fine starts to panic
141
+ // (provided that the registration of the newly added metric happens before the
142
+ // registration of the previously existing metric). This may come as an even
143
+ // bigger surprise with the global registry, where simply importing another
144
+ // package can trigger a panic (if the newly imported package registers metrics
145
+ // in its init function). At least, in the prometheus package, creation of
146
+ // metrics and other collectors is separate from registration. You first create
147
+ // the metric, and then you decide explicitly if you want to register it with a
148
+ // local or the global registry, and if you want to handle the error or risk a
149
+ // panic. With the constructors in the promauto package, registration is
150
+ // automatic, and if it fails, it will always panic. Furthermore, the
151
+ // constructors will often be called in the var section of a file, which means
152
+ // that panicking will happen as a side effect of merely importing a package.
153
+ //
154
+ // A separate package allows conservative users to entirely ignore it. And
155
+ // whoever wants to use it, will do so explicitly, with an opportunity to read
156
+ // this warning.
157
+ //
158
+ // Enjoy promauto responsibly!
127
159
package promauto
128
160
129
161
import "github.com/prometheus/client_golang/prometheus"
@@ -132,92 +164,192 @@ import "github.com/prometheus/client_golang/prometheus"
132
164
// but it automatically registers the Counter with the
133
165
// prometheus.DefaultRegisterer. If the registration fails, NewCounter panics.
134
166
func NewCounter (opts prometheus.CounterOpts ) prometheus.Counter {
135
- c := prometheus .NewCounter (opts )
136
- prometheus .MustRegister (c )
137
- return c
167
+ return With (prometheus .DefaultRegisterer ).NewCounter (opts )
138
168
}
139
169
140
170
// NewCounterVec works like the function of the same name in the prometheus
141
171
// package but it automatically registers the CounterVec with the
142
172
// prometheus.DefaultRegisterer. If the registration fails, NewCounterVec
143
173
// panics.
144
174
func NewCounterVec (opts prometheus.CounterOpts , labelNames []string ) * prometheus.CounterVec {
145
- c := prometheus .NewCounterVec (opts , labelNames )
146
- prometheus .MustRegister (c )
147
- return c
175
+ return With (prometheus .DefaultRegisterer ).NewCounterVec (opts , labelNames )
148
176
}
149
177
150
178
// NewCounterFunc works like the function of the same name in the prometheus
151
179
// package but it automatically registers the CounterFunc with the
152
180
// prometheus.DefaultRegisterer. If the registration fails, NewCounterFunc
153
181
// panics.
154
182
func NewCounterFunc (opts prometheus.CounterOpts , function func () float64 ) prometheus.CounterFunc {
155
- g := prometheus .NewCounterFunc (opts , function )
156
- prometheus .MustRegister (g )
157
- return g
183
+ return With (prometheus .DefaultRegisterer ).NewCounterFunc (opts , function )
158
184
}
159
185
160
186
// NewGauge works like the function of the same name in the prometheus package
161
187
// but it automatically registers the Gauge with the
162
188
// prometheus.DefaultRegisterer. If the registration fails, NewGauge panics.
163
189
func NewGauge (opts prometheus.GaugeOpts ) prometheus.Gauge {
164
- g := prometheus .NewGauge (opts )
165
- prometheus .MustRegister (g )
166
- return g
190
+ return With (prometheus .DefaultRegisterer ).NewGauge (opts )
167
191
}
168
192
169
193
// NewGaugeVec works like the function of the same name in the prometheus
170
194
// package but it automatically registers the GaugeVec with the
171
195
// prometheus.DefaultRegisterer. If the registration fails, NewGaugeVec panics.
172
196
func NewGaugeVec (opts prometheus.GaugeOpts , labelNames []string ) * prometheus.GaugeVec {
173
- g := prometheus .NewGaugeVec (opts , labelNames )
174
- prometheus .MustRegister (g )
175
- return g
197
+ return With (prometheus .DefaultRegisterer ).NewGaugeVec (opts , labelNames )
176
198
}
177
199
178
200
// NewGaugeFunc works like the function of the same name in the prometheus
179
201
// package but it automatically registers the GaugeFunc with the
180
202
// prometheus.DefaultRegisterer. If the registration fails, NewGaugeFunc panics.
181
203
func NewGaugeFunc (opts prometheus.GaugeOpts , function func () float64 ) prometheus.GaugeFunc {
182
- g := prometheus .NewGaugeFunc (opts , function )
183
- prometheus .MustRegister (g )
184
- return g
204
+ return With (prometheus .DefaultRegisterer ).NewGaugeFunc (opts , function )
185
205
}
186
206
187
207
// NewSummary works like the function of the same name in the prometheus package
188
208
// but it automatically registers the Summary with the
189
209
// prometheus.DefaultRegisterer. If the registration fails, NewSummary panics.
190
210
func NewSummary (opts prometheus.SummaryOpts ) prometheus.Summary {
191
- s := prometheus .NewSummary (opts )
192
- prometheus .MustRegister (s )
193
- return s
211
+ return With (prometheus .DefaultRegisterer ).NewSummary (opts )
194
212
}
195
213
196
214
// NewSummaryVec works like the function of the same name in the prometheus
197
215
// package but it automatically registers the SummaryVec with the
198
216
// prometheus.DefaultRegisterer. If the registration fails, NewSummaryVec
199
217
// panics.
200
218
func NewSummaryVec (opts prometheus.SummaryOpts , labelNames []string ) * prometheus.SummaryVec {
201
- s := prometheus .NewSummaryVec (opts , labelNames )
202
- prometheus .MustRegister (s )
203
- return s
219
+ return With (prometheus .DefaultRegisterer ).NewSummaryVec (opts , labelNames )
204
220
}
205
221
206
222
// NewHistogram works like the function of the same name in the prometheus
207
223
// package but it automatically registers the Histogram with the
208
224
// prometheus.DefaultRegisterer. If the registration fails, NewHistogram panics.
209
225
func NewHistogram (opts prometheus.HistogramOpts ) prometheus.Histogram {
210
- h := prometheus .NewHistogram (opts )
211
- prometheus .MustRegister (h )
212
- return h
226
+ return With (prometheus .DefaultRegisterer ).NewHistogram (opts )
213
227
}
214
228
215
229
// NewHistogramVec works like the function of the same name in the prometheus
216
230
// package but it automatically registers the HistogramVec with the
217
231
// prometheus.DefaultRegisterer. If the registration fails, NewHistogramVec
218
232
// panics.
219
233
func NewHistogramVec (opts prometheus.HistogramOpts , labelNames []string ) * prometheus.HistogramVec {
234
+ return With (prometheus .DefaultRegisterer ).NewHistogramVec (opts , labelNames )
235
+ }
236
+
237
+ // Factory provides factory methods to create Collectors that are automatically
238
+ // registered with a Registerer. Create a Factory with the With function,
239
+ // providing a Registerer to auto-register created Collectors with. The zero
240
+ // value of a Factory creates Collectors that are not registered with any
241
+ // Registerer. All methods of the Factory panic if the registration fails.
242
+ type Factory struct {
243
+ r prometheus.Registerer
244
+ }
245
+
246
+ // With creates a Factory using the provided Registerer for registration of the
247
+ // created Collectors.
248
+ func With (r prometheus.Registerer ) Factory { return Factory {r } }
249
+
250
+ // NewCounter works like the function of the same name in the prometheus package
251
+ // but it automatically registers the Counter with the Factory's Registerer.
252
+ func (f Factory ) NewCounter (opts prometheus.CounterOpts ) prometheus.Counter {
253
+ c := prometheus .NewCounter (opts )
254
+ if f .r != nil {
255
+ f .r .MustRegister (c )
256
+ }
257
+ return c
258
+ }
259
+
260
+ // NewCounterVec works like the function of the same name in the prometheus
261
+ // package but it automatically registers the CounterVec with the Factory's
262
+ // Registerer.
263
+ func (f Factory ) NewCounterVec (opts prometheus.CounterOpts , labelNames []string ) * prometheus.CounterVec {
264
+ c := prometheus .NewCounterVec (opts , labelNames )
265
+ if f .r != nil {
266
+ f .r .MustRegister (c )
267
+ }
268
+ return c
269
+ }
270
+
271
+ // NewCounterFunc works like the function of the same name in the prometheus
272
+ // package but it automatically registers the CounterFunc with the Factory's
273
+ // Registerer.
274
+ func (f Factory ) NewCounterFunc (opts prometheus.CounterOpts , function func () float64 ) prometheus.CounterFunc {
275
+ c := prometheus .NewCounterFunc (opts , function )
276
+ if f .r != nil {
277
+ f .r .MustRegister (c )
278
+ }
279
+ return c
280
+ }
281
+
282
+ // NewGauge works like the function of the same name in the prometheus package
283
+ // but it automatically registers the Gauge with the Factory's Registerer.
284
+ func (f Factory ) NewGauge (opts prometheus.GaugeOpts ) prometheus.Gauge {
285
+ g := prometheus .NewGauge (opts )
286
+ if f .r != nil {
287
+ f .r .MustRegister (g )
288
+ }
289
+ return g
290
+ }
291
+
292
+ // NewGaugeVec works like the function of the same name in the prometheus
293
+ // package but it automatically registers the GaugeVec with the Factory's
294
+ // Registerer.
295
+ func (f Factory ) NewGaugeVec (opts prometheus.GaugeOpts , labelNames []string ) * prometheus.GaugeVec {
296
+ g := prometheus .NewGaugeVec (opts , labelNames )
297
+ if f .r != nil {
298
+ f .r .MustRegister (g )
299
+ }
300
+ return g
301
+ }
302
+
303
+ // NewGaugeFunc works like the function of the same name in the prometheus
304
+ // package but it automatically registers the GaugeFunc with the Factory's
305
+ // Registerer.
306
+ func (f Factory ) NewGaugeFunc (opts prometheus.GaugeOpts , function func () float64 ) prometheus.GaugeFunc {
307
+ g := prometheus .NewGaugeFunc (opts , function )
308
+ if f .r != nil {
309
+ f .r .MustRegister (g )
310
+ }
311
+ return g
312
+ }
313
+
314
+ // NewSummary works like the function of the same name in the prometheus package
315
+ // but it automatically registers the Summary with the Factory's Registerer.
316
+ func (f Factory ) NewSummary (opts prometheus.SummaryOpts ) prometheus.Summary {
317
+ s := prometheus .NewSummary (opts )
318
+ if f .r != nil {
319
+ f .r .MustRegister (s )
320
+ }
321
+ return s
322
+ }
323
+
324
+ // NewSummaryVec works like the function of the same name in the prometheus
325
+ // package but it automatically registers the SummaryVec with the Factory's
326
+ // Registerer.
327
+ func (f Factory ) NewSummaryVec (opts prometheus.SummaryOpts , labelNames []string ) * prometheus.SummaryVec {
328
+ s := prometheus .NewSummaryVec (opts , labelNames )
329
+ if f .r != nil {
330
+ f .r .MustRegister (s )
331
+ }
332
+ return s
333
+ }
334
+
335
+ // NewHistogram works like the function of the same name in the prometheus
336
+ // package but it automatically registers the Histogram with the Factory's
337
+ // Registerer.
338
+ func (f Factory ) NewHistogram (opts prometheus.HistogramOpts ) prometheus.Histogram {
339
+ h := prometheus .NewHistogram (opts )
340
+ if f .r != nil {
341
+ f .r .MustRegister (h )
342
+ }
343
+ return h
344
+ }
345
+
346
+ // NewHistogramVec works like the function of the same name in the prometheus
347
+ // package but it automatically registers the HistogramVec with the Factory's
348
+ // Registerer.
349
+ func (f Factory ) NewHistogramVec (opts prometheus.HistogramOpts , labelNames []string ) * prometheus.HistogramVec {
220
350
h := prometheus .NewHistogramVec (opts , labelNames )
221
- prometheus .MustRegister (h )
351
+ if f .r != nil {
352
+ f .r .MustRegister (h )
353
+ }
222
354
return h
223
355
}
0 commit comments