Add exclude[] parameter (#3116)

This PR adds a `exclude[]` URL parameter to exclude specific enabled collectors.
Compared to `collect[]` parameter, the `exclude[]` parameter results in a filtered list which equals enabled collectors minus excluded ones.

Signed-off-by: Siavash Safi <git@hosted.run>
This commit is contained in:
Siavash Safi 2024-09-14 19:45:47 +02:00 committed by GitHub
parent 090957658e
commit 7a97429e57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 44 additions and 11 deletions

View File

@ -339,13 +339,21 @@ mv /path/to/directory/role.prom.$$ /path/to/directory/role.prom
The `node_exporter` will expose all metrics from enabled collectors by default. This is the recommended way to collect metrics to avoid errors when comparing metrics of different families. The `node_exporter` will expose all metrics from enabled collectors by default. This is the recommended way to collect metrics to avoid errors when comparing metrics of different families.
For advanced use the `node_exporter` can be passed an optional list of collectors to filter metrics. The `collect[]` parameter may be used multiple times. In Prometheus configuration you can use this syntax under the [scrape config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#<scrape_config>). For advanced use the `node_exporter` can be passed an optional list of collectors to filter metrics. The parameters `collect[]` and `exclude[]` can be used multiple times (but cannot be combined). In Prometheus configuration you can use this syntax under the [scrape config](https://prometheus.io/docs/prometheus/latest/configuration/configuration/#<scrape_config>).
Collect only `cpu` and `meminfo` collector metrics:
``` ```
params: params:
collect[]: collect[]:
- foo - cpu
- bar - meminfo
```
Collect all enabled collector metrics but exclude `netdev`:
```
params:
exclude[]:
- netdev
``` ```
This can be useful for having different Prometheus servers collect specific metrics from nodes. This can be useful for having different Prometheus servers collect specific metrics from nodes.

View File

@ -21,6 +21,7 @@ import (
"os" "os"
"os/user" "os/user"
"runtime" "runtime"
"slices"
"sort" "sort"
"github.com/prometheus/common/promslog" "github.com/prometheus/common/promslog"
@ -42,6 +43,8 @@ import (
// newHandler. // newHandler.
type handler struct { type handler struct {
unfilteredHandler http.Handler unfilteredHandler http.Handler
// enabledCollectors list is used for logging and filtering
enabledCollectors []string
// exporterMetricsRegistry is a separate registry for the metrics about // exporterMetricsRegistry is a separate registry for the metrics about
// the exporter itself. // the exporter itself.
exporterMetricsRegistry *prometheus.Registry exporterMetricsRegistry *prometheus.Registry
@ -73,16 +76,39 @@ func newHandler(includeExporterMetrics bool, maxRequests int, logger *slog.Logge
// ServeHTTP implements http.Handler. // ServeHTTP implements http.Handler.
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
filters := r.URL.Query()["collect[]"] collects := r.URL.Query()["collect[]"]
h.logger.Debug("collect query:", "filters", filters) h.logger.Debug("collect query:", "collects", collects)
if len(filters) == 0 { excludes := r.URL.Query()["exclude[]"]
h.logger.Debug("exclude query:", "excludes", excludes)
if len(collects) == 0 && len(excludes) == 0 {
// No filters, use the prepared unfiltered handler. // No filters, use the prepared unfiltered handler.
h.unfilteredHandler.ServeHTTP(w, r) h.unfilteredHandler.ServeHTTP(w, r)
return return
} }
if len(collects) > 0 && len(excludes) > 0 {
h.logger.Debug("rejecting combined collect and exclude queries")
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Combined collect and exclude queries are not allowed."))
return
}
filters := &collects
if len(excludes) > 0 {
// In exclude mode, filtered collectors = enabled - excludeed.
f := []string{}
for _, c := range h.enabledCollectors {
if (slices.Index(excludes, c)) == -1 {
f = append(f, c)
}
}
filters = &f
}
// To serve filtered metrics, we create a filtering handler on the fly. // To serve filtered metrics, we create a filtering handler on the fly.
filteredHandler, err := h.innerHandler(filters...) filteredHandler, err := h.innerHandler(*filters...)
if err != nil { if err != nil {
h.logger.Warn("Couldn't create filtered metrics handler:", "err", err) h.logger.Warn("Couldn't create filtered metrics handler:", "err", err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
@ -107,12 +133,11 @@ func (h *handler) innerHandler(filters ...string) (http.Handler, error) {
// only once upon startup. // only once upon startup.
if len(filters) == 0 { if len(filters) == 0 {
h.logger.Info("Enabled collectors") h.logger.Info("Enabled collectors")
collectors := []string{}
for n := range nc.Collectors { for n := range nc.Collectors {
collectors = append(collectors, n) h.enabledCollectors = append(h.enabledCollectors, n)
} }
sort.Strings(collectors) sort.Strings(h.enabledCollectors)
for _, c := range collectors { for _, c := range h.enabledCollectors {
h.logger.Info(c) h.logger.Info(c)
} }
} }