diff --git a/lib/exposer.cc b/lib/exposer.cc index b7d34bad..aa883006 100644 --- a/lib/exposer.cc +++ b/lib/exposer.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include "exposer.h" @@ -27,31 +28,53 @@ MetricsHandler::MetricsHandler( numScrapes_(numScrapesFamily_->add({})) {} static std::string serializeToDelimitedProtobuf( - const std::vector>& collectables) { + const std::vector& metrics) { std::ostringstream ss; - for (auto&& wcollectable : collectables) { - auto collectable = wcollectable.lock(); - if (!collectable) { - continue; + for (auto&& metric : metrics) { + { + google::protobuf::io::OstreamOutputStream rawOutput{&ss}; + google::protobuf::io::CodedOutputStream output(&rawOutput); + + const int size = metric.ByteSize(); + output.WriteVarint32(size); } - for (auto&& metricFamily : collectable->collect()) { - { - google::protobuf::io::OstreamOutputStream rawOutput{&ss}; - google::protobuf::io::CodedOutputStream output(&rawOutput); + auto buffer = std::string{}; + metric.SerializeToString(&buffer); + ss << buffer; + } + return ss.str(); +} + +static std::string serializeToJson( + const std::vector& metrics) { + using google::protobuf::util::MessageDifferencer; - const int size = metricFamily.ByteSize(); - output.WriteVarint32(size); - } + std::stringstream ss; + ss << "["; - auto buffer = std::string{}; - metricFamily.SerializeToString(&buffer); - ss << buffer; + for (auto&& metric : metrics) { + std::string result; + google::protobuf::util::MessageToJsonString( + metric, &result, google::protobuf::util::JsonPrintOptions()); + ss << result; + if (!MessageDifferencer::Equals(metric, metrics.back())) { + ss << ","; } } + ss << "]"; return ss.str(); } +static std::string serializeToHumanReadable( + const std::vector& metrics) { + auto result = std::string{}; + for (auto&& metric : metrics) { + result += metric.DebugString() + "\n"; + } + return result; +} + static std::string getAcceptedEncoding(struct mg_connection* conn) { auto request_info = mg_get_request_info(conn); for (int i = 0; i < request_info->num_headers; i++) { @@ -68,69 +91,35 @@ bool MetricsHandler::handleGet(CivetServer* server, using namespace io::prometheus::client; auto acceptedEncoding = getAcceptedEncoding(conn); + auto metrics = collectMetrics(); + + auto body = std::string{}; + auto contentType = std::string{}; + if (acceptedEncoding.find("application/vnd.google.protobuf") != std::string::npos) { - auto body = serializeToDelimitedProtobuf(collectables_); - mg_printf(conn, - "HTTP/1.1 200 OK\r\n" - "Content-Type: " - "application/vnd.google.protobuf; " - "proto=io.prometheus.client.MetricFamily; " - "encoding=delimited\r\n" - "Content-Length: "); - mg_printf(conn, "%lu\r\n\r\n", body.size()); - mg_write(conn, body.data(), body.size()); - bytesTransfered_->inc(body.size()); + body = serializeToDelimitedProtobuf(metrics); + contentType = + "application/vnd.google.protobuf; " + "proto=io.prometheus.client.MetricFamily; " + "encoding=delimited"; } else if (acceptedEncoding.find("application/json") != std::string::npos) { - std::stringstream ss; - ss << "["; - - for (auto&& wcollectable : collectables_) { - auto collectable = wcollectable.lock(); - if (!collectable) { - continue; - } - - for (auto&& metricFamily : collectable->collect()) { - std::string result; - google::protobuf::util::MessageToJsonString( - metricFamily, &result, google::protobuf::util::JsonPrintOptions()); - ss << result; - if (collectable != collectables_.back().lock()) { - ss << ","; - } - } - } - ss << "]"; - auto body = ss.str(); - mg_printf(conn, - "HTTP/1.1 200 OK\r\n" - "Content-Type: application/json\r\n" - "Content-Length: "); - mg_printf(conn, "%lu\r\n\r\n", body.size()); - mg_write(conn, body.data(), body.size()); - bytesTransfered_->inc(body.size()); + body = serializeToJson(metrics); + contentType = "application/json"; } else { - auto body = std::string{}; - for (auto&& wcollectable : collectables_) { - auto collectable = wcollectable.lock(); - if (!collectable) { - continue; - } - - for (auto&& metricFamily : collectable->collect()) { - body += metricFamily.DebugString() + "\n"; - } - mg_printf(conn, - "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Content-Length: "); - mg_printf(conn, "%lu\r\n\r\n", body.size()); - mg_write(conn, body.data(), body.size()); - bytesTransfered_->inc(body.size()); - } + body = serializeToHumanReadable(metrics); + contentType = "text/plain"; } + mg_printf(conn, + "HTTP/1.1 200 OK\r\n" + "Content-Type: %s\r\n", + contentType.c_str()); + mg_printf(conn, "Content-Length: %lu\r\n\r\n", body.size()); + mg_write(conn, body.data(), body.size()); + + bytesTransfered_->inc(body.size()); + numScrapes_->inc(); return true; } @@ -148,4 +137,22 @@ void Exposer::registerCollectable( const std::weak_ptr& collectable) { collectables_.push_back(collectable); } + +std::vector +MetricsHandler::collectMetrics() const { + auto collectedMetrics = std::vector{}; + + for (auto&& wcollectable : collectables_) { + auto collectable = wcollectable.lock(); + if (!collectable) { + continue; + } + + for (auto metric : collectable->collect()) { + collectedMetrics.push_back(metric); + } + } + + return collectedMetrics; +} } diff --git a/lib/exposer.h b/lib/exposer.h index 534c4f8b..9e22d70d 100644 --- a/lib/exposer.h +++ b/lib/exposer.h @@ -16,6 +16,9 @@ class MetricsHandler : public CivetHandler { bool handleGet(CivetServer* server, struct mg_connection* conn); + private: + std::vector collectMetrics() const; + const std::vector>& collectables_; Family* bytesTransferedFamily_; Counter* bytesTransfered_;