diff options
author | beorn7 <beorn@soundcloud.com> | 2018-11-08 16:26:21 +0100 |
---|---|---|
committer | beorn7 <beorn@soundcloud.com> | 2018-11-13 14:22:25 +0100 |
commit | cd2331a1852384263e75cf988aeabe39fe33ffe0 (patch) | |
tree | b0cf2de989d15715fe485b6f8c2dcac73882b4f9 /node_exporter.go | |
parent | b1eec66640af3d1940f74484fe1a3c0487204801 (diff) | |
download | prometheus_node_collector-cd2331a1852384263e75cf988aeabe39fe33ffe0.tar.bz2 prometheus_node_collector-cd2331a1852384263e75cf988aeabe39fe33ffe0.tar.xz prometheus_node_collector-cd2331a1852384263e75cf988aeabe39fe33ffe0.zip |
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>
Diffstat (limited to 'node_exporter.go')
-rw-r--r-- | node_exporter.go | 139 |
1 files changed, 95 insertions, 44 deletions
diff --git a/node_exporter.go b/node_exporter.go index 79863b9..0475a6a 100644 --- a/node_exporter.go +++ b/node_exporter.go | |||
@@ -27,51 +27,118 @@ import ( | |||
27 | "gopkg.in/alecthomas/kingpin.v2" | 27 | "gopkg.in/alecthomas/kingpin.v2" |
28 | ) | 28 | ) |
29 | 29 | ||
30 | func init() { | 30 | // handler wraps an unfiltered http.Handler but uses a filtered handler, |
31 | prometheus.MustRegister(version.NewCollector("node_exporter")) | 31 | // created on the fly, if filtering is requested. Create instances with |
32 | // newHandler. | ||
33 | type handler struct { | ||
34 | unfilteredHandler http.Handler | ||
35 | // exporterMetricsRegistry is a separate registry for the metrics about | ||
36 | // the exporter itself. | ||
37 | exporterMetricsRegistry *prometheus.Registry | ||
38 | includeExporterMetrics bool | ||
32 | } | 39 | } |
33 | 40 | ||
34 | func handler(w http.ResponseWriter, r *http.Request) { | 41 | func newHandler(includeExporterMetrics bool) *handler { |
42 | h := &handler{ | ||
43 | exporterMetricsRegistry: prometheus.NewRegistry(), | ||
44 | includeExporterMetrics: includeExporterMetrics, | ||
45 | } | ||
46 | if h.includeExporterMetrics { | ||
47 | h.exporterMetricsRegistry.MustRegister( | ||
48 | prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}), | ||
49 | prometheus.NewGoCollector(), | ||
50 | ) | ||
51 | } | ||
52 | if innerHandler, err := h.innerHandler(); err != nil { | ||
53 | log.Fatalf("Couldn't create metrics handler: %s", err) | ||
54 | } else { | ||
55 | h.unfilteredHandler = innerHandler | ||
56 | } | ||
57 | return h | ||
58 | } | ||
59 | |||
60 | // ServeHTTP implements http.Handler. | ||
61 | func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
35 | filters := r.URL.Query()["collect[]"] | 62 | filters := r.URL.Query()["collect[]"] |
36 | log.Debugln("collect query:", filters) | 63 | log.Debugln("collect query:", filters) |
37 | 64 | ||
38 | nc, err := collector.NewNodeCollector(filters...) | 65 | if len(filters) == 0 { |
66 | // No filters, use the prepared unfiltered handler. | ||
67 | h.unfilteredHandler.ServeHTTP(w, r) | ||
68 | return | ||
69 | } | ||
70 | // To serve filtered metrics, we create a filtering handler on the fly. | ||
71 | filteredHandler, err := h.innerHandler(filters...) | ||
39 | if err != nil { | 72 | if err != nil { |
40 | log.Warnln("Couldn't create", err) | 73 | log.Warnln("Couldn't create filtered metrics handler:", err) |
41 | w.WriteHeader(http.StatusBadRequest) | 74 | w.WriteHeader(http.StatusBadRequest) |
42 | w.Write([]byte(fmt.Sprintf("Couldn't create %s", err))) | 75 | w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err))) |
43 | return | 76 | return |
44 | } | 77 | } |
78 | filteredHandler.ServeHTTP(w, r) | ||
79 | } | ||
45 | 80 | ||
46 | registry := prometheus.NewRegistry() | 81 | // innerHandler is used to create buth the one unfiltered http.Handler to be |
47 | err = registry.Register(nc) | 82 | // wrapped by the outer handler and also the filtered handlers created on the |
83 | // fly. The former is accomplished by calling innerHandler without any arguments | ||
84 | // (in which case it will log all the collectors enabled via command-line | ||
85 | // flags). | ||
86 | func (h *handler) innerHandler(filters ...string) (http.Handler, error) { | ||
87 | nc, err := collector.NewNodeCollector(filters...) | ||
48 | if err != nil { | 88 | if err != nil { |
49 | log.Errorln("Couldn't register collector:", err) | 89 | return nil, fmt.Errorf("couldn't create collector: %s", err) |
50 | w.WriteHeader(http.StatusInternalServerError) | 90 | } |
51 | w.Write([]byte(fmt.Sprintf("Couldn't register collector: %s", err))) | 91 | |
52 | return | 92 | // Only log the creation of an unfiltered handler, which should happen |
93 | // only once upon startup. | ||
94 | if len(filters) == 0 { | ||
95 | log.Infof("Enabled collectors:") | ||
96 | collectors := []string{} | ||
97 | for n := range nc.Collectors { | ||
98 | collectors = append(collectors, n) | ||
99 | } | ||
100 | sort.Strings(collectors) | ||
101 | for _, n := range collectors { | ||
102 | log.Infof(" - %s", n) | ||
103 | } | ||
53 | } | 104 | } |
54 | 105 | ||
55 | gatherers := prometheus.Gatherers{ | 106 | r := prometheus.NewRegistry() |
56 | prometheus.DefaultGatherer, | 107 | r.MustRegister(version.NewCollector("node_exporter")) |
57 | registry, | 108 | if err := r.Register(nc); err != nil { |
109 | return nil, fmt.Errorf("couldn't register node collector: %s", err) | ||
58 | } | 110 | } |
59 | // Delegate http serving to Prometheus client library, which will call collector.Collect. | 111 | handler := promhttp.HandlerFor( |
60 | h := promhttp.InstrumentMetricHandler( | 112 | prometheus.Gatherers{h.exporterMetricsRegistry, r}, |
61 | registry, | 113 | promhttp.HandlerOpts{ |
62 | promhttp.HandlerFor(gatherers, | 114 | ErrorLog: log.NewErrorLogger(), |
63 | promhttp.HandlerOpts{ | 115 | ErrorHandling: promhttp.ContinueOnError, |
64 | ErrorLog: log.NewErrorLogger(), | 116 | }, |
65 | ErrorHandling: promhttp.ContinueOnError, | ||
66 | }), | ||
67 | ) | 117 | ) |
68 | h.ServeHTTP(w, r) | 118 | if h.includeExporterMetrics { |
119 | // Note that we have to use h.exporterMetricsRegistry here to | ||
120 | // use the same promhttp metrics for all expositions. | ||
121 | handler = promhttp.InstrumentMetricHandler( | ||
122 | h.exporterMetricsRegistry, handler, | ||
123 | ) | ||
124 | } | ||
125 | return handler, nil | ||
69 | } | 126 | } |
70 | 127 | ||
71 | func main() { | 128 | func main() { |
72 | var ( | 129 | var ( |
73 | listenAddress = kingpin.Flag("web.listen-address", "Address on which to expose metrics and web interface.").Default(":9100").String() | 130 | listenAddress = kingpin.Flag( |
74 | metricsPath = kingpin.Flag("web.telemetry-path", "Path under which to expose metrics.").Default("/metrics").String() | 131 | "web.listen-address", |
132 | "Address on which to expose metrics and web interface.", | ||
133 | ).Default(":9100").String() | ||
134 | metricsPath = kingpin.Flag( | ||
135 | "web.telemetry-path", | ||
136 | "Path under which to expose metrics.", | ||
137 | ).Default("/metrics").String() | ||
138 | disableExporterMetrics = kingpin.Flag( | ||
139 | "web.disable-exporter-metrics", | ||
140 | "Exclude metrics about the exporter itself (promhttp_*, process_*, go_*).", | ||
141 | ).Bool() | ||
75 | ) | 142 | ) |
76 | 143 | ||
77 | log.AddFlags(kingpin.CommandLine) | 144 | log.AddFlags(kingpin.CommandLine) |
@@ -82,22 +149,7 @@ func main() { | |||
82 | log.Infoln("Starting node_exporter", version.Info()) | 149 | log.Infoln("Starting node_exporter", version.Info()) |
83 | log.Infoln("Build context", version.BuildContext()) | 150 | log.Infoln("Build context", version.BuildContext()) |
84 | 151 | ||
85 | // This instance is only used to check collector creation and logging. | 152 | http.Handle(*metricsPath, newHandler(!*disableExporterMetrics)) |
86 | nc, err := collector.NewNodeCollector() | ||
87 | if err != nil { | ||
88 | log.Fatalf("Couldn't create collector: %s", err) | ||
89 | } | ||
90 | log.Infof("Enabled collectors:") | ||
91 | collectors := []string{} | ||
92 | for n := range nc.Collectors { | ||
93 | collectors = append(collectors, n) | ||
94 | } | ||
95 | sort.Strings(collectors) | ||
96 | for _, n := range collectors { | ||
97 | log.Infof(" - %s", n) | ||
98 | } | ||
99 | |||
100 | http.HandleFunc(*metricsPath, handler) | ||
101 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { | 153 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
102 | w.Write([]byte(`<html> | 154 | w.Write([]byte(`<html> |
103 | <head><title>Node Exporter</title></head> | 155 | <head><title>Node Exporter</title></head> |
@@ -109,8 +161,7 @@ func main() { | |||
109 | }) | 161 | }) |
110 | 162 | ||
111 | log.Infoln("Listening on", *listenAddress) | 163 | log.Infoln("Listening on", *listenAddress) |
112 | err = http.ListenAndServe(*listenAddress, nil) | 164 | if err := http.ListenAndServe(*listenAddress, nil); err != nil { |
113 | if err != nil { | ||
114 | log.Fatal(err) | 165 | log.Fatal(err) |
115 | } | 166 | } |
116 | } | 167 | } |