summaryrefslogtreecommitdiff
path: root/bind-exporter/metrics.go
diff options
context:
space:
mode:
Diffstat (limited to 'bind-exporter/metrics.go')
-rw-r--r--bind-exporter/metrics.go434
1 files changed, 434 insertions, 0 deletions
diff --git a/bind-exporter/metrics.go b/bind-exporter/metrics.go
new file mode 100644
index 0000000..185f437
--- /dev/null
+++ b/bind-exporter/metrics.go
@@ -0,0 +1,434 @@
1package main
2
3import (
4 "context"
5 "encoding/json"
6 "fmt"
7 "net/http"
8
9 "github.com/prometheus/client_golang/prometheus"
10)
11
12type bindMetric struct {
13 Name string
14 Help string
15 Labels []string
16 Extract func(*prometheus.Desc, *BindStats) []prometheus.Metric
17 desc *prometheus.Desc
18}
19
20type bindViewMetric struct {
21 Name string
22 Help string
23 Labels []string
24 Extract func(*prometheus.Desc, string, *View) []prometheus.Metric
25 desc *prometheus.Desc
26}
27
28type bindZoneMetric struct {
29 Name string
30 Help string
31 Labels []string
32 Extract func(*prometheus.Desc, string, *View, *Zone) []prometheus.Metric
33 desc *prometheus.Desc
34}
35
36type bindSocketMetric struct {
37 Name string
38 Help string
39 Labels []string
40 Suffix string
41 Extract func(*prometheus.Desc, string, map[string]int) []prometheus.Metric
42 desc *prometheus.Desc
43}
44
45type bindCollector struct {
46 ctx context.Context
47 target string
48 metrics []*bindMetric
49 viewMetrics []*bindViewMetric
50 zoneMetrics []*bindZoneMetric
51 socketMetrics []*bindSocketMetric
52}
53
54var _ prometheus.Collector = (*bindCollector)(nil)
55
56func socketMetricExtractor(d *prometheus.Desc, suffix string, m map[string]int) []prometheus.Metric {
57 out := []prometheus.Metric{}
58
59 for _, p := range []string{"UDP", "TCP"} {
60 for _, v := range []string{"4", "6"} {
61 if value, ok := m[p+v+suffix]; ok {
62 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(value), p, v))
63 }
64 }
65 }
66
67 if value, ok := m["Raw"+suffix]; ok {
68 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(value), "Raw", ""))
69 }
70
71 return out
72}
73
74func kvExtractor(d *prometheus.Desc, m map[string]int) []prometheus.Metric {
75 out := make([]prometheus.Metric, 0, len(m))
76
77 for k, v := range m {
78 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), k))
79 }
80
81 return out
82}
83
84func singleGaugeIntMetric(d *prometheus.Desc, v int) []prometheus.Metric {
85 return singleGaugeInt64Metric(d, int64(v))
86}
87
88func singleGaugeInt64Metric(d *prometheus.Desc, v int64) []prometheus.Metric {
89 return []prometheus.Metric{prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v))}
90}
91
92func NewBindCollector(ctx context.Context, target string) prometheus.Collector {
93 c := &bindCollector{
94 ctx: ctx,
95 target: target,
96 metrics: []*bindMetric{
97 &bindMetric{
98 Name: "bind_boot_time",
99 Help: "Unix timestamp of when the server was last booted",
100 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
101 return singleGaugeInt64Metric(d, s.BootTime.Unix())
102 },
103 },
104 &bindMetric{
105 Name: "bind_config_time",
106 Help: "Unix timestamp of when the server was last configured",
107 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
108 return singleGaugeInt64Metric(d, s.ConfigTime.Unix())
109 },
110 },
111 &bindMetric{
112 Name: "bind_memory_total_use",
113 Help: "Total memory bytes in used by the server",
114 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
115 return singleGaugeIntMetric(d, s.MemoryStats.TotalUse)
116 },
117 },
118 &bindMetric{
119 Name: "bind_memory_in_use",
120 Help: "Total memory bytes currently in use by the server",
121 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
122 return singleGaugeIntMetric(d, s.MemoryStats.InUse)
123 },
124 },
125 &bindMetric{
126 Name: "bind_memory_malloced",
127 Help: "Total memory bytes currently malloced by the server",
128 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
129 return singleGaugeIntMetric(d, s.MemoryStats.Malloced)
130 },
131 },
132 &bindMetric{
133 Name: "bind_server_op_codes",
134 Help: "Operation codes requested from the server as a whole",
135 Labels: []string{"opcode"},
136 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
137 return kvExtractor(d, s.OperationCodeCount)
138 },
139 },
140 &bindMetric{
141 Name: "bind_notify_out",
142 Help: "Count of notifies sent by IP version",
143 Labels: []string{"ipv"},
144 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
145 out := []prometheus.Metric{}
146 if v4out, ok := s.ZoneStats["NotifyOutv4"]; ok {
147 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v4out), "4"))
148 }
149 if v6out, ok := s.ZoneStats["NotifyOutv6"]; ok {
150 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v6out), "6"))
151 }
152 return out
153 },
154 },
155 &bindMetric{
156 Name: "bind_server_response_codes",
157 Help: "Response codes sent from the server as a whole",
158 Labels: []string{"rcode"},
159 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
160 return kvExtractor(d, s.ResponseCodeCount)
161 },
162 },
163 &bindMetric{
164 Name: "bind_server_query_types",
165 Help: "Query types sent to the server as a whole",
166 Labels: []string{"qtype"},
167 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
168 return kvExtractor(d, s.QueryTypeCount)
169 },
170 },
171 &bindMetric{
172 Name: "bind_server_stats",
173 Help: "Name server statistics for the server as a whole",
174 Labels: []string{"stat"},
175 Extract: func(d *prometheus.Desc, s *BindStats) []prometheus.Metric {
176 return kvExtractor(d, s.NameserverStats)
177 },
178 },
179 },
180 viewMetrics: []*bindViewMetric{
181 &bindViewMetric{
182 Name: "bind_view_resolver_stats",
183 Help: "Resolver stats for a view",
184 Labels: []string{"view", "stat"},
185 Extract: func(d *prometheus.Desc, name string, v *View) []prometheus.Metric {
186 out := make([]prometheus.Metric, 0, len(v.Resolver.Stats))
187 for k, v := range v.Resolver.Stats {
188 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), name, k))
189 }
190 return out
191 },
192 },
193 &bindViewMetric{
194 Name: "bind_view_cache_stats",
195 Help: "Cache stats for a view",
196 Labels: []string{"view", "stat"},
197 Extract: func(d *prometheus.Desc, name string, v *View) []prometheus.Metric {
198 out := make([]prometheus.Metric, 0, len(v.Resolver.CacheStats))
199 for k, v := range v.Resolver.CacheStats {
200 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), name, k))
201 }
202 return out
203 },
204 },
205 &bindViewMetric{
206 Name: "bind_view_query_count",
207 Help: "Count of queries by qtype in a view",
208 Labels: []string{"view", "qtype"},
209 Extract: func(d *prometheus.Desc, name string, v *View) []prometheus.Metric {
210 out := make([]prometheus.Metric, 0, len(v.Resolver.QueryTypes))
211 for k, v := range v.Resolver.QueryTypes {
212 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), name, k))
213 }
214 return out
215 },
216 },
217 &bindViewMetric{
218 Name: "bind_view_cache_entry_count",
219 Help: "Cache entry count for a view by RRtype",
220 Labels: []string{"view", "rrtype"},
221 Extract: func(d *prometheus.Desc, name string, v *View) []prometheus.Metric {
222 out := make([]prometheus.Metric, 0, len(v.Resolver.CacheRecordTypes))
223 for k, v := range v.Resolver.CacheRecordTypes {
224 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), name, k))
225 }
226 return out
227 },
228 },
229 },
230 zoneMetrics: []*bindZoneMetric{
231 &bindZoneMetric{
232 Name: "bind_zone_serial",
233 Help: "Serial number of zone",
234 Labels: []string{"view", "zone", "type"},
235 Extract: func(d *prometheus.Desc, viewName string, v *View, z *Zone) []prometheus.Metric {
236 return []prometheus.Metric{
237 prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(z.Serial), viewName, z.Name, z.Type),
238 }
239 },
240 },
241 &bindZoneMetric{
242 Name: "bind_zone_load_time",
243 Help: "Zone load time of a zone",
244 Labels: []string{"view", "zone", "type"},
245 Extract: func(d *prometheus.Desc, viewName string, v *View, z *Zone) []prometheus.Metric {
246 return []prometheus.Metric{
247 prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(z.LoadTime.Unix()), viewName, z.Name, z.Type),
248 }
249 },
250 },
251 &bindZoneMetric{
252 Name: "bind_zone_response_codes",
253 Help: "Response codes sent from the server for a zone in a view",
254 Labels: []string{"view", "zone", "type", "rcode"},
255 Extract: func(d *prometheus.Desc, viewName string, v *View, z *Zone) []prometheus.Metric {
256 out := make([]prometheus.Metric, 0, len(z.ResponseCodes))
257 for k, v := range z.ResponseCodes {
258 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), viewName, z.Name, z.Type, k))
259 }
260 return out
261 },
262 },
263 &bindZoneMetric{
264 Name: "bind_zone_query_types",
265 Help: "Query types sent to the server for a zone in a view",
266 Labels: []string{"view", "zone", "type", "qtype"},
267 Extract: func(d *prometheus.Desc, viewName string, v *View, z *Zone) []prometheus.Metric {
268 out := make([]prometheus.Metric, 0, len(z.QueryTypes))
269 for k, v := range z.QueryTypes {
270 out = append(out, prometheus.MustNewConstMetric(d, prometheus.GaugeValue, float64(v), viewName, z.Name, z.Type, k))
271 }
272 return out
273 },
274 },
275 },
276 socketMetrics: []*bindSocketMetric{
277 &bindSocketMetric{
278 Name: "bind_socket_open_count",
279 Help: "Count of sockets opened by protocol and version",
280 Suffix: "Open",
281 Labels: []string{"proto", "version"},
282 Extract: socketMetricExtractor,
283 },
284 &bindSocketMetric{
285 Name: "bind_socket_close_count",
286 Help: "Count of sockets closed by protocol and version",
287 Suffix: "Close",
288 Labels: []string{"proto", "version"},
289 Extract: socketMetricExtractor,
290 },
291 &bindSocketMetric{
292 Name: "bind_socket_bind_fail_count",
293 Help: "Count of sockets bind failures by protocol and version",
294 Suffix: "BindFail",
295 Labels: []string{"proto", "version"},
296 Extract: socketMetricExtractor,
297 },
298 &bindSocketMetric{
299 Name: "bind_socket_connect_count",
300 Help: "Count of sockets connections by protocol and version",
301 Suffix: "Conn",
302 Labels: []string{"proto", "version"},
303 Extract: socketMetricExtractor,
304 },
305 &bindSocketMetric{
306 Name: "bind_tcp_accept_fail_count",
307 Help: "Count of TCP sockets accept failures by version",
308 Suffix: "AcceptFail",
309 Labels: []string{"proto", "version"},
310 Extract: socketMetricExtractor,
311 },
312 &bindSocketMetric{
313 Name: "bind_tcp_connect_fail_count",
314 Help: "Count of sockets connect failures by version",
315 Suffix: "ConnFail",
316 Labels: []string{"proto", "version"},
317 Extract: socketMetricExtractor,
318 },
319 &bindSocketMetric{
320 Name: "bind_accept_count",
321 Help: "Count of sockets accept success by version",
322 Suffix: "Accept",
323 Labels: []string{"proto", "version"},
324 Extract: socketMetricExtractor,
325 },
326 &bindSocketMetric{
327 Name: "bind_socket_recv_error_count",
328 Help: "Count of sockets receive error by protocol and version",
329 Suffix: "RecvErr",
330 Labels: []string{"proto", "version"},
331 Extract: socketMetricExtractor,
332 },
333 &bindSocketMetric{
334 Name: "bind_socket_active_count",
335 Help: "Count of active sockets by protocol and version",
336 Suffix: "Active",
337 Labels: []string{"proto", "version"},
338 Extract: socketMetricExtractor,
339 },
340 },
341 }
342
343 for _, m := range c.metrics {
344 m.desc = prometheus.NewDesc(m.Name, m.Help, m.Labels, nil)
345 }
346
347 for _, m := range c.viewMetrics {
348 m.desc = prometheus.NewDesc(m.Name, m.Help, m.Labels, nil)
349 }
350
351 for _, m := range c.zoneMetrics {
352 m.desc = prometheus.NewDesc(m.Name, m.Help, m.Labels, nil)
353 }
354
355 for _, m := range c.socketMetrics {
356 m.desc = prometheus.NewDesc(m.Name, m.Help, m.Labels, nil)
357 }
358
359 return c
360}
361
362func (c *bindCollector) Describe(ch chan<- *prometheus.Desc) {
363 for _, m := range c.metrics {
364 ch <- m.desc
365 }
366
367 for _, m := range c.viewMetrics {
368 ch <- m.desc
369 }
370
371 for _, m := range c.zoneMetrics {
372 ch <- m.desc
373 }
374
375 for _, m := range c.socketMetrics {
376 ch <- m.desc
377 }
378}
379
380func (c *bindCollector) Collect(ch chan<- prometheus.Metric) {
381 var s BindStats
382
383 req, err := http.NewRequestWithContext(c.ctx, http.MethodGet, c.target, nil)
384 if err != nil {
385 ch <- prometheus.NewInvalidMetric(
386 prometheus.NewDesc("bind_error", "Error building http request", nil, nil),
387 fmt.Errorf("Error building http request: %w", err))
388 return
389 }
390
391 res, err := http.DefaultClient.Do(req)
392 if err != nil {
393 ch <- prometheus.NewInvalidMetric(
394 prometheus.NewDesc("bind_error", "Error retrieving bind metrics", nil, nil),
395 fmt.Errorf("HTTP error while retrieving bind metrics: %w", err))
396 return
397 }
398 defer res.Body.Close()
399
400 if err := json.NewDecoder(res.Body).Decode(&s); err != nil {
401 ch <- prometheus.NewInvalidMetric(
402 prometheus.NewDesc("bind_error", "Error decoding bind metrics", nil, nil),
403 fmt.Errorf("Error JSON decoding bind metrics: %w", err))
404 return
405 }
406
407 for _, m := range c.metrics {
408 for _, v := range m.Extract(m.desc, &s) {
409 ch <- v
410 }
411 }
412
413 for vn, v := range s.Views {
414 for _, m := range c.viewMetrics {
415 for _, mv := range m.Extract(m.desc, vn, &v) {
416 ch <- mv
417 }
418 }
419
420 for _, z := range v.Zones {
421 for _, m := range c.zoneMetrics {
422 for _, mv := range m.Extract(m.desc, vn, &v, &z) {
423 ch <- mv
424 }
425 }
426 }
427 }
428
429 for _, m := range c.socketMetrics {
430 for _, mv := range m.Extract(m.desc, m.Suffix, s.SocketStats) {
431 ch <- mv
432 }
433 }
434}