From 694010191e3848e202419e21e63a90703c40b66c Mon Sep 17 00:00:00 2001 From: Pere Diaz Bou Date: Tue, 19 Apr 2022 13:19:38 +0200 Subject: [PATCH] exporter: Cleanups on DaemonMetricCollector and google styling 1. Formatted all exporter files to google c++ styling. 2. Refactored send_requests a bit to increase readabilty. 3. Removed unused functions + variables. 4. Moved fs::path socketdir to a static const char* SOCKETDIR. Signed-off-by: Pere Diaz Bou --- src/exporter/DaemonMetricCollector.cc | 200 +++++++++------- src/exporter/DaemonMetricCollector.h | 31 +-- src/exporter/http_server.cc | 325 +++++++++++--------------- 3 files changed, 268 insertions(+), 288 deletions(-) diff --git a/src/exporter/DaemonMetricCollector.cc b/src/exporter/DaemonMetricCollector.cc index 962723415931e..51d3fe7c52e19 100644 --- a/src/exporter/DaemonMetricCollector.cc +++ b/src/exporter/DaemonMetricCollector.cc @@ -1,55 +1,59 @@ #include "DaemonMetricCollector.h" #include "common/admin_socket_client.h" -#include "include/common_fwd.h" #include "common/perf_counters.h" +#include "include/common_fwd.h" -#include -#include +#include #include +#include #include -#include +#include +#include using json_object = boost::json::object; using json_value = boost::json::value; using json_array = boost::json::array; +const char *DaemonMetricCollector::SOCKETDIR = "/var/run/ceph/"; + void DaemonMetricCollector::request_loop(boost::asio::deadline_timer &timer) { - timer.async_wait([&](const boost::system::error_code& e) { + timer.async_wait([&](const boost::system::error_code &e) { std::cerr << e << std::endl; update_sockets(); - send_requests(); + dump_asok_metrics(); timer.expires_from_now(boost::posix_time::seconds(stats_period)); request_loop(timer); }); } void DaemonMetricCollector::main() { - stats_period = 5; // TODO: let's do 5 for now and expose this to change in the future + // TODO: let's do 5 for now and expose this to change in the future + stats_period = 5; boost::asio::io_service io; - boost::asio::deadline_timer timer(io, boost::posix_time::seconds(stats_period)); + boost::asio::deadline_timer timer(io, + boost::posix_time::seconds(stats_period)); request_loop(timer); io.run(); } -std::string DaemonMetricCollector::get_metrics() { - return metrics; -} +std::string DaemonMetricCollector::get_metrics() { return metrics; } template -void add_metric(std::stringstream &ss, T value, std::string name, std::string description, - std::string mtype, std::string labels) { +void add_metric(std::stringstream &ss, T value, std::string name, + std::string description, std::string mtype, + std::string labels) { ss << "# HELP " << name << " " << description << "\n"; ss << "# TYPE " << name << " " << mtype << "\n"; ss << name << "{" << labels << "} " << value << "\n"; } -void add_double_or_int_metric(std::stringstream &ss, json_value value, std::string name, std::string description, - std::string mtype, std::string labels) { +void add_double_or_int_metric(std::stringstream &ss, json_value value, + std::string name, std::string description, + std::string mtype, std::string labels) { if (value.is_int64()) { int64_t v = value.as_int64(); add_metric(ss, v, name, description, mtype, labels); - } - else if (value.is_double()) { + } else if (value.is_double()) { double v = value.as_double(); add_metric(ss, v, name, description, mtype, labels); } @@ -60,100 +64,126 @@ std::string boost_string_to_std(boost::json::string js) { return res; } -std::string quote(std::string value) { - return "\"" + value + "\""; -} +std::string quote(std::string value) { return "\"" + value + "\""; } -bool Ishyphen(char ch) -{ - return ch == '-' ; -} +bool is_hyphen(char ch) { return ch == '-'; } -void DaemonMetricCollector::send_requests() { - std::string result; - for(auto client : clients) { +void DaemonMetricCollector::dump_asok_metrics() { + std::stringstream ss; + for (auto client : clients) { AdminSocketClient &sock_client = client.second; std::string daemon_name = client.first; - std::string request("{\"prefix\":\"perf dump\"}"); - std::string response; - sock_client.do_request(request, &response); - if (response.size() > 0) { - json_object dump = boost::json::parse(response).as_object(); - request = "{\"prefix\":\"perf schema\"}"; - response = ""; - sock_client.do_request(request, &response); - json_object schema = boost::json::parse(response).as_object(); + std::string perf_dump_response = asok_request(sock_client, "perf dump"); + if (perf_dump_response.size() > 0) { + json_object dump = boost::json::parse(perf_dump_response).as_object(); + std::string perf_schema_response = + asok_request(sock_client, "perf schema"); + json_object schema = boost::json::parse(perf_schema_response).as_object(); for (auto perf : schema) { std::string perf_group = perf.key().to_string(); json_object perf_group_object = perf.value().as_object(); for (auto perf_counter : perf_group_object) { std::string perf_name = perf_counter.key().to_string(); json_object perf_info = perf_counter.value().as_object(); - if (perf_info["priority"].as_int64() < PerfCountersBuilder::PRIO_USEFUL) { + if (perf_info["priority"].as_int64() < + PerfCountersBuilder::PRIO_USEFUL) { continue; } - int64_t type = perf_info["type"].as_int64(); - std::string mtype = boost_string_to_std(perf_info["metric_type"].as_string()); - std::string description = boost_string_to_std(perf_info["description"].as_string()); - std::stringstream ss; std::string name = "ceph_" + perf_group + "_" + perf_name; - std::replace_if(name.begin(), name.end(), Ishyphen, '_'); + std::replace_if(name.begin(), name.end(), is_hyphen, '_'); - std::string labels; - // Labels // FIXME: test this, based on mgr_module perfpath_to_path_labels - if (daemon_name.find("rgw") != std::string::npos) { - std::string tmp = daemon_name.substr(16, std::string::npos); - std::string::size_type pos = tmp.find('.'); - labels = std::string("instance_id=") + quote("rgw." + tmp.substr(0, pos)); - } else { - labels = "ceph_daemon=" + quote(daemon_name); - if (daemon_name.find("rbd-mirror") != std::string::npos) { - std::regex re("^rbd_mirror_image_([^/]+)/(?:(?:([^/]+)/)?)(.*)\\.(replay(?:_bytes|_latency)?)$"); - std::smatch match; - if (std::regex_search(daemon_name, match, re) == true) { - name = "ceph_rbd_mirror_image_" + match.str(4); - labels += ",pool=" + quote(match.str(1)); - labels += ",namespace=" + quote(match.str(2)); - labels += ",image=" + quote(match.str(3)); - } - } - } + auto labels_and_name = get_labels_and_metric_name(daemon_name, name); + std::string labels = labels_and_name.first; + name = labels_and_name.second; - // value and add_metric json_value perf_values = dump[perf_group].as_object()[perf_name]; - if (type & PERFCOUNTER_LONGRUNAVG) { - int64_t count = perf_values.as_object()["avgcount"].as_int64(); - add_metric(ss, count, name + "_count", description, mtype, labels); - json_value sum_value = perf_values.as_object()["sum"]; - add_double_or_int_metric(ss, sum_value, name + "_sum", description, mtype, labels); - } else if(type & PERFCOUNTER_TIME) { - if (perf_values.is_int64()) { - double value = perf_values.as_int64() / 1000000000.0f; - add_metric(ss, value, name, description, mtype, labels); - } - else if (perf_values.is_double()) { - double value = perf_values.as_double() / 1000000000.0f; - add_metric(ss, value, name, description, mtype, labels); - } - } else { - add_double_or_int_metric(ss, perf_values, name, description, mtype, labels); - } - result += ss.str(); + dump_asok_metric(ss, perf_info, perf_values, name, labels); } } } } - metrics = result; + metrics = ss.str(); +} + +std::string DaemonMetricCollector::asok_request(AdminSocketClient &asok, + std::string command) { + std::string request("{\"prefix\": \"" + command + "\"}"); + std::string response; + asok.do_request(request, &response); + return response; +} + +std::pair +DaemonMetricCollector::get_labels_and_metric_name(std::string daemon_name, + std::string metric_name) { + std::string labels, new_metric_name; + new_metric_name = metric_name; + if (daemon_name.find("rgw") != std::string::npos) { + std::string tmp = daemon_name.substr(16, std::string::npos); + std::string::size_type pos = tmp.find('.'); + labels = std::string("instance_id=") + quote("rgw." + tmp.substr(0, pos)); + } else { + labels = "ceph_daemon=" + quote(daemon_name); + if (daemon_name.find("rbd-mirror") != std::string::npos) { + std::regex re("^rbd_mirror_image_([^/]+)/(?:(?:([^/]+)/" + ")?)(.*)\\.(replay(?:_bytes|_latency)?)$"); + std::smatch match; + if (std::regex_search(daemon_name, match, re) == true) { + new_metric_name = "ceph_rbd_mirror_image_" + match.str(4); + labels += ",pool=" + quote(match.str(1)); + labels += ",namespace=" + quote(match.str(2)); + labels += ",image=" + quote(match.str(3)); + } + } + } + return {labels, new_metric_name}; } +/* +perf_values can be either a int/double or a json_object. Since + json_value is a wrapper of both we use that class. + */ +void DaemonMetricCollector::dump_asok_metric(std::stringstream &ss, + json_object perf_info, + json_value perf_values, + std::string name, + std::string labels) { + int64_t type = perf_info["type"].as_int64(); + std::string metric_type = + boost_string_to_std(perf_info["metric_type"].as_string()); + std::string description = + boost_string_to_std(perf_info["description"].as_string()); + + if (type & PERFCOUNTER_LONGRUNAVG) { + int64_t count = perf_values.as_object()["avgcount"].as_int64(); + add_metric(ss, count, name + "_count", description, metric_type, labels); + json_value sum_value = perf_values.as_object()["sum"]; + add_double_or_int_metric(ss, sum_value, name + "_sum", description, + metric_type, labels); + } else if (type & PERFCOUNTER_TIME) { + if (perf_values.is_int64()) { + double value = perf_values.as_int64() / 1000000000.0f; + add_metric(ss, value, name, description, metric_type, labels); + } else if (perf_values.is_double()) { + double value = perf_values.as_double() / 1000000000.0f; + add_metric(ss, value, name, description, metric_type, labels); + } + } else { + add_double_or_int_metric(ss, perf_values, name, description, metric_type, + labels); + } +} void DaemonMetricCollector::update_sockets() { - for (const auto & entry : std::filesystem::recursive_directory_iterator(socketdir)) { + for (const auto &entry : + std::filesystem::recursive_directory_iterator(SOCKETDIR)) { if (entry.path().extension() == ".asok") { std::string daemon_socket_name = entry.path().filename().string(); // remove .asok - std::string daemon_name = daemon_socket_name.substr(0, daemon_socket_name.size() - 5); - if (clients.find(daemon_name) == clients.end() && !(daemon_name.find("mgr") != std::string::npos)) { + std::string daemon_name = + daemon_socket_name.substr(0, daemon_socket_name.size() - 5); + if (clients.find(daemon_name) == clients.end() && + !(daemon_name.find("mgr") != std::string::npos)) { AdminSocketClient sock(entry.path().string()); clients.insert({daemon_name, std::move(sock)}); } @@ -163,7 +193,7 @@ void DaemonMetricCollector::update_sockets() { DaemonMetricCollector *_collector_instance = nullptr; -DaemonMetricCollector& collector_instance() { +DaemonMetricCollector &collector_instance() { if (_collector_instance == nullptr) { _collector_instance = new DaemonMetricCollector(); } diff --git a/src/exporter/DaemonMetricCollector.h b/src/exporter/DaemonMetricCollector.h index 4b7af587f0725..3bd445fc1cbfc 100644 --- a/src/exporter/DaemonMetricCollector.h +++ b/src/exporter/DaemonMetricCollector.h @@ -1,36 +1,37 @@ #pragma once #include "common/admin_socket_client.h" -#include #include +#include #include -#include -#include +#include +#include +#include #include +#include #include -#include - -namespace fs = std::filesystem; class DaemonMetricCollector { - public: - int i; +public: void main(); std::string get_metrics(); + static const char *SOCKETDIR; private: - // TODO: add clients - // check removed sockets - // list dir of sockets std::map clients; std::string metrics; int stats_period; // time to wait before sending requests again - fs::path socketdir = "/var/run/ceph/"; void update_sockets(); void request_loop(boost::asio::deadline_timer &timer); - void send_requests(); - void start_mgr_connection(); + + void dump_asok_metrics(); + void dump_asok_metric(std::stringstream &ss, boost::json::object perf_info, + boost::json::value perf_values, std::string name, + std::string labels); + std::pair + get_labels_and_metric_name(std::string daemon_name, std::string metric_name); + std::string asok_request(AdminSocketClient &asok, std::string command); }; -DaemonMetricCollector& collector_instance(); +DaemonMetricCollector &collector_instance(); diff --git a/src/exporter/http_server.cc b/src/exporter/http_server.cc index afb1ffd1b39fb..074dd787665c8 100644 --- a/src/exporter/http_server.cc +++ b/src/exporter/http_server.cc @@ -1,219 +1,168 @@ #include "http_server.h" -#include "exporter/DaemonMetricCollector.h" #include "common/hostname.h" +#include "exporter/DaemonMetricCollector.h" -#include -#include +#include #include #include #include -#include +#include #include #include #include #include +#include #include #include -#include - -namespace beast = boost::beast; // from -namespace http = beast::http; // from -namespace net = boost::asio; // from -using tcp = boost::asio::ip::tcp; // from - -namespace my_program_state -{ - std::size_t - request_count() - { - static std::size_t count = 0; - return ++count; - } - std::time_t - now() - { - return std::time(0); - } -} +namespace beast = boost::beast; // from +namespace http = beast::http; // from +namespace net = boost::asio; // from +using tcp = boost::asio::ip::tcp; // from -class http_connection : public std::enable_shared_from_this -{ +class http_connection : public std::enable_shared_from_this { public: - http_connection(tcp::socket socket) - : socket_(std::move(socket)) - { - } + http_connection(tcp::socket socket) : socket_(std::move(socket)) {} - // Initiate the asynchronous operations associated with the connection. - void start() - { - read_request(); - check_deadline(); - } + // Initiate the asynchronous operations associated with the connection. + void start() { + read_request(); + check_deadline(); + } private: - tcp::socket socket_; - beast::flat_buffer buffer_{8192}; - http::request request_; - http::response response_; - - net::steady_timer deadline_{ - socket_.get_executor(), std::chrono::seconds(60)}; - - // Asynchronously receive a complete request message. - void read_request() - { - // auto self = shared_from_this(); - - http::async_read( - socket_, - buffer_, - request_, - [&](beast::error_code ec, - std::size_t bytes_transferred) - { - boost::ignore_unused(bytes_transferred); - if(!ec) - process_request(); - }); - } - - // Determine what needs to be done with the request message. - void process_request() - { - response_.version(request_.version()); - response_.keep_alive(request_.keep_alive()); - - switch(request_.method()) - { - case http::verb::get: - response_.result(http::status::ok); - create_response(); - break; - - default: - // We return responses indicating an error if - // we do not recognize the request method. - response_.result(http::status::bad_request); - response_.set(http::field::content_type, "text/plain"); - beast::ostream(response_.body()) - << "Invalid request-method '" - << std::string(request_.method_string()) - << "'"; - break; - } - - write_response(); + tcp::socket socket_; + beast::flat_buffer buffer_{8192}; + http::request request_; + http::response response_; + + net::steady_timer deadline_{socket_.get_executor(), std::chrono::seconds(60)}; + + // Asynchronously receive a complete request message. + void read_request() { + // auto self = shared_from_this(); + + http::async_read(socket_, buffer_, request_, + [&](beast::error_code ec, std::size_t bytes_transferred) { + boost::ignore_unused(bytes_transferred); + if (!ec) + process_request(); + }); + } + + // Determine what needs to be done with the request message. + void process_request() { + response_.version(request_.version()); + response_.keep_alive(request_.keep_alive()); + + switch (request_.method()) { + case http::verb::get: + response_.result(http::status::ok); + create_response(); + break; + + default: + // We return responses indicating an error if + // we do not recognize the request method. + response_.result(http::status::bad_request); + response_.set(http::field::content_type, "text/plain"); + beast::ostream(response_.body()) + << "Invalid request-method '" << std::string(request_.method_string()) + << "'"; + break; } - // Construct a response message based on the program state. - void create_response() - { - if (request_.target() == "/") { - response_.set(http::field::content_type, "text/html; charset=utf-8"); - beast::ostream(response_.body()) - << "\n" - << "Ceph Exporter\n" - << "\n" - << "

