From: Remi Gacogne Date: Thu, 5 Nov 2020 14:24:50 +0000 (+0100) Subject: dnsdist: Implement Lua custom web endpoints X-Git-Tag: dnsdist-1.6.0-alpha0~13^2~3 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=88d4fe878bb830b55fe13f49c21974cca11da699;p=thirdparty%2Fpdns.git dnsdist: Implement Lua custom web endpoints --- diff --git a/pdns/dnsdist-lua.cc b/pdns/dnsdist-lua.cc index b16d466c87..16276d1b10 100644 --- a/pdns/dnsdist-lua.cc +++ b/pdns/dnsdist-lua.cc @@ -2332,6 +2332,7 @@ vector> setupLua(LuaContext& luaCtx, bool client, bool setupLuaInspection(luaCtx); setupLuaRules(luaCtx); setupLuaVars(luaCtx); + setupLuaWeb(luaCtx); #ifdef LUAJIT_VERSION luaCtx.executeCode(getLuaFFIWrappers()); diff --git a/pdns/dnsdist-lua.hh b/pdns/dnsdist-lua.hh index 4818345e65..97f2e1d0de 100644 --- a/pdns/dnsdist-lua.hh +++ b/pdns/dnsdist-lua.hh @@ -105,4 +105,5 @@ void setupLuaBindingsProtoBuf(LuaContext& luaCtx, bool client, bool configCheck) void setupLuaRules(LuaContext& luaCtx); void setupLuaInspection(LuaContext& luaCtx); void setupLuaVars(LuaContext& luaCtx); +void setupLuaWeb(LuaContext& luaCtx); void setupLuaLoadBalancingContext(LuaContext& luaCtx); diff --git a/pdns/dnsdist-web.cc b/pdns/dnsdist-web.cc index 00da6abe1f..a5d406a408 100644 --- a/pdns/dnsdist-web.cc +++ b/pdns/dnsdist-web.cc @@ -307,917 +307,972 @@ static json11::Json::array someResponseRulesToJson(GlobalStateHolder>* return responseRules; } -static void connectionThread(int sock, ComboAddress remote) +using namespace json11; + +static void handlePrometheus(const YaHTTP::Request& req, YaHTTP::Response& resp) { - setThreadName("dnsdist/webConn"); + handleCORS(req, resp); + resp.status = 200; + + std::ostringstream output; + static const std::set metricBlacklist = { "latency-count", "latency-sum" }; + for (const auto& e : g_stats.entries) { + if (e.first == "special-memory-usage") + continue; // Too expensive for get-all + std::string metricName = std::get<0>(e); + + // Prometheus suggest using '_' instead of '-' + std::string prometheusMetricName = "dnsdist_" + boost::replace_all_copy(metricName, "-", "_"); + if (metricBlacklist.count(metricName) != 0) { + continue; + } - using namespace json11; - vinfolog("Webserver handling connection from %s", remote.toStringWithPort()); + MetricDefinition metricDetails; + if (!s_metricDefinitions.getMetricDetails(metricName, metricDetails)) { + vinfolog("Do not have metric details for %s", metricName); + continue; + } - try { - YaHTTP::AsyncRequestLoader yarl; - YaHTTP::Request req; - bool finished = false; + std::string prometheusTypeName = s_metricDefinitions.getPrometheusStringMetricType(metricDetails.prometheusType); - yarl.initialize(&req); - while(!finished) { - int bytes; - char buf[1024]; - bytes = read(sock, buf, sizeof(buf)); - if (bytes > 0) { - string data = string(buf, bytes); - finished = yarl.feed(data); - } else { - // read error OR EOF - break; - } + if (prometheusTypeName == "") { + vinfolog("Unknown Prometheus type for %s", metricName); + continue; } - yarl.finalize(); - string command=req.getvars["command"]; + // for these we have the help and types encoded in the sources: + output << "# HELP " << prometheusMetricName << " " << metricDetails.description << "\n"; + output << "# TYPE " << prometheusMetricName << " " << prometheusTypeName << "\n"; + output << prometheusMetricName << " "; - req.getvars.erase("_"); // jQuery cache buster + if (const auto& val = boost::get(&std::get<1>(e))) + output << (*val)->load(); + else if (const auto& dval = boost::get(&std::get<1>(e))) + output << **dval; + else + output << (*boost::get(&std::get<1>(e)))(std::get<0>(e)); - YaHTTP::Response resp; - resp.version = req.version; - const string charset = "; charset=utf-8"; + output << "\n"; + } - { - std::lock_guard lock(g_webserverConfig.lock); + // Latency histogram buckets + output << "# HELP dnsdist_latency Histogram of responses by latency (in milliseconds)\n"; + output << "# TYPE dnsdist_latency histogram\n"; + uint64_t latency_amounts = g_stats.latency0_1; + output << "dnsdist_latency_bucket{le=\"1\"} " << latency_amounts << "\n"; + latency_amounts += g_stats.latency1_10; + output << "dnsdist_latency_bucket{le=\"10\"} " << latency_amounts << "\n"; + latency_amounts += g_stats.latency10_50; + output << "dnsdist_latency_bucket{le=\"50\"} " << latency_amounts << "\n"; + latency_amounts += g_stats.latency50_100; + output << "dnsdist_latency_bucket{le=\"100\"} " << latency_amounts << "\n"; + latency_amounts += g_stats.latency100_1000; + output << "dnsdist_latency_bucket{le=\"1000\"} " << latency_amounts << "\n"; + latency_amounts += g_stats.latencySlow; // Should be the same as latency_count + output << "dnsdist_latency_bucket{le=\"+Inf\"} " << latency_amounts << "\n"; + output << "dnsdist_latency_sum " << g_stats.latencySum << "\n"; + output << "dnsdist_latency_count " << getLatencyCount(std::string()) << "\n"; + + auto states = g_dstates.getLocal(); + const string statesbase = "dnsdist_server_"; + + output << "# HELP " << statesbase << "status " << "Whether this backend is up (1) or down (0)" << "\n"; + output << "# TYPE " << statesbase << "status " << "gauge" << "\n"; + output << "# HELP " << statesbase << "queries " << "Amount of queries relayed to server" << "\n"; + output << "# TYPE " << statesbase << "queries " << "counter" << "\n"; + output << "# HELP " << statesbase << "responses " << "Amount of responses received from this server" << "\n"; + output << "# TYPE " << statesbase << "responses " << "counter" << "\n"; + output << "# HELP " << statesbase << "drops " << "Amount of queries not answered by server" << "\n"; + output << "# TYPE " << statesbase << "drops " << "counter" << "\n"; + output << "# HELP " << statesbase << "latency " << "Server's latency when answering questions in milliseconds" << "\n"; + output << "# TYPE " << statesbase << "latency " << "gauge" << "\n"; + output << "# HELP " << statesbase << "senderrors " << "Total number of OS send errors while relaying queries" << "\n"; + output << "# TYPE " << statesbase << "senderrors " << "counter" << "\n"; + output << "# HELP " << statesbase << "outstanding " << "Current number of queries that are waiting for a backend response" << "\n"; + output << "# TYPE " << statesbase << "outstanding " << "gauge" << "\n"; + output << "# HELP " << statesbase << "order " << "The order in which this server is picked" << "\n"; + output << "# TYPE " << statesbase << "order " << "gauge" << "\n"; + output << "# HELP " << statesbase << "weight " << "The weight within the order in which this server is picked" << "\n"; + output << "# TYPE " << statesbase << "weight " << "gauge" << "\n"; + output << "# HELP " << statesbase << "tcpdiedsendingquery " << "The number of TCP I/O errors while sending the query" << "\n"; + output << "# TYPE " << statesbase << "tcpdiedsendingquery " << "counter" << "\n"; + output << "# HELP " << statesbase << "tcpdiedreadingresponse " << "The number of TCP I/O errors while reading the response" << "\n"; + output << "# TYPE " << statesbase << "tcpdiedreadingresponse " << "counter" << "\n"; + output << "# HELP " << statesbase << "tcpgaveup " << "The number of TCP connections failing after too many attempts" << "\n"; + output << "# TYPE " << statesbase << "tcpgaveup " << "counter" << "\n"; + output << "# HELP " << statesbase << "tcpreadtimeouts " << "The number of TCP read timeouts" << "\n"; + output << "# TYPE " << statesbase << "tcpreadtimeouts " << "counter" << "\n"; + output << "# HELP " << statesbase << "tcpwritetimeouts " << "The number of TCP write timeouts" << "\n"; + output << "# TYPE " << statesbase << "tcpwritetimeouts " << "counter" << "\n"; + output << "# HELP " << statesbase << "tcpcurrentconnections " << "The number of current TCP connections" << "\n"; + output << "# TYPE " << statesbase << "tcpcurrentconnections " << "gauge" << "\n"; + output << "# HELP " << statesbase << "tcpavgqueriesperconn " << "The average number of queries per TCP connection" << "\n"; + output << "# TYPE " << statesbase << "tcpavgqueriesperconn " << "gauge" << "\n"; + output << "# HELP " << statesbase << "tcpavgconnduration " << "The average duration of a TCP connection (ms)" << "\n"; + output << "# TYPE " << statesbase << "tcpavgconnduration " << "gauge" << "\n"; + + for (const auto& state : *states) { + string serverName; + + if (state->getName().empty()) + serverName = state->remote.toStringWithPort(); + else + serverName = state->getName(); + + boost::replace_all(serverName, ".", "_"); + + const std::string label = boost::str(boost::format("{server=\"%1%\",address=\"%2%\"}") + % serverName % state->remote.toStringWithPort()); + + output << statesbase << "status" << label << " " << (state->isUp() ? "1" : "0") << "\n"; + output << statesbase << "queries" << label << " " << state->queries.load() << "\n"; + output << statesbase << "responses" << label << " " << state->responses.load() << "\n"; + output << statesbase << "drops" << label << " " << state->reuseds.load() << "\n"; + output << statesbase << "latency" << label << " " << state->latencyUsec/1000.0 << "\n"; + output << statesbase << "senderrors" << label << " " << state->sendErrors.load() << "\n"; + output << statesbase << "outstanding" << label << " " << state->outstanding.load() << "\n"; + output << statesbase << "order" << label << " " << state->order << "\n"; + output << statesbase << "weight" << label << " " << state->weight << "\n"; + output << statesbase << "tcpdiedsendingquery" << label << " " << state->tcpDiedSendingQuery << "\n"; + output << statesbase << "tcpdiedreadingresponse" << label << " " << state->tcpDiedReadingResponse << "\n"; + output << statesbase << "tcpgaveup" << label << " " << state->tcpGaveUp << "\n"; + output << statesbase << "tcpreadtimeouts" << label << " " << state->tcpReadTimeouts << "\n"; + output << statesbase << "tcpwritetimeouts" << label << " " << state->tcpWriteTimeouts << "\n"; + output << statesbase << "tcpcurrentconnections" << label << " " << state->tcpCurrentConnections << "\n"; + output << statesbase << "tcpavgqueriesperconn" << label << " " << state->tcpAvgQueriesPerConnection << "\n"; + output << statesbase << "tcpavgconnduration" << label << " " << state->tcpAvgConnectionDuration << "\n"; + } - addCustomHeaders(resp, g_webserverConfig.customHeaders); - addSecurityHeaders(resp, g_webserverConfig.customHeaders); + const string frontsbase = "dnsdist_frontend_"; + output << "# HELP " << frontsbase << "queries " << "Amount of queries received by this frontend" << "\n"; + output << "# TYPE " << frontsbase << "queries " << "counter" << "\n"; + output << "# HELP " << frontsbase << "responses " << "Amount of responses sent by this frontend" << "\n"; + output << "# TYPE " << frontsbase << "responses " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tcpdiedreadingquery " << "Amount of TCP connections terminated while reading the query from the client" << "\n"; + output << "# TYPE " << frontsbase << "tcpdiedreadingquery " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tcpdiedsendingresponse " << "Amount of TCP connections terminated while sending a response to the client" << "\n"; + output << "# TYPE " << frontsbase << "tcpdiedsendingresponse " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tcpgaveup " << "Amount of TCP connections terminated after too many attempts to get a connection to the backend" << "\n"; + output << "# TYPE " << frontsbase << "tcpgaveup " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tcpclientimeouts " << "Amount of TCP connections terminated by a timeout while reading from the client" << "\n"; + output << "# TYPE " << frontsbase << "tcpclientimeouts " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tcpdownstreamtimeouts " << "Amount of TCP connections terminated by a timeout while reading from the backend" << "\n"; + output << "# TYPE " << frontsbase << "tcpdownstreamtimeouts " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tcpcurrentconnections " << "Amount of current incoming TCP connections from clients" << "\n"; + output << "# TYPE " << frontsbase << "tcpcurrentconnections " << "gauge" << "\n"; + output << "# HELP " << frontsbase << "tcpavgqueriesperconnection " << "The average number of queries per TCP connection" << "\n"; + output << "# TYPE " << frontsbase << "tcpavgqueriesperconnection " << "gauge" << "\n"; + output << "# HELP " << frontsbase << "tcpavgconnectionduration " << "The average duration of a TCP connection (ms)" << "\n"; + output << "# TYPE " << frontsbase << "tcpavgconnectionduration " << "gauge" << "\n"; + output << "# HELP " << frontsbase << "tlsqueries " << "Number of queries received by dnsdist over TLS, by TLS version" << "\n"; + output << "# TYPE " << frontsbase << "tlsqueries " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tlsnewsessions " << "Amount of new TLS sessions negotiated" << "\n"; + output << "# TYPE " << frontsbase << "tlsnewsessions " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tlsresumptions " << "Amount of TLS sessions resumed" << "\n"; + output << "# TYPE " << frontsbase << "tlsresumptions " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tlsunknownticketkeys " << "Amount of attempts to resume TLS session from an unknown key (possibly expired)" << "\n"; + output << "# TYPE " << frontsbase << "tlsunknownticketkeys " << "counter" << "\n"; + output << "# HELP " << frontsbase << "tlsinactiveticketkeys " << "Amount of TLS sessions resumed from an inactive key" << "\n"; + output << "# TYPE " << frontsbase << "tlsinactiveticketkeys " << "counter" << "\n"; + + output << "# HELP " << frontsbase << "tlshandshakefailures " << "Amount of TLS handshake failures" << "\n"; + output << "# TYPE " << frontsbase << "tlshandshakefailures " << "counter" << "\n"; + + std::map frontendDuplicates; + for (const auto& front : g_frontends) { + if (front->udpFD == -1 && front->tcpFD == -1) + continue; + + const string frontName = front->local.toStringWithPort(); + const string proto = front->getType(); + const string fullName = frontName + "_" + proto; + uint64_t threadNumber = 0; + auto dupPair = frontendDuplicates.insert({fullName, 1}); + if (!dupPair.second) { + threadNumber = dupPair.first->second; + ++(dupPair.first->second); } - /* indicate that the connection will be closed after completion of the response */ - resp.headers["Connection"] = "close"; + const std::string label = boost::str(boost::format("{frontend=\"%1%\",proto=\"%2%\",thread=\"%3%\"} ") + % frontName % proto % threadNumber); + + output << frontsbase << "queries" << label << front->queries.load() << "\n"; + output << frontsbase << "responses" << label << front->responses.load() << "\n"; + if (front->isTCP()) { + output << frontsbase << "tcpdiedreadingquery" << label << front->tcpDiedReadingQuery.load() << "\n"; + output << frontsbase << "tcpdiedsendingresponse" << label << front->tcpDiedSendingResponse.load() << "\n"; + output << frontsbase << "tcpgaveup" << label << front->tcpGaveUp.load() << "\n"; + output << frontsbase << "tcpclientimeouts" << label << front->tcpClientTimeouts.load() << "\n"; + output << frontsbase << "tcpdownstreamtimeouts" << label << front->tcpDownstreamTimeouts.load() << "\n"; + output << frontsbase << "tcpcurrentconnections" << label << front->tcpCurrentConnections.load() << "\n"; + output << frontsbase << "tcpavgqueriesperconnection" << label << front->tcpAvgQueriesPerConnection.load() << "\n"; + output << frontsbase << "tcpavgconnectionduration" << label << front->tcpAvgConnectionDuration.load() << "\n"; + if (front->hasTLS()) { + output << frontsbase << "tlsnewsessions" << label << front->tlsNewSessions.load() << "\n"; + output << frontsbase << "tlsresumptions" << label << front->tlsResumptions.load() << "\n"; + output << frontsbase << "tlsunknownticketkeys" << label << front->tlsUnknownTicketKey.load() << "\n"; + output << frontsbase << "tlsinactiveticketkeys" << label << front->tlsInactiveTicketKey.load() << "\n"; + + output << frontsbase << "tlsqueries{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",tls=\"tls10\"} " << front->tls10queries.load() << "\n"; + output << frontsbase << "tlsqueries{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",tls=\"tls11\"} " << front->tls11queries.load() << "\n"; + output << frontsbase << "tlsqueries{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",tls=\"tls12\"} " << front->tls12queries.load() << "\n"; + output << frontsbase << "tlsqueries{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",tls=\"tls13\"} " << front->tls13queries.load() << "\n"; + output << frontsbase << "tlsqueries{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",tls=\"unknown\"} " << front->tlsUnknownqueries.load() << "\n"; - /* no need to send back the API key if any */ - resp.headers.erase("X-API-Key"); + const TLSErrorCounters* errorCounters = nullptr; + if (front->tlsFrontend != nullptr) { + errorCounters = &front->tlsFrontend->d_tlsCounters; + } + else if (front->dohFrontend != nullptr) { + errorCounters = &front->dohFrontend->d_tlsCounters; + } - if(req.method == "OPTIONS") { - /* the OPTIONS method should not require auth, otherwise it breaks CORS */ - handleCORS(req, resp); - resp.status=200; + if (errorCounters != nullptr) { + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"dhKeyTooSmall\"} " << errorCounters->d_dhKeyTooSmall << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"inappropriateFallBack\"} " << errorCounters->d_inappropriateFallBack << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"noSharedCipher\"} " << errorCounters->d_noSharedCipher << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"unknownCipherType\"} " << errorCounters->d_unknownCipherType << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"unknownKeyExchangeType\"} " << errorCounters->d_unknownKeyExchangeType << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"unknownProtocol\"} " << errorCounters->d_unknownProtocol << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"unsupportedEC\"} " << errorCounters->d_unsupportedEC << "\n"; + output << frontsbase << "tlshandshakefailures{frontend=\"" << frontName << "\",proto=\"" << proto << "\",thread=\"" << threadNumber << "\",error=\"unsupportedProtocol{\"} " << errorCounters->d_unsupportedProtocol << "\n"; + } + } } - else if (!compareAuthorization(req)) { - YaHTTP::strstr_map_t::iterator header = req.headers.find("authorization"); - if (header != req.headers.end()) - errlog("HTTP Request \"%s\" from %s: Web Authentication failed", req.url.path, remote.toStringWithPort()); - resp.status=401; - resp.body="

Unauthorized

"; - resp.headers["WWW-Authenticate"] = "basic realm=\"PowerDNS\""; + } + + output << "# HELP " << frontsbase << "http_connects " << "Number of DoH TCP connections established to this frontend" << "\n"; + output << "# TYPE " << frontsbase << "http_connects " << "counter" << "\n"; + + output << "# HELP " << frontsbase << "doh_http_method_queries " << "Number of DoH queries received by dnsdist, by HTTP method" << "\n"; + output << "# TYPE " << frontsbase << "doh_http_method_queries " << "counter" << "\n"; + + output << "# HELP " << frontsbase << "doh_http_version_queries " << "Number of DoH queries received by dnsdist, by HTTP version" << "\n"; + output << "# TYPE " << frontsbase << "doh_http_version_queries " << "counter" << "\n"; + + output << "# HELP " << frontsbase << "doh_bad_requests " << "Number of requests that could not be converted to a DNS query" << "\n"; + output << "# TYPE " << frontsbase << "doh_bad_requests " << "counter" << "\n"; + + output << "# HELP " << frontsbase << "doh_responses " << "Number of responses sent, by type" << "\n"; + output << "# TYPE " << frontsbase << "doh_responses " << "counter" << "\n"; + output << "# HELP " << frontsbase << "doh_version_status_responses " << "Number of requests that could not be converted to a DNS query" << "\n"; + output << "# TYPE " << frontsbase << "doh_version_status_responses " << "counter" << "\n"; + +#ifdef HAVE_DNS_OVER_HTTPS + std::map dohFrontendDuplicates; + for(const auto& doh : g_dohlocals) { + const string frontName = doh->d_local.toStringWithPort(); + uint64_t threadNumber = 0; + auto dupPair = frontendDuplicates.insert({frontName, 1}); + if (!dupPair.second) { + threadNumber = dupPair.first->second; + ++(dupPair.first->second); } - else if(!isMethodAllowed(req)) { - resp.status=405; + const std::string addrlabel = boost::str(boost::format("frontend=\"%1%\",thread=\"%2%\"") % frontName % threadNumber); + const std::string label = "{" + addrlabel + "} "; + + output << frontsbase << "http_connects" << label << doh->d_httpconnects << "\n"; + output << frontsbase << "doh_http_method_queries{method=\"get\"," << addrlabel << "} " << doh->d_getqueries << "\n"; + output << frontsbase << "doh_http_method_queries{method=\"post\"," << addrlabel << "} " << doh->d_postqueries << "\n"; + + output << frontsbase << "doh_http_version_queries{version=\"1\"," << addrlabel << "} " << doh->d_http1Stats.d_nbQueries << "\n"; + output << frontsbase << "doh_http_version_queries{version=\"2\"," << addrlabel << "} " << doh->d_http2Stats.d_nbQueries << "\n"; + + output << frontsbase << "doh_bad_requests{" << addrlabel << "} " << doh->d_badrequests << "\n"; + + output << frontsbase << "doh_responses{type=\"error\"," << addrlabel << "} " << doh->d_errorresponses << "\n"; + output << frontsbase << "doh_responses{type=\"redirect\"," << addrlabel << "} " << doh->d_redirectresponses << "\n"; + output << frontsbase << "doh_responses{type=\"valid\"," << addrlabel << "} " << doh->d_validresponses << "\n"; + + output << frontsbase << "doh_version_status_responses{httpversion=\"1\",status=\"200\"," << addrlabel << "} " << doh->d_http1Stats.d_nb200Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"1\",status=\"400\"," << addrlabel << "} " << doh->d_http1Stats.d_nb400Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"1\",status=\"403\"," << addrlabel << "} " << doh->d_http1Stats.d_nb403Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"1\",status=\"500\"," << addrlabel << "} " << doh->d_http1Stats.d_nb500Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"1\",status=\"502\"," << addrlabel << "} " << doh->d_http1Stats.d_nb502Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"1\",status=\"other\"," << addrlabel << "} " << doh->d_http1Stats.d_nbOtherResponses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"2\",status=\"200\"," << addrlabel << "} " << doh->d_http2Stats.d_nb200Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"2\",status=\"400\"," << addrlabel << "} " << doh->d_http2Stats.d_nb400Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"2\",status=\"403\"," << addrlabel << "} " << doh->d_http2Stats.d_nb403Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"2\",status=\"500\"," << addrlabel << "} " << doh->d_http2Stats.d_nb500Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"2\",status=\"502\"," << addrlabel << "} " << doh->d_http2Stats.d_nb502Responses << "\n"; + output << frontsbase << "doh_version_status_responses{httpversion=\"2\",status=\"other\"," << addrlabel << "} " << doh->d_http2Stats.d_nbOtherResponses << "\n"; + } +#endif /* HAVE_DNS_OVER_HTTPS */ + + auto localPools = g_pools.getLocal(); + const string cachebase = "dnsdist_pool_"; + output << "# HELP dnsdist_pool_servers " << "Number of servers in that pool" << "\n"; + output << "# TYPE dnsdist_pool_servers " << "gauge" << "\n"; + output << "# HELP dnsdist_pool_active_servers " << "Number of available servers in that pool" << "\n"; + output << "# TYPE dnsdist_pool_active_servers " << "gauge" << "\n"; + + output << "# HELP dnsdist_pool_cache_size " << "Maximum number of entries that this cache can hold" << "\n"; + output << "# TYPE dnsdist_pool_cache_size " << "gauge" << "\n"; + output << "# HELP dnsdist_pool_cache_entries " << "Number of entries currently present in that cache" << "\n"; + output << "# TYPE dnsdist_pool_cache_entries " << "gauge" << "\n"; + output << "# HELP dnsdist_pool_cache_hits " << "Number of hits from that cache" << "\n"; + output << "# TYPE dnsdist_pool_cache_hits " << "counter" << "\n"; + output << "# HELP dnsdist_pool_cache_misses " << "Number of misses from that cache" << "\n"; + output << "# TYPE dnsdist_pool_cache_misses " << "counter" << "\n"; + output << "# HELP dnsdist_pool_cache_deferred_inserts " << "Number of insertions into that cache skipped because it was already locked" << "\n"; + output << "# TYPE dnsdist_pool_cache_deferred_inserts " << "counter" << "\n"; + output << "# HELP dnsdist_pool_cache_deferred_lookups " << "Number of lookups into that cache skipped because it was already locked" << "\n"; + output << "# TYPE dnsdist_pool_cache_deferred_lookups " << "counter" << "\n"; + output << "# HELP dnsdist_pool_cache_lookup_collisions " << "Number of lookups into that cache that triggered a collision (same hash but different entry)" << "\n"; + output << "# TYPE dnsdist_pool_cache_lookup_collisions " << "counter" << "\n"; + output << "# HELP dnsdist_pool_cache_insert_collisions " << "Number of insertions into that cache that triggered a collision (same hash but different entry)" << "\n"; + output << "# TYPE dnsdist_pool_cache_insert_collisions " << "counter" << "\n"; + output << "# HELP dnsdist_pool_cache_ttl_too_shorts " << "Number of insertions into that cache skipped because the TTL of the answer was not long enough" << "\n"; + output << "# TYPE dnsdist_pool_cache_ttl_too_shorts " << "counter" << "\n"; + + for (const auto& entry : *localPools) { + string poolName = entry.first; + + if (poolName.empty()) { + poolName = "_default_"; } - else if(req.url.path=="/jsonstat") { - handleCORS(req, resp); - resp.status=200; - - if(command=="stats") { - auto obj=Json::object { - { "packetcache-hits", 0}, - { "packetcache-misses", 0}, - { "over-capacity-drops", 0 }, - { "too-old-drops", 0 }, - { "server-policy", g_policy.getLocal()->getName()} - }; + const string label = "{pool=\"" + poolName + "\"}"; + const std::shared_ptr pool = entry.second; + output << "dnsdist_pool_servers" << label << " " << pool->countServers(false) << "\n"; + output << "dnsdist_pool_active_servers" << label << " " << pool->countServers(true) << "\n"; + + if (pool->packetCache != nullptr) { + const auto& cache = pool->packetCache; + + output << cachebase << "cache_size" <