prometheus-node-exporter-lua: add unbound stats collector
authorBruno Marinier <redacted>
Mon, 8 Jun 2026 19:25:29 +0000 (15:25 -0400)
committerEtienne Champetier <redacted>
Wed, 10 Jun 2026 19:58:34 +0000 (15:58 -0400)
- New unbound.lua exporter module for unbound stats
- Updated Makefile

Signed-off-by: Bruno Marinier <redacted>
utils/prometheus-node-exporter-lua/Makefile
utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/unbound.lua [new file with mode: 0644]

index 58ed19e920008779fdbb8a7970d668cdf2067bfe..8eaa9a79ff3fb9ebdb4d67b911ce5039e4eb4d7f 100644 (file)
@@ -4,7 +4,7 @@
 include $(TOPDIR)/rules.mk
 
 PKG_NAME:=prometheus-node-exporter-lua
-PKG_VERSION:=2026.05.10
+PKG_VERSION:=2026.06.05
 PKG_RELEASE:=1
 
 PKG_MAINTAINER:=Etienne CHAMPETIER <champetier.etienne@gmail.com>
@@ -301,6 +301,22 @@ define Package/prometheus-node-exporter-lua-modemmanager/install
        $(INSTALL_DATA) ./files/usr/lib/lua/prometheus-collectors/modemmanager.lua $(1)/usr/lib/lua/prometheus-collectors/
 endef
 
+define Package/prometheus-node-exporter-lua-unbound
+  $(call Package/prometheus-node-exporter-lua/Default)
+  TITLE+= (unbound stats collector)
+  DEPENDS:=prometheus-node-exporter-lua +unbound-daemon
+endef
+
+define Package/prometheus-node-exporter-lua-unbound/description
+  Adds a Unbound stats collector to the Prometheus node exporter.
+  Refer to https://github.com/marinierb/prometheus-node-exporter-lua-unbound for configuration details.
+endef
+
+define Package/prometheus-node-exporter-lua-unbound/install
+       $(INSTALL_DIR) $(1)/usr/lib/lua/prometheus-collectors
+       $(INSTALL_DATA) ./files/usr/lib/lua/prometheus-collectors/unbound.lua $(1)/usr/lib/lua/prometheus-collectors/
+endef
+
 $(eval $(call BuildPackage,prometheus-node-exporter-lua))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-bmx7))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-dawn))
@@ -324,3 +340,4 @@ $(eval $(call BuildPackage,prometheus-node-exporter-lua-mwan3))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-ethtool))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-nft-counters))
 $(eval $(call BuildPackage,prometheus-node-exporter-lua-modemmanager))
+$(eval $(call BuildPackage,prometheus-node-exporter-lua-unbound))
diff --git a/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/unbound.lua b/utils/prometheus-node-exporter-lua/files/usr/lib/lua/prometheus-collectors/unbound.lua
new file mode 100644 (file)
index 0000000..0555e8d
--- /dev/null
@@ -0,0 +1,56 @@
+-- Unbound stats exporter
+
+local socket = require("socket")
+local unix = require("socket.unix")
+
+local function scrape()
+    local metrics = {
+        ["total.num.queries"                ] = metric("unbound_num_queries_total",                "counter"),
+        ["total.num.queries_ip_ratelimited" ] = metric("unbound_num_queries_ip_ratelimited_total", "counter"),
+        ["total.num.queries_cookie_valid"   ] = metric("unbound_num_queries_cookie_valid_total",   "counter"),
+        ["total.num.queries_cookie_client"  ] = metric("unbound_num_queries_cookie_client_total",  "counter"),
+        ["total.num.queries_cookie_invalid" ] = metric("unbound_num_queries_cookie_invalid_total", "counter"),
+        ["total.num.queries_discard_timeout"] = metric("unbound_num_queries_discard_timeout_total","counter"),
+        ["total.num.queries_wait_limit"     ] = metric("unbound_num_queries_wait_limit_total",     "counter"),
+        ["total.num.cachehits"              ] = metric("unbound_cachehits_total",                  "counter"),
+        ["total.num.cachemiss"              ] = metric("unbound_cachemiss_total",                  "counter"),
+        ["total.num.prefetch"               ] = metric("unbound_prefetch_total",                   "counter"),
+        ["total.num.queries_timed_out"      ] = metric("unbound_num_queries_timed_out_total",      "counter"),
+        ["total.query.queue_time_us.max"    ] = metric("unbound_query_queue_time_us_max",          "gauge"),
+        ["total.num.expired"                ] = metric("unbound_num_expired_total",                "counter"),
+        ["total.num.recursivereplies"       ] = metric("unbound_num_recursivereplies_total",       "counter"),
+        ["total.num.dns_error_reports"      ] = metric("unbound_num_dns_error_reports_total",      "counter"),
+        ["total.requestlist.avg"            ] = metric("unbound_requestlist_avg",                  "gauge"),
+        ["total.requestlist.max"            ] = metric("unbound_requestlist_max",                  "gauge"),
+        ["total.requestlist.overwritten"    ] = metric("unbound_requestlist_overwritten_total",    "counter"),
+        ["total.requestlist.exceeded"       ] = metric("unbound_requestlist_exceeded_total",       "counter"),
+        ["total.requestlist.current.all"    ] = metric("unbound_requestlist_current_all",          "gauge"),
+        ["total.requestlist.current.user"   ] = metric("unbound_requestlist_current_user",         "gauge"),
+        ["total.recursion.time.avg"         ] = metric("unbound_recursion_time_avg",               "gauge"),
+        ["total.recursion.time.median"      ] = metric("unbound_recursion_time_median",            "gauge"),
+    }
+
+    local sock = unix()
+    local ok, err = sock:connect("/run/unbound.ctl")
+    if not ok then
+        return nil, "failed to connect to unbound socket: " .. (err or "unknown")
+    end
+
+    sock:settimeout(1)
+    sock:send("UBCT1 stats_noreset\n")
+    local out = sock:receive("*a")
+
+    sock:close()
+
+    for line in out:gmatch("[^\r\n]+") do
+        local key, val = line:match("^([^=]+)=(.+)$")
+        if key and val then
+            local n = tonumber(val)
+            if metrics[key] then
+                metrics[key]({}, n)
+            end
+        end
+    end
+end
+
+return { scrape = scrape }
git clone https://git.99rst.org/PROJECT