Ceph Exporter

\n" - - << "

Metrics

" - << "\n" - << "\n"; - } - else if(request_.target() == "/metrics") - { - response_.set(http::field::content_type, "text/plain; charset=utf-8"); - DaemonMetricCollector &collector = collector_instance(); - std::string metrics = collector.get_metrics(); - beast::ostream(response_.body()) << metrics << std::endl; - } - else - { - response_.result(http::status::not_found); - response_.set(http::field::content_type, "text/plain"); - beast::ostream(response_.body()) << "File not found\r\n"; - } - } - - // Asynchronously transmit the response message. - void write_response() - { - auto self = shared_from_this(); - - response_.content_length(response_.body().size()); - - http::async_write( - socket_, - response_, - [self](beast::error_code ec, std::size_t) - { - self->socket_.shutdown(tcp::socket::shutdown_send, ec); - self->deadline_.cancel(); - }); - } - - // Check whether we have spent enough time on this connection. - void check_deadline() - { - auto self = shared_from_this(); - - deadline_.async_wait( - [self](beast::error_code ec) - { - if(!ec) - { - // Close socket to cancel any outstanding operation. - self->socket_.close(ec); - } - }); + write_response(); + } + + // Construct a response message based on the program state. + void create_response() { + if (request_.target() == "/") { + response_.set(http::field::content_type, "text/html; charset=utf-8"); + beast::ostream(response_.body()) + << "\n" + << "Ceph Exporter\n" + << "\n" + << "

Ceph Exporter

\n" + + << "

Metrics

" + << "\n" + << "\n"; + } else if (request_.target() == "/metrics") { + response_.set(http::field::content_type, "text/plain; charset=utf-8"); + DaemonMetricCollector &collector = collector_instance(); + std::string metrics = collector.get_metrics(); + beast::ostream(response_.body()) << metrics << std::endl; + } else { + response_.result(http::status::not_found); + response_.set(http::field::content_type, "text/plain"); + beast::ostream(response_.body()) << "File not found\r\n"; } + } + + // Asynchronously transmit the response message. + void write_response() { + auto self = shared_from_this(); + + response_.content_length(response_.body().size()); + + http::async_write(socket_, response_, + [self](beast::error_code ec, std::size_t) { + self->socket_.shutdown(tcp::socket::shutdown_send, ec); + self->deadline_.cancel(); + }); + } + + // Check whether we have spent enough time on this connection. + void check_deadline() { + auto self = shared_from_this(); + + deadline_.async_wait([self](beast::error_code ec) { + if (!ec) { + // Close socket to cancel any outstanding operation. + self->socket_.close(ec); + } + }); + } }; // "Loop" forever accepting new connections. -void http_server(tcp::acceptor& acceptor, tcp::socket& socket) -{ - acceptor.async_accept(socket, - [&](beast::error_code ec) - { - if(!ec) - std::make_shared(std::move(socket))->start(); - http_server(acceptor, socket); - }); +void http_server(tcp::acceptor &acceptor, tcp::socket &socket) { + acceptor.async_accept(socket, [&](beast::error_code ec) { + if (!ec) + std::make_shared(std::move(socket))->start(); + http_server(acceptor, socket); + }); } std::string dns_lookup(std::string hostname) { - boost::asio::io_service io_service; - boost::asio::ip::tcp::resolver resolver(io_service); - boost::asio::ip::tcp::resolver::query query(hostname, "9926"); - boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query); - boost::asio::ip::tcp::endpoint endpoint = iter->endpoint(); - std::string ip_address = endpoint.address().to_string(); - - return ip_address; + boost::asio::io_service io_service; + boost::asio::ip::tcp::resolver resolver(io_service); + boost::asio::ip::tcp::resolver::query query(hostname, "9926"); + boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query); + boost::asio::ip::tcp::endpoint endpoint = iter->endpoint(); + std::string ip_address = endpoint.address().to_string(); + + return ip_address; } void http_server_thread_entrypoint() { - try - { - std::string hostname = ceph_get_short_hostname(); - - std::string ip_address = dns_lookup(hostname); - auto const address = net::ip::make_address(ip_address); - unsigned short port = static_cast(std::atoi("9926")); - - net::io_context ioc{1}; - - tcp::acceptor acceptor{ioc, {address, port}}; - tcp::socket socket{ioc}; - http_server(acceptor, socket); - std::cout << "Http server started" << std::endl; - ioc.run(); - } - catch(std::exception const& e) - { - std::cerr << "Error: " << e.what() << std::endl; - exit(EXIT_FAILURE); - } + try { + std::string hostname = ceph_get_short_hostname(); + + std::string ip_address = dns_lookup(hostname); + auto const address = net::ip::make_address(ip_address); + unsigned short port = 9926; + + net::io_context ioc{1}; + + tcp::acceptor acceptor{ioc, {address, port}}; + tcp::socket socket{ioc}; + http_server(acceptor, socket); + std::cout << "Http server started" << std::endl; + ioc.run(); + } catch (std::exception const &e) { + std::cerr << "Error: " << e.what() << std::endl; + exit(EXIT_FAILURE); + } } -- 2.39.5