// Copyright 2018 The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // Package promauto provides constructors for the usual Prometheus metrics that // return them already registered with the global registry // (prometheus.DefaultRegisterer). This allows very compact code, avoiding any // references to the registry altogether, but all the constructors in this // package will panic if the registration fails. // // The following example is a complete program to create a histogram of normally // distributed random numbers from the math/rand package: // // package main // // import ( // "math/rand" // "net/http" // // "github.com/prometheus/client_golang/prometheus" // "github.com/prometheus/client_golang/prometheus/promauto" // "github.com/prometheus/client_golang/prometheus/promhttp" // ) // // var histogram = promauto.NewHistogram(prometheus.HistogramOpts{ // Name: "random_numbers", // Help: "A histogram of normally distributed random numbers.", // Buckets: prometheus.LinearBuckets(-3, .1, 61), // }) // // func Random() { // for { // histogram.Observe(rand.NormFloat64()) // } // } // // func main() { // go Random() // http.Handle("/metrics", promhttp.Handler()) // http.ListenAndServe(":1971", nil) // } // // Prometheus's version of a minimal hello-world program: // // package main // // import ( // "fmt" // "net/http" // // "github.com/prometheus/client_golang/prometheus" // "github.com/prometheus/client_golang/prometheus/promauto" // "github.com/prometheus/client_golang/prometheus/promhttp" // ) // // func main() { // http.Handle("/", promhttp.InstrumentHandlerCounter( // promauto.NewCounterVec( // prometheus.CounterOpts{ // Name: "hello_requests_total", // Help: "Total number of hello-world requests by HTTP code.", // }, // []string{"code"}, // ), // http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // fmt.Fprint(w, "Hello, world!") // }), // )) // http.Handle("/metrics", promhttp.Handler()) // http.ListenAndServe(":1971", nil) // } // // This appears very handy. So why are these constructors locked away in a // separate package? There are two caveats: // // First, in more complex programs, global state is often quite problematic. // That's the reason why the metrics constructors in the prometheus package do // not interact with the global prometheus.DefaultRegisterer on their own. You // are free to use the Register or MustRegister functions to register them with // the global prometheus.DefaultRegisterer, but you could as well choose a local // Registerer (usually created with prometheus.NewRegistry, but there are other // scenarios, e.g. testing). // // The second issue is that registration may fail, e.g. if a metric inconsistent // with the newly to be registered one is already registered. But how to signal // and handle a panic in the automatic registration with the default registry? // The only way is panicking. While panicking on invalid input provided by the // programmer is certainly fine, things are a bit more subtle in this case: You // might just add another package to the program, and that package (in its init // function) happens to register a metric with the same name as your code. Now, // all of a sudden, either your code or the code of the newly imported package // panics, depending on initialization order, without any opportunity to handle // the case gracefully. Even worse is a scenario where registration happens // later during the runtime (e.g. upon loading some kind of plugin), where the // panic could be triggered long after the code has been deployed to // production. A possibility to panic should be explicitly called out by the // Must… idiom, cf. prometheus.MustRegister. But adding a separate set of // constructors in the prometheus package called MustRegisterNewCounterVec or // similar would be quite unwieldy. Adding an extra MustRegister method to each // metric, returning the registered metric, would result in nice code for those // using the method, but would pollute every single metric interface for // everybody avoiding the global registry. // // To address both issues, the problematic auto-registering and possibly // panicking constructors are all in this package with a clear warning // ahead. And whoever cares about avoiding global state and possibly panicking // function calls can simply ignore the existence of the promauto package // altogether. // // A final note: There is a similar case in the net/http package of the standard // library. It has DefaultServeMux as a global instance of ServeMux, and the // Handle function acts on it, panicking if a handler for the same pattern has // already been registered. However, one might argue that the whole HTTP routing // is usually set up closely together in the same package or file, while // Prometheus metrics tend to be spread widely over the codebase, increasing the // chance of surprising registration failures. Furthermore, the use of global // state in net/http has been criticized widely, and some avoid it altogether. package promauto import "github.com/prometheus/client_golang/prometheus" // NewCounter works like the function of the same name in the prometheus package // but it automatically registers the Counter with the // prometheus.DefaultRegisterer. If the registration fails, NewCounter panics. func NewCounter(opts prometheus.CounterOpts) prometheus.Counter { c := prometheus.NewCounter(opts) prometheus.MustRegister(c) return c } // NewCounterVec works like the function of the same name in the prometheus // package but it automatically registers the CounterVec with the // prometheus.DefaultRegisterer. If the registration fails, NewCounterVec // panics. func NewCounterVec(opts prometheus.CounterOpts, labelNames []string) *prometheus.CounterVec { c := prometheus.NewCounterVec(opts, labelNames) prometheus.MustRegister(c) return c } // NewCounterFunc works like the function of the same name in the prometheus // package but it automatically registers the CounterFunc with the // prometheus.DefaultRegisterer. If the registration fails, NewCounterFunc // panics. func NewCounterFunc(opts prometheus.CounterOpts, function func() float64) prometheus.CounterFunc { g := prometheus.NewCounterFunc(opts, function) prometheus.MustRegister(g) return g } // NewGauge works like the function of the same name in the prometheus package // but it automatically registers the Gauge with the // prometheus.DefaultRegisterer. If the registration fails, NewGauge panics. func NewGauge(opts prometheus.GaugeOpts) prometheus.Gauge { g := prometheus.NewGauge(opts) prometheus.MustRegister(g) return g } // NewGaugeVec works like the function of the same name in the prometheus // package but it automatically registers the GaugeVec with the // prometheus.DefaultRegisterer. If the registration fails, NewGaugeVec panics. func NewGaugeVec(opts prometheus.GaugeOpts, labelNames []string) *prometheus.GaugeVec { g := prometheus.NewGaugeVec(opts, labelNames) prometheus.MustRegister(g) return g } // NewGaugeFunc works like the function of the same name in the prometheus // package but it automatically registers the GaugeFunc with the // prometheus.DefaultRegisterer. If the registration fails, NewGaugeFunc panics. func NewGaugeFunc(opts prometheus.GaugeOpts, function func() float64) prometheus.GaugeFunc { g := prometheus.NewGaugeFunc(opts, function) prometheus.MustRegister(g) return g } // NewSummary works like the function of the same name in the prometheus package // but it automatically registers the Summary with the // prometheus.DefaultRegisterer. If the registration fails, NewSummary panics. func NewSummary(opts prometheus.SummaryOpts) prometheus.Summary { s := prometheus.NewSummary(opts) prometheus.MustRegister(s) return s } // NewSummaryVec works like the function of the same name in the prometheus // package but it automatically registers the SummaryVec with the // prometheus.DefaultRegisterer. If the registration fails, NewSummaryVec // panics. func NewSummaryVec(opts prometheus.SummaryOpts, labelNames []string) *prometheus.SummaryVec { s := prometheus.NewSummaryVec(opts, labelNames) prometheus.MustRegister(s) return s } // NewHistogram works like the function of the same name in the prometheus // package but it automatically registers the Histogram with the // prometheus.DefaultRegisterer. If the registration fails, NewHistogram panics. func NewHistogram(opts prometheus.HistogramOpts) prometheus.Histogram { h := prometheus.NewHistogram(opts) prometheus.MustRegister(h) return h } // NewHistogramVec works like the function of the same name in the prometheus // package but it automatically registers the HistogramVec with the // prometheus.DefaultRegisterer. If the registration fails, NewHistogramVec // panics. func NewHistogramVec(opts prometheus.HistogramOpts, labelNames []string) *prometheus.HistogramVec { h := prometheus.NewHistogramVec(opts, labelNames) prometheus.MustRegister(h) return h }