Add --web.disable-exporter-metrics flag
If this flag is set, the metrics about the exporter itself (go_*, process_*, promhttp_*) will be excluded from /metrics. The Kingpin way of handling boolean flags makes the negative flag wording (_dis_able) the most reasonably one. This also refactors the flow in node_exporter.go quite a bit to avoid mixing up the global and a local registry and to avoid re-creating a registry even if no filtering is requested. Signed-off-by: beorn7 <beorn@soundcloud.com>
This commit is contained in:
parent
b1eec66640
commit
cd2331a185
137
node_exporter.go
137
node_exporter.go
|
@ -27,51 +27,118 @@ import (
|
|||
"gopkg.in/alecthomas/kingpin.v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(version.NewCollector("node_exporter"))
|
||||
// handler wraps an unfiltered http.Handler but uses a filtered handler,
|
||||
// created on the fly, if filtering is requested. Create instances with
|
||||
// newHandler.
|
||||
type handler struct {
|
||||
unfilteredHandler http.Handler
|
||||
// exporterMetricsRegistry is a separate registry for the metrics about
|
||||
// the exporter itself.
|
||||
exporterMetricsRegistry *prometheus.Registry
|
||||
includeExporterMetrics bool
|
||||
}
|
||||
|
||||
func handler(w http.ResponseWriter, r *http.Request) {
|
||||
func newHandler(includeExporterMetrics bool) *handler {
|
||||
h := &handler{
|
||||
exporterMetricsRegistry: prometheus.NewRegistry(),
|
||||
includeExporterMetrics: includeExporterMetrics,
|
||||
}
|
||||
if h.includeExporterMetrics {
|
||||
h.exporterMetricsRegistry.MustRegister(
|
||||
prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
|
||||
prometheus.NewGoCollector(),
|
||||
)
|
||||
}
|
||||
if innerHandler, err := h.innerHandler(); err != nil {
|
||||
log.Fatalf("Couldn't create metrics handler: %s", err)
|
||||
} else {
|
||||
h.unfilteredHandler = innerHandler
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler.
|
||||
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
filters := r.URL.Query()["collect[]"]
|
||||
log.Debugln("collect query:", filters)
|
||||
|
||||
if len(filters) == 0 {
|
||||
// No filters, use the prepared unfiltered handler.
|
||||
h.unfilteredHandler.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
// To serve filtered metrics, we create a filtering handler on the fly.
|
||||
filteredHandler, err := h.innerHandler(filters...)
|
||||
if err != nil {
|
||||
log.Warnln("Couldn't create filtered metrics handler:", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err)))
|
||||
return
|
||||
}
|
||||
filteredHandler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// innerHandler is used to create buth the one unfiltered http.Handler to be
|
||||
// wrapped by the outer handler and also the filtered handlers created on the
|
||||
// fly. The former is accomplished by calling innerHandler without any arguments
|
||||
// (in which case it will log all the collectors enabled via command-line
|
||||
// flags).
|
||||
func (h *handler) innerHandler(filters ...string) (http.Handler, error) {
|
||||
nc, err := collector.NewNodeCollector(filters...)
|
||||
if err != nil {
|
||||
log.Warnln("Couldn't create", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't create %s", err)))
|
||||
return
|
||||
return nil, fmt.Errorf("couldn't create collector: %s", err)
|
||||
}
|
||||
|
||||
registry := prometheus.NewRegistry()
|
||||
err = registry.Register(nc)
|
||||
if err != nil {
|
||||
log.Errorln("Couldn't register collector:", err)
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(fmt.Sprintf("Couldn't register collector: %s", err)))
|
||||
return
|
||||
// Only log the creation of an unfiltered handler, which should happen
|
||||
// only once upon startup.
|
||||
if len(filters) == 0 {
|
||||
log.Infof("Enabled collectors:")
|
||||
collectors := []string{}
|
||||
for n := range nc.Collectors {
|
||||
collectors = append(collectors, n)
|
||||
}
|
||||
sort.Strings(collectors)
|
||||
for _, n := range collectors {
|
||||
log.Infof(" - %s", n)
|
||||
}
|
||||
}
|
||||
|
||||
gatherers := prometheus.Gatherers{
|
||||
prometheus.DefaultGatherer,
|
||||
registry,
|
||||
r := prometheus.NewRegistry()
|
||||
r.MustRegister(version.NewCollector("node_exporter"))
|
||||
if err := r.Register(nc); err != nil {
|
||||
return nil, fmt.Errorf("couldn't register node collector: %s", err)
|
||||
}
|
||||
// Delegate http serving to Prometheus client library, which will call collector.Collect.
|
||||
h := promhttp.InstrumentMetricHandler(
|
||||
registry,
|
||||
promhttp.HandlerFor(gatherers,
|
||||
handler := promhttp.HandlerFor(
|
||||
prometheus.Gatherers{h.exporterMetricsRegistry, r},
|
||||
promhttp.HandlerOpts{
|
||||
ErrorLog: log.NewErrorLogger(),
|
||||
ErrorHandling: promhttp.ContinueOnError,
|
||||
}),
|
||||
},
|
||||
)
|
||||
h.ServeHTTP(w, r)
|
||||
if h.includeExporterMetrics {
|
||||
// Note that we have to use h.exporterMetricsRegistry here to
|
||||
// use the same promhttp metrics for all expositions.
|
||||
handler = promhttp.InstrumentMetricHandler(
|
||||
h.exporterMetricsRegistry, handler,
|
||||
)
|
||||
}
|
||||
return handler, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
var (
|
||||
listenAddress = kingpin.Flag("web.listen-address", "Address on which to expose metrics and web interface.").Default(":9100").String()
|
||||
metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String()
|
||||
listenAddress = kingpin.Flag(
|
||||
"web.listen-address",
|
||||
"Address on which to expose metrics and web interface.",
|
||||
).Default(":9100").String()
|
||||
metricsPath = kingpin.Flag(
|
||||
"web.telemetry-path",
|
||||
"Path under which to expose metrics.",
|
||||
).Default("/metrics").String()
|
||||
disableExporterMetrics = kingpin.Flag(
|
||||
"web.disable-exporter-metrics",
|
||||
"Exclude metrics about the exporter itself (promhttp_*, process_*, go_*).",
|
||||
).Bool()
|
||||
)
|
||||
|
||||
log.AddFlags(kingpin.CommandLine)
|
||||
|
@ -82,22 +149,7 @@ func main() {
|
|||
log.Infoln("Starting node_exporter", version.Info())
|
||||
log.Infoln("Build context", version.BuildContext())
|
||||
|
||||
// This instance is only used to check collector creation and logging.
|
||||
nc, err := collector.NewNodeCollector()
|
||||
if err != nil {
|
||||
log.Fatalf("Couldn't create collector: %s", err)
|
||||
}
|
||||
log.Infof("Enabled collectors:")
|
||||
collectors := []string{}
|
||||
for n := range nc.Collectors {
|
||||
collectors = append(collectors, n)
|
||||
}
|
||||
sort.Strings(collectors)
|
||||
for _, n := range collectors {
|
||||
log.Infof(" - %s", n)
|
||||
}
|
||||
|
||||
http.HandleFunc(*metricsPath, handler)
|
||||
http.Handle(*metricsPath, newHandler(!*disableExporterMetrics))
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`<html>
|
||||
<head><title>Node Exporter</title></head>
|
||||
|
@ -109,8 +161,7 @@ func main() {
|
|||
})
|
||||
|
||||
log.Infoln("Listening on", *listenAddress)
|
||||
err = http.ListenAndServe(*listenAddress, nil)
|
||||
if err != nil {
|
||||
if err := http.ListenAndServe(*listenAddress, nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue