diff --git a/pull/include/prometheus/exposer.h b/pull/include/prometheus/exposer.h index 93e0b2b..0e83a7a 100644 --- a/pull/include/prometheus/exposer.h +++ b/pull/include/prometheus/exposer.h @@ -6,9 +6,12 @@ #include #include #include +#include +#include #include "prometheus/collectable.h" #include "prometheus/detail/pull_export.h" +#include "prometheus/registry.h" class CivetServer; struct CivetCallbacks; @@ -17,6 +20,7 @@ namespace prometheus { namespace detail { class Endpoint; +class SerialMetricsHandler; } // namespace detail class PROMETHEUS_CPP_PULL_EXPORT Exposer { @@ -53,4 +57,18 @@ class PROMETHEUS_CPP_PULL_EXPORT Exposer { std::mutex mutex_; }; +class SerialExposer { + public: + explicit SerialExposer(const std::function update_callback = std::function {}); + std::pair, std::string> + operator()(const std::map& req_headers); + void RegisterCollectable(const std::weak_ptr& collectable); + ~SerialExposer(); + + private: + std::vector> collectables_; + std::shared_ptr exposer_registry_; + std::unique_ptr metrics_handler_; +}; + } // namespace prometheus diff --git a/pull/src/exposer.cc b/pull/src/exposer.cc index 38a28e0..0d62bf3 100644 --- a/pull/src/exposer.cc +++ b/pull/src/exposer.cc @@ -1,4 +1,5 @@ #include "prometheus/exposer.h" +#include "handler.h" #include #include @@ -63,4 +64,28 @@ detail::Endpoint& Exposer::GetEndpointForUri(const std::string& uri) { return *endpoints_.back().get(); } +SerialExposer::SerialExposer( + const std::function update_callback +) : + exposer_registry_(std::make_shared()), + metrics_handler_(new detail::SerialMetricsHandler { + collectables_, *exposer_registry_, update_callback + }) +{ + RegisterCollectable(exposer_registry_); +} + +SerialExposer::~SerialExposer() {} + +std::pair, std::string> +SerialExposer::operator()(const std::map& req_headers) { + return (*this->metrics_handler_)(req_headers); +} + +void SerialExposer::RegisterCollectable( + const std::weak_ptr& collectable +) { + collectables_.push_back(collectable); +} + } // namespace prometheus diff --git a/pull/src/handler.cc b/pull/src/handler.cc index 5a55001..b6ffa85 100644 --- a/pull/src/handler.cc +++ b/pull/src/handler.cc @@ -172,5 +172,124 @@ void MetricsHandler::CleanupStalePointers( }), std::end(collectables)); } + +////////////////////////////////////////////////////// +/// Serial Metrics Handler /// +////////////////////////////////////////////////////// + +SerialMetricsHandler::SerialMetricsHandler( + const std::vector>& collectables, + Registry& registry, + const std::function update_callback) +: + collectables_(collectables), + bytes_transferred_family_( + BuildCounter() + .Name("exposer_transferred_bytes_total") + .Help("Transferred bytes to metrics services") + .Register(registry)), + bytes_transferred_(bytes_transferred_family_.Add({})), + num_scrapes_family_(BuildCounter() + .Name("exposer_scrapes_total") + .Help("Number of times metrics were scraped") + .Register(registry)), + num_scrapes_(num_scrapes_family_.Add({})), + request_latencies_family_( + BuildSummary() + .Name("exposer_request_latencies") + .Help("Latencies of serving scrape requests, in microseconds") + .Register(registry)), + request_latencies_(request_latencies_family_.Add( + {}, Summary::Quantiles{{0.5, 0.05}, {0.9, 0.01}, {0.99, 0.001}})), + update_callback_(update_callback) +{} + +std::vector SerialMetricsHandler::CollectMetrics() const { + auto collected_metrics = std::vector{}; + + for (auto&& wcollectable : collectables_) { + auto collectable = wcollectable.lock(); + if (!collectable) { + continue; + } + + auto&& metrics = collectable->Collect(); + collected_metrics.insert(collected_metrics.end(), + std::make_move_iterator(metrics.begin()), + std::make_move_iterator(metrics.end())); + } + + return collected_metrics; +} + +using std::pair; +using std::vector; +using std::string; +using std::map; + +#ifdef HAVE_ZLIB +static bool IsEncodingAccepted(const map& headers, string encoding) { + auto accept_encoding = headers.find("Accept-Encoding"); + if (accept_encoding == headers.end()) { + return false; + } + return accept_encoding->second == encoding; +} +#endif + +static pair, std::string> WriteResponse( + const std::map& req_headers, + const std::string& body +) { + map headers {}; + + headers.insert({ "Content-Type", "text/plain" }); + +#ifdef HAVE_ZLIB + auto acceptsGzip = IsEncodingAccepted(req_headers, "gzip"); + + if (acceptsGzip) { + auto compressed = GZipCompress(body); + + if (!compressed.empty()) { + headers.insert({"Content-Encoding", "gzip"}); + headers.insert({"Content-Length", std::to_string(static_cast(compressed.size()))}); + + // Convert the vector into std::string + std::string res_body { compressed.begin(), compressed.end() }; + + return { headers, res_body }; + } + } +#endif + headers.insert({"Content-Length", std::to_string(static_cast(body.size()))}); + + return { headers, body }; +} + +pair, string> SerialMetricsHandler::operator()( + const map& req_headers +) { + auto start_time_of_request = std::chrono::steady_clock::now(); + + // Execute the callback updating the metrics + this->update_callback_(); + + auto metrics = CollectMetrics(); + auto serializer = std::unique_ptr{ new TextSerializer() }; + auto headers_body = WriteResponse(req_headers, serializer->Serialize(metrics)); + + auto stop_time_of_request = std::chrono::steady_clock::now(); + auto duration = std::chrono::duration_cast( + stop_time_of_request - start_time_of_request + ); + + request_latencies_.Observe(duration.count()); + bytes_transferred_.Increment(headers_body.second.size()); + num_scrapes_.Increment(); + + return headers_body; +} + } // namespace detail } // namespace prometheus diff --git a/pull/src/handler.h b/pull/src/handler.h index 10c90f9..8b62aab 100644 --- a/pull/src/handler.h +++ b/pull/src/handler.h @@ -3,6 +3,7 @@ #include #include #include +#include #include "CivetServer.h" #include "prometheus/collectable.h" @@ -34,6 +35,36 @@ class MetricsHandler : public CivetHandler { Counter& num_scrapes_; Family& request_latencies_family_; Summary& request_latencies_; + std::function update_callback_ {}; }; + +////////////////////////////////////////////////////// +/// Serial Metrics Handler /// +////////////////////////////////////////////////////// + +class SerialMetricsHandler { + public: + SerialMetricsHandler( + const std::vector>& collectables, + Registry& registry, + const std::function update_callback = std::function {} + ); + + std::pair, std::string> + operator()(const std::map& req_headers); + + private: + std::vector CollectMetrics() const; + + const std::vector>& collectables_; + Family& bytes_transferred_family_; + Counter& bytes_transferred_; + Family& num_scrapes_family_; + Counter& num_scrapes_; + Family& request_latencies_family_; + Summary& request_latencies_; + std::function update_callback_ {}; +}; + } // namespace detail } // namespace prometheus