From b2590c20220e1df4eb2eb10a4be11c6effe181d8 Mon Sep 17 00:00:00 2001 From: "W.C.A. Wijngaards" Date: Thu, 2 Apr 2026 10:37:56 +0200 Subject: [PATCH] - add-prometheus-metrics, set metrics to type 'counter', for number of query metrics, and do not reset the stats. There is a warning when statistics-cumulative has the wrong value. But the stats are not reset from the metrics endpoint regardless. The contrib/metrics.awk script is also updated, and the documentation recommends the cumulative setting. --- contrib/metrics.awk | 16 ++++++++-------- daemon/metrics.c | 31 ++++++++++++++++--------------- doc/unbound.conf.rst | 12 +++++++++++- 3 files changed, 35 insertions(+), 24 deletions(-) diff --git a/contrib/metrics.awk b/contrib/metrics.awk index ca48c035a..670aa1093 100644 --- a/contrib/metrics.awk +++ b/contrib/metrics.awk @@ -2,7 +2,7 @@ # and output prometheus metrics style output. # use these options: # server: extended-statistics: yes -# statistics-cumulative: no +# statistics-cumulative: yes # statistics-interval: 0 # remote-control: control-enable: yes # Can use it like unbound-control stats | awk -f "metrics.awk" @@ -17,7 +17,7 @@ BEGIN { # print the output metrics END { print "# HELP unbound_hits_queries Unbound DNS traffic and cache hits" - print "# TYPE unbound_hits_queries gauge" + print "# TYPE unbound_hits_queries counter" print "unbound_hits_queries{type=\"total.num.queries\"} " val["total.num.queries"]; for (x=0; x<99; x++) { if(val["thread" $x ".num.queries"] != "") { @@ -70,7 +70,7 @@ END { print "" print "# HELP unbound_by_type_queries Unbound DNS queries by type" - print "# TYPE unbound_by_type_queries gauge" + print "# TYPE unbound_by_type_queries counter" for(x in val) { if(x ~ /^num.query.type./) { if(val[x] != "") { @@ -82,7 +82,7 @@ END { print "" print "# HELP unbound_by_class_queries Unbound DNS queries by class" - print "# TYPE unbound_by_class_queries gauge" + print "# TYPE unbound_by_class_queries counter" for(x in val) { if(x ~ /^num.query.class./) { if(val[x] != "") { @@ -94,7 +94,7 @@ END { print "" print "# HELP unbound_by_opcode_queries Unbound DNS queries by opcode" - print "# TYPE unbound_by_opcode_queries gauge" + print "# TYPE unbound_by_opcode_queries counter" for(x in val) { if(x ~ /^num.query.opcode./) { if(val[x] != "") { @@ -106,7 +106,7 @@ END { print "" print "# HELP unbound_by_rcode_queries Unbound DNS answers by rcode" - print "# TYPE unbound_by_rcode_queries gauge" + print "# TYPE unbound_by_rcode_queries counter" for(x in val) { if(x ~ /^num.answer.rcode./) { if(val[x] != "") { @@ -118,7 +118,7 @@ END { print "" print "# HELP unbound_by_flags_queries Unbound DNS queries by flags" - print "# TYPE unbound_by_flags_queries gauge" + print "# TYPE unbound_by_flags_queries counter" for(x in val) { if(x ~ /^num.query.flags./) { if(val[x] != "") { @@ -136,7 +136,7 @@ END { print "" print "# HELP unbound_histogram_seconds Unbound DNS histogram of reply time" - print "# TYPE unbound_histogram_seconds gauge" + print "# TYPE unbound_histogram_seconds counter" print "unbound_histogram_seconds{bucket=\"000000.000000.to.000000.000001\"} " val["histogram.000000.000000.to.000000.000001"]; print "unbound_histogram_seconds{bucket=\"000000.000001.to.000000.000002\"} " val["histogram.000000.000001.to.000000.000002"]; print "unbound_histogram_seconds{bucket=\"000000.000002.to.000000.000004\"} " val["histogram.000000.000002.to.000000.000004"]; diff --git a/daemon/metrics.c b/daemon/metrics.c index 773eca9f9..076a65235 100644 --- a/daemon/metrics.c +++ b/daemon/metrics.c @@ -257,6 +257,8 @@ daemon_metrics_open_ports(struct daemon_metrics* metrics, struct config_file* cfg) { assert(cfg->metrics_enable); + if(!cfg->stat_cumulative) + log_warn("metrics-enable: yes but statistics-cumulative: no, access to control command 'stats' would reset the stat counters, perhaps set 'statistics-cumulative: yes'."); if(cfg->metrics_ifs.first) { char** rcif = NULL; int i, num_rcif = 0; @@ -334,7 +336,7 @@ metrics_print_types(struct evbuffer *reply) { char* prefix = METRICS_PREFIX; print_metric_help_and_type(reply, prefix, "hits_queries", - "Unbound DNS traffic and cache hits", "gauge"); + "Unbound DNS traffic and cache hits", "counter"); print_metric_help_and_type(reply, prefix, "queue_queries", "Unbound requestlist size", "gauge"); print_metric_help_and_type(reply, prefix, "recursion_time", @@ -540,7 +542,7 @@ metrics_print_hist(struct evbuffer* reply, struct ub_stats_info* s) size_t i; print_metric_help_and_type(reply, prefix, "histogram_seconds", - "Unbound DNS histogram of reply time", "gauge"); + "Unbound DNS histogram of reply time", "counter"); hist = timehist_setup(); if(!hist) { @@ -581,7 +583,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* TYPE */ print_metric_help_and_type(reply, prefix, "by_type_queries", - "Unbound DNS queries by type", "gauge"); + "Unbound DNS queries by type", "counter"); for(i=0; isvr.qtype[i] == 0) continue; @@ -610,7 +612,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* CLASS */ print_metric_help_and_type(reply, prefix, "by_class_queries", - "Unbound DNS queries by class", "gauge"); + "Unbound DNS queries by class", "counter"); for(i=0; isvr.qclass[i] == 0) continue; @@ -630,7 +632,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* OPCODE */ print_metric_help_and_type(reply, prefix, "by_opcode_queries", - "Unbound DNS queries by opcode", "gauge"); + "Unbound DNS queries by opcode", "counter"); for(i=0; isvr.qopcode[i] == 0) continue; @@ -646,7 +648,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* RCODE */ print_metric_help_and_type(reply, prefix, "by_rcode_queries", - "Unbound DNS answers by rcode", "gauge"); + "Unbound DNS answers by rcode", "counter"); for(i=0; i LDNS_RCODE_REFUSED && s->svr.ans_rcode[i] == 0) @@ -667,7 +669,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* FLAGS */ print_metric_help_and_type(reply, prefix, "by_flags_queries", - "Unbound DNS queries by flags", "gauge"); + "Unbound DNS queries by flags", "counter"); INFO_EXT_STATS("by_flags_queries", "flag", "QR", s->svr.qbit_QR); INFO_EXT_STATS("by_flags_queries", "flag", "AA", s->svr.qbit_AA); INFO_EXT_STATS("by_flags_queries", "flag", "TC", s->svr.qbit_TC); @@ -683,7 +685,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* transport */ print_metric_help_and_type(reply, prefix, "by_transport_queries", - "Unbound DNS queries by transport", "gauge"); + "Unbound DNS queries by transport", "counter"); INFO_EXT_STATS("by_transport_queries", "transport", "tcp", s->svr.qtcp); INFO_EXT_STATS("by_transport_queries", "transport", "tcpout", @@ -705,13 +707,13 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* iteration */ print_metric_help_and_type(reply, prefix, "ratelimited_queries", - "Unbound DNS queries ratelimited", "gauge"); + "Unbound DNS queries ratelimited", "counter"); INFO_EXT_STATS("ratelimited_queries", "type", "ratelimited", s->svr.queries_ratelimited); /* validation */ print_metric_help_and_type(reply, prefix, "validation_queries", - "Unbound DNS queries DNSSEC validated", "gauge"); + "Unbound DNS queries DNSSEC validated", "counter"); INFO_EXT_STATS("validation_queries", "type", "secure", s->svr.ans_secure); INFO_EXT_STATS("validation_queries", "type", "bogus", @@ -727,7 +729,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* threat detection */ print_metric_help_and_type(reply, prefix, "threat_queries", - "Unbound DNS queries threats", "gauge"); + "Unbound DNS queries threats", "counter"); INFO_EXT_STATS("threat_queries", "type", "unwanted.queries", s->svr.unwanted_queries); INFO_EXT_STATS("threat_queries", "type", "unwanted.replies", @@ -753,7 +755,7 @@ metrics_print_ext(struct evbuffer* reply, struct ub_stats_info* s, /* applied RPZ actions */ print_metric_help_and_type(reply, prefix, "rpz_actions", - "Unbound DNS RPZ actions", "gauge"); + "Unbound DNS RPZ actions", "counter"); for(i=0; isvr.shared_secret_cache_count); @@ -867,8 +869,7 @@ metrics_http_callback(struct evhttp_request *req, void *p) evhttp_add_header(evhttp_request_get_output_headers(req), "Content-Type", "text/plain; version=0.0.4"); - do_metrics_stats(reply, metrics->worker, - !metrics->worker->daemon->cfg->stat_cumulative); + do_metrics_stats(reply, metrics->worker, 0 /* no reset */); evhttp_send_reply(req, HTTP_OK, NULL, reply); verbose(VERB_DETAIL, "metrics operation completed, response sent"); evbuffer_free(reply); diff --git a/doc/unbound.conf.rst b/doc/unbound.conf.rst index 259e990e9..642bfd1ee 100644 --- a/doc/unbound.conf.rst +++ b/doc/unbound.conf.rst @@ -3443,10 +3443,20 @@ These options are part of the ``server:`` section. command, but with metric names following the prometheus specification. (Requires libevent2) - Beware, that when using + Use it with settings, extended-statistics: yes that collects more + information, + :ref:`extended-statistics` . + And set statistics-cumulative: yes, because the metrics are + defined as cumulative counters for the number of queries, + :ref:`statistics-cumulative` . + + Access from the metrics endpoint does not reset the statistics. + Beware, if statistics-cumulative is disabled, that when using :ref:`stats