From 662719675888cc338e348b4a733e015155e73a9d Mon Sep 17 00:00:00 2001 From: SaiPisey2 Date: Wed, 26 Aug 2026 23:44:34 +0530 Subject: [PATCH] fix: report bind_recursive_clients as a gauge BIND reports RecursClients in the same statistics counters as the running totals, and the exporter typed every entry of serverMetricStats as a counter. RecursClients is the number of recursive clients currently being served, so it goes up and down and is a gauge. The metric name already reflects that: every other entry in the map ends in _total, and this one does not. Type the entries explicitly and mark RecursClients as a gauge. The sample value is unchanged, only the reported type. Rates computed over it were meaningless before this, since the series is not monotonic. Asserts the type in the exporter tests, which previously only covered the sample line. Signed-off-by: SaiPisey2 --- bind_exporter.go | 19 +++++++++++++++++-- bind_exporter_test.go | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/bind_exporter.go b/bind_exporter.go index 7f89374c..eb42b2a0 100644 --- a/bind_exporter.go +++ b/bind_exporter.go @@ -208,6 +208,12 @@ var ( nil, nil, ), } + // serverMetricGauges lists the serverMetricStats entries that report a + // current value rather than a running total. BIND reports them in the same + // statistics counters as the totals, so they have to be typed explicitly. + serverMetricGauges = map[string]struct{}{ + "RecursClients": {}, + } tasksRunning = prometheus.NewDesc( prometheus.BuildFQName(namespace, "", "tasks_running"), "Number of running tasks.", @@ -280,7 +286,7 @@ func (c *serverCollector) Collect(ch chan<- prometheus.Metric) { } if desc, ok := serverMetricStats[s.Name]; ok { ch <- prometheus.MustNewConstMetric( - desc, prometheus.CounterValue, float64(s.Counter), + desc, serverMetricValueType(s.Name), float64(s.Counter), ) } } @@ -292,12 +298,21 @@ func (c *serverCollector) Collect(ch chan<- prometheus.Metric) { for _, s := range c.stats.Server.ZoneStatistics { if desc, ok := serverMetricStats[s.Name]; ok { ch <- prometheus.MustNewConstMetric( - desc, prometheus.CounterValue, float64(s.Counter), + desc, serverMetricValueType(s.Name), float64(s.Counter), ) } } } +// serverMetricValueType reports the value type to use for a serverMetricStats +// entry. +func serverMetricValueType(name string) prometheus.ValueType { + if _, ok := serverMetricGauges[name]; ok { + return prometheus.GaugeValue + } + return prometheus.CounterValue +} + type viewCollector struct { logger *slog.Logger stats *bind.Statistics diff --git a/bind_exporter_test.go b/bind_exporter_test.go index 868d0b2b..9250ff85 100644 --- a/bind_exporter_test.go +++ b/bind_exporter_test.go @@ -40,6 +40,8 @@ var ( `bind_zone_transfer_success_total 25`, `bind_zone_transfer_failure_total 1`, `bind_recursive_clients 76`, + // Current recursive clients is a gauge, not a counter. + `# TYPE bind_recursive_clients gauge`, `bind_config_time_seconds 1.626325868e+09`, `bind_response_rcodes_total{rcode="NOERROR"} 989812`, `bind_response_rcodes_total{rcode="NXDOMAIN"} 33958`,