From: Jess Bees Date: Mon, 27 Oct 2025 14:44:39 +0000 (-0400) Subject: Add prometheus types/descriptions to dynamic metrics. X-Git-Tag: rec-5.4.0-alpha1~40^2~10 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=58b6580dff100582679ef90cb31473f6e9601a40;p=thirdparty%2Fpdns.git Add prometheus types/descriptions to dynamic metrics. This commit adds optional types and descriptions to dynamic metrics, so they can be written to the prometheus metrics web endpoint in comments. Adds `initMetric` function to Lua, which is similar to getMetric, but has two additional arguments: the metric's prometheus type, and the metric's description. Metrics that are first declared with `getMetric` will have no type or description, and subsequent calls to `initMetric` will have no effect (the same way that calling `getMetric` multiple times with different prometheus metric names will have no effect). Signed-off-by: Jess Bees --- diff --git a/pdns/recursordist/lua-recursor4.cc b/pdns/recursordist/lua-recursor4.cc index ada61fbcd6..b8a030fcad 100644 --- a/pdns/recursordist/lua-recursor4.cc +++ b/pdns/recursordist/lua-recursor4.cc @@ -430,6 +430,25 @@ void RecursorLua4::postPrepareContext() // NOLINT(readability-function-cognitive d_pd.emplace_back("now", &g_now); + d_lw->writeFunction("initMetric", [](const std::string& str, const std::string& maybePrometheusName, const std::string& maybePrometheusTypeName, boost::optional maybeDescr){ + // Shift arguments, because it's actually the second `prometheusName` argument which is optional + // to match the optional `prometheusName` in `getMetric`. + std::string prometheusName; + std::string prometheusTypeName; + std::string prometheusDescr; + if (maybeDescr) { + prometheusName = maybePrometheusName; + prometheusTypeName = maybePrometheusTypeName; + prometheusDescr = *maybeDescr; + } else { + prometheusDescr = prometheusTypeName; + prometheusTypeName = prometheusName; + prometheusName = ""; + } + + return DynMetric{initDynMetric(str, prometheusName, prometheusTypeName, prometheusDescr)}; + }); + d_lw->writeFunction("getMetric", [](const std::string& str, boost::optional prometheusName) { return DynMetric{getDynMetric(str, prometheusName ? *prometheusName : "")}; }); diff --git a/pdns/recursordist/rec_channel.hh b/pdns/recursordist/rec_channel.hh index 061683b67b..78ecd9d06d 100644 --- a/pdns/recursordist/rec_channel.hh +++ b/pdns/recursordist/rec_channel.hh @@ -104,6 +104,8 @@ struct StatsMapEntry { std::string d_prometheusName; std::string d_value; + std::optional d_prometheusTypeName = std::nullopt; + std::optional d_prometheusDescr = std::nullopt; }; class PrefixDashNumberCompare @@ -139,6 +141,7 @@ std::vector* pleaseGetLargeAnswerRemotes(); std::vector* pleaseGetTimeouts(); DNSName getRegisteredName(const DNSName& dom); std::atomic* getDynMetric(const std::string& str, const std::string& prometheusName); +std::atomic* initDynMetric(const std::string& str, const std::string& prometheusName, const std::string& prometheusTypeName, const std::string& prometheusDescr); std::optional getStatByName(const std::string& name); bool isStatDisabled(StatComponent component, const std::string& name); void disableStat(StatComponent component, const string& name); diff --git a/pdns/recursordist/rec_channel_rec.cc b/pdns/recursordist/rec_channel_rec.cc index c9dcc86836..2610db82a3 100644 --- a/pdns/recursordist/rec_channel_rec.cc +++ b/pdns/recursordist/rec_channel_rec.cc @@ -118,6 +118,8 @@ struct dynmetrics { std::atomic* d_ptr; std::string d_prometheusName; + std::optional d_prometheusTypeName; + std::optional d_prometheusDescr; }; static LockGuarded> d_dynmetrics; @@ -177,6 +179,11 @@ static std::string getPrometheusName(const std::string& arg) } std::atomic* getDynMetric(const std::string& str, const std::string& prometheusName) +{ + return initDynMetric(str, prometheusName, "", ""); +} + +std::atomic* initDynMetric(const std::string& str, const std::string& prometheusName, const std::string& prometheusTypeName, const std::string& prometheusDescr) { auto locked = d_dynmetrics.lock(); auto iter = locked->find(str); @@ -192,7 +199,16 @@ std::atomic* getDynMetric(const std::string& str, const std::stri name = getPrometheusName(name); } - auto ret = dynmetrics{new std::atomic(), std::move(name)}; + std::optional typeName; + if (!prometheusTypeName.empty()) { + typeName = std::optional(std::move(prometheusTypeName)); + } + std::optional descr; + if (!prometheusDescr.empty()) { + descr = std::optional(std::move(prometheusDescr)); + } + + auto ret = dynmetrics{new std::atomic(), std::move(name), typeName, descr}; (*locked)[str] = ret; return ret.d_ptr; } @@ -258,7 +274,7 @@ StatsMap getAllStatsMap(StatComponent component) { for (const auto& value : *(d_dynmetrics.lock())) { if (disabledlistMap.count(value.first) == 0) { - ret.emplace(value.first, StatsMapEntry{value.second.d_prometheusName, std::to_string(*value.second.d_ptr)}); + ret.emplace(value.first, StatsMapEntry{value.second.d_prometheusName, std::to_string(*value.second.d_ptr), value.second.d_prometheusTypeName, value.second.d_prometheusDescr}); } } } diff --git a/pdns/recursordist/ws-recursor.cc b/pdns/recursordist/ws-recursor.cc index 074e5256cb..f2fa083fa0 100644 --- a/pdns/recursordist/ws-recursor.cc +++ b/pdns/recursordist/ws-recursor.cc @@ -555,12 +555,15 @@ static void prometheusMetrics(HttpRequest* /* req */, HttpResponse* resp) for (const auto& tup : varmap) { std::string metricName = tup.first; std::string prometheusMetricName = tup.second.d_prometheusName; + std::string prometheusTypeName; + std::string prometheusDescr; std::string helpname = tup.second.d_prometheusName; MetricDefinition metricDetails; if (s_metricDefinitions.getMetricDetails(metricName, metricDetails)) { - std::string prometheusTypeName = MetricDefinitionStorage::getPrometheusStringMetricType( + prometheusTypeName = MetricDefinitionStorage::getPrometheusStringMetricType( metricDetails.d_prometheusType); + prometheusDescr = metricDetails.d_description; if (prometheusTypeName.empty()) { continue; @@ -573,8 +576,20 @@ static void prometheusMetrics(HttpRequest* /* req */, HttpResponse* resp) // name is XXX_count, strip the _count part helpname = helpname.substr(0, helpname.length() - 6); } + } else { + if (tup.second.d_prometheusTypeName) { + prometheusTypeName = *tup.second.d_prometheusTypeName; + } + if (tup.second.d_prometheusDescr) { + prometheusDescr = *tup.second.d_prometheusDescr; + } + } + + if (!prometheusTypeName.empty()) { output << "# TYPE " << helpname << " " << prometheusTypeName << "\n"; - output << "# HELP " << helpname << " " << metricDetails.d_description << "\n"; + } + if (!prometheusDescr.empty()) { + output << "# HELP " << helpname << " " << prometheusDescr << "\n"; } output << prometheusMetricName << " " << tup.second.d_value << "\n"; }