Add collect[] parameter (#699)

* Add `collect[]` parameter

* Add TODo comment about staticcheck ignored

* Restore promhttp.HandlerOpts

* Log a warning and return HTTP error instead of failing

* Check collector existence and status, cleanups

* Fix warnings and error messages

* Don't panic, return error if collector registration failed

* Update README
This commit is contained in:
Siavash Safi 2017-10-14 14:23:42 +02:00 committed by Ben Kochie
parent 8f9edf87b5
commit f3a7022602
4 changed files with 91 additions and 14 deletions

View File

@ -27,6 +27,10 @@ DOCKER_IMAGE_TAG ?= $(subst /,-,$(shell git rev-parse --abbrev-ref HEAD))
MACH ?= $(shell uname -m) MACH ?= $(shell uname -m)
DOCKERFILE ?= Dockerfile DOCKERFILE ?= Dockerfile
# TODO: Remove deprecated and problematic InstrumentHandlerFunc usage.
STATICCHECK_IGNORE = \
github.com/prometheus/node_exporter/node_exporter.go:SA1019
ifeq ($(GOHOSTARCH),amd64) ifeq ($(GOHOSTARCH),amd64)
# Only supported on amd64 # Only supported on amd64
test-flags := -race test-flags := -race
@ -99,7 +103,7 @@ vet:
staticcheck: $(STATICCHECK) staticcheck: $(STATICCHECK)
@echo ">> running staticcheck" @echo ">> running staticcheck"
@$(STATICCHECK) $(pkgs) @$(STATICCHECK) -ignore "$(STATICCHECK_IGNORE)" $(pkgs)
build: $(PROMU) build: $(PROMU)
@echo ">> building binaries" @echo ">> building binaries"

View File

@ -107,6 +107,39 @@ echo 'role{role="application_server"} 1' > /path/to/directory/role.prom.$$
mv /path/to/directory/role.prom.$$ /path/to/directory/role.prom mv /path/to/directory/role.prom.$$ /path/to/directory/role.prom
``` ```
### Filtering enabled collectors
The node_exporter will expose all metrics from enabled collectors by default, but it can be passed an optional list of collectors to filter metrics. The `collect[]` parameter accepts values matching enabled collector names.
This can be useful for specifying different scrape intervals for different collectors in Prometheus:
```yaml
scrape_configs:
- job_name: 'node resources'
scrape_interval: 15s
static_configs:
- targets:
- '192.168.1.2:9100'
params:
collect[]:
- cpu
- meminfo
- diskstats
- netdev
- netstat
- job_name: 'node storage'
scrape_interval: 1m
static_configs:
- targets:
- '192.168.1.2:9100'
params:
collect[]:
- filefd
- filesystem
- xfs
```
## Building and running ## Building and running
Prerequisites: Prerequisites:

View File

@ -80,7 +80,18 @@ type nodeCollector struct {
} }
// NewNodeCollector creates a new NodeCollector // NewNodeCollector creates a new NodeCollector
func NewNodeCollector() (*nodeCollector, error) { func NewNodeCollector(filters ...string) (*nodeCollector, error) {
f := make(map[string]bool)
for _, filter := range filters {
enabled, exist := collectorState[filter]
if !exist {
return nil, fmt.Errorf("missing collector: %s", filter)
}
if !*enabled {
return nil, fmt.Errorf("disabled collector: %s", filter)
}
f[filter] = true
}
collectors := make(map[string]Collector) collectors := make(map[string]Collector)
for key, enabled := range collectorState { for key, enabled := range collectorState {
if *enabled { if *enabled {
@ -88,7 +99,9 @@ func NewNodeCollector() (*nodeCollector, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
collectors[key] = collector if len(f) == 0 || f[key] {
collectors[key] = collector
}
} }
} }
return &nodeCollector{Collectors: collectors}, nil return &nodeCollector{Collectors: collectors}, nil

View File

@ -14,6 +14,7 @@
package main package main
import ( import (
"fmt"
"net/http" "net/http"
_ "net/http/pprof" _ "net/http/pprof"
@ -29,6 +30,40 @@ func init() {
prometheus.MustRegister(version.NewCollector("node_exporter")) prometheus.MustRegister(version.NewCollector("node_exporter"))
} }
func handler(w http.ResponseWriter, r *http.Request) {
filters := r.URL.Query()["collect[]"]
log.Debugln("collect query:", filters)
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
}
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
}
gatherers := prometheus.Gatherers{
prometheus.DefaultGatherer,
registry,
}
// Delegate http serving to Prometheus client library, which will call collector.Collect.
h := promhttp.HandlerFor(gatherers,
promhttp.HandlerOpts{
ErrorLog: log.NewErrorLogger(),
ErrorHandling: promhttp.ContinueOnError,
})
h.ServeHTTP(w, r)
}
func main() { func main() {
var ( var (
listenAddress = kingpin.Flag("web.listen-address", "Address on which to expose metrics and web interface.").Default(":9100").String() listenAddress = kingpin.Flag("web.listen-address", "Address on which to expose metrics and web interface.").Default(":9100").String()
@ -43,6 +78,7 @@ func main() {
log.Infoln("Starting node_exporter", version.Info()) log.Infoln("Starting node_exporter", version.Info())
log.Infoln("Build context", version.BuildContext()) log.Infoln("Build context", version.BuildContext())
// This instance is only used to check collector creation and logging.
nc, err := collector.NewNodeCollector() nc, err := collector.NewNodeCollector()
if err != nil { if err != nil {
log.Fatalf("Couldn't create collector: %s", err) log.Fatalf("Couldn't create collector: %s", err)
@ -52,17 +88,8 @@ func main() {
log.Infof(" - %s", n) log.Infof(" - %s", n)
} }
if err := prometheus.Register(nc); err != nil { // TODO(ts): Remove deprecated and problematic InstrumentHandlerFunc usage.
log.Fatalf("Couldn't register collector: %s", err) http.HandleFunc(*metricsPath, prometheus.InstrumentHandlerFunc("prometheus", handler))
}
handler := promhttp.HandlerFor(prometheus.DefaultGatherer,
promhttp.HandlerOpts{
ErrorLog: log.NewErrorLogger(),
ErrorHandling: promhttp.ContinueOnError,
})
// TODO(ts): Remove deprecated and problematic InstrumentHandler usage.
http.Handle(*metricsPath, prometheus.InstrumentHandler("prometheus", handler))
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(`<html> w.Write([]byte(`<html>
<head><title>Node Exporter</title></head> <head><title>Node Exporter</title></head>