From 2433a728feee5734828583f3c3f2a8b5c0f25f71 Mon Sep 17 00:00:00 2001 From: Dominik Charousset Date: Sun, 28 Apr 2024 15:14:25 +0200 Subject: [PATCH] Add prometheus-cpp, drop legacy telemetry API --- CMakeLists.txt | 46 ++ libbroker/CMakeLists.txt | 41 +- libbroker/broker/endpoint.cc | 183 ++------ libbroker/broker/endpoint.hh | 53 +-- libbroker/broker/fwd.hh | 18 + libbroker/broker/internal/channel.hh | 49 +-- libbroker/broker/internal/clone_actor.cc | 5 +- libbroker/broker/internal/clone_actor.hh | 5 +- libbroker/broker/internal/core_actor.cc | 93 +++-- libbroker/broker/internal/core_actor.hh | 23 +- libbroker/broker/internal/core_actor.test.cc | 5 +- libbroker/broker/internal/flow_scope.hh | 1 - libbroker/broker/internal/master_actor.cc | 29 +- libbroker/broker/internal/master_actor.hh | 11 +- libbroker/broker/internal/metric_collector.cc | 316 -------------- libbroker/broker/internal/metric_collector.hh | 200 --------- .../broker/internal/metric_collector.test.cc | 178 -------- libbroker/broker/internal/metric_exporter.cc | 39 -- libbroker/broker/internal/metric_exporter.hh | 194 --------- .../broker/internal/metric_exporter.test.cc | 229 ---------- libbroker/broker/internal/metric_factory.cc | 149 ++++--- libbroker/broker/internal/metric_factory.hh | 108 +++-- libbroker/broker/internal/metric_scraper.cc | 161 ------- libbroker/broker/internal/metric_scraper.hh | 113 ----- libbroker/broker/internal/metric_view.cc | 101 ----- libbroker/broker/internal/metric_view.hh | 118 ------ libbroker/broker/internal/peering.hh | 2 - libbroker/broker/internal/prometheus.cc | 361 ---------------- libbroker/broker/internal/prometheus.hh | 79 ---- libbroker/broker/internal/store_actor.cc | 4 +- libbroker/broker/internal/store_actor.hh | 14 +- .../broker/internal/with_native_labels.hh | 47 --- libbroker/broker/telemetry/counter.cc | 79 ---- libbroker/broker/telemetry/counter.hh | 143 ------- libbroker/broker/telemetry/fwd.hh | 358 ---------------- libbroker/broker/telemetry/gauge.cc | 94 ----- libbroker/broker/telemetry/gauge.hh | 166 -------- libbroker/broker/telemetry/histogram.cc | 144 ------- libbroker/broker/telemetry/histogram.hh | 145 ------- libbroker/broker/telemetry/histogram.test.cc | 85 ---- libbroker/broker/telemetry/metric_family.cc | 132 ------ libbroker/broker/telemetry/metric_family.hh | 64 --- libbroker/broker/telemetry/metric_registry.cc | 293 ------------- libbroker/broker/telemetry/metric_registry.hh | 393 ------------------ .../broker/telemetry/metric_registry_impl.cc | 13 - .../broker/telemetry/metric_registry_impl.hh | 105 ----- 46 files changed, 367 insertions(+), 4822 deletions(-) delete mode 100644 libbroker/broker/internal/metric_collector.cc delete mode 100644 libbroker/broker/internal/metric_collector.hh delete mode 100644 libbroker/broker/internal/metric_collector.test.cc delete mode 100644 libbroker/broker/internal/metric_exporter.cc delete mode 100644 libbroker/broker/internal/metric_exporter.hh delete mode 100644 libbroker/broker/internal/metric_exporter.test.cc delete mode 100644 libbroker/broker/internal/metric_scraper.cc delete mode 100644 libbroker/broker/internal/metric_scraper.hh delete mode 100644 libbroker/broker/internal/metric_view.cc delete mode 100644 libbroker/broker/internal/metric_view.hh delete mode 100644 libbroker/broker/internal/prometheus.cc delete mode 100644 libbroker/broker/internal/prometheus.hh delete mode 100644 libbroker/broker/internal/with_native_labels.hh delete mode 100644 libbroker/broker/telemetry/counter.cc delete mode 100644 libbroker/broker/telemetry/counter.hh delete mode 100644 libbroker/broker/telemetry/fwd.hh delete mode 100644 libbroker/broker/telemetry/gauge.cc delete mode 100644 libbroker/broker/telemetry/gauge.hh delete mode 100644 libbroker/broker/telemetry/histogram.cc delete mode 100644 libbroker/broker/telemetry/histogram.hh delete mode 100644 libbroker/broker/telemetry/histogram.test.cc delete mode 100644 libbroker/broker/telemetry/metric_family.cc delete mode 100644 libbroker/broker/telemetry/metric_family.hh delete mode 100644 libbroker/broker/telemetry/metric_registry.cc delete mode 100644 libbroker/broker/telemetry/metric_registry.hh delete mode 100644 libbroker/broker/telemetry/metric_registry_impl.cc delete mode 100644 libbroker/broker/telemetry/metric_registry_impl.hh diff --git a/CMakeLists.txt b/CMakeLists.txt index dcb01f51..f2188624 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,6 +3,7 @@ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) project(broker C CXX) include(CMakePackageConfigHelpers) +include(FetchContent) include(GNUInstallDirs) include(cmake/CommonCMakeConfig.cmake) @@ -208,6 +209,51 @@ function(add_bundled_caf) add_subdirectory(caf EXCLUDE_FROM_ALL) endfunction() +FetchContent_Declare( + dl_prometheus_cpp + GIT_REPOSITORY https://github.com/jupp0r/prometheus-cpp.git + GIT_TAG v1.2.4) + +# Bundle prometheus-cpp setup as an interface library. +add_library(broker-prometheus-cpp INTERFACE) + +function(add_prometheus_cpp) + # Skip if already configured. + # Get prometheus-cpp if the target is not already available. + if(NOT TARGET prometheus-cpp::core) + set(BUILD_SHARED_LIBS OFF) # bundle prometheus-cpp as static library + set(ENABLE_PUSH OFF) + set(ENABLE_TESTING OFF) + set(GENERATE_PKGCONFIG OFF) + set(CIVETWEB_ENABLE_DEBUG_TOOLS OFF) + set(OVERRIDE_CXX_STANDARD_FLAGS OFF) + if(NOT dl_prometheus_cpp_POPULATED) + FetchContent_Populate(dl_prometheus_cpp) + add_subdirectory(${dl_prometheus_cpp_SOURCE_DIR} + ${dl_prometheus_cpp_BINARY_DIR} + EXCLUDE_FROM_ALL) + endif() + # Tell CMake to look for the headers in the build tree. + target_include_directories( + broker-prometheus-cpp + INTERFACE + $ + $ + $ + $) + endif() + # Link against prometheus-cpp. + target_link_libraries( + broker-prometheus-cpp + INTERFACE + prometheus-cpp::core + prometheus-cpp::pull) + # Prometheus-cpp sets cxx_std_11, but we need cxx_std_17. + target_compile_features(broker-prometheus-cpp INTERFACE cxx_std_17) +endfunction() + +add_prometheus_cpp() + # NOTE: building and linking against an external CAF version is NOT supported! # This variable is FOR DEVELOPMENT ONLY. The only officially supported CAF # version is the bundled version! diff --git a/libbroker/CMakeLists.txt b/libbroker/CMakeLists.txt index ed4dab7a..9e3c0dc3 100644 --- a/libbroker/CMakeLists.txt +++ b/libbroker/CMakeLists.txt @@ -54,15 +54,10 @@ set(BROKER_SRC broker/internal/json_type_mapper.cc broker/internal/master_actor.cc broker/internal/master_resolver.cc - broker/internal/metric_collector.cc - broker/internal/metric_exporter.cc broker/internal/metric_factory.cc - broker/internal/metric_scraper.cc - broker/internal/metric_view.cc broker/internal/peering.cc broker/internal/pending_connection.cc broker/internal/println.cc - broker/internal/prometheus.cc broker/internal/store_actor.cc broker/internal/web_socket.cc broker/internal/wire_format.cc @@ -83,12 +78,6 @@ set(BROKER_SRC broker/store_event.cc broker/subnet.cc broker/subscriber.cc - broker/telemetry/counter.cc - broker/telemetry/gauge.cc - broker/telemetry/histogram.cc - broker/telemetry/metric_family.cc - broker/telemetry/metric_registry.cc - broker/telemetry/metric_registry_impl.cc broker/time.cc broker/topic.cc broker/variant.cc @@ -110,7 +99,13 @@ if (ENABLE_SHARED) MACOSX_RPATH true OUTPUT_NAME broker) target_link_libraries(broker PUBLIC ${LINK_LIBS}) - target_link_libraries(broker PRIVATE CAF::core CAF::io CAF::net) + target_link_libraries( + broker + PRIVATE + CAF::core + CAF::io + CAF::net + broker-prometheus-cpp) install(TARGETS broker EXPORT BrokerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -127,7 +122,13 @@ if (ENABLE_STATIC) set_target_properties(broker_static PROPERTIES POSITION_INDEPENDENT_CODE ON) endif() target_link_libraries(broker_static PUBLIC ${LINK_LIBS}) - target_link_libraries(broker_static PRIVATE CAF::core CAF::io CAF::net) + target_link_libraries( + broker_static + PRIVATE + CAF::core + CAF::io + CAF::net + broker-prometheus-cpp) install(TARGETS broker_static EXPORT BrokerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -172,8 +173,6 @@ set(BROKER_TEST_SRC broker/internal/channel.test.cc broker/internal/core_actor.test.cc broker/internal/json.test.cc - broker/internal/metric_collector.test.cc - broker/internal/metric_exporter.test.cc broker/internal/wire_format.test.cc broker/master.test.cc broker/peering.test.cc @@ -185,7 +184,6 @@ set(BROKER_TEST_SRC broker/store.test.cc broker/store_event.test.cc broker/subscriber.test.cc - broker/telemetry/histogram.test.cc broker/topic.test.cc broker/variant.test.cc broker/zeek.test.cc @@ -199,8 +197,15 @@ set(BROKER_TEST_SRC # endif() add_executable(broker-test ${BROKER_TEST_SRC}) -target_link_libraries(broker-test PRIVATE - ${main_lib_target} CAF::core CAF::io CAF::net CAF::test) +target_link_libraries( + broker-test + PRIVATE + ${main_lib_target} + CAF::core + CAF::io + CAF::net + CAF::test + broker-prometheus-cpp) foreach(file_path ${BROKER_TEST_SRC}) get_filename_component(test_dir ${file_path} DIRECTORY) diff --git a/libbroker/broker/endpoint.cc b/libbroker/broker/endpoint.cc index 06a5e65b..bfa356c3 100644 --- a/libbroker/broker/endpoint.cc +++ b/libbroker/broker/endpoint.cc @@ -10,8 +10,6 @@ #include "broker/internal/json_client.hh" #include "broker/internal/json_type_mapper.hh" #include "broker/internal/logger.hh" -#include "broker/internal/metric_exporter.hh" -#include "broker/internal/prometheus.hh" #include "broker/internal/type_id.hh" #include "broker/internal/web_socket.hh" #include "broker/port.hh" @@ -27,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -49,8 +48,6 @@ #include "broker/internal/connector.hh" #include "broker/internal/core_actor.hh" #include "broker/internal/logger.hh" -#include "broker/internal/metric_exporter.hh" -#include "broker/internal/prometheus.hh" #include "broker/publisher.hh" #include "broker/status_subscriber.hh" #include "broker/subscriber.hh" @@ -60,6 +57,9 @@ #include #include +#include +#include + #ifdef BROKER_WINDOWS # include "Winsock2.h" #endif @@ -270,130 +270,6 @@ endpoint::background_task::~background_task() { // nop } -namespace { - -class prometheus_http_task : public endpoint::background_task { -public: - prometheus_http_task(caf::actor_system& sys) : mpx_(&sys) { - // nop - } - - template - bool has(caf::string_view name) { - auto res = caf::get_as(mpx_.system().config(), name); - return static_cast(res); - } - - expected start(uint16_t port, caf::actor core, - const char* in = nullptr, bool reuse = true) { - caf::io::doorman_ptr dptr; - if (auto maybe_dptr = mpx_.new_tcp_doorman(port, in, reuse)) { - dptr = std::move(*maybe_dptr); - } else { - return facade(maybe_dptr.error()); - } - auto actual_port = dptr->port(); - using impl = internal::prometheus_actor; - mpx_supervisor_ = mpx_.make_supervisor(); - caf::actor_config cfg{&mpx_}; - worker_ = mpx_.system().spawn_impl(cfg, std::move(dptr), - std::move(core)); - struct { // TODO: replace with std::latch when available. - std::mutex mx; - std::condition_variable cv; - bool lit = false; - void ignite() { - std::unique_lock guard{mx}; - lit = true; - cv.notify_all(); - } - void wait() { - std::unique_lock guard{mx}; - while (!lit) - cv.wait(guard); - } - } beacon; - auto run_mpx = [this, &beacon] { - BROKER_TRACE(""); - mpx_.thread_id(std::this_thread::get_id()); - beacon.ignite(); - mpx_.run(); - }; - thread_ = mpx_.system().launch_thread("broker.prom", run_mpx); - beacon.wait(); - return actual_port; - } - - ~prometheus_http_task() override { - if (mpx_supervisor_) { - mpx_.dispatch([=] { - auto base_ptr = caf::actor_cast(worker_); - auto ptr = static_cast(base_ptr); - if (!ptr->getf(caf::abstract_actor::is_terminated_flag)) { - ptr->context(&mpx_); - ptr->quit(); - ptr->finalize(); - } - }); - mpx_supervisor_.reset(); - thread_.join(); - } - } - - caf::actor telemetry_exporter() { - return worker_; - } - -private: - caf::io::network::default_multiplexer mpx_; - caf::io::network::multiplexer::supervisor_ptr mpx_supervisor_; - caf::actor worker_; - std::thread thread_; -}; - -} // namespace - -// --- metrics_exporter_t::endpoint class -------------------------------------- - -using string_list = std::vector; - -void endpoint::metrics_exporter_t::set_interval(caf::timespan new_interval) { - if (new_interval.count() > 0) - caf::anon_send(native(parent_->telemetry_exporter_), atom::put_v, - new_interval); -} - -void endpoint::metrics_exporter_t::set_target(topic new_target) { - if (!new_target.empty()) - caf::anon_send(native(parent_->telemetry_exporter_), atom::put_v, - std::move(new_target)); -} - -void endpoint::metrics_exporter_t::set_id(std::string new_id) { - if (!new_id.empty()) - caf::anon_send(native(parent_->telemetry_exporter_), atom::put_v, - std::move(new_id)); -} - -void endpoint::metrics_exporter_t::set_prefixes(string_list new_prefixes) { - // We only wrap the prefixes into a filter to get around assigning a type ID - // to std::vector (which technically would require us to change - // Broker ID on the network). - filter_type boxed; - for (auto& str : new_prefixes) - boxed.emplace_back(std::move(str)); - caf::anon_send(native(parent_->telemetry_exporter_), atom::put_v, - std::move(boxed)); -} - -void endpoint::metrics_exporter_t::set_import_topics(string_list new_topics) { - filter_type filter; - for (auto& str : new_topics) - filter.emplace_back(std::move(str)); - caf::anon_send(native(parent_->telemetry_exporter_), atom::join_v, - std::move(filter)); -} - // --- endpoint class ---------------------------------------------------------- namespace { @@ -485,12 +361,16 @@ endpoint::endpoint() : endpoint(configuration{}, endpoint_id::random()) { // nop } -endpoint::endpoint(configuration config) - : endpoint(std::move(config), endpoint_id::random()) { +endpoint::endpoint(configuration config, prometheus_registry_ptr registry) + : endpoint(std::move(config), endpoint_id::random(), std::move(registry)) { // nop } -endpoint::endpoint(configuration config, endpoint_id id) : id_(id) { +endpoint::endpoint(configuration config, endpoint_id id, + prometheus_registry_ptr registry) + : id_(id), registry_(std::move(registry)) { + if (registry_ == nullptr) + registry_ = std::make_shared(); // Spin up the actor system. auto broker_cfg = config.options(); auto ssl_cfg = config.openssl_options(); @@ -560,31 +440,23 @@ endpoint::endpoint(configuration config, endpoint_id id) : id_(id) { domain_options adaptation{opts.disable_forwarding}; if (auto sp = caf::get_as(cfg, "caf.scheduler.policy"); sp && *sp == "testing") { - core = sys.spawn(id_, filter_type{}, clock_.get(), &adaptation, - std::move(conn_ptr)); + core = sys.spawn(registry_, id_, filter_type{}, clock_.get(), + &adaptation, std::move(conn_ptr)); } else { - core = sys.spawn(id_, filter_type{}, clock_.get(), - &adaptation, std::move(conn_ptr)); + core = sys.spawn(registry_, id_, filter_type{}, + clock_.get(), &adaptation, + std::move(conn_ptr)); } core_ = facade(core); - // Spin up a Prometheus actor if configured or an exporter. + // Spin up a Prometheus exposer if configured. if (auto port = caf::get_as(cfg, "broker.metrics.port")) { - auto ptask = std::make_unique(sys); - auto addr = caf::get_or(cfg, "broker.metrics.address", std::string{}); - if (auto actual_port = - ptask->start(port->number(), native(core_), - addr.empty() ? nullptr : addr.c_str())) { - BROKER_INFO("expose metrics on port" << *actual_port); - telemetry_exporter_ = facade(ptask->telemetry_exporter()); - background_tasks_.emplace_back(std::move(ptask)); - } else { - BROKER_ERROR("failed to expose metrics:" << actual_port.error()); - } - } else { - using exporter_t = internal::metric_exporter_actor; - auto params = internal::metric_exporter_params::from(cfg); - auto hdl = sys.spawn(native(core_), std::move(params)); - telemetry_exporter_ = facade(hdl); + auto str = caf::get_or(cfg, "broker.metrics.address", ""s); + if (!str.empty()) + str += ':'; + str += std::to_string(port->number()); + BROKER_INFO("expose metrics on" << str); + exposer_ = std::make_unique(str); + exposer_->RegisterCollectable(registry_); } // Spin up a WebSocket server when requested. if (auto port = caf::get_as(cfg, "broker.web-socket.port")) @@ -639,13 +511,6 @@ void endpoint::shutdown() { self->wait_for(native(hdl)); workers_.clear(); } - BROKER_DEBUG("stop the telemetry exporter"); - self->send_exit(native(telemetry_exporter_), - caf::exit_reason::user_shutdown); - if (sched) - sched->run(); - self->wait_for(native(telemetry_exporter_)); - telemetry_exporter_ = nullptr; } BROKER_DEBUG("stop" << background_tasks_.size() << "background tasks"); background_tasks_.clear(); diff --git a/libbroker/broker/endpoint.hh b/libbroker/broker/endpoint.hh index 70400b26..2f5ef98b 100644 --- a/libbroker/broker/endpoint.hh +++ b/libbroker/broker/endpoint.hh @@ -32,6 +32,10 @@ #include "broker/topic.hh" #include "broker/worker.hh" +namespace prometheus { +class Exposer; +} // namespace prometheus + namespace broker::internal { struct endpoint_access; @@ -85,41 +89,6 @@ public: internal::endpoint_context* ctx_; }; - /// Utility class for configuring the metrics exporter. - class metrics_exporter_t { - public: - explicit metrics_exporter_t(endpoint* parent) : parent_(parent) { - // nop - } - - metrics_exporter_t(const metrics_exporter_t&) noexcept = default; - metrics_exporter_t& operator=(const metrics_exporter_t&) noexcept = default; - - /// Changes the frequency for publishing scraped metrics to the topic. - /// Passing a zero-length interval has no effect. - void set_interval(timespan new_interval); - - /// Sets a new target topic for the metrics. Passing an empty topic has no - /// effect. - void set_target(topic new_target); - - /// Sets a new ID for the metrics exporter. Passing an empty string has no - /// effect. - void set_id(std::string new_id); - - /// Sets a prefix selection for the metrics exporter. An empty vector means - /// *all*. - void set_prefixes(std::vector new_prefixes); - - /// Sets a new filter for the Prometheus metrics exporter to collect metrics - /// from remote endpoints. An empty vector means *none*. - /// @note Has no effect when not configuring Prometheus export. - void set_import_topics(std::vector new_topics); - - private: - endpoint* parent_; - }; - struct background_task { virtual ~background_task(); }; @@ -130,10 +99,12 @@ public: endpoint(); - explicit endpoint(configuration config); + explicit endpoint(configuration config, + prometheus_registry_ptr registry = nullptr); /// @private - endpoint(configuration config, endpoint_id this_peer); + endpoint(configuration config, endpoint_id this_peer, + prometheus_registry_ptr registry = nullptr); endpoint(endpoint&&) = delete; endpoint(const endpoint&) = delete; @@ -523,11 +494,6 @@ public: shutdown_options_.unset(flag); } - /// Returns a configuration object for the metrics exporter. - metrics_exporter_t metrics_exporter() { - return metrics_exporter_t{this}; - } - bool is_shutdown() const { return ctx_ == nullptr; } @@ -569,11 +535,12 @@ private: endpoint_id id_; worker core_; shutdown_options shutdown_options_; - worker telemetry_exporter_; bool await_stores_on_shutdown_ = false; std::vector workers_; std::unique_ptr clock_; std::vector> background_tasks_; + prometheus_registry_ptr registry_; + std::unique_ptr exposer_; }; } // namespace broker diff --git a/libbroker/broker/fwd.hh b/libbroker/broker/fwd.hh index c48f9e14..2bd70989 100644 --- a/libbroker/broker/fwd.hh +++ b/libbroker/broker/fwd.hh @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -193,3 +194,20 @@ class LogWrite; class IdentifierUpdate; } // namespace broker::zeek + +// -- third-party types -------------------------------------------------------- + +namespace prometheus { +class Counter; +class Gauge; +class Historgram; +class Registry; +} // namespace prometheus + +// -- type aliases for third-party libraries ----------------------------------- + +namespace broker { + +using prometheus_registry_ptr = std::shared_ptr; + +} // namespace broker diff --git a/libbroker/broker/internal/channel.hh b/libbroker/broker/internal/channel.hh index 42e874f2..d32ec5e2 100644 --- a/libbroker/broker/internal/channel.hh +++ b/libbroker/broker/internal/channel.hh @@ -16,6 +16,8 @@ #include "broker/lamport_timestamp.hh" #include "broker/none.hh" +#include + namespace broker::internal { /// A message-driven channel for ensuring reliable and ordered transport over an @@ -160,23 +162,19 @@ public: /// Bundles metrics for the producer. struct metrics_t { /// Keeps track of how many output channels exist. - caf::telemetry::int_gauge* output_channels = nullptr; + prometheus::Gauge* output_channels = nullptr; /// Keeps track of how many messages currently wait for an ACK. - caf::telemetry::int_gauge* unacknowledged = nullptr; + prometheus::Gauge* unacknowledged = nullptr; /// Keeps track of how many messages were sent (acknowledged) in total. - caf::telemetry::int_counter* processed = nullptr; + prometheus::Counter* processed = nullptr; - void init(caf::telemetry::metric_registry& reg, std::string_view name) { + void init(prometheus::Registry& reg, std::string name) { metric_factory factory{reg}; output_channels = factory.store.output_channels_instance(name); unacknowledged = factory.store.unacknowledged_updates_instance(name); - processed = factory.store.processed_updates_instance(name); - } - - void init(caf::actor_system& sys, std::string_view name) { - init(sys.metrics(), name); + processed = factory.store.processed_updates_instance(std::move(name)); } bool initialized() const noexcept { @@ -186,23 +184,23 @@ public: void inc_output_channels() { if (output_channels) - output_channels->inc(); + output_channels->Increment(); } void dec_output_channels() { if (output_channels) - output_channels->dec(); + output_channels->Decrement(); } void inc_unacknowledged() { if (unacknowledged) - unacknowledged->inc(); + unacknowledged->Increment(); } void shipped(int64_t num) { if (unacknowledged) { - unacknowledged->dec(num); - processed->inc(num); + unacknowledged->Decrement(num); + processed->Increment(num); } } }; @@ -530,20 +528,17 @@ public: /// Bundles metrics for the consumer. struct metrics_t { /// Keeps track of how many output channels exist. - caf::telemetry::int_gauge* input_channels = nullptr; + prometheus::Gauge* input_channels = nullptr; /// Keeps track of how many messages are currently buffered because they /// arrived out-of-order. - caf::telemetry::int_gauge* out_of_order_updates = nullptr; + prometheus::Gauge* out_of_order_updates = nullptr; - void init(caf::telemetry::metric_registry& reg, std::string_view name) { + void init(prometheus::Registry& reg, std::string name) { metric_factory mf{reg}; input_channels = mf.store.input_channels_instance(name); - out_of_order_updates = mf.store.out_of_order_updates_instance(name); - } - - void init(caf::actor_system& sys, std::string_view name) { - init(sys.metrics(), name); + out_of_order_updates = + mf.store.out_of_order_updates_instance(std::move(name)); } bool initialized() const noexcept { @@ -553,22 +548,22 @@ public: void inc_input_channels() { if (input_channels) - input_channels->inc(); + input_channels->Increment(); } void dec_input_channels() { if (input_channels) - input_channels->dec(); + input_channels->Decrement(); } void inc_out_of_order_updates() { if (out_of_order_updates) - out_of_order_updates->inc(); + out_of_order_updates->Increment(); } - void dec_out_of_order_updates(int64_t n = 1) { + void dec_out_of_order_updates(double n = 1) { if (out_of_order_updates) - out_of_order_updates->dec(n); + out_of_order_updates->Decrement(n); } }; diff --git a/libbroker/broker/internal/clone_actor.cc b/libbroker/broker/internal/clone_actor.cc index 97840d78..5cf4f5bc 100644 --- a/libbroker/broker/internal/clone_actor.cc +++ b/libbroker/broker/internal/clone_actor.cc @@ -40,13 +40,14 @@ double now(endpoint::clock* clock) { // -- initialization ----------------------------------------------------------- -clone_state::clone_state(caf::event_based_actor* ptr, endpoint_id this_endpoint, +clone_state::clone_state(caf::event_based_actor* ptr, + prometheus_registry_ptr reg, endpoint_id this_endpoint, std::string nm, caf::timespan master_timeout, caf::actor parent, endpoint::clock* ep_clock, caf::async::consumer_resource in_res, caf::async::producer_resource out_res) : super(ptr), input(this), max_sync_interval(master_timeout) { - super::init(this_endpoint, ep_clock, std::move(nm), std::move(parent), + super::init(reg, this_endpoint, ep_clock, std::move(nm), std::move(parent), std::move(in_res), std::move(out_res)); master_topic = store_name / topic::master_suffix(); super::init(input); diff --git a/libbroker/broker/internal/clone_actor.hh b/libbroker/broker/internal/clone_actor.hh index 38191f64..3a17a66c 100644 --- a/libbroker/broker/internal/clone_actor.hh +++ b/libbroker/broker/internal/clone_actor.hh @@ -34,8 +34,9 @@ public: // -- initialization --------------------------------------------------------- - clone_state(caf::event_based_actor* ptr, endpoint_id this_endpoint, - std::string nm, caf::timespan master_timeout, caf::actor parent, + clone_state(caf::event_based_actor* ptr, prometheus_registry_ptr reg, + endpoint_id this_endpoint, std::string nm, + caf::timespan master_timeout, caf::actor parent, endpoint::clock* ep_clock, caf::async::consumer_resource in_res, caf::async::producer_resource out_res); diff --git a/libbroker/broker/internal/core_actor.cc b/libbroker/broker/internal/core_actor.cc index 79ba07b5..a0761b6b 100644 --- a/libbroker/broker/internal/core_actor.cc +++ b/libbroker/broker/internal/core_actor.cc @@ -116,8 +116,8 @@ using status_collector_actor = caf::stateful_actor; // -- constructors and destructors --------------------------------------------- -core_actor_state::metrics_t::metrics_t(caf::actor_system& sys) { - metric_factory factory{sys}; +core_actor_state::metrics_t::metrics_t(prometheus::Registry& reg) { + metric_factory factory{reg}; // Initialize connection metrics. auto [native, ws] = factory.core.connections_instances(); native_connections = native; @@ -132,7 +132,8 @@ core_actor_state::metrics_t::metrics_t(caf::actor_system& sys) { message_metric_sets[5].assign(proc.pong, buf.pong); } -core_actor_state::core_actor_state(caf::event_based_actor* self, +core_actor_state::core_actor_state(caf::event_based_actor* self, // + prometheus_registry_ptr reg, endpoint_id this_peer, filter_type initial_filter, endpoint::clock* clock, @@ -142,9 +143,10 @@ core_actor_state::core_actor_state(caf::event_based_actor* self, id(this_peer), filter(std::make_shared(std::move(initial_filter))), clock(clock), - metrics(self->system()), + metrics(*reg), unsafe_inputs(self), - flow_inputs(self) { + flow_inputs(self), + registry(reg) { // Read config and check for extra configuration parameters. ttl = caf::get_or(self->config(), "broker.ttl", defaults::ttl); if (adaptation && adaptation->disable_forwarding) { @@ -185,8 +187,8 @@ caf::behavior core_actor_state::make_behavior() { auto sender = get_sender(msg); // Update metrics. auto& metrics = metrics_for(get_type(msg)); - metrics.processed->inc(); - metrics.buffered->dec(); + metrics.processed->Increment(); + metrics.buffered->Decrement(); // Ignore our own outputs. if (is_local(msg)) return; @@ -409,7 +411,7 @@ caf::behavior core_actor_state::make_behavior() { ->make_observable() // .from_resource(std::move(src)) .do_on_next([this](const data_message&) { - metrics_for(packed_message_type::data).buffered->inc(); + metrics_for(packed_message_type::data).buffered->Increment(); }) .map([this](const data_message& msg) { return node_message{msg}; }) .compose(local_publisher_scope_adder()) @@ -625,8 +627,8 @@ table core_actor_state::message_metrics_snapshot() const { for (size_t msg_type = 1; msg_type < 6; ++msg_type) { auto& msg_metrics = metrics.message_metric_sets[msg_type]; table vals; - vals.emplace("processed"s, msg_metrics.processed->value()); - vals.emplace("buffered"s, msg_metrics.buffered->value()); + vals.emplace("processed"s, msg_metrics.processed->Value()); + vals.emplace("buffered"s, msg_metrics.buffered->Value()); auto key = static_cast(msg_type); result.emplace(to_string(key), std::move(vals)); } @@ -683,8 +685,8 @@ table core_actor_state::status_snapshot() const { add("id", to_string(id)); add("cluster-node", env_or_default("CLUSTER_NODE", "unknown")); add("time", caf::timestamp_to_string(caf::make_timestamp())); - add("native-connections", metrics.native_connections->value()); - add("web-socket-connections", metrics.web_socket_connections->value()); + add("native-connections", metrics.native_connections->Value()); + add("web-socket-connections", metrics.web_socket_connections->Value()); add("message-metrics", message_metrics_snapshot()); add("peerings", peer_stats_snapshot()); add("local-subscribers", local_subscriber_stats_snapshot()); @@ -823,7 +825,7 @@ caf::error core_actor_state::init_new_peer(endpoint_id peer_id, return caf::make_error(ec::invalid_status, to_string(status)); } // All sanity checks have passed, update our state. - metrics.native_connections->inc(); + metrics.native_connections->Increment(); // Hook into the central merge point for forwarding the data to the peer. auto filter_ptr = std::make_shared(filter); auto ptr = std::make_shared(addr, filter_ptr, id, peer_id); @@ -855,7 +857,7 @@ caf::error core_actor_state::init_new_peer(endpoint_id peer_id, in // Add instrumentation for metrics. .do_on_next([this](const node_message& msg) { - metrics_for(get_type(msg)).buffered->inc(); + metrics_for(get_type(msg)).buffered->Increment(); }) // Handle peer disconnect events. .do_on_complete([this, peer_id, ptr]() mutable { @@ -938,7 +940,7 @@ caf::error core_actor_state::init_new_client(const network_info& addr, "cannot add client without valid input buffer"); } // All sanity checks have passed, update our state. - metrics.web_socket_connections->inc(); + metrics.web_socket_connections->Increment(); // We cannot simply treat a client like we treat a local publisher or // subscriber, because events from the client must be visible locally. Hence, // we assign a UUID to each client and treat it almost like a peer. @@ -966,26 +968,27 @@ caf::error core_actor_state::init_new_client(const network_info& addr, subscriptions.emplace_back(sub); } // Push messages received from the client into the central merge point. - auto [in, ks] = self->make_observable() - .from_resource(std::move(in_res)) - // If the client closes this buffer, we assume a disconnect. - .do_finally([this, client_id, addr, type] { - BROKER_DEBUG("client" << addr << "disconnected"); - client_removed(client_id, addr, type); - metrics.web_socket_connections->dec(); - }) - .map([this, client_id](const data_message& msg) { - metrics_for(packed_message_type::data).buffered->inc(); - node_message result; - if (msg->sender() == client_id) - result = msg; - else - result = msg->with(client_id, msg->receiver()); - return result; - }) - // Ignore any errors from the client. - .on_error_complete() - .compose(add_killswitch_t{}); + auto [in, ks] = + self->make_observable() + .from_resource(std::move(in_res)) + // If the client closes this buffer, we assume a disconnect. + .do_finally([this, client_id, addr, type] { + BROKER_DEBUG("client" << addr << "disconnected"); + client_removed(client_id, addr, type); + metrics.web_socket_connections->Decrement(); + }) + .map([this, client_id](const data_message& msg) { + metrics_for(packed_message_type::data).buffered->Increment(); + node_message result; + if (msg->sender() == client_id) + result = msg; + else + result = msg->with(client_id, msg->receiver()); + return result; + }) + // Ignore any errors from the client. + .on_error_complete() + .compose(add_killswitch_t{}); flow_inputs.push(in); subscriptions.emplace_back(ks); return caf::none; @@ -1046,10 +1049,10 @@ caf::result core_actor_state::attach_master(const std::string& name, auto resources2 = make_spsc_buffer_resource(); auto& [con2, prod2] = resources2; // Spin up the master and connect it to our flows. - auto hdl = self->system().spawn(id, name, std::move(ptr), - caf::actor{self}, clock, - std::move(con1), - std::move(prod2)); + auto hdl = + self->system().spawn(registry, id, name, std::move(ptr), + caf::actor{self}, clock, + std::move(con1), std::move(prod2)); filter_type filter{name / topic::master_suffix()}; subscribe(filter); command_outputs @@ -1062,7 +1065,7 @@ caf::result core_actor_state::attach_master(const std::string& name, ->make_observable() // .from_resource(con2) .map([this](const command_message& msg) { - metrics_for(packed_message_type::command).buffered->inc(); + metrics_for(packed_message_type::command).buffered->Increment(); return node_message{msg}; }) .as_observable(); @@ -1098,8 +1101,10 @@ core_actor_state::attach_clone(const std::string& name, double resync_interval, auto& [con1, prod1] = resources1; auto resources2 = make_spsc_buffer_resource(); auto& [con2, prod2] = resources2; - auto hdl = self->system().spawn( - id, name, tout, caf::actor{self}, clock, std::move(con1), std::move(prod2)); + auto hdl = self->system().spawn(registry, id, name, tout, + caf::actor{self}, clock, + std::move(con1), + std::move(prod2)); filter_type filter{name / topic::clone_suffix()}; subscribe(filter); command_outputs @@ -1112,7 +1117,7 @@ core_actor_state::attach_clone(const std::string& name, double resync_interval, ->make_observable() // .from_resource(con2) .map([this](const command_message& msg) { - metrics_for(packed_message_type::command).buffered->inc(); + metrics_for(packed_message_type::command).buffered->Increment(); return node_message{msg}; }) .as_observable(); @@ -1137,7 +1142,7 @@ void core_actor_state::shutdown_stores() { // -- dispatching of messages to peers regardless of subscriptions ------------ void core_actor_state::dispatch(const node_message& msg) { - metrics_for(get_type(msg)).buffered->inc(); + metrics_for(get_type(msg)).buffered->Increment(); unsafe_inputs.push(msg); } diff --git a/libbroker/broker/internal/core_actor.hh b/libbroker/broker/internal/core_actor.hh index af33ebcb..5d855d07 100644 --- a/libbroker/broker/internal/core_actor.hh +++ b/libbroker/broker/internal/core_actor.hh @@ -12,9 +12,8 @@ #include #include #include -#include -#include +#include #include #include #include @@ -32,13 +31,13 @@ public: /// Bundles message-related metrics that have a label dimension for the type. struct message_metrics_t { /// Counts how many messages were processed since starting the core. - caf::telemetry::int_counter* processed = nullptr; + prometheus::Counter* processed = nullptr; /// Keeps track of how many messages are currently buffered at the core. - caf::telemetry::int_gauge* buffered = nullptr; + prometheus::Gauge* buffered = nullptr; - void assign(caf::telemetry::int_counter* processed_instance, - caf::telemetry::int_gauge* buffered_instance) noexcept { + void assign(prometheus::Counter* processed_instance, + prometheus::Gauge* buffered_instance) noexcept { processed = processed_instance; buffered = buffered_instance; } @@ -46,13 +45,13 @@ public: /// Bundles metrics for the core. struct metrics_t { - explicit metrics_t(caf::actor_system& sys); + explicit metrics_t(prometheus::Registry& reg); /// Keeps track of how many native peers are currently connected. - caf::telemetry::int_gauge* native_connections = nullptr; + prometheus::Gauge* native_connections = nullptr; /// Keeps track of how many WebSocket clients are currently connected. - caf::telemetry::int_gauge* web_socket_connections = nullptr; + prometheus::Gauge* web_socket_connections = nullptr; /// Stores the metrics for all message types. std::array message_metric_sets; @@ -68,7 +67,8 @@ public: // --- constructors and destructors ------------------------------------------ - core_actor_state(caf::event_based_actor* self, endpoint_id this_peer, + core_actor_state(caf::event_based_actor* self, + prometheus_registry_ptr registry, endpoint_id this_peer, filter_type initial_filter, endpoint::clock* clock = nullptr, const domain_options* adaptation = nullptr, connector_ptr conn = nullptr); @@ -353,6 +353,9 @@ public: /// Counts messages that were published directly via message, i.e., without /// using the back-pressure of flows. int64_t published_via_async_msg = 0; + + /// Stores a reference to the metrics registry. + prometheus_registry_ptr registry; }; using core_actor = caf::stateful_actor; diff --git a/libbroker/broker/internal/core_actor.test.cc b/libbroker/broker/internal/core_actor.test.cc index 08ef2692..ab6e8f69 100644 --- a/libbroker/broker/internal/core_actor.test.cc +++ b/libbroker/broker/internal/core_actor.test.cc @@ -30,6 +30,8 @@ struct fixture : test_coordinator_fixture { std::vector bridges; + prometheus_registry_ptr registry; + using data_message_list = std::vector; data_message_list test_data = data_message_list({ @@ -60,6 +62,7 @@ struct fixture : test_coordinator_fixture { } fixture() { + registry = std::make_shared(); // We don't do networking, but our flares use the socket API. ep1.id = endpoint_id::random(1); ep2.id = endpoint_id::random(2); @@ -68,7 +71,7 @@ struct fixture : test_coordinator_fixture { template void spin_up(endpoint_state& ep, Ts&... xs) { - ep.hdl = sys.spawn(ep.id, ep.filter); + ep.hdl = sys.spawn(registry, ep.id, ep.filter); MESSAGE(ep.id << " is running at " << ep.hdl); if constexpr (sizeof...(Ts) == 0) run(); diff --git a/libbroker/broker/internal/flow_scope.hh b/libbroker/broker/internal/flow_scope.hh index 36df3b2b..44e39d36 100644 --- a/libbroker/broker/internal/flow_scope.hh +++ b/libbroker/broker/internal/flow_scope.hh @@ -3,7 +3,6 @@ #include #include #include -#include #include diff --git a/libbroker/broker/internal/master_actor.cc b/libbroker/broker/internal/master_actor.cc index 7a2caee2..40ddbcb0 100644 --- a/libbroker/broker/internal/master_actor.cc +++ b/libbroker/broker/internal/master_actor.cc @@ -44,21 +44,22 @@ auto to_caf_res(expected&& x) { // -- metrics ------------------------------------------------------------------ -master_state::metrics_t::metrics_t(caf::actor_system& sys, +master_state::metrics_t::metrics_t(prometheus::Registry& reg, const std::string& name) noexcept { - metric_factory factory{sys}; + metric_factory factory{reg}; entries = factory.store.entries_instance(name); } // -- initialization ----------------------------------------------------------- master_state::master_state( - caf::event_based_actor* ptr, endpoint_id this_endpoint, std::string nm, - backend_pointer bp, caf::actor parent, endpoint::clock* ep_clock, + caf::event_based_actor* ptr, prometheus_registry_ptr reg, + endpoint_id this_endpoint, std::string nm, backend_pointer bp, + caf::actor parent, endpoint::clock* ep_clock, caf::async::consumer_resource in_res, caf::async::producer_resource out_res) - : super(ptr), output(this), metrics(ptr->system(), nm) { - super::init(this_endpoint, ep_clock, std::move(nm), std::move(parent), + : super(ptr), output(this), metrics(*reg, nm) { + super::init(reg, this_endpoint, ep_clock, std::move(nm), std::move(parent), std::move(in_res), std::move(out_res)); super::init(output); clones_topic = store_name / topic::clone_suffix(); @@ -70,7 +71,7 @@ master_state::master_state( detail::die("failed to get master expiries while initializing"); } if (auto entries = backend->size(); entries && *entries > 0) { - metrics.entries->value(static_cast(*entries)); + metrics.entries->Set(*entries); } BROKER_INFO("attached master" << id << "to" << store_name); } @@ -188,7 +189,7 @@ void master_state::tick() { expire_command cmd{key, id}; emit_expire_event(cmd); broadcast(std::move(cmd)); - metrics.entries->dec(); + metrics.entries->Decrement(); } i = expirations.erase(i); } else { @@ -229,7 +230,7 @@ void master_state::consume(put_command& x) { emit_update_event(x, *old_value); } else { emit_insert_event(x); - metrics.entries->inc(); + metrics.entries->Increment(); } broadcast(std::move(x)); } @@ -261,7 +262,7 @@ void master_state::consume(put_unique_command& x) { } set_expire_time(x.key, x.expiry); emit_insert_event(x); - metrics.entries->inc(); + metrics.entries->Increment(); // Broadcast a regular "put" command (clones don't have to do their own // existence check) followed by the (positive) result message. broadcast( @@ -281,7 +282,7 @@ void master_state::consume(erase_command& x) { return; // TODO: propagate failure? to all clones? as status msg? } emit_erase_event(x.key, x.publisher); - metrics.entries->dec(); + metrics.entries->Decrement(); broadcast(std::move(x)); } @@ -309,7 +310,7 @@ void master_state::consume(add_command& x) { emit_update_event(cmd, *old_value); } else { emit_insert_event(cmd); - metrics.entries->inc(); + metrics.entries->Increment(); } broadcast(std::move(cmd)); } @@ -355,11 +356,11 @@ void master_state::consume(clear_command& x) { if (auto keys = get_if(*keys_res)) { for (auto& key : *keys) emit_erase_event(key, x.publisher); - metrics.entries->value(0); + metrics.entries->Set(0); } else if (auto keys = get_if(*keys_res)) { for (auto& key : *keys) emit_erase_event(key, x.publisher); - metrics.entries->value(0); + metrics.entries->Set(0); } else if (!is(*keys_res)) { BROKER_ERROR("backend->keys() returned an unexpected result type"); } diff --git a/libbroker/broker/internal/master_actor.hh b/libbroker/broker/internal/master_actor.hh index 52714034..9ad782f7 100644 --- a/libbroker/broker/internal/master_actor.hh +++ b/libbroker/broker/internal/master_actor.hh @@ -6,7 +6,6 @@ #include #include #include -#include #include "broker/data.hh" #include "broker/detail/abstract_backend.hh" @@ -37,10 +36,10 @@ public: /// Bundles metrics for the master. struct metrics_t { - metrics_t(caf::actor_system& sys, const std::string& name) noexcept; + metrics_t(prometheus::Registry& reg, const std::string& name) noexcept; /// Keeps track of how many entries the store currently has. - caf::telemetry::int_gauge* entries = nullptr; + prometheus::Gauge* entries = nullptr; }; template @@ -58,9 +57,9 @@ public: // -- initialization --------------------------------------------------------- - master_state(caf::event_based_actor* ptr, endpoint_id this_endpoint, - std::string nm, backend_pointer bp, caf::actor parent, - endpoint::clock* clock, + master_state(caf::event_based_actor* ptr, prometheus_registry_ptr reg, + endpoint_id this_endpoint, std::string nm, backend_pointer bp, + caf::actor parent, endpoint::clock* clock, caf::async::consumer_resource in_res, caf::async::producer_resource out_res); diff --git a/libbroker/broker/internal/metric_collector.cc b/libbroker/broker/internal/metric_collector.cc deleted file mode 100644 index 1c919aa2..00000000 --- a/libbroker/broker/internal/metric_collector.cc +++ /dev/null @@ -1,316 +0,0 @@ -#include - -#include "broker/internal/metric_collector.hh" - -#include "broker/internal/logger.hh" - -namespace ct = caf::telemetry; - -namespace broker::internal { - -namespace { - -template -class remote_counter : public metric_collector::remote_metric { -public: - using super = metric_collector::remote_metric; - - static constexpr auto type_tag = std::is_same_v - ? ct::metric_type::int_counter - : ct::metric_type::dbl_counter; - - using super::super; - - void update(metric_view mv) override { - if (mv.type() == type_tag) { - value_ = get(mv.value()); - } else { - BROKER_ERROR("conflicting remote metric update received!"); - } - } - - void append_to(ct::collector::prometheus& f) override { - f.append_counter(this->parent_, this, value_); - } - -private: - T value_ = 0; -}; - -template -class remote_gauge : public metric_collector::remote_metric { -public: - using super = metric_collector::remote_metric; - - static constexpr auto type_tag = std::is_same_v - ? ct::metric_type::int_gauge - : ct::metric_type::dbl_gauge; - - using super::super; - - void update(metric_view mv) override { - if (mv.type() == type_tag) { - value_ = get(mv.value()); - } else { - BROKER_ERROR("conflicting remote metric update received!"); - } - } - - void append_to(ct::collector::prometheus& f) override { - f.append_gauge(this->parent_, this, value_); - } - -private: - T value_ = 0; -}; - -template -class remote_histogram : public metric_collector::remote_metric { -public: - using super = metric_collector::remote_metric; - - static constexpr auto type_tag = std::is_same_v - ? ct::metric_type::int_histogram - : ct::metric_type::dbl_histogram; - - using super::super; - - using native_bucket = typename ct::histogram::bucket_type; - - void update(metric_view mv) override { - if (mv.type() == type_tag) { - auto& vals = get(mv.value()); - BROKER_ASSERT(vals.size() >= 2); - buckets_.clear(); - std::for_each(vals.begin(), vals.end() - 1, [this](const auto& kvp_data) { - auto& kvp = get(kvp_data); - buckets_.emplace_back(get(kvp[0]), get(kvp[1])); - }); - sum_ = get(vals.back()); - } else { - BROKER_ERROR("conflicting remote metric update received!"); - } - } - - void append_to(ct::collector::prometheus& f) override { - // The CAF collector expects histogram buckets, which have a `counter` - // member. Since we can't assign values to counters (only increase them), we - // work around this limitations by simply re-creating the "native" buckets - // each time. - std::unique_ptr buf{new native_bucket[buckets_.size()]}; - for (size_t index = 0; index < buckets_.size(); ++index) { - auto [upper_bound, count] = buckets_[index]; - buf[index].upper_bound = upper_bound; - if (count > 0) - buf[index].count.inc(count); - } - auto buf_span = caf::make_span(buf.get(), buckets_.size()); - f.append_histogram(this->parent_, this, buf_span, sum_); - } - -private: - std::vector> buckets_; - T sum_ = 0; -}; - -} // namespace - -// -- member types ------------------------------------------------------------- - -metric_collector::remote_metric::remote_metric( - label_list labels, const caf::telemetry::metric_family* parent) - : super(std::move(labels)), parent_(parent) { - // nop -} - -metric_collector::remote_metric::~remote_metric() { - // nop -} - -// --- constructors and destructors -------------------------------------------- - -metric_collector::metric_collector() { - // nop -} - -metric_collector::~metric_collector() { - // nop -} - -// -- data management ---------------------------------------------------------- - -size_t metric_collector::insert_or_update(const data& content) { - if (auto vec = get_if(content)) - return insert_or_update(*vec); - else - return 0; -} - -size_t metric_collector::insert_or_update(const vector& vec) { - auto has_meta_data = [](const data& x) { - if (auto meta = get_if(x); meta && meta->size() == 2) - return is((*meta)[0]) && is((*meta)[1]); - else - return false; - }; - if (vec.size() >= 2 && has_meta_data(vec[0])) { - auto& meta = get(vec[0]); - auto& endpoint_name = get(meta[0]); - auto& ts = get(meta[1]); - return insert_or_update(endpoint_name, ts, - caf::make_span(vec.data() + 1, vec.size() - 1)); - } else { - return 0; - } -} - -size_t metric_collector::insert_or_update(const std::string& endpoint_name, - timestamp ts, - caf::span rows) { - using caf::telemetry::metric_type; - auto res = size_t{0}; - if (advance_time(endpoint_name, ts)) - for (const auto& row_data : rows) - if (auto mv = metric_view{row_data}) - if (auto ptr = instance(endpoint_name, mv)) { - ptr->update(mv); - ++res; - } - return res; -} - -std::string_view metric_collector::prometheus_text() { - if (generator_.begin_scrape()) { - for (auto& [prefix, names] : prefixes_) - for (auto& [name, scope] : names) - for (auto& instance : scope.instances) - instance->append_to(generator_); - generator_.end_scrape(); - } - auto res = generator_.str(); - return {res.data(), res.size()}; -} - -void metric_collector::clear() { - label_names_.clear(); - prefixes_.clear(); - last_seen_.clear(); - generator_.reset(); -} - -// -- time management ---------------------------------------------------------- - -bool metric_collector::advance_time(const std::string& endpoint_name, - timestamp current_time) { - auto [i, added] = last_seen_.emplace(endpoint_name, current_time); - if (added) { - return true; - } else if (current_time > i->second) { - i->second = current_time; - return true; - } else { - return false; - } -} - -// -- lookups ---------------------------------------------------------------- - -void metric_collector::labels_for(const std::string& endpoint_name, - metric_view row, - metric_collector::label_view_list& result) { - using namespace std::literals; - result.clear(); - result.reserve(row.labels().size() + 1); - // Get insertion point for "endpoint" to keep the vector sorted. Then copy all - // labels to their final destination. - auto pos = row.labels().lower_bound(ep_key_); - for (auto i = row.labels().begin(); i != pos; ++i) - result.emplace_back(get(i->first), - get(i->second)); - result.emplace_back("endpoint"sv, endpoint_name); - for (auto i = pos; i != row.labels().end(); ++i) - result.emplace_back(get(i->first), - get(i->second)); -} - -metric_collector::string_span -metric_collector::label_names_for(metric_view row) { - label_names_.clear(); - label_names_.reserve(row.labels().size() + 1); - label_names_.emplace_back("endpoint"); - for (const auto& kvp : row.labels()) - label_names_.emplace_back(get(kvp.first)); - std::sort(label_names_.begin(), label_names_.end()); - return label_names_; -} - -namespace { - -auto owned(std::string_view x) { - return std::string{x}; -} - -auto owned(metric_collector::string_span xs) { - std::vector result; - if (!xs.empty()) { - result.reserve(xs.size()); - for (auto& x : xs) - result.emplace_back(owned(x)); - } - return result; -} - -auto owned(const metric_collector::label_view_list& xs) { - metric_collector::label_list result; - if (!xs.empty()) { - result.reserve(xs.size()); - for (auto& x : xs) - result.emplace_back(x); - } - return result; -} - -constexpr metric_collector::labels_equal labels_equal_v{}; - -} // namespace - -metric_collector::remote_metric* -metric_collector::instance(const std::string& endpoint_name, metric_view mv) { - auto& names = prefixes_[mv.prefix()]; - auto& scope = names[mv.name()]; - if (scope.family == nullptr) { - auto ptr = new ct::metric_family(mv.type(), mv.prefix(), mv.name(), - owned(label_names_for(mv)), mv.helptext(), - mv.unit(), mv.is_sum()); - scope.family.reset(ptr); - } - auto* fptr = scope.family.get(); - labels_for(endpoint_name, mv, labels_); - auto i = scope.instances.lower_bound(labels_); - if (i != scope.instances.end() && labels_equal_v(*i, labels_)) - return i->get(); - auto add = [&](auto* ptr) { - auto j = scope.instances.insert(i, instance_ptr{ptr}); - BROKER_ASSERT(j->get() == ptr); - return ptr; - }; - using ct::metric_type; - switch (mv.type()) { - case metric_type::int_counter: - return add(new remote_counter(owned(labels_), fptr)); - case metric_type::dbl_counter: - return add(new remote_counter(owned(labels_), fptr)); - case metric_type::int_gauge: - return add(new remote_gauge(owned(labels_), fptr)); - case metric_type::dbl_gauge: - return add(new remote_gauge(owned(labels_), fptr)); - case metric_type::int_histogram: - return add(new remote_histogram(owned(labels_), fptr)); - case metric_type::dbl_histogram: - return add(new remote_histogram(owned(labels_), fptr)); - default: - return nullptr; - } -} - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_collector.hh b/libbroker/broker/internal/metric_collector.hh deleted file mode 100644 index 6f82c476..00000000 --- a/libbroker/broker/internal/metric_collector.hh +++ /dev/null @@ -1,200 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "broker/data.hh" -#include "broker/detail/assert.hh" -#include "broker/internal/metric_view.hh" -#include "broker/time.hh" -#include "broker/topic.hh" - -namespace broker::internal { - -/// Subscribes to a topic for receiving remote metrics and makes them accessible -/// in the Prometheus text format. -class metric_collector { -public: - // -- member types ----------------------------------------------------------- - - template - using mono_pair = std::pair; - - using label_list = std::vector; - - using label_view_list = std::vector; - - using string_span = caf::span; - - class remote_metric : public caf::telemetry::metric { - public: - using super = caf::telemetry::metric; - - remote_metric(label_list labels, - const caf::telemetry::metric_family* parent); - - ~remote_metric() override; - - virtual void update(metric_view mv) = 0; - - virtual void append_to(caf::telemetry::collector::prometheus&) = 0; - - protected: - const caf::telemetry::metric_family* parent_; - }; - - using family_ptr = std::unique_ptr; - - using instance_ptr = std::unique_ptr; - - /// Predicate for checking whether a list of labels is less than another list - /// of labels. Automatically un-boxes `instance_ptr`. - struct labels_less { - using is_transparent = std::true_type; - - /// Compares a two individual label elements. - template - auto cmp_element(const T1& lhs, const T2& rhs) const noexcept { - auto cmp1 = lhs.name().compare(rhs.name()); - return cmp1 != 0 ? cmp1 : lhs.value().compare(rhs.value()); - } - - template - bool operator()(const T1& lhs, const T2& rhs) const noexcept { - using remote_metric = std::unique_ptr; - if constexpr (std::is_same_v) { - return (*this)(lhs->labels(), rhs); - } else if constexpr (std::is_same_v) { - return (*this)(lhs, rhs->labels()); - } else { - if (lhs.size() != rhs.size()) - return lhs.size() < rhs.size(); - // Empty lists are equal. - if (lhs.empty()) - return false; - // Compare the first n - 1 labels. - size_t index = 0; - for (; index < lhs.size() - 1; ++index) { - auto res = cmp_element(lhs[index], rhs[index]); - // If a label was less than the corresponding label in the other list, - // we can return true immediately. - if (res < 0) - return true; - // If a label was greater than the corresponding label in the other - // list, we can return false immediately. - if (res > 0) - return false; - // Otherwise, the labels are equal and we continue with the next - // label. - } - // Compare the last label. Must be less. - return lhs[index] < rhs[index]; - } - } - }; - - /// Predicate for checking whether two lists of labels are equal. - /// Automatically un-boxes `instance_ptr`. - struct labels_equal { - using is_transparent = std::true_type; - - template - bool operator()(const T1& lhs, const T2& rhs) const { - using remote_metric = std::unique_ptr; - if constexpr (std::is_same_v) { - return (*this)(lhs->labels(), rhs); - } else if constexpr (std::is_same_v) { - return (*this)(lhs, rhs->labels()); - } else { - if (lhs.size() != rhs.size()) - return false; - for (size_t index = 0; index < lhs.size(); ++index) { - if (lhs[index] != rhs[index]) - return false; - } - return true; - } - } - }; - - // --- constructors and destructors ------------------------------------------ - - metric_collector(); - - ~metric_collector(); - - // -- data management -------------------------------------------------------- - - size_t insert_or_update(const data& content); - - size_t insert_or_update(const vector& vec); - - size_t insert_or_update(const std::string& endpoint_name, timestamp ts, - caf::span rows); - - /// Returns the recorded metrics in the Prometheus text format. - [[nodiscard]] std::string_view prometheus_text(); - - void clear(); - -private: - // -- private member types --------------------------------------------------- - - struct metric_scope { - /// The metric family. - family_ptr family; - /// The instances of the metric family, sorted by label values. - std::set instances; - }; - - using name_map = std::unordered_map; - - using prefix_map = std::unordered_map; - - // -- time management -------------------------------------------------------- - - /// Tries to advance the last-seen-time for given endpoint. - bool advance_time(const std::string& endpoint_name, timestamp current_time); - - // -- lookups ---------------------------------------------------------------- - - /// Extracts the names for all label dimensions from `mv`. - string_span label_names_for(metric_view mv); - - /// Extracts the label dimensions from `mv` and stores them in `result`. - void labels_for(const std::string& endpoint_name, metric_view mv, - label_view_list& result); - - /// Retrieves or lazily creates a metric object for `mv`. - remote_metric* instance(const std::string& endpoint_name, metric_view mv); - - /// Caches labels (key/value pairs) for instance lookups. - std::vector labels_; - - /// Caches label names (dimensions) for instance lookups. - std::vector label_names_; - - /// Top-level lookup structure to navigate from prefixes to name maps. - prefix_map prefixes_; - - /// Stores last-seen-times by endpoints. - std::unordered_map last_seen_; - - /// Generates Prometheus-formatted text. - caf::telemetry::collector::prometheus generator_; - - /// Caches the string "endpoint" as a broker::data instance. Having this as a - /// member avoids constructing this object each time in `labels_for`. - data ep_key_ = data{"endpoint"}; -}; - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_collector.test.cc b/libbroker/broker/internal/metric_collector.test.cc deleted file mode 100644 index 562fab98..00000000 --- a/libbroker/broker/internal/metric_collector.test.cc +++ /dev/null @@ -1,178 +0,0 @@ -#include "broker/internal/metric_collector.hh" - -#include "broker/broker-test.test.hh" - -#include "broker/internal/metric_exporter.hh" - -namespace atom = broker::internal::atom; - -using namespace broker; - -using namespace std::literals; - -namespace { - -// Works around a weird bug in CAF that prevents us from passing the pointer -// directly. -struct collector_ptr { - internal::metric_collector* value; -}; - -caf::behavior dummy_core(collector_ptr ptr) { - return { - [collector{ptr.value}](atom::publish, data_message msg) { - CHECK_EQUAL(get_topic(msg), "/all/them/metrics"sv); - CHECK_GREATER_EQUAL(collector->insert_or_update(get_data(msg).to_data()), - 6u); - }, - }; -} - -struct fixture : base_fixture { - internal::metric_collector collector; - - caf::actor core; - caf::actor exporter; - - caf::telemetry::int_gauge* foo_g1; - caf::telemetry::dbl_gauge* foo_g2; - caf::telemetry::int_counter* foo_c1; - caf::telemetry::dbl_counter* foo_c2; - caf::telemetry::int_histogram* foo_h1; - caf::telemetry::dbl_histogram* foo_h2; - - fixture() { - // Initialize metrics. - auto& reg = sys.metrics(); - foo_g1 = reg.gauge_singleton("foo", "g1", "Int Gauge!"); - foo_g2 = reg.gauge_singleton("foo", "g2", "Dbl Gauge!"); - foo_c1 = reg.counter_singleton("foo", "c1", "Int Counter!"); - foo_c2 = reg.counter_singleton("foo", "c2", "Dbl Counter!"); - std::array int_buckets{{8, 16, 32}}; - auto h1_fam = reg.histogram_family("foo", "h1", {"sys"}, int_buckets, - "Int Histogram!", "seconds"); - foo_h1 = h1_fam->get_or_add({{"sys", "broker"}}); - std::array dbl_buckets{{8.0, 16.0, 32.0}}; - auto h2_fam = reg.histogram_family("foo", "h2", {"sys"}, - dbl_buckets, "Dbl Histogram!", - "seconds"); - foo_h2 = h2_fam->get_or_add({{"sys", "broker"}}); - // Spin up actors. - core = sys.spawn(dummy_core, collector_ptr{&collector}); - exporter = sys.spawn( - core, std::vector{}, caf::timespan{2s}, "/all/them/metrics", - "exporter-1"); - sched.run(); - } - - ~fixture() { - anon_send_exit(exporter, caf::exit_reason::user_shutdown); - } -}; - -bool contains(std::string_view str, std::string_view what) { - return str.find(what) != std::string_view::npos; -} - -#define PROM_CONTAINS(what) CHECK(contains(prom_txt, what)) - -} // namespace - -FIXTURE_SCOPE(telemetry_collector_tests, fixture) - -TEST(less predicate) { - internal::metric_collector::labels_less less; - using labels_list = std::vector; - { // Single label (equal). - auto lhs = labels_list{{"type", "native"}}; - auto rhs = labels_list{{"type", "native"}}; - CHECK(!less(lhs, rhs)); - CHECK(!less(rhs, lhs)); - } - { // Single label (unequal). - auto lhs = labels_list{{"type", "native"}}; - auto rhs = labels_list{{"type", "web-socket"}}; - CHECK(less(lhs, rhs)); - CHECK(!less(rhs, lhs)); - } - { // Two equal labels. - auto lhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - auto rhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - CHECK(!less(lhs, rhs)); - CHECK(!less(rhs, lhs)); - } - { // Two labels, with the first being smaller. - auto lhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - auto rhs = labels_list{{"endpoint", "exporter-2"}, {"type", "native"}}; - CHECK(less(lhs, rhs)); - CHECK(!less(rhs, lhs)); - } - { // Two labels, with the second being smaller. - auto lhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - auto rhs = labels_list{{"endpoint", "exporter-1"}, {"type", "web-socket"}}; - CHECK(less(lhs, rhs)); - CHECK(!less(rhs, lhs)); - } -} - -TEST(equal predicate) { - internal::metric_collector::labels_equal equal; - using labels_list = std::vector; - { // Single label (equal). - auto lhs = labels_list{{"type", "native"}}; - auto rhs = labels_list{{"type", "native"}}; - CHECK(equal(lhs, rhs)); - } - { // Single label (unequal). - auto lhs = labels_list{{"type", "native"}}; - auto rhs = labels_list{{"type", "web-socket"}}; - CHECK(!equal(lhs, rhs)); - } - { // Two equal labels. - auto lhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - auto rhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - CHECK(equal(lhs, rhs)); - CHECK(equal(rhs, lhs)); - } - { // Two labels, with the first being smaller. - auto lhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - auto rhs = labels_list{{"endpoint", "exporter-2"}, {"type", "native"}}; - CHECK(!equal(lhs, rhs)); - CHECK(!equal(rhs, lhs)); - } - { // Two labels, with the second being smaller. - auto lhs = labels_list{{"endpoint", "exporter-1"}, {"type", "native"}}; - auto rhs = labels_list{{"endpoint", "exporter-1"}, {"type", "web-socket"}}; - CHECK(!equal(lhs, rhs)); - CHECK(!equal(rhs, lhs)); - } -} - -TEST(a collector consumes the output of an exporter) { - MESSAGE("fill in some data"); - foo_g1->inc(1); - foo_g2->inc(2.0); - foo_c1->inc(4); - foo_c2->inc(8.0); - foo_h1->observe(16); - foo_h2->observe(32.0); - MESSAGE("get the exporter to publish a current snapshot of the metrics"); - sched.advance_time(2s); - sched.run(); - MESSAGE("read back what the exporter generated in Prometheus text format"); - auto prom_txt = collector.prometheus_text(); - PROM_CONTAINS(R"(foo_g1{endpoint="exporter-1"} 1)"); - PROM_CONTAINS(R"(foo_g2{endpoint="exporter-1"} 2)"); - PROM_CONTAINS(R"(foo_c1{endpoint="exporter-1"} 4)"); - PROM_CONTAINS(R"(foo_c2{endpoint="exporter-1"} 8)"); - PROM_CONTAINS(R"(foo_h1_seconds_bucket)"); - PROM_CONTAINS(R"(foo_h1_seconds_sum{endpoint="exporter-1",sys="broker"} 16)"); - PROM_CONTAINS( - R"(foo_h1_seconds_count{endpoint="exporter-1",sys="broker"} 1)"); - PROM_CONTAINS(R"(foo_h2_seconds_bucket)"); - PROM_CONTAINS(R"(foo_h2_seconds_sum{endpoint="exporter-1",sys="broker"} 32)"); - PROM_CONTAINS( - R"(foo_h2_seconds_count{endpoint="exporter-1",sys="broker"} 1)"); -} - -FIXTURE_SCOPE_END() diff --git a/libbroker/broker/internal/metric_exporter.cc b/libbroker/broker/internal/metric_exporter.cc deleted file mode 100644 index cd70b4ae..00000000 --- a/libbroker/broker/internal/metric_exporter.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "broker/internal/metric_exporter.hh" - -#include - -#include -#include - -#include "broker/defaults.hh" - -namespace broker::internal { - -metric_exporter_params -metric_exporter_params::from(const caf::actor_system_config& cfg) { - using std::string; - using dict_type = caf::config_value::dictionary; - metric_exporter_params result; - if (auto idp = caf::get_if(&cfg, "broker.metrics.endpoint-name"); - idp && !idp->empty()) { - result.id = *idp; - } - if (auto dict = caf::get_if(&cfg, "broker.metrics.export")) { - if (auto tp = caf::get_if(dict, "topic"); tp && !tp->empty()) { - result.target = *tp; - if (result.id.empty()) - result.id = result.target.suffix(); - } - result.interval = caf::get_or(*dict, "interval", - defaults::metrics::export_interval); - if (result.interval.count() == 0) - result.interval = defaults::metrics::export_interval; - } - return result; -} - -[[nodiscard]] bool metric_exporter_params::valid() const noexcept { - return !target.empty(); -} - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_exporter.hh b/libbroker/broker/internal/metric_exporter.hh deleted file mode 100644 index f458c6f1..00000000 --- a/libbroker/broker/internal/metric_exporter.hh +++ /dev/null @@ -1,194 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "broker/detail/next_tick.hh" -#include "broker/filter_type.hh" -#include "broker/internal/logger.hh" -#include "broker/internal/metric_scraper.hh" -#include "broker/internal/type_id.hh" -#include "broker/message.hh" -#include "broker/topic.hh" - -namespace broker::internal { - -/// Wraps user-defined parameters for the exporter to enable sanity checking -/// before actually spawning an exporter. -struct metric_exporter_params { - std::vector selected_prefixes; - caf::timespan interval = caf::timespan{0}; - topic target; - std::string id; - static metric_exporter_params from(const caf::actor_system_config& cfg); - [[nodiscard]] bool valid() const noexcept; -}; - -/// State for an actor that periodically exports local metrics by publishing -/// `data`-encoded metrics to a user-defined Broker topic. -template -class metric_exporter_state { -public: - // -- initialization --------------------------------------------------------- - - metric_exporter_state(Self* self, caf::actor core, - std::vector selected_prefixes, - caf::timespan interval, topic target, std::string id) - : self(self), - core(std::move(core)), - interval(interval), - target(std::move(target)), - proc_importer(self->system().metrics()), - impl(std::move(selected_prefixes), std::move(id)) { - // nop - } - - metric_exporter_state(Self* self, caf::actor core, - metric_exporter_params&& params) - : metric_exporter_state(self, std::move(core), - std::move(params.selected_prefixes), - params.interval, std::move(params.target), - std::move(params.id)) { - // nop - } - - caf::behavior make_behavior() { - BROKER_ASSERT(core); - self->monitor(core); - self->set_down_handler([this](const caf::down_msg& down) { - if (down.source == core) { - BROKER_INFO(self->name() - << "received a down message from the core: bye"); - self->quit(down.reason); - } - }); - cold_boot(); - return { - [this](caf::tick_atom) { - if (running_) { - proc_importer.update(); - impl.scrape(self->system().metrics()); - // Send nothing if we only have meta data (or nothing) to send. - if (const auto& rows = impl.rows(); rows.size() > 1) - self->send(core, atom::publish_v, make_data_message(target, rows)); - auto t = detail::next_tick(tick_init, self->clock().now(), interval); - self->scheduled_send(self, t, caf::tick_atom_v); - } - }, - [this](atom::put, caf::timespan new_interval) { - set_interval(new_interval); - }, - [this](atom::put, topic& new_target) { - set_target(std::move(new_target)); - }, - [this](atom::put, std::string& new_id) { set_id(std::move(new_id)); }, - [this](atom::put, filter_type& new_prefixes_filter) { - set_prefixes(std::move(new_prefixes_filter)); - }, - [this](atom::join, const filter_type&) { - // Ignored. The prometheus_actor "overrides" this handler but it has no - // effect on a "plain" exporter. - }, - }; - } - - // -- utility functions ------------------------------------------------------ - - [[nodiscard]] bool has_valid_config() const noexcept { - return !target.empty(); - } - - [[nodiscard]] bool running() const noexcept { - return running_; - } - - /// Starts the timed loop if the exporter hasn't been running yet and all - /// preconditions are met. - void cold_boot() { - if (!running_ && has_valid_config()) { - BROKER_INFO("start publishing metrics to topic" << target); - impl.scrape(self->system().metrics()); - tick_init = self->clock().now(); - self->scheduled_send(self, tick_init + interval, caf::tick_atom_v); - running_ = true; - } - } - - void set_interval(caf::timespan new_interval) { - if (new_interval.count() > 0) { - // Use the new interval from the next tick onward. - if (running_) - tick_init = detail::next_tick(tick_init, self->clock().now(), interval); - interval = new_interval; - cold_boot(); - } - } - - void set_target(topic new_target) { - if (!new_target.empty()) { - BROKER_INFO("publish metrics to topic" << new_target); - target = std::move(new_target); - if (impl.id().empty()) - impl.id(std::string{target.suffix()}); - cold_boot(); - } - } - - void set_id(std::string new_id) { - if (!new_id.empty()) { - impl.id(std::move(new_id)); - cold_boot(); - } - } - - void set_prefixes(filter_type new_prefixes_filter) { - // We only wrap the prefixes into a filter to get around assigning a type ID - // to std::vector (which technically would require us to change - // Broker ID on the network). - std::vector new_prefixes; - for (auto& prefix : new_prefixes_filter) - new_prefixes.emplace_back(std::move(prefix).move_string()); - impl.selected_prefixes(std::move(new_prefixes)); - cold_boot(); - } - - // -- member variables ------------------------------------------------------- - - /// Points to the actor (exporter) that owns this state object. - Self* self; - - /// Handle to the core actor. - caf::actor core; - - /// Configures how frequent the exporter collects metrics from the system. - caf::timespan interval; - - /// Caches the time point of our initialization for scheduling ticks. - caf::actor_clock::time_point tick_init; - - /// Configures the topic for periodically publishing scrape results to. - topic target; - - /// Adds metrics for CPU and RAM usage. - caf::telemetry::importer::process proc_importer; - - /// The actual exporter for collecting the metrics. - metric_scraper impl; - - bool running_ = false; - - static inline const char* name = "broker.exporter"; -}; - -using metric_exporter_actor = - caf::stateful_actor>; - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_exporter.test.cc b/libbroker/broker/internal/metric_exporter.test.cc deleted file mode 100644 index 3f5208e2..00000000 --- a/libbroker/broker/internal/metric_exporter.test.cc +++ /dev/null @@ -1,229 +0,0 @@ -#include "broker/internal/metric_exporter.hh" - -#include "broker/broker-test.test.hh" - -#include "broker/internal/metric_view.hh" - -namespace atom = broker::internal::atom; - -using namespace broker; -using namespace std::literals; - -namespace { - -struct dummy_core_state { - data_message last_message; - - caf::behavior make_behavior() { - return { - [this](atom::publish, data_message& msg) { - last_message = std::move(msg); - }, - }; - } -}; - -using dummy_core_actor = caf::stateful_actor; - -struct metric_row { - std::string prefix; - std::string name; - std::string type; - std::string unit; - std::string helptext; - bool is_sum; - table labels; - data value; -}; - -template -bool inspect(Inspector& f, metric_row& row) { - return f.object(row).fields( - f.field("prefix", row.prefix), f.field("name", row.name), - f.field("type", row.type), f.field("unit", row.unit), - f.field("helptext", row.helptext), f.field("is_sum", row.is_sum), - f.field("labels", row.labels), f.field("value", row.value)); -} - -bool operator==(const metric_row& lhs, const vector& rhs) { - if (auto mv = internal::metric_view{rhs}) - return lhs.prefix == mv.prefix() && lhs.name == mv.name() - && lhs.type == mv.type_str() && lhs.unit == mv.unit() - && lhs.helptext == mv.helptext() && lhs.is_sum == mv.is_sum() - && lhs.labels == mv.labels() && lhs.value == mv.value(); - else - return false; -} - -bool operator==(const vector& lhs, const metric_row& rhs) { - return rhs == lhs; -} - -bool operator==(const metric_row& lhs, const data& rhs) { - if (auto vec = get_if(rhs)) - return lhs == *vec; - else - return false; -} - -bool operator==(const data& lhs, const metric_row& rhs) { - return rhs == lhs; -} - -struct fixture : base_fixture { - caf::actor core; - caf::actor aut; - caf::telemetry::int_gauge* foo_bar; - caf::telemetry::int_histogram* foo_hist; - caf::telemetry::int_gauge* bar_foo; - - fixture() { - auto& reg = sys.metrics(); - foo_bar = reg.gauge_singleton("foo", "bar", "FooBar!"); - std::array buckets{{8, 16, 32}}; - auto foo_hist_fam = reg.histogram_family("foo", "hist", {"sys"}, buckets, - "FooHist!", "seconds"); - foo_hist = foo_hist_fam->get_or_add({{"sys", "broker"}}); - bar_foo = reg.gauge_singleton("bar", "foo", "BarFoo!"); - std::vector selection{"foo"}; - core = sys.spawn(); - aut = sys.spawn(core, std::move(selection), - caf::timespan{2s}, - "all/them/metrics", - "exporter-1"); - sched.run(); - } - - ~fixture() { - anon_send_exit(aut, caf::exit_reason::user_shutdown); - } - - auto& state() { - return deref(aut).state; - } - - auto& core_state() { - return deref(core).state; - } - - const auto& rows() { - return state().impl.rows(); - } - - const auto& row(size_t index) { - return rows().at(index); - } - - data foo_hist_buckets(int64_t le_8, int64_t le_16, int64_t le_32, - int64_t gt_32, int64_t sum) { - vector result; - result.emplace_back(vector{8, le_8}); - result.emplace_back(vector{16, le_16}); - result.emplace_back(vector{32, le_32}); - result.emplace_back(vector{std::numeric_limits::max(), gt_32}); - result.emplace_back(sum); - return data{std::move(result)}; - } -}; - -} // namespace - -FIXTURE_SCOPE(telemetry_exporter_tests, fixture) - -TEST(the exporter runs once per interval) { - foo_bar->inc(); - foo_hist->observe(4); - foo_hist->observe(12); - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - auto is_meta_data = [this](const data& x) { - using namespace std::literals; - if (auto row = get_if(x); row && row->size() == 2) - return row->at(0) == "exporter-1"s && is(row->at(1)); - else - return false; - }; - if (CHECK(rows().size() == 3)) { - CHECK(is_meta_data(row(0))); - CHECK_EQUAL(row(1), (metric_row{"foo", "bar", "gauge", "1", "FooBar!", - false, table{}, data{1}})); - CHECK_EQUAL(row(2), (metric_row{"foo", "hist", "histogram", "seconds", - "FooHist!", false, table{{"sys", "broker"}}, - foo_hist_buckets(1, 1, 0, 0, 16)})); - } - foo_bar->inc(); - foo_hist->observe(64); - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - if (CHECK(rows().size() == 3)) { - CHECK(is_meta_data(row(0))); - CHECK_EQUAL(row(1), (metric_row{"foo", "bar", "gauge", "1", "FooBar!", - false, table{}, data{2}})); - CHECK_EQUAL(row(2), (metric_row{"foo", "hist", "histogram", "seconds", - "FooHist!", false, table{{"sys", "broker"}}, - foo_hist_buckets(1, 1, 0, 1, 80)})); - } -} - -TEST(the exporter allows changing the interval at runtime) { - inject((atom::put, timespan), to(aut).with(atom::put_v, timespan{3s})); - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - sched.advance_time(2s); - disallow((caf::tick_atom), to(aut)); - sched.advance_time(1s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); -} - -TEST(the exporter allows changing the topic at runtime) { - auto last_topic = [this] { return get_topic(core_state().last_message); }; - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - CHECK_EQUAL(last_topic(), "all/them/metrics"sv); - inject((atom::put, topic), to(aut).with(atom::put_v, "foo/bar"_t)); - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - CHECK_EQUAL(last_topic(), "foo/bar"sv); -} - -TEST(the exporter allows changing the ID at runtime) { - auto has_id = [this](const data& x, const std::string& what) { - using namespace std::literals; - if (auto row = get_if(x); row && row->size() == 2) - return row->at(0) == what; - else - return false; - }; - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - if (CHECK(!rows().empty())) - CHECK(has_id(row(0), "exporter-1")); - inject((atom::put, std::string), to(aut).with(atom::put_v, "foobar")); - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - if (CHECK(!rows().empty())) - CHECK(has_id(row(0), "foobar")); -} - -TEST(the exporter allows changing the prefix selection at runtime) { - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - CHECK_EQUAL(rows().size(), 3u); - inject((atom::put, filter_type), - to(aut).with(atom::put_v, filter_type{"foo", "bar"})); - sched.advance_time(2s); - expect((caf::tick_atom), to(aut)); - expect((atom::publish, data_message), from(aut).to(core)); - CHECK_EQUAL(rows().size(), 4u); -} - -FIXTURE_SCOPE_END() diff --git a/libbroker/broker/internal/metric_factory.cc b/libbroker/broker/internal/metric_factory.cc index 97c6287f..4345e990 100644 --- a/libbroker/broker/internal/metric_factory.cc +++ b/libbroker/broker/internal/metric_factory.cc @@ -6,76 +6,66 @@ namespace broker::internal { // -- 'imports' to safe ourselves some typing ---------------------------------- -using dbl_gauge = metric_factory::dbl_gauge; +using gauge = metric_factory::gauge; -using dbl_gauge_family = metric_factory::dbl_gauge_family; +using gauge_family = metric_factory::gauge_family; -using int_gauge = metric_factory::int_gauge; +using counter = metric_factory::counter; -using int_gauge_family = metric_factory::int_gauge_family; +using counter_family = metric_factory::counter_family; -using dbl_counter = metric_factory::dbl_counter; +using histogram = metric_factory::histogram; -using dbl_counter_family = metric_factory::dbl_counter_family; - -using int_counter = metric_factory::int_counter; - -using int_counter_family = metric_factory::int_counter_family; - -using dbl_histogram = metric_factory::dbl_histogram; - -using dbl_histogram_family = metric_factory::dbl_histogram_family; - -using int_histogram = metric_factory::int_histogram; - -using int_histogram_family = metric_factory::int_histogram_family; +using histogram_family = metric_factory::histogram_family; // -- core metrics ------------------------------------------------------------- using core_t = metric_factory::core_t; -int_gauge_family* core_t::connections_family() { - return reg_->gauge_family("broker", "connections", {"type"}, - "Number of active network connections."); +gauge_family* core_t::connections_family() { + return &prometheus::BuildGauge() + .Name("broker_connections") + .Help("Number of active network connections.") + .Register(*reg_); } core_t::connections_t core_t::connections_instances() { auto fm = connections_family(); return { - fm->get_or_add({{"type", "native"}}), - fm->get_or_add({{"type", "web-socket"}}), + &fm->Add({{"type", "native"}}), + &fm->Add({{"type", "web-socket"}}), }; } -int_counter_family* core_t::processed_messages_family() { - return reg_->counter_family("broker", "processed-messages", {"type"}, - "Total number of processed messages.", "1", true); +counter_family* core_t::processed_messages_family() { + return &prometheus::BuildCounter() + .Name("broker_processed_messages_total") + .Help("Total number of processed messages.") + .Register(*reg_); } core_t::processed_messages_t core_t::processed_messages_instances() { auto fm = processed_messages_family(); return { - fm->get_or_add({{"type", "data"}}), - fm->get_or_add({{"type", "command"}}), - fm->get_or_add({{"type", "routing-update"}}), - fm->get_or_add({{"type", "ping"}}), - fm->get_or_add({{"type", "pong"}}), + &fm->Add({{"type", "data"}}), &fm->Add({{"type", "command"}}), + &fm->Add({{"type", "routing-update"}}), &fm->Add({{"type", "ping"}}), + &fm->Add({{"type", "pong"}}), }; } -int_gauge_family* core_t::buffered_messages_family() { - return reg_->gauge_family("broker", "buffered-messages", {"type"}, - "Number of currently buffered messages."); +gauge_family* core_t::buffered_messages_family() { + return &prometheus::BuildGauge() + .Name("broker_buffered_messages") + .Help("Number of currently buffered messages.") + .Register(*reg_); } core_t::buffered_messages_t core_t::buffered_messages_instances() { auto fm = buffered_messages_family(); return { - fm->get_or_add({{"type", "data"}}), - fm->get_or_add({{"type", "command"}}), - fm->get_or_add({{"type", "routing-update"}}), - fm->get_or_add({{"type", "ping"}}), - fm->get_or_add({{"type", "pong"}}), + &fm->Add({{"type", "data"}}), &fm->Add({{"type", "command"}}), + &fm->Add({{"type", "routing-update"}}), &fm->Add({{"type", "ping"}}), + &fm->Add({{"type", "pong"}}), }; } @@ -83,67 +73,70 @@ core_t::buffered_messages_t core_t::buffered_messages_instances() { using store_t = metric_factory::store_t; -int_gauge_family* store_t::input_channels_family() { - return reg_->gauge_family("broker", "store-input-channels", {"name"}, - "Number of active input channels in a data store."); -} - -int_gauge* store_t::input_channels_instance(std::string_view name) { - return input_channels_family()->get_or_add({{"name", name}}); +gauge_family* store_t::input_channels_family() { + return &prometheus::BuildGauge() + .Name("broker_store_input_channels") + .Help("Number of active input channels in a data store.") + .Register(*reg_); } -int_gauge_family* store_t::out_of_order_updates_family() { - return reg_->gauge_family("broker", "store-input-channels", {"name"}, - "Number of active input channels in a data store."); +gauge* store_t::input_channels_instance(std::string name) { + return &input_channels_family()->Add({{"name", std::move(name)}}); } -int_gauge* store_t::out_of_order_updates_instance(std::string_view name) { - return out_of_order_updates_family()->get_or_add({{"name", name}}); +gauge_family* store_t::out_of_order_updates_family() { + return &prometheus::BuildGauge() + .Name("broker_store_out_of_order_updates") + .Help("Number of out-of-order data store updates.") + .Register(*reg_); } -int_gauge_family* store_t::output_channels_family() { - return reg_->gauge_family( - "broker", "store-output-channels", {"name"}, - "Number of active output channels in a data store."); +gauge* store_t::out_of_order_updates_instance(std::string name) { + return &out_of_order_updates_family()->Add({{"name", std::move(name)}}); } -int_gauge* store_t::output_channels_instance(std::string_view name) { - return output_channels_family()->get_or_add({{"name", name}}); +gauge_family* store_t::output_channels_family() { + return &prometheus::BuildGauge() + .Name("broker_store_output_channels") + .Help("Number of active output channels in a data store.") + .Register(*reg_); } -int_gauge_family* store_t::entries_family() { - return reg_->gauge_family("broker", "store-entries", {"name"}, - "Number of entries in the data store."); +gauge* store_t::output_channels_instance(std::string name) { + return &output_channels_family()->Add({{"name", std::move(name)}}); } -int_gauge* store_t::entries_instance(std::string_view name) { - return entries_family()->get_or_add({{"name", name}}); +gauge_family* store_t::entries_family() { + return &prometheus::BuildGauge() + .Name("broker_store_entries") + .Help("Number of entries in the data store.") + .Register(*reg_); } -int_counter_family* store_t::processed_updates_family() { - return reg_->counter_family("broker", "store-processed-updates", {"name"}, - "Number of processed data store updates.", "1", - true); +gauge* store_t::entries_instance(std::string name) { + return &entries_family()->Add({{"name", std::move(name)}}); } -int_counter* store_t::processed_updates_instance(std::string_view name) { - return processed_updates_family()->get_or_add({{"name", name}}); +counter_family* store_t::processed_updates_family() { + return &prometheus::BuildCounter() + .Name("broker_store_processed_updates_total") + .Help("Total number of processed data store updates.") + .Register(*reg_); } -int_gauge_family* store_t::unacknowledged_updates_family() { - return reg_->gauge_family("broker", "store-unacknowledged-updates", {"name"}, - "Number of unacknowledged data store updates."); +counter* store_t::processed_updates_instance(std::string name) { + return &processed_updates_family()->Add({{"name", std::move(name)}}); } -int_gauge* store_t::unacknowledged_updates_instance(std::string_view name) { - return unacknowledged_updates_family()->get_or_add({{"name", name}}); +gauge_family* store_t::unacknowledged_updates_family() { + return &prometheus::BuildGauge() + .Name("broker_store_unacknowledged_updates") + .Help("Number of unacknowledged data store updates.") + .Register(*reg_); } -// --- constructors ------------------------------------------------------------ - -metric_factory::metric_factory(caf::actor_system& sys) noexcept - : metric_factory(sys.metrics()) { - // nop +gauge* store_t::unacknowledged_updates_instance(std::string name) { + return &unacknowledged_updates_family()->Add({{"name", std::move(name)}}); } } // namespace broker::internal diff --git a/libbroker/broker/internal/metric_factory.hh b/libbroker/broker/internal/metric_factory.hh index 5f961090..b38d6012 100644 --- a/libbroker/broker/internal/metric_factory.hh +++ b/libbroker/broker/internal/metric_factory.hh @@ -1,10 +1,14 @@ #pragma once -#include -#include -#include -#include -#include +#include "broker/fwd.hh" + +#include +#include +#include +#include +#include + +#include namespace broker::internal { @@ -14,36 +18,24 @@ public: // -- convenience type aliases ----------------------------------------------- template - using family_t = caf::telemetry::metric_family_impl; - - using dbl_gauge = caf::telemetry::dbl_gauge; - - using dbl_gauge_family = family_t; - - using int_gauge = caf::telemetry::int_gauge; + using family_t = prometheus::Family; - using int_gauge_family = family_t; + using gauge = prometheus::Gauge; - using dbl_counter = caf::telemetry::dbl_counter; + using gauge_family = family_t; - using dbl_counter_family = family_t; + using counter = prometheus::Counter; - using int_counter = caf::telemetry::int_counter; + using counter_family = family_t; - using int_counter_family = family_t; + using histogram = prometheus::Histogram; - using dbl_histogram = caf::telemetry::dbl_histogram; - - using dbl_histogram_family = family_t; - - using int_histogram = caf::telemetry::int_histogram; - - using int_histogram_family = family_t; + using histogram_family = family_t; /// Bundles all Broker metrics for the core system. class core_t { public: - explicit core_t(caf::telemetry::metric_registry* reg) : reg_(reg) {} + explicit core_t(prometheus::Registry& reg) : reg_(®) {} core_t(const core_t&) noexcept = default; @@ -52,11 +44,11 @@ public: /// Keeps track of the active connections. /// /// Label dimensions: `type` ('native' or 'web-socket'). - int_gauge_family* connections_family(); + gauge_family* connections_family(); struct connections_t { - int_gauge* native; - int_gauge* web_socket; + gauge* native; + gauge* web_socket; }; /// Returns all instances of `broker.connections`. @@ -66,14 +58,14 @@ public: /// /// Label dimensions: `type` ('data', 'command', 'routing-update', 'ping', /// or 'pong'). - int_counter_family* processed_messages_family(); + counter_family* processed_messages_family(); struct processed_messages_t { - int_counter* data; - int_counter* command; - int_counter* routing_update; - int_counter* ping; - int_counter* pong; + counter* data; + counter* command; + counter* routing_update; + counter* ping; + counter* pong; }; /// Returns all instances of `broker.processed-messages`. @@ -83,76 +75,76 @@ public: /// /// Label dimensions: `type` ('data', 'command', 'routing-update', 'ping', /// or 'pong'). - int_gauge_family* buffered_messages_family(); + gauge_family* buffered_messages_family(); struct buffered_messages_t { - int_gauge* data; - int_gauge* command; - int_gauge* routing_update; - int_gauge* ping; - int_gauge* pong; + gauge* data; + gauge* command; + gauge* routing_update; + gauge* ping; + gauge* pong; }; /// Returns all instances of `broker.buffered-messages`. buffered_messages_t buffered_messages_instances(); private: - caf::telemetry::metric_registry* reg_; + prometheus::Registry* reg_; }; /// Bundles all Broker metrics for the data stores. class store_t { public: - explicit store_t(caf::telemetry::metric_registry* reg) : reg_(reg) {} + explicit store_t(prometheus::Registry& reg) : reg_(®) {} store_t(const store_t&) noexcept = default; store_t& operator=(const store_t&) noexcept = default; /// Counts how many input channels a data store currently has. - int_gauge_family* input_channels_family(); + gauge_family* input_channels_family(); /// Returns an instance of `broker.store-input-channels` for the given /// data store. - int_gauge* input_channels_instance(std::string_view name); + gauge* input_channels_instance(std::string name); /// Counts how many inputs are currently buffered because they arrived /// out-of-order. - int_gauge_family* out_of_order_updates_family(); + gauge_family* out_of_order_updates_family(); /// Returns an instance of `broker.store-out-of-order-updates` for the given /// data store. - int_gauge* out_of_order_updates_instance(std::string_view name); + gauge* out_of_order_updates_instance(std::string name); /// Counts how many output channels a data store currently has. - int_gauge_family* output_channels_family(); + gauge_family* output_channels_family(); /// Returns an instance of `broker.store-output-channels` for the given /// data store. - int_gauge* output_channels_instance(std::string_view name); + gauge* output_channels_instance(std::string name); /// Counts how many entries a data store currently has. - int_gauge_family* entries_family(); + gauge_family* entries_family(); /// Returns an instance of `broker.store-entries` for the given data store. - int_gauge* entries_instance(std::string_view name); + gauge* entries_instance(std::string name); /// Counts how many updates were processed in total. - int_counter_family* processed_updates_family(); + counter_family* processed_updates_family(); /// Returns an instance of `broker.store-processed-updates` for the /// given data store. - int_counter* processed_updates_instance(std::string_view name); + counter* processed_updates_instance(std::string name); /// Counts how many updates are currently unacknowledged. - int_gauge_family* unacknowledged_updates_family(); + gauge_family* unacknowledged_updates_family(); /// Returns an instance of `broker.store-unacknowledged-updates` for the /// given data store. - int_gauge* unacknowledged_updates_instance(std::string_view name); + gauge* unacknowledged_updates_instance(std::string name); private: - caf::telemetry::metric_registry* reg_; + prometheus::Registry* reg_; }; // -- properties ------------------------------------------------------------- @@ -163,10 +155,8 @@ public: // --- constructors ---------------------------------------------------------- - explicit metric_factory(caf::actor_system& sys) noexcept; - - explicit metric_factory(caf::telemetry::metric_registry& reg) noexcept - : core(®), store(®) { + explicit metric_factory(prometheus::Registry& reg) noexcept + : core(reg), store(reg) { // nop } diff --git a/libbroker/broker/internal/metric_scraper.cc b/libbroker/broker/internal/metric_scraper.cc deleted file mode 100644 index 00effe55..00000000 --- a/libbroker/broker/internal/metric_scraper.cc +++ /dev/null @@ -1,161 +0,0 @@ -#include "broker/internal/metric_scraper.hh" - -#include -#include -#include -#include -#include -#include - -#include "broker/defaults.hh" -#include "broker/detail/assert.hh" -#include "broker/detail/next_tick.hh" -#include "broker/internal/logger.hh" -#include "broker/message.hh" - -namespace ct = caf::telemetry; - -namespace broker::internal { - -namespace { - -table to_table(const std::vector& labels) { - auto str = [](auto str_view) { - return std::string{str_view.begin(), str_view.end()}; - }; - table result; - for (const auto& lbl : labels) - result.emplace(str(lbl.name()), str(lbl.value())); - return result; -} - -// Packs buckets + sum into a Broker vector, where each bucket is a pair of -// numbers (upper bound plus count). -template -vector pack_histogram(const Buckets& buckets, ValueType sum) { - vector result; - result.reserve(buckets.size() + 1); // +1 for the trailing sum - for (auto& bucket : buckets) { - vector packed_bucket; - packed_bucket.reserve(2); - packed_bucket.emplace_back(bucket.upper_bound); - packed_bucket.emplace_back(bucket.count.value()); - result.emplace_back(std::move(packed_bucket)); - } - result.emplace_back(sum); - return result; -} - -template -vector pack_histogram(const Histogram* ptr) { - return pack_histogram(ptr->buckets(), ptr->sum()); -} - -} // namespace - -metric_scraper::metric_scraper(std::string id) : id_(std::move(id)) { - // nop -} - -metric_scraper::metric_scraper(std::vector selected_prefixes, - std::string id) - : selected_prefixes_(std::move(selected_prefixes)), id_(std::move(id)) { - // nop -} - -bool metric_scraper::selected(const ct::metric_family* family) { - auto matches = [family](const auto& prefix) { - // For now, we only match prefixes on an equality-basis, no globbing. - return family->prefix() == prefix; - }; - return selected_prefixes_.empty() - || std::any_of(selected_prefixes_.begin(), selected_prefixes_.end(), - matches); -} - -void metric_scraper::scrape(caf::telemetry::metric_registry& registry) { - last_scrape_ = now(); - if (!rows_.empty()) { - rows_.resize(1); - BROKER_ASSERT(is(rows_[0])); - auto& meta = get(rows_[0]); - BROKER_ASSERT(meta.size() == 2); - BROKER_ASSERT(is(meta[0])); - BROKER_ASSERT(is(meta[1])); - get(meta[1]) = last_scrape_; - } else { - vector meta; - meta.emplace_back(id_); - meta.emplace_back(last_scrape_); - rows_.emplace_back(std::move(meta)); - } - BROKER_ASSERT(rows_.size() == 1); - registry.collect(*this); -} - -void metric_scraper::id(std::string new_id) { - id_ = std::move(new_id); - rows_.clear(); // Force re-creation of the meta information on next scrape. -} - -void metric_scraper::operator()(const ct::metric_family* family, - const ct::metric* instance, - const ct::dbl_counter* counter) { - if (selected(family)) - add_row(family, "counter", to_table(instance->labels()), counter->value()); -} - -void metric_scraper::operator()(const ct::metric_family* family, - const ct::metric* instance, - const ct::int_counter* counter) { - if (selected(family)) - add_row(family, "counter", to_table(instance->labels()), counter->value()); -} - -void metric_scraper::operator()(const ct::metric_family* family, - const ct::metric* instance, - const ct::dbl_gauge* gauge) { - if (selected(family)) - add_row(family, "gauge", to_table(instance->labels()), gauge->value()); -} - -void metric_scraper::operator()(const ct::metric_family* family, - const ct::metric* instance, - const ct::int_gauge* gauge) { - if (selected(family)) - add_row(family, "gauge", to_table(instance->labels()), gauge->value()); -} - -void metric_scraper::operator()(const ct::metric_family* family, - const ct::metric* instance, - const ct::dbl_histogram* histogram) { - if (selected(family)) - add_row(family, "histogram", to_table(instance->labels()), - pack_histogram(histogram)); -} - -void metric_scraper::operator()(const ct::metric_family* family, - const ct::metric* instance, - const ct::int_histogram* histogram) { - if (selected(family)) - add_row(family, "histogram", to_table(instance->labels()), - pack_histogram(histogram)); -} - -template -void metric_scraper::add_row(const caf::telemetry::metric_family* family, - std::string type, table labels, T value) { - vector row; - row.reserve(8); - row.emplace_back(family->prefix()); - row.emplace_back(family->name()); - row.emplace_back(std::move(type)); - row.emplace_back(family->unit()); - row.emplace_back(family->helptext()); - row.emplace_back(family->is_sum()); - row.emplace_back(std::move(labels)); - row.emplace_back(std::move(value)); - rows_.emplace_back(std::move(row)); -} - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_scraper.hh b/libbroker/broker/internal/metric_scraper.hh deleted file mode 100644 index 236e0091..00000000 --- a/libbroker/broker/internal/metric_scraper.hh +++ /dev/null @@ -1,113 +0,0 @@ -#pragma once - -#include "broker/data.hh" -#include "broker/topic.hh" - -#include - -namespace broker::internal { - -/// Scrapes local CAF metrics and encodes them into `data` objects (for -/// decoding, see `metric_view`). The `data`-encoded metrics then may get -/// published to a Broker topic by an exporter or Prometheus actor. -class metric_scraper { -public: - // -- constructors, destructors, and assignment operators -------------------- - - metric_scraper(std::string id); - - metric_scraper(std::vector selected_prefixes, std::string id); - - // -- properties ------------------------------------------------------------- - - [[nodiscard]] const auto& selected_prefixes() const noexcept { - return selected_prefixes_; - } - - template - void selected_prefixes(T&& new_prefixes) { - selected_prefixes_ = std::forward(new_prefixes); - } - - [[nodiscard]] auto last_scrape() const noexcept { - return last_scrape_; - } - - [[nodiscard]] const auto& id() const noexcept { - return id_; - } - - void id(std::string new_id); - - [[nodiscard]] const auto& rows() const noexcept { - return rows_; - } - - /// Checks whether `selected_prefixes` is empty (an empty filter means *select - /// all*) or `family->prefix()` is in `selected_prefixes`. - bool selected(const caf::telemetry::metric_family* family); - - // -- callbacks for the metric registry -------------------------------------- - - /// Encodes selected metrics from the CAF metrics registry to a table-like - /// data structure and stores the result in `rows`. - void scrape(caf::telemetry::metric_registry& registry); - - void operator()(const caf::telemetry::metric_family* family, - const caf::telemetry::metric* instance, - const caf::telemetry::dbl_counter* counter); - - void operator()(const caf::telemetry::metric_family* family, - const caf::telemetry::metric* instance, - const caf::telemetry::int_counter* counter); - - void operator()(const caf::telemetry::metric_family* family, - const caf::telemetry::metric* instance, - const caf::telemetry::dbl_gauge* gauge); - - void operator()(const caf::telemetry::metric_family* family, - const caf::telemetry::metric* instance, - const caf::telemetry::int_gauge* gauge); - - void operator()(const caf::telemetry::metric_family* family, - const caf::telemetry::metric* instance, - const caf::telemetry::dbl_histogram* histogram); - - void operator()(const caf::telemetry::metric_family* family, - const caf::telemetry::metric* instance, - const caf::telemetry::int_histogram* histogram); - - template - void operator()(const caf::telemetry::metric_family*, - const caf::telemetry::metric*, const T*) { - // nop - } - -private: - // -- private utility -------------------------------------------------------- - - /// Encodes a single metric as a row in our output vector. - template - void add_row(const caf::telemetry::metric_family* family, std::string type, - table labels, T value); - - // -- member variables ------------------------------------------------------- - - /// Configures which metrics the scraper includes. - std::vector selected_prefixes_; - - /// Stores the system time of the last call to `scrape()`. - timestamp last_scrape_; - - /// Configures the ID of this scraper to allow subscribers to disambiguate - /// remote metrics. By default, the scraper uses the target suffix as ID. For - /// example, constructing the scraper with target topic - /// `/zeek/metrics/worker-1` would set this field to "worker-1". - std::string id_; - - /// Contains the result for the last scraping run as data rows. The first row - /// is reserved for meta data (scraper ID plus timestamp). - vector rows_; -}; - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_view.cc b/libbroker/broker/internal/metric_view.cc deleted file mode 100644 index 9b93257f..00000000 --- a/libbroker/broker/internal/metric_view.cc +++ /dev/null @@ -1,101 +0,0 @@ -#include "broker/internal/metric_view.hh" - -#include - -namespace broker::internal { - -metric_view::metric_view(const vector* row) - : type_(static_cast(0)) { - using std::string; - auto at = [row](field x) -> const auto& { - return (*row)[index(x)]; - }; - auto row_ok = row != nullptr && row->size() == row_size - && is(at(field::prefix)) && is(at(field::name)) - && is(at(field::type)) && is(at(field::unit)) - && is(at(field::helptext)) - && is(at(field::is_sum)) - && has_properly_typed_labels(*row); - if (row_ok && get_type(*row, type_)) { - row_ = row; - } else { - row_ = nullptr; - } -} - -metric_view::metric_view(const vector& row) : metric_view(&row) { - // nop -} - -metric_view::metric_view(const data& row_data) - : metric_view(get_if(row_data)) { - // nop -} - -bool metric_view::has_properly_typed_labels(const vector& row) noexcept { - if (auto tbl = get_if(row[index(field::labels)])) { - auto is_string_pair = [](const auto& kvp) { - return is(kvp.first) && is(kvp.second); - }; - return std::all_of(tbl->begin(), tbl->end(), is_string_pair); - } else { - return false; - } -} - -namespace { - -template -struct pair_predicate { - bool operator()(const data& x) const noexcept { - if (auto vec = get_if(x); vec && vec->size() == 2) { - return is((*vec)[0]) && is((*vec)[1]); - } else { - return false; - } - } -}; - -} // namespace - -bool metric_view::get_type(const vector& row, - caf::telemetry::metric_type& var) noexcept { - using caf::telemetry::metric_type; - auto good = [&var](metric_type value) { - var = value; - return true; - }; - static constexpr bool bad = false; - const auto* t = broker::get_if(row[index(field::type)]); - if (t == nullptr) { - return bad; - } - const auto& v = row[index(field::value)]; - if (*t == "counter") { - if (is(v)) { - return good(metric_type::int_counter); - } else if (is(v)) { - return good(metric_type::dbl_counter); - } - } else if (*t == "gauge") { - if (is(v)) { - return good(metric_type::int_gauge); - } else if (is(v)) { - return good(metric_type::dbl_gauge); - } - } else if (*t == "histogram") { - if (auto vals = get_if(v); vals && vals->size() >= 2) { - if (std::all_of(vals->begin(), vals->end() - 1, pair_predicate{}) - && is(vals->back())) { - return good(metric_type::int_histogram); - } else if (std::all_of(vals->begin(), vals->end() - 1, - pair_predicate{}) - && is(vals->back())) { - return good(metric_type::dbl_histogram); - } - } - } - return bad; -} - -} // namespace broker::internal diff --git a/libbroker/broker/internal/metric_view.hh b/libbroker/broker/internal/metric_view.hh deleted file mode 100644 index f1d621c9..00000000 --- a/libbroker/broker/internal/metric_view.hh +++ /dev/null @@ -1,118 +0,0 @@ -#pragma once - -#include - -#include - -#include "broker/data.hh" -#include "broker/detail/assert.hh" - -namespace broker::internal { - -/// Provides a view into a `broker::data` that represents a Broker metric. -class metric_view { -public: - // -- constants -------------------------------------------------------------- - - enum class field : size_t { - prefix, - name, - type, - unit, - helptext, - is_sum, - labels, - value, - end_of_row, - }; - - static constexpr size_t index(field x) noexcept { - return static_cast(x); - } - - static constexpr size_t row_size = static_cast(field::end_of_row); - - // -- constructors, destructors, and assignment operators -------------------- - - explicit metric_view(const vector* row); - - explicit metric_view(const vector& row); - - explicit metric_view(const data& row_data); - - metric_view(const metric_view&) noexcept = default; - - metric_view& operator=(const metric_view&) noexcept = default; - - // -- properties ------------------------------------------------------------- - - bool valid() const noexcept { - return row_ != nullptr; - } - - explicit operator bool() const noexcept { - return valid(); - } - - /// @pre `valid()` - const std::string& prefix() const { - return {get(field::prefix)}; - } - - /// @pre `valid()` - const std::string& name() const { - return {get(field::name)}; - } - - /// @pre `valid()` - caf::telemetry::metric_type type() const noexcept { - return type_; - } - - /// @pre `valid()` - const std::string& type_str() const { - return {get(field::type)}; - } - - /// @pre `valid()` - const std::string& unit() const { - return {get(field::unit)}; - } - - /// @pre `valid()` - const std::string& helptext() const { - return {get(field::helptext)}; - } - - /// @pre `valid()` - bool is_sum() const { - return get(field::is_sum); - } - - /// @pre `valid()` - const table& labels() const { - return get
(field::labels); - } - - /// @pre `valid()` - const data& value() const { - return (*row_)[index(field::value)]; - } - -private: - static bool has_properly_typed_labels(const vector& row) noexcept; - - static bool get_type(const vector& row, - caf::telemetry::metric_type& var) noexcept; - - template - const T& get(field x) const { - BROKER_ASSERT(row_ != nullptr); - return broker::get((*row_)[index(x)]); - } - - const vector* row_; - caf::telemetry::metric_type type_; -}; - -} // namespace broker::internal diff --git a/libbroker/broker/internal/peering.hh b/libbroker/broker/internal/peering.hh index abcb56d9..0bbfdf2e 100644 --- a/libbroker/broker/internal/peering.hh +++ b/libbroker/broker/internal/peering.hh @@ -12,8 +12,6 @@ #include #include #include -#include -#include #include diff --git a/libbroker/broker/internal/prometheus.cc b/libbroker/broker/internal/prometheus.cc deleted file mode 100644 index 29d53343..00000000 --- a/libbroker/broker/internal/prometheus.cc +++ /dev/null @@ -1,361 +0,0 @@ -#include "broker/internal/prometheus.hh" - -#include -#include - -#include -#include - -#include "broker/internal/logger.hh" -#include "broker/internal/metric_exporter.hh" -#include "broker/message.hh" - -using namespace std::literals; - -namespace broker::internal { - -namespace { - -using std::string_view; - -// Cap incoming HTTP requests. -constexpr size_t max_request_size = 512ul * 1024ul; - -// A GET request for Prometheus metrics. -constexpr string_view prom_request_start = "GET /metrics HTTP/1."; - -// A GET request for JSON-formatted status snapshots. -constexpr string_view status_request_start = "GET /v1/status/json HTTP/1."; - -// HTTP response for requests that exceed the size limit. -constexpr string_view request_too_large = - "HTTP/1.1 413 Request Entity Too Large\r\n" - "Connection: Closed\r\n\r\n"; - -// HTTP response for requests that don't start with "GET /metrics HTTP/1". -constexpr string_view request_not_supported = "HTTP/1.1 501 Not Implemented\r\n" - "Connection: Closed\r\n\r\n"; - -// HTTP header when sending a plain text. -constexpr string_view request_ok_text = "HTTP/1.1 200 OK\r\n" - "Content-Type: text/plain\r\n" - "Connection: Closed\r\n\r\n"; - -// HTTP header when sending a JSON. -constexpr string_view request_ok_json = "HTTP/1.1 200 OK\r\n" - "Content-Type: application/json\r\n" - "Connection: Closed\r\n\r\n"; - -} // namespace - -// -- constructors, destructors, and assignment operators ---------------------- - -prometheus_actor::prometheus_actor(caf::actor_config& cfg, - caf::io::doorman_ptr ptr, caf::actor core) - : super(cfg), core_(std::move(core)) { - filter_ = caf::get_or(config(), "broker.metrics.import.topics", - filter_type{}); - add_doorman(std::move(ptr)); -} - -// -- overrides ---------------------------------------------------------------- - -void prometheus_actor::on_exit() { - requests_.clear(); - core_ = nullptr; - exporter_.reset(); -} - -const char* prometheus_actor::name() const { - return "broker.telemetry-prometheus"; -} - -caf::behavior prometheus_actor::make_behavior() { - if (!core_) { - BROKER_ERROR("started a Prometheus actor with an invalid core handle"); - return {}; - } - monitor(core_); - set_down_handler([this](const caf::down_msg& msg) { - if (msg.source == core_) { - BROKER_INFO("the core terminated:" << msg.reason); - quit(msg.reason); - } - }); - if (!filter_.empty()) { - BROKER_INFO("collect remote metrics from topics" << filter_); - send(core_, atom::join_v, filter_); - } - auto bhvr = caf::message_handler{ - [this](const caf::io::new_data_msg& msg) { - // Ignore data we're no longer interested in. - auto iter = requests_.find(msg.handle); - if (iter == requests_.end() || iter->second.async_id != 0) - return; - auto& req = iter->second.buf; - if (req.size() + msg.buf.size() > max_request_size) { - write(msg.handle, caf::as_bytes(caf::make_span(request_too_large))); - flush_and_close(msg.handle); - return; - } - req.insert(req.end(), msg.buf.begin(), msg.buf.end()); - auto req_str = string_view{reinterpret_cast(req.data()), - req.size()}; - // Stop here if the HTTP header isn't complete yet. - if (req_str.find("\r\n\r\n"sv) == std::string_view::npos) - return; - // Dispatch to a handler or send an error if nothing matches. - if (caf::starts_with(req_str, prom_request_start)) { - BROKER_DEBUG("serve HTTP request for /metrics"); - on_metrics_request(msg.handle); - return; - } - if (caf::starts_with(req_str, status_request_start)) { - BROKER_DEBUG("serve HTTP request for /v1/status/json"); - on_status_request(msg.handle); - return; - } - BROKER_DEBUG("reject unsupported HTTP request: " - << std::string{req_str.substr(0, req_str.find("\r\n"sv))}); - write(msg.handle, caf::as_bytes(caf::make_span(request_not_supported))); - flush_and_close(msg.handle); - }, - [this](const caf::io::new_connection_msg& msg) { - // Pre-allocate buffer for maximum request size and start reading. - requests_[msg.handle].buf.reserve(max_request_size); - configure_read(msg.handle, caf::io::receive_policy::at_most(1024)); - }, - [this](const caf::io::connection_closed_msg& msg) { - requests_.erase(msg.handle); - if (num_connections() + num_doormen() == 0) - quit(); - }, - [this](const caf::io::acceptor_closed_msg&) { - BROKER_ERROR("Prometheus actor lost its acceptor!"); - if (num_connections() + num_doormen() == 0) - quit(); - }, - [this](const data_message& msg) { - BROKER_TRACE(BROKER_ARG(msg)); - collector_.insert_or_update(get_data(msg).to_data()); - }, - [this](atom::join, const filter_type& filter) { - filter_ = filter; - BROKER_INFO("collect remote metrics from topics" << filter_); - send(core_, atom::join_v, filter_); - }, - }; - auto params = metric_exporter_params::from(config()); - exporter_ = std::make_unique(this, core_, - std::move(params)); - return bhvr.or_else(exporter_->make_behavior()); -} - -void prometheus_actor::flush_and_close(caf::io::connection_handle hdl) { - flush(hdl); - close(hdl); - requests_.erase(hdl); - if (num_connections() + num_doormen() == 0) - quit(); -} - -void prometheus_actor::on_metrics_request(caf::io::connection_handle hdl) { - // Collect metrics, ship response, and close. If the user configured - // neither Broker-side import nor export of metrics, we fall back to the - // default CAF Prometheus export. - auto hdr = caf::as_bytes(caf::make_span(request_ok_text)); - BROKER_ASSERT(exporter_ != nullptr); - if (!exporter_->running()) { - exporter_->proc_importer.update(); - exporter_->impl.scrape(system().metrics()); - } - collector_.insert_or_update(exporter_->impl.rows()); - auto text = collector_.prometheus_text(); - auto payload = caf::as_bytes(caf::make_span(text)); - auto& dst = wr_buf(hdl); - dst.insert(dst.end(), hdr.begin(), hdr.end()); - dst.insert(dst.end(), payload.begin(), payload.end()); - flush_and_close(hdl); -} - -void prometheus_actor::on_status_request(caf::io::connection_handle hdl) { - auto aid = new_u64_id(); - request(core_, 5s, atom::get_v, atom::status_v) - .then( - [this, hdl, aid](const table& tbl) { // - on_status_request_cb(hdl, aid, tbl); - }, - [this, hdl, aid](const caf::error& what) { - table tbl; - tbl.emplace(data{"error"s}, data{caf::to_string(what)}); - on_status_request_cb(hdl, aid, tbl); - }); - requests_[hdl].async_id = aid; -} - -namespace { - -class jsonizer { -public: - using char_buf = std::vector; - - explicit jsonizer(char_buf& out) : out_(out) {} - - template - void operator()(const T& x) { - add("null"sv); - } - - void operator()(const boolean& x) { - if (x) - add("true"sv); - else - add("false"sv); - } - - void operator()(const count& x) { - add(std::to_string(x)); - } - - void operator()(const integer& x) { - add(std::to_string(x)); - } - - void operator()(const real& x) { - add(std::to_string(x)); - } - - void operator()(const std::string& x) { - add('"'); - for (auto ch : x) { - switch (ch) { - case '"': - add(R"(\")"); - break; - default: - add(ch); - } - } - add('"'); - } - - void operator()(const set& xs) { - apply_sequence(xs); - } - - void operator()(const table& xs) { - // Short-circuit empty tables. - if (xs.empty()) { - add("{}"sv); - return; - } - // We only support tables with string keys. Map everything else to 'null'. - auto has_string_key = [](const auto& kvp) { - return is(kvp.first); - }; - if (!std::all_of(xs.begin(), xs.end(), has_string_key)) { - add(R"_("")_"sv); - return; - } - // Iterate through the table, mapping it a JSON object. - auto i = xs.begin(); - block_open('{'); - apply_kvp(*i++); - while (i != xs.end()) { - add_sep(); - apply_kvp(*i++); - } - block_close('}'); - } - - void operator()(const vector& xs) { - apply_sequence(xs); - } - - template - void apply_sequence(const Container& xs) { - // Short-circuit empty containers. - if (xs.empty()) { - add("[]"sv); - return; - } - // Iterate through the container, mapping it a JSON array. - auto i = xs.begin(); - block_open('['); - std::visit(*this, (*i++).get_data()); - while (i != xs.end()) { - add_sep(); - std::visit(*this, (*i++).get_data()); - } - block_close(']'); - } - - template - void apply_kvp(const KeyValuePair& kvp) { - (*this)(get(kvp.first)); - add(": "sv); - std::visit(*this, kvp.second.get_data()); - } - -private: - void add(std::string_view str) { - out_.insert(out_.end(), str.begin(), str.end()); - } - - void add(char ch) { - out_.push_back(ch); - } - - void add_nl() { - add('\n'); - out_.insert(out_.end(), indent_, ' '); - } - - void add_sep() { - add(','); - add_nl(); - } - - void block_open(char ch) { - add(ch); - indent_ += 2; - add_nl(); - } - - void block_close(char ch) { - indent_ -= 2; - add_nl(); - add(ch); - } - - char_buf& out_; - size_t indent_ = 0; -}; - -} // namespace - -void prometheus_actor::on_status_request_cb(caf::io::connection_handle hdl, - uint64_t async_id, - const table& res) { - // Sanity checking. - auto iter = requests_.find(hdl); - if (iter == requests_.end()) - return; - auto& req = iter->second; - if (req.async_id != async_id) - return; - // Generate JSON output. - json_buf_.clear(); - jsonizer f{json_buf_}; - f(res); - json_buf_.push_back('\n'); - // Send result and close connection. - auto hdr = caf::as_bytes(caf::make_span(request_ok_json)); - auto payload = caf::as_bytes(caf::make_span(json_buf_)); - auto& dst = wr_buf(hdl); - dst.insert(dst.end(), hdr.begin(), hdr.end()); - dst.insert(dst.end(), payload.begin(), payload.end()); - flush_and_close(hdl); -} - -} // namespace broker::internal diff --git a/libbroker/broker/internal/prometheus.hh b/libbroker/broker/internal/prometheus.hh deleted file mode 100644 index 943e9529..00000000 --- a/libbroker/broker/internal/prometheus.hh +++ /dev/null @@ -1,79 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -#include "broker/filter_type.hh" -#include "broker/internal/metric_collector.hh" -#include "broker/internal/metric_exporter.hh" -#include "broker/internal/metric_scraper.hh" - -namespace broker::internal { - -/// Makes local and remote metrics available to Prometheus via HTTP. The -/// Prometheus actor collects and exports local metrics as well as imports -/// remote metrics by subscribing to user-defined topics where other endpoints -/// periodically publish their metrics. Hence, Broker never starts an exporter -/// when starting a Prometheus actor since that would be redundant. -class prometheus_actor : public caf::io::broker { -public: - // -- member types ----------------------------------------------------------- - - using super = caf::io::broker; - - using exporter_state_type = metric_exporter_state; - - struct request_state { - uint64_t async_id = 0; - caf::byte_buffer buf; - }; - - // -- constructors, destructors, and assignment operators -------------------- - - explicit prometheus_actor(caf::actor_config& cfg, caf::io::doorman_ptr ptr, - caf::actor core); - - // -- overrides -------------------------------------------------------------- - - void on_exit() override; - - const char* name() const override; - - caf::behavior make_behavior() override; - -private: - void flush_and_close(caf::io::connection_handle hdl); - - void on_metrics_request(caf::io::connection_handle hdl); - - void on_status_request(caf::io::connection_handle hdl); - - void on_status_request_cb(caf::io::connection_handle hdl, uint64_t async_id, - const table& res); - - /// Caches input per open connection for parsing the HTTP header. - std::unordered_map requests_; - - /// Combines various metrics into a single Prometheus output. - metric_collector collector_; - - /// Handle to the Broker endpoint actor. - caf::actor core_; - - /// Filter for subscribing to metrics-related topics. - filter_type filter_; - - /// Optional export of local metrics if the user configured a value for - /// "broker.metrics.export.topic". - std::unique_ptr exporter_; - - /// Buffer for writing JSON output. - std::vector json_buf_; -}; - -} // namespace broker::internal diff --git a/libbroker/broker/internal/store_actor.cc b/libbroker/broker/internal/store_actor.cc index b1b19451..3e444f36 100644 --- a/libbroker/broker/internal/store_actor.cc +++ b/libbroker/broker/internal/store_actor.cc @@ -66,11 +66,13 @@ store_actor_state::store_actor_state(caf::event_based_actor* selfptr) // nop } -void store_actor_state::init(endpoint_id this_endpoint, endpoint::clock* clock, +void store_actor_state::init(prometheus_registry_ptr reg, + endpoint_id this_endpoint, endpoint::clock* clock, std::string&& store_name, caf::actor&& core, consumer_resource in_res, producer_resource out_res) { BROKER_ASSERT(clock != nullptr); + this->registry = reg; this->clock = clock; this->store_name = std::move(store_name); this->id.endpoint = this_endpoint; diff --git a/libbroker/broker/internal/store_actor.hh b/libbroker/broker/internal/store_actor.hh index f33ccf50..e849466a 100644 --- a/libbroker/broker/internal/store_actor.hh +++ b/libbroker/broker/internal/store_actor.hh @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -68,8 +69,8 @@ public: /// Initializes the state. /// @pre `ptr != nullptr` /// @pre `clock != nullptr` - void init(endpoint_id this_endpoint, endpoint::clock* clock, std::string&& id, - caf::actor&& core, + void init(prometheus_registry_ptr reg, endpoint_id this_endpoint, + endpoint::clock* clock, std::string&& id, caf::actor&& core, caf::async::consumer_resource in_res, caf::async::producer_resource out_res); @@ -81,7 +82,7 @@ public: defaults::store::heartbeat_interval)); out.connection_timeout_factor(get_or(cfg, "broker.store.connection-timeout", defaults::store::connection_timeout)); - out.metrics().init(self->system(), store_name); + out.metrics().init(*registry, store_name); } template @@ -99,7 +100,7 @@ public: in.heartbeat_interval(heartbeat_interval); in.connection_timeout_factor(connection_timeout); in.nack_timeout(nack_timeout); - in.metrics().init(self->system(), store_name); + in.metrics().init(*registry, store_name); } template @@ -190,7 +191,7 @@ public: result.emplace("last-seq"s, in.last_seq()); result.emplace("num-ticks"s, in.num_ticks()); if (auto out_of_order_updates = in.metrics().out_of_order_updates) - result.emplace("buffered"s, out_of_order_updates->value()); + result.emplace("buffered"s, out_of_order_updates->Value()); return result; } @@ -254,6 +255,9 @@ public: std::unordered_map attached_states; caf::flow::item_publisher out; + + /// Registry for our Prometheus metrics. + prometheus_registry_ptr registry; }; } // namespace broker::internal diff --git a/libbroker/broker/internal/with_native_labels.hh b/libbroker/broker/internal/with_native_labels.hh deleted file mode 100644 index dfe4ce9d..00000000 --- a/libbroker/broker/internal/with_native_labels.hh +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "broker/span.hh" -#include "broker/telemetry/fwd.hh" - -#include - -#include - -namespace broker::internal { - -template -auto with_native_labels(span xs, - F&& continuation) { - namespace ct = caf::telemetry; - if (xs.size() <= 10) { - ct::label_view buf[10] = { - {{}, {}}, {{}, {}}, {{}, {}}, {{}, {}}, {{}, {}}, - {{}, {}}, {{}, {}}, {{}, {}}, {{}, {}}, {{}, {}}, - }; - for (size_t index = 0; index < xs.size(); ++index) - buf[index] = ct::label_view{xs[index].first, xs[index].second}; - return continuation(span{buf, xs.size()}); - } else { - std::vector buf; - for (const auto& x : xs) - buf.emplace_back(x.first, x.second); - return continuation(span{buf}); - } -} - -template -auto with_native_labels(span xs, F&& continuation) { - if (xs.size() <= 10) { - caf::string_view buf[10]; - for (size_t index = 0; index < xs.size(); ++index) - buf[index] = xs[index]; - return continuation(span{buf, xs.size()}); - } else { - std::vector buf; - for (const auto& x : xs) - buf.emplace_back(x); - return continuation(span{buf}); - } -} - -} // namespace broker::internal diff --git a/libbroker/broker/telemetry/counter.cc b/libbroker/broker/telemetry/counter.cc deleted file mode 100644 index b180a973..00000000 --- a/libbroker/broker/telemetry/counter.cc +++ /dev/null @@ -1,79 +0,0 @@ -#include "broker/telemetry/counter.hh" - -#include "broker/internal/with_native_labels.hh" - -#include -#include -#include - -namespace ct = caf::telemetry; - -namespace broker::telemetry { - -namespace { - -auto& deref(dbl_counter_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const dbl_counter_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -auto& deref(int_counter_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const int_counter_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -auto& deref(metric_family_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -} // namespace - -void inc(dbl_counter_hdl* hdl) noexcept { - deref(hdl).inc(); -} - -void inc(dbl_counter_hdl* hdl, double amount) noexcept { - deref(hdl).inc(amount); -} - -double value(const dbl_counter_hdl* hdl) noexcept { - return deref(hdl).value(); -} - -dbl_counter_hdl* dbl_counter_get_or_add(metric_family_hdl* hdl, - span labels) { - return internal::with_native_labels(labels, [hdl](auto native_labels) { - using derived_t = ct::metric_family_impl; - auto res = static_cast(deref(hdl)).get_or_add(native_labels); - return reinterpret_cast(res); - }); -} - -int64_t inc(int_counter_hdl* hdl) noexcept { - return ++deref(hdl); -} - -void inc(int_counter_hdl* hdl, int64_t amount) noexcept { - deref(hdl).inc(amount); -} - -int64_t value(const int_counter_hdl* hdl) noexcept { - return deref(hdl).value(); -} - -int_counter_hdl* int_counter_get_or_add(metric_family_hdl* hdl, - span labels) { - return internal::with_native_labels(labels, [hdl](auto native_labels) { - using derived_t = ct::metric_family_impl; - auto res = static_cast(deref(hdl)).get_or_add(native_labels); - return reinterpret_cast(res); - }); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/counter.hh b/libbroker/broker/telemetry/counter.hh deleted file mode 100644 index 1812dd90..00000000 --- a/libbroker/broker/telemetry/counter.hh +++ /dev/null @@ -1,143 +0,0 @@ -#pragma once - -#include "broker/span.hh" -#include "broker/telemetry/fwd.hh" -#include "broker/telemetry/metric_family.hh" - -#include -#include - -namespace broker::telemetry { - -/// Increments the value of @p hdl by @p amount. -void inc(dbl_counter_hdl*, double amount) noexcept; - -/// Returns the value of @p hdl. -double value(dbl_counter_hdl*) noexcept; - -/// @pre @p hdl must be a dbl_counter family handle. -dbl_counter_hdl* dbl_counter_get_or_add(metric_family_hdl, - span labels); - -/// Increments the value of @p hdl by one. -int64_t inc(int_counter_hdl*) noexcept; - -/// Increments the value of @p hdl by @p amount. -void inc(int_counter_hdl*, int64_t amount) noexcept; - -/// Returns the value of @p hdl. -int64_t value(int_counter_hdl*) noexcept; - -/// @pre @p hdl must be an int_counter family handle. -int_counter_hdl* int_counter_get_or_add(metric_family_hdl*, - span labels); - -/// A handle to a metric that represents an single value that can only go up. -template -class counter { -public: - using handle = detail::counter_hdl_t*; - - explicit constexpr counter(handle hdl) noexcept : hdl_(hdl) { - // nop - } - - counter() = delete; - - counter(const counter&) noexcept = default; - - counter& operator=(const counter&) noexcept = default; - - /// Increments the value by 1. - void inc() noexcept { - telemetry::inc(hdl_); - } - - /// Increments the value by @p amount. - /// @pre `amount >= 0` - void inc(T amount) noexcept { - telemetry::inc(hdl_, amount); - } - - /// Increments the value by 1. - /// @return The new value. - template - std::enable_if_t, V> operator++() noexcept { - return telemetry::inc(hdl_); - } - - /// @return The current value. - T value() const noexcept { - return telemetry::value(hdl_); - } - - /// @return Whether @c this and @p other refer to the same counter. - constexpr bool is_same_as(counter other) const noexcept { - return hdl_ == other.hdl_; - } - -private: - handle hdl_; -}; - -/// @relates counter -template -constexpr bool operator==(counter x, counter y) { - return x.is_same_as(y); -} - -/// @relates counter -template -constexpr bool operator!=(counter x, counter y) { - return !(x == y); -} - -/// Manages a collection of @ref counter metrics. -template -class counter_family : public metric_family { -public: - using super = metric_family; - - using handle = detail::counter_fam_t*; - - explicit counter_family(handle hdl) noexcept : super(hdl) { - // nop - } - - counter_family(const counter_family&) noexcept = default; - - counter_family& operator=(const counter_family&) noexcept = default; - - /// Returns the metrics handle for given labels, creating a new instance - /// lazily if necessary. - counter get_or_add(span labels) { - if constexpr (std::is_same_v) - return counter{dbl_counter_get_or_add(super::hdl_, labels)}; - else - return counter{int_counter_get_or_add(super::hdl_, labels)}; - } - - /// @copydoc get_or_add - counter get_or_add(std::initializer_list labels) { - return get_or_add(span{labels.begin(), labels.size()}); - } - - /// @return Whether @c this and @p other refer to the same family. - constexpr bool is_same_as(counter_family other) const noexcept { - return hdl_ == other.hdl_; - } -}; - -/// @relates counter_family -template -constexpr bool operator==(counter_family x, counter_family y) { - return x.is_same_as(y); -} - -/// @relates counter_family -template -constexpr bool operator!=(counter_family x, counter_family y) { - return !(x == y); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/fwd.hh b/libbroker/broker/telemetry/fwd.hh deleted file mode 100644 index a5a2b6f3..00000000 --- a/libbroker/broker/telemetry/fwd.hh +++ /dev/null @@ -1,358 +0,0 @@ -#pragma once - -#include "broker/span.hh" - -#include -#include -#include - -namespace broker::telemetry { - -// -- API types ---------------------------------------------------------------- - -template -class counter; - -template -class counter_family; - -template -class gauge; - -template -class gauge_family; - -template -class histogram; - -template -class histogram_family; - -// -- convenience aliases ------------------------------------------------------ - -using label_view = std::pair; - -using const_label_list = span; - -using dbl_counter = counter; - -using dbl_counter_family = counter_family; - -using int_counter = counter; - -using int_counter_family = counter_family; - -using dbl_gauge = gauge; - -using dbl_gauge_family = gauge_family; - -using int_gauge = gauge; - -using int_gauge_family = gauge_family; - -using dbl_histogram = histogram; - -using dbl_histogram_family = histogram_family; - -using int_histogram = histogram; - -using int_histogram_family = histogram_family; - -// -- opaque handle types ------------------------------------------------------ - -class dbl_counter_family_hdl; - -class dbl_counter_hdl; - -class dbl_gauge_family_hdl; - -class dbl_gauge_hdl; - -class dbl_histogram_family_hdl; - -class dbl_histogram_hdl; - -class int_counter_family_hdl; - -class int_counter_hdl; - -class int_gauge_family_hdl; - -class int_gauge_hdl; - -class int_histogram_family_hdl; - -class int_histogram_hdl; - -class metric_family_hdl; - -// -- free function interface for counters ------------------------------------- - -void inc(dbl_counter_hdl*) noexcept; - -void inc(dbl_counter_hdl*, double amount) noexcept; - -double value(const dbl_counter_hdl*) noexcept; - -dbl_counter_hdl* dbl_counter_get_or_add(metric_family_hdl*, - span labels); - -int64_t inc(int_counter_hdl*) noexcept; - -void inc(int_counter_hdl*, int64_t amount) noexcept; - -int64_t value(const int_counter_hdl*) noexcept; - -int_counter_hdl* int_counter_get_or_add(metric_family_hdl*, - span labels); - -// -- free function interface for gauges --------------------------------------- - -void inc(dbl_gauge_hdl* hdl) noexcept; - -void inc(dbl_gauge_hdl* hdl, double amount) noexcept; - -void dec(dbl_gauge_hdl* hdl, double amount) noexcept; - -void dec(dbl_gauge_hdl* hdl) noexcept; - -double value(const dbl_gauge_hdl* hdl) noexcept; - -dbl_gauge_hdl* dbl_gauge_get_or_add(metric_family_hdl* hdl, - span labels); - -int64_t inc(int_gauge_hdl* hdl) noexcept; - -void inc(int_gauge_hdl* hdl, int64_t amount) noexcept; - -int64_t dec(int_gauge_hdl* hdl) noexcept; - -void dec(int_gauge_hdl* hdl, int64_t amount) noexcept; - -int64_t value(const int_gauge_hdl* hdl) noexcept; - -int_gauge_hdl* int_gauge_get_or_add(metric_family_hdl* hdl, - span labels); - -// -- free function interface for histograms families -------------------------- - -size_t num_buckets(const dbl_histogram_family_hdl*) noexcept; - -double upper_bound_at(const dbl_histogram_family_hdl*, size_t index) noexcept; - -size_t num_buckets(const int_histogram_family_hdl*) noexcept; - -int64_t upper_bound_at(const int_histogram_family_hdl*, size_t index) noexcept; - -// -- free function interface for histograms ----------------------------------- - -void observe(dbl_histogram_hdl*, double value) noexcept; - -double sum(const dbl_histogram_hdl*) noexcept; - -size_t num_buckets(const dbl_histogram_hdl*) noexcept; - -double count_at(const dbl_histogram_hdl*, size_t index) noexcept; - -double upper_bound_at(const dbl_histogram_hdl*, size_t index) noexcept; - -dbl_histogram_hdl* dbl_histogram_get_or_add(metric_family_hdl*, - span labels); - -void observe(int_histogram_hdl*, int64_t value) noexcept; - -int64_t sum(const int_histogram_hdl*) noexcept; - -size_t num_buckets(const int_histogram_hdl*) noexcept; - -int64_t count_at(const int_histogram_hdl*, size_t index) noexcept; - -int64_t upper_bound_at(const int_histogram_hdl*, size_t index) noexcept; - -int_histogram_hdl* int_histogram_get_or_add(metric_family_hdl*, - span labels); - -// -- free function interface for metric families ------------------------------ - -metric_family_hdl* upcast(dbl_counter_family_hdl*); - -const metric_family_hdl* upcast(const dbl_counter_family_hdl*); - -metric_family_hdl* upcast(dbl_gauge_family_hdl*); - -const metric_family_hdl* upcast(const dbl_gauge_family_hdl*); - -metric_family_hdl* upcast(dbl_histogram_family_hdl*); - -const metric_family_hdl* upcast(const dbl_histogram_family_hdl*); - -metric_family_hdl* upcast(int_counter_family_hdl*); - -const metric_family_hdl* upcast(const int_counter_family_hdl*); - -metric_family_hdl* upcast(int_gauge_family_hdl*); - -const metric_family_hdl* upcast(const int_gauge_family_hdl*); - -metric_family_hdl* upcast(int_histogram_family_hdl*); - -const metric_family_hdl* upcast(const int_histogram_family_hdl*); - -dbl_counter_family_hdl* as_dbl_counter_family(metric_family_hdl*); - -const dbl_counter_family_hdl* as_dbl_counter_family(const metric_family_hdl*); - -int_counter_family_hdl* as_int_counter_family(metric_family_hdl*); - -const int_counter_family_hdl* as_int_counter_family(const metric_family_hdl*); - -dbl_gauge_family_hdl* as_dbl_gauge_family(metric_family_hdl*); - -const dbl_gauge_family_hdl* as_dbl_gauge_family(const metric_family_hdl*); - -int_gauge_family_hdl* as_int_gauge_family(metric_family_hdl*); - -const int_gauge_family_hdl* as_int_gauge_family(const metric_family_hdl*); - -dbl_histogram_family_hdl* as_dbl_histogram_family(metric_family_hdl*); - -const dbl_histogram_family_hdl* -as_dbl_histogram_family(const metric_family_hdl*); - -int_histogram_family_hdl* as_int_histogram_family(metric_family_hdl*); - -const int_histogram_family_hdl* -as_int_histogram_family(const metric_family_hdl*); - -std::string_view prefix(const metric_family_hdl*) noexcept; - -std::string_view name(const metric_family_hdl*) noexcept; - -span label_names(const metric_family_hdl*) noexcept; - -std::string_view helptext(const metric_family_hdl*) noexcept; - -std::string_view unit(const metric_family_hdl*) noexcept; - -bool is_sum(const metric_family_hdl*) noexcept; - -// -- free function interface for the Broker metric registry ------------------- - -class metric_registry_impl; - -void intrusive_ptr_add_ref(const metric_registry_impl* ptr); - -void intrusive_ptr_release(const metric_registry_impl* ptr); - -inline void Ref(const metric_registry_impl* ptr) { - intrusive_ptr_add_ref(ptr); -} - -inline void Unref(const metric_registry_impl* ptr) { - intrusive_ptr_release(ptr); -} - -int_counter_family_hdl* -int_counter_fam(metric_registry_impl*, std::string_view pre, - std::string_view name, span labels, - std::string_view helptext, std::string_view unit, bool is_sum); - -dbl_counter_family_hdl* -dbl_counter_fam(metric_registry_impl*, std::string_view pre, - std::string_view name, span labels, - std::string_view helptext, std::string_view unit, bool is_sum); - -int_gauge_family_hdl* int_gauge_fam(metric_registry_impl*, std::string_view pre, - std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit, bool is_sum); - -dbl_gauge_family_hdl* dbl_gauge_fam(metric_registry_impl*, std::string_view pre, - std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit, bool is_sum); - -int_histogram_family_hdl* -int_histogram_fam(metric_registry_impl*, std::string_view pre, - std::string_view name, span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum); - -dbl_histogram_family_hdl* -dbl_histogram_fam(metric_registry_impl*, std::string_view pre, - std::string_view name, span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum); - -} // namespace broker::telemetry - -// -- opaque types by value type lookup ---------------------------------------- - -namespace broker::detail { - -template -struct counter_hdl_oracle; - -template <> -struct counter_hdl_oracle { - using fam_type = telemetry::dbl_counter_family_hdl; - using hdl_type = telemetry::dbl_counter_hdl; -}; - -template <> -struct counter_hdl_oracle { - using fam_type = telemetry::int_counter_family_hdl; - using hdl_type = telemetry::int_counter_hdl; -}; - -template -using counter_fam_t = typename counter_hdl_oracle::fam_type; - -template -using counter_hdl_t = typename counter_hdl_oracle::hdl_type; - -template -struct gauge_hdl_oracle; - -template <> -struct gauge_hdl_oracle { - using fam_type = telemetry::dbl_gauge_family_hdl; - using hdl_type = telemetry::dbl_gauge_hdl; -}; - -template <> -struct gauge_hdl_oracle { - using fam_type = telemetry::int_gauge_family_hdl; - using hdl_type = telemetry::int_gauge_hdl; -}; - -template -using gauge_fam_t = typename gauge_hdl_oracle::fam_type; - -template -using gauge_hdl_t = typename gauge_hdl_oracle::hdl_type; - -template -struct histogram_hdl_oracle; - -template <> -struct histogram_hdl_oracle { - using fam_type = telemetry::dbl_histogram_family_hdl; - using hdl_type = telemetry::dbl_histogram_hdl; -}; - -template <> -struct histogram_hdl_oracle { - using fam_type = telemetry::int_histogram_family_hdl; - using hdl_type = telemetry::int_histogram_hdl; -}; - -template -using histogram_fam_t = typename histogram_hdl_oracle::fam_type; - -template -using histogram_hdl_t = typename histogram_hdl_oracle::hdl_type; - -} // namespace broker::detail diff --git a/libbroker/broker/telemetry/gauge.cc b/libbroker/broker/telemetry/gauge.cc deleted file mode 100644 index 1f364c17..00000000 --- a/libbroker/broker/telemetry/gauge.cc +++ /dev/null @@ -1,94 +0,0 @@ -#include "broker/telemetry/gauge.hh" - -#include "broker/internal/with_native_labels.hh" - -#include -#include -#include - -namespace ct = caf::telemetry; - -namespace broker::telemetry { - -namespace { - -auto& deref(dbl_gauge_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const dbl_gauge_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -auto& deref(int_gauge_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const int_gauge_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -auto& deref(metric_family_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -} // namespace - -void inc(dbl_gauge_hdl* hdl) noexcept { - deref(hdl).inc(); -} - -void inc(dbl_gauge_hdl* hdl, double amount) noexcept { - deref(hdl).inc(amount); -} - -void dec(dbl_gauge_hdl* hdl) noexcept { - deref(hdl).dec(); -} -void dec(dbl_gauge_hdl* hdl, double amount) noexcept { - deref(hdl).dec(amount); -} - -double value(const dbl_gauge_hdl* hdl) noexcept { - return deref(hdl).value(); -} - -dbl_gauge_hdl* dbl_gauge_get_or_add(metric_family_hdl* hdl, - span labels) { - return internal::with_native_labels(labels, [hdl](auto native_labels) { - using derived_t = ct::metric_family_impl; - auto res = static_cast(deref(hdl)).get_or_add(native_labels); - return reinterpret_cast(res); - }); -} - -int64_t inc(int_gauge_hdl* hdl) noexcept { - return ++deref(hdl); -} - -void inc(int_gauge_hdl* hdl, int64_t amount) noexcept { - deref(hdl).inc(amount); -} - -int64_t dec(int_gauge_hdl* hdl) noexcept { - return --deref(hdl); -} - -void dec(int_gauge_hdl* hdl, int64_t amount) noexcept { - deref(hdl).dec(amount); -} - -int64_t value(const int_gauge_hdl* hdl) noexcept { - return deref(hdl).value(); -} - -int_gauge_hdl* int_gauge_get_or_add(metric_family_hdl* hdl, - span labels) { - return internal::with_native_labels(labels, [hdl](auto native_labels) { - using derived_t = ct::metric_family_impl; - auto res = static_cast(deref(hdl)).get_or_add(native_labels); - return reinterpret_cast(res); - }); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/gauge.hh b/libbroker/broker/telemetry/gauge.hh deleted file mode 100644 index 601958e0..00000000 --- a/libbroker/broker/telemetry/gauge.hh +++ /dev/null @@ -1,166 +0,0 @@ -#pragma once - -#include "broker/span.hh" -#include "broker/telemetry/fwd.hh" -#include "broker/telemetry/metric_family.hh" - -#include -#include - -namespace broker::telemetry { - -/// Increments the value of @p hdl by @p amount. -void inc(telemetry::dbl_gauge_hdl* hdl, double amount) noexcept; - -/// Decrements the value of @p hdl by @p amount. -void dec(dbl_gauge_hdl* hdl, double amount) noexcept; - -/// Returns the value of @p hdl. -double value(dbl_gauge_hdl* hdl) noexcept; - -/// @pre @p hdl must be a dbl_gauge family handle. -dbl_gauge_hdl* dbl_gauge_get_or_add(metric_family_hdl* hdl, - span labels); - -/// Increments the value of @p hdl by one. -int64_t inc(int_gauge_hdl* hdl) noexcept; - -/// Increments the value of @p hdl by @p amount. -void inc(int_gauge_hdl* hdl, int64_t amount) noexcept; - -/// Decrements the value of @p hdl by one. -int64_t dec(int_gauge_hdl* hdl) noexcept; - -/// Decrements the value of @p hdl by @p amount. -void dec(int_gauge_hdl* hdl, int64_t amount) noexcept; - -/// Returns the value of @p hdl. -int64_t value(int_gauge_hdl* hdl) noexcept; - -/// @pre @p hdl must be an int_gauge family handle. -int_gauge_hdl* int_gauge_get_or_add(metric_family_hdl* hdl, - span labels); - -/// A handle to a metric that represents an single value. Wraps an opaque gauge -/// handle to provide a class-based interface. -template -class gauge { -public: - using handle = detail::gauge_hdl_t*; - - explicit constexpr gauge(handle hdl) noexcept : hdl_(hdl) { - // nop - } - - gauge() = delete; - - gauge(const gauge&) noexcept = default; - - gauge& operator=(const gauge&) noexcept = default; - - /// Increments the value by 1. - void inc() noexcept { - telemetry::inc(hdl_); - } - - /// Increments the value by @p amount. - /// @pre `amount >= 0` - void inc(T amount) noexcept { - telemetry::inc(hdl_, amount); - } - - /// Increments the value by 1. - /// @return The new value. - template - std::enable_if_t, V> operator++() noexcept { - return telemetry::inc(hdl_); - } - - /// Decrements the value by 1. - void dec() noexcept; - - /// Decrements the value by @p amount. - void dec(int64_t amount) noexcept; - - /// Decrements the value by 1. - /// @return The new value. - template - std::enable_if_t, V> operator--() noexcept { - return telemetry::dec(hdl_); - } - - /// @return The current value. - T value() const noexcept { - return telemetry::value(hdl_); - } - - /// @return Whether @c this and @p other refer to the same gauge. - constexpr bool is_same_as(gauge other) const noexcept { - return hdl_ == other.hdl_; - } - -private: - handle hdl_; -}; - -/// @relates gauge -template -constexpr bool operator==(gauge x, gauge y) { - return x.is_same_as(y); -} - -/// @relates gauge -template -constexpr bool operator!=(gauge x, gauge y) { - return !(x == y); -} - -/// Manages a collection of @ref gauge metrics. -template -class gauge_family : public metric_family { -public: - using super = metric_family; - - using handle = detail::gauge_fam_t*; - - explicit gauge_family(handle hdl) noexcept : super(hdl) { - // nop - } - - gauge_family(const gauge_family&) noexcept = default; - - gauge_family& operator=(const gauge_family&) noexcept = default; - - /// Returns the metrics handle for given labels, creating a new instance - /// lazily if necessary. - gauge get_or_add(span labels) { - if constexpr (std::is_same_v) - return gauge{dbl_gauge_get_or_add(super::hdl_, labels)}; - else - return gauge{int_gauge_get_or_add(super::hdl_, labels)}; - } - - /// @copydoc get_or_add - gauge get_or_add(std::initializer_list labels) { - return get_or_add(span{labels.begin(), labels.size()}); - } - - /// @return Whether @c this and @p other refer to the same family. - constexpr bool is_same_as(gauge_family other) const noexcept { - return hdl_ == other.hdl_; - } -}; - -/// @relates gauge_family -template -constexpr bool operator==(gauge_family x, gauge_family y) { - return x.is_same_as(y); -} - -/// @relates gauge_family -template -constexpr bool operator!=(gauge_family x, gauge_family y) { - return !(x == y); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/histogram.cc b/libbroker/broker/telemetry/histogram.cc deleted file mode 100644 index 350583dd..00000000 --- a/libbroker/broker/telemetry/histogram.cc +++ /dev/null @@ -1,144 +0,0 @@ -#include "broker/telemetry/histogram.hh" - -#include "broker/detail/assert.hh" -#include "broker/internal/with_native_labels.hh" - -#include -#include -#include - -#include - -namespace ct = caf::telemetry; - -namespace broker::telemetry { - -namespace { - -auto& deref(dbl_histogram_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const dbl_histogram_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -auto& deref(int_histogram_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const int_histogram_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -auto& deref(metric_family_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -const auto& deref(const dbl_histogram_family_hdl* hdl) { - using native_type = ct::metric_family_impl; - return *reinterpret_cast(hdl); -} - -const auto& deref(const int_histogram_family_hdl* hdl) { - using native_type = ct::metric_family_impl; - return *reinterpret_cast(hdl); -} - -} // namespace - -// -- free function interface for histograms families -------------------------- - -size_t num_buckets(const dbl_histogram_family_hdl* hdl) noexcept { - // +1 for the implicit "Infinite" bucket. - return deref(hdl).extra_setting().size() + 1; -} - -double upper_bound_at(const dbl_histogram_family_hdl* hdl, - size_t index) noexcept { - using limits = std::numeric_limits; - const auto& xs = deref(hdl).extra_setting(); - return index < xs.size() ? xs[index] : limits::infinity(); -} - -size_t num_buckets(const int_histogram_family_hdl* hdl) noexcept { - // +1 for the implicit "Infinite" bucket. - return deref(hdl).extra_setting().size() + 1; -} - -int64_t upper_bound_at(const int_histogram_family_hdl* hdl, - size_t index) noexcept { - using limits = std::numeric_limits; - const auto& xs = deref(hdl).extra_setting(); - return index < xs.size() ? xs[index] : limits::max(); -} - -// -- free function interface for histograms instances ------------------------- - -void observe(dbl_histogram_hdl* hdl, double value) noexcept { - deref(hdl).observe(value); -} - -double sum(const dbl_histogram_hdl* hdl) noexcept { - return deref(hdl).sum(); -} - -size_t num_buckets(const dbl_histogram_hdl* hdl) noexcept { - return deref(hdl).buckets().size(); -} - -double count_at(const dbl_histogram_hdl* hdl, size_t index) noexcept { - auto xs = deref(hdl).buckets(); - BROKER_ASSERT(index < xs.size()); - return static_cast(xs[index].count.value()); -} - -double upper_bound_at(const dbl_histogram_hdl* hdl, size_t index) noexcept { - auto xs = deref(hdl).buckets(); - BROKER_ASSERT(index < xs.size()); - return xs[index].upper_bound; -} - -dbl_histogram_hdl* dbl_histogram_get_or_add(metric_family_hdl* hdl, - span xs) { - return internal::with_native_labels(xs, [hdl](auto native_labels) { - using derived_t = ct::metric_family_impl; - auto res = static_cast(deref(hdl)).get_or_add(native_labels); - return reinterpret_cast(res); - }); -} - -void observe(int_histogram_hdl* hdl, int64_t value) noexcept { - deref(hdl).observe(value); -} - -int64_t sum(const int_histogram_hdl* hdl) noexcept { - return deref(hdl).sum(); -} - -size_t num_buckets(const int_histogram_hdl* hdl) noexcept { - return deref(hdl).buckets().size(); -} - -int64_t count_at(const int_histogram_hdl* hdl, size_t index) noexcept { - auto xs = deref(hdl).buckets(); - BROKER_ASSERT(index < xs.size()); - return xs[index].count.value(); -} - -int64_t upper_bound_at(const int_histogram_hdl* hdl, size_t index) noexcept { - auto xs = deref(hdl).buckets(); - BROKER_ASSERT(index < xs.size()); - return xs[index].upper_bound; -} - -int_histogram_hdl* int_histogram_get_or_add(metric_family_hdl* hdl, - span xs) { - return internal::with_native_labels(xs, [hdl](auto native_labels) { - using derived_t = ct::metric_family_impl; - auto res = static_cast(deref(hdl)).get_or_add(native_labels); - return reinterpret_cast(res); - }); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/histogram.hh b/libbroker/broker/telemetry/histogram.hh deleted file mode 100644 index 811b27d6..00000000 --- a/libbroker/broker/telemetry/histogram.hh +++ /dev/null @@ -1,145 +0,0 @@ -#pragma once - -#include "broker/span.hh" -#include "broker/telemetry/fwd.hh" -#include "broker/telemetry/metric_family.hh" - -#include - -namespace broker::telemetry { - -/// A handle to a metric that represents an single value that can only go up. -template -class histogram { -public: - using handle = detail::histogram_hdl_t*; - - explicit constexpr histogram(handle hdl) noexcept : hdl_(hdl) { - // nop - } - - histogram() = delete; - - histogram(const histogram&) noexcept = default; - - histogram& operator=(const histogram&) noexcept = default; - - /// Increments all buckets with an upper bound less than or equal to @p value - /// by one and adds @p value to the total sum of all observed values. - void observe(T value) noexcept { - return telemetry::observe(hdl_, value); - } - - /// @return The sum of all observed values. - T sum() const noexcept { - return telemetry::sum(hdl_); - } - - /// @return The number of buckets, including the implicit "infinite" bucket. - size_t num_buckets() const noexcept { - return telemetry::num_buckets(hdl_); - } - - /// @return The number of observations in the bucket at @p index. - /// @pre index < NumBuckets() - T count_at(size_t index) const noexcept { - return telemetry::count_at(hdl_); - } - - /// @return The upper bound of the bucket at @p index. - /// @pre index < NumBuckets() - T upper_bound_at(size_t index) const noexcept { - return telemetry::upper_bound_at(hdl_, index); - } - - /// @return Whether @c this and @p other refer to the same histogram. - constexpr bool is_same_as(histogram other) const noexcept { - return hdl_ == other.hdl_; - } - -private: - handle hdl_; -}; - -/// @relates histogram -template -constexpr bool operator==(histogram x, histogram y) { - return x.is_same_as(y); -} - -/// @relates histogram -template -constexpr bool operator!=(histogram x, histogram y) { - return !(x == y); -} - -/// Manages a collection of @ref histogram metrics. -template -class histogram_family : public metric_family { -public: - using super = metric_family; - - using handle = detail::histogram_fam_t*; - using const_handle = const detail::histogram_fam_t*; - - explicit histogram_family(handle hdl) noexcept : super(hdl) { - // nop - } - - histogram_family(const histogram_family&) noexcept = default; - - histogram_family& operator=(const histogram_family&) noexcept = default; - - /// Returns the metrics handle for given labels, creating a new instance - /// lazily if necessary. - histogram get_or_add(span labels) { - if constexpr (std::is_same_v) - return histogram{dbl_histogram_get_or_add(super::hdl_, labels)}; - else - return histogram{int_histogram_get_or_add(super::hdl_, labels)}; - } - - /// @copydoc get_or_add - histogram get_or_add(std::initializer_list labels) { - return get_or_add(span{labels.begin(), labels.size()}); - } - - /// @return Whether @c this and @p other refer to the same family. - constexpr bool is_same_as(histogram_family other) const noexcept { - return hdl_ == other.hdl_; - } - - /// @return The number of buckets, including the implicit "infinite" bucket. - size_t num_buckets() const noexcept { - return telemetry::num_buckets(hdl()); - } - - /// @return The upper bound of the bucket at @p index. - /// @pre index < NumBuckets() - T upper_bound_at(size_t index) const noexcept { - return telemetry::upper_bound_at(hdl(), index); - } - -private: - const_handle hdl() const { - if constexpr (std::is_same_v) { - return as_dbl_histogram_family(super::hdl_); - } else { - return as_int_histogram_family(super::hdl_); - } - } -}; - -/// @relates histogram_family -template -constexpr bool operator==(histogram_family x, histogram_family y) { - return x.is_same_as(y); -} - -/// @relates histogram_family -template -constexpr bool operator!=(histogram_family x, histogram_family y) { - return !(x == y); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/histogram.test.cc b/libbroker/broker/telemetry/histogram.test.cc deleted file mode 100644 index 3859a7c1..00000000 --- a/libbroker/broker/telemetry/histogram.test.cc +++ /dev/null @@ -1,85 +0,0 @@ -#include "broker/telemetry/histogram.hh" - -#include "broker/broker-test.test.hh" - -#include "broker/telemetry/metric_registry.hh" - -#include -#include - -using namespace broker; -using namespace std::literals; - -namespace { - -template -auto to_vector(Container xs) { - using value_type = std::remove_const_t; - return std::vector(xs.begin(), xs.end()); -} - -} // namespace - -SCENARIO("telemetry managers provide access to histogram families") { - GIVEN("a telemetry registry") { - auto reg = telemetry::metric_registry::pre_init_instance(); - WHEN("retrieving an int_histogram family") { - int64_t buckets[] = {10, 20}; - auto family = reg.histogram_family("broker", "payload-size", {"protocol"}, - buckets, "test", "bytes"); - THEN("the family object stores the parameters") { - CHECK_EQ(family.prefix(), "broker"sv); - CHECK_EQ(family.name(), "payload-size"sv); - CHECK_EQ(to_vector(family.label_names()), std::vector{"protocol"s}); - CHECK_EQ(family.helptext(), "test"sv); - CHECK_EQ(family.unit(), "bytes"sv); - CHECK_EQ(family.is_sum(), false); - if (CHECK_EQ(family.num_buckets(), 3u)) { - using limits = std::numeric_limits; - CHECK_EQ(family.upper_bound_at(0), 10); - CHECK_EQ(family.upper_bound_at(1), 20); - CHECK_EQ(family.upper_bound_at(2), limits::max()); - } - } - AND_THEN("get_or_add returns the same metric for the same labels") { - auto first = family.get_or_add({{"protocol", "tcp"}}); - auto second = family.get_or_add({{"protocol", "tcp"}}); - CHECK_EQ(first, second); - } - AND_THEN("get_or_add returns different metric for the disjoint labels") { - auto first = family.get_or_add({{"protocol", "tcp"}}); - auto second = family.get_or_add({{"protocol", "udp"}}); - CHECK_NE(first, second); - } - } - WHEN("retrieving a dbl_histogram family") { - double buckets[] = {10.0, 20.0}; - auto family = reg.histogram_family("broker", "parse-time", - {"protocol"}, buckets, "test", - "seconds"); - THEN("the family object stores the parameters") { - CHECK_EQ(family.prefix(), "broker"sv); - CHECK_EQ(family.name(), "parse-time"sv); - CHECK_EQ(to_vector(family.label_names()), std::vector{"protocol"s}); - CHECK_EQ(family.helptext(), "test"sv); - CHECK_EQ(family.unit(), "seconds"sv); - CHECK_EQ(family.is_sum(), false); - if (CHECK_EQ(family.num_buckets(), 3u)) { - CHECK_EQ(family.upper_bound_at(0), 10.0); - CHECK_EQ(family.upper_bound_at(1), 20.0); - CHECK(std::isinf(family.upper_bound_at(2))); - } - } - AND_THEN("get_or_add returns the same metric for the same labels") { - auto first = family.get_or_add({{"protocol", "tcp"}}); - auto second = family.get_or_add({{"protocol", "tcp"}}); - CHECK_EQ(first, second); - } - AND_THEN("get_or_add returns different metric for the disjoint labels") { - auto first = family.get_or_add({{"protocol", "tcp"}}); - auto second = family.get_or_add({{"protocol", "udp"}}); - CHECK_NE(first, second); - } - } - } -} diff --git a/libbroker/broker/telemetry/metric_family.cc b/libbroker/broker/telemetry/metric_family.cc deleted file mode 100644 index eb4d8490..00000000 --- a/libbroker/broker/telemetry/metric_family.cc +++ /dev/null @@ -1,132 +0,0 @@ -#include "broker/telemetry/metric_family.hh" - -#include -#include -#include -#include -#include - -namespace ct = caf::telemetry; - -namespace broker::telemetry { - -namespace { - -template -struct native_hdl_oracle; - -// Generates the implementation for a `native_hdl_oracle` specialization. -#define GEN_NATIVE_HDL_ORACLE(BrokerType, NativeType) \ - template <> \ - struct native_hdl_oracle { \ - using type = NativeType; \ - }; - -GEN_NATIVE_HDL_ORACLE(dbl_counter_family_hdl, - ct::metric_family_impl) - -GEN_NATIVE_HDL_ORACLE(dbl_gauge_family_hdl, - ct::metric_family_impl) - -GEN_NATIVE_HDL_ORACLE(dbl_histogram_family_hdl, - ct::metric_family_impl) - -GEN_NATIVE_HDL_ORACLE(int_counter_family_hdl, - ct::metric_family_impl) - -GEN_NATIVE_HDL_ORACLE(int_gauge_family_hdl, - ct::metric_family_impl) - -GEN_NATIVE_HDL_ORACLE(int_histogram_family_hdl, - ct::metric_family_impl) - -template -using native_hdl_t = typename native_hdl_oracle::type; - -auto& deref(const metric_family_hdl* hdl) { - return *reinterpret_cast(hdl); -} - -template -metric_family_hdl* upcast_impl(T* ptr) { - using native_ptr_t = native_hdl_t*; - ct::metric_family* native_ptr = reinterpret_cast(ptr); - return reinterpret_cast(native_ptr); -} - -template -const metric_family_hdl* upcast_impl(const T* ptr) { - using native_ptr_t = const native_hdl_t*; - const ct::metric_family* native_ptr = reinterpret_cast(ptr); - return reinterpret_cast(native_ptr); -} - -template -T* downcast_impl(metric_family_hdl* ptr) { - auto native_ptr = reinterpret_cast(ptr); - auto native_dptr = static_cast*>(native_ptr); - return reinterpret_cast(native_dptr); -} - -template -const T* downcast_impl(const metric_family_hdl* ptr) { - auto native_ptr = reinterpret_cast(ptr); - auto native_dptr = static_cast*>(native_ptr); - return reinterpret_cast(native_dptr); -} - -} // namespace - -// Generates the implementations (const and non-const) for the `upcast` -// overloads and for the `as_...` overloads. -#define GEN_CASTS(BrokerType) \ - metric_family_hdl* upcast(BrokerType##_hdl* ptr) { \ - return upcast_impl(ptr); \ - } \ - const metric_family_hdl* upcast(const BrokerType##_hdl* ptr) { \ - return upcast_impl(ptr); \ - } \ - BrokerType##_hdl* as_##BrokerType(metric_family_hdl* ptr) { \ - return downcast_impl(ptr); \ - } \ - const BrokerType##_hdl* as_##BrokerType(const metric_family_hdl* ptr) { \ - return downcast_impl(ptr); \ - } - -GEN_CASTS(dbl_counter_family) - -GEN_CASTS(dbl_gauge_family) - -GEN_CASTS(dbl_histogram_family) - -GEN_CASTS(int_counter_family) - -GEN_CASTS(int_gauge_family) - -GEN_CASTS(int_histogram_family) - -std::string_view prefix(const metric_family_hdl* hdl) noexcept { - return deref(hdl).prefix(); -} - -std::string_view name(const metric_family_hdl* hdl) noexcept { - return deref(hdl).name(); -} - -span label_names(const metric_family_hdl* hdl) noexcept { - return deref(hdl).label_names(); -} - -std::string_view helptext(const metric_family_hdl* hdl) noexcept { - return deref(hdl).helptext(); -} - -std::string_view unit(const metric_family_hdl* hdl) noexcept { - return deref(hdl).unit(); -} - -bool is_sum(const metric_family_hdl* hdl) noexcept { - return deref(hdl).is_sum(); -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/metric_family.hh b/libbroker/broker/telemetry/metric_family.hh deleted file mode 100644 index 31fb8c66..00000000 --- a/libbroker/broker/telemetry/metric_family.hh +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "broker/span.hh" -#include "broker/telemetry/fwd.hh" - -#include - -namespace broker::telemetry { - -/// Manages a collection (family) of metrics. All members of the family share -/// the same prefix (namespace), name, and label dimensions. -class metric_family { -public: - metric_family() = delete; - metric_family(const metric_family&) noexcept = default; - metric_family& operator=(const metric_family&) noexcept = default; - - /// @return The prefix (namespace) this family belongs to. Builtin metrics - /// of Zeek return @c zeek. Custom metrics, e.g., created in a - /// script, may use a prefix that represents the application/script - /// or protocol (e.g. @c http) name. - std::string_view prefix() const noexcept { - return telemetry::prefix(hdl_); - } - - /// @return The human-readable name of the metric, e.g., @p open-connections. - std::string_view name() const noexcept { - return telemetry::name(hdl_); - } - - /// @return The names for all label dimensions. - span label_names() const noexcept { - return telemetry::label_names(hdl_); - } - - /// @return A short explanation of the metric. - std::string_view helptext() const noexcept { - return telemetry::helptext(hdl_); - } - - /// @return The unit of measurement, preferably a base unit such as @c bytes - /// or @c seconds. Dimensionless counts return the pseudo-unit @c 1. - std::string_view unit() const noexcept { - return telemetry::unit(hdl_); - } - - /// @return Whether metrics of this family accumulate values, where only the - /// total value is of interest. For example, the total number of - /// HTTP requests. - bool is_sum() const noexcept { - return telemetry::is_sum(hdl_); - } - -protected: - /// @pre `hdl != nullptr` - template - constexpr explicit metric_family(Derived* hdl) noexcept : hdl_(upcast(hdl)) { - // nop - } - - metric_family_hdl* hdl_; -}; - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/metric_registry.cc b/libbroker/broker/telemetry/metric_registry.cc deleted file mode 100644 index 3a44656d..00000000 --- a/libbroker/broker/telemetry/metric_registry.cc +++ /dev/null @@ -1,293 +0,0 @@ -#include - -#include "broker/telemetry/metric_registry.hh" - -#include "broker/internal/endpoint_access.hh" -#include "broker/internal/with_native_labels.hh" - -#include -#include -#include -#include - -namespace ct = caf::telemetry; - -namespace broker::telemetry { - -// -- free function interface for the Broker metric registry ------------------- - -void intrusive_ptr_add_ref(const metric_registry_impl* ptr) { - ptr->ref(); -} - -void intrusive_ptr_release(const metric_registry_impl* ptr) { - ptr->deref(); -} - -int_counter_family_hdl* -int_counter_fam(metric_registry_impl* impl, std::string_view pre, - std::string_view name, span labels, - std::string_view helptext, std::string_view unit, bool is_sum) { - return impl->int_counter_fam(pre, name, labels, helptext, unit, is_sum); -} - -dbl_counter_family_hdl* -dbl_counter_fam(metric_registry_impl* impl, std::string_view pre, - std::string_view name, span labels, - std::string_view helptext, std::string_view unit, bool is_sum) { - return impl->dbl_counter_fam(pre, name, labels, helptext, unit, is_sum); -} - -int_gauge_family_hdl* int_gauge_fam(metric_registry_impl* impl, - std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit, bool is_sum) { - return impl->int_gauge_fam(pre, name, labels, helptext, unit, is_sum); -} - -dbl_gauge_family_hdl* dbl_gauge_fam(metric_registry_impl* impl, - std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit, bool is_sum) { - return impl->dbl_gauge_fam(pre, name, labels, helptext, unit, is_sum); -} - -int_histogram_family_hdl* -int_histogram_fam(metric_registry_impl* impl, std::string_view pre, - std::string_view name, span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum) { - return impl->int_histogram_fam(pre, name, labels, ubounds, helptext, unit, - is_sum); -} - -dbl_histogram_family_hdl* -dbl_histogram_fam(metric_registry_impl* impl, std::string_view pre, - std::string_view name, span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum) { - return impl->dbl_histogram_fam(pre, name, labels, ubounds, helptext, unit, - is_sum); -} - -// -- member functions of metric_registry -------------------------------------- - -metric_registry::metric_registry(metric_registry_impl* impl, - bool add_ref) noexcept - : impl_(impl) { - if (impl_ && add_ref) - impl_->ref(); -} - -metric_registry::metric_registry(metric_registry&& other) noexcept - : impl_(other.impl_) { - other.impl_ = nullptr; -} - -metric_registry::metric_registry(const metric_registry& other) noexcept - : metric_registry(other.impl_, true) {} - -metric_registry& metric_registry::operator=(metric_registry&& other) noexcept { - std::swap(impl_, other.impl_); - return *this; -} - -metric_registry& -metric_registry::operator=(const metric_registry& other) noexcept { - metric_registry tmp{other}; - std::swap(impl_, tmp.impl_); - return *this; -} - -metric_registry::~metric_registry() { - if (impl_) - impl_->deref(); -} - -namespace { - -// -- free functions used by impl_base::collect() - -void extract_labels(const ct::metric* instance, std::vector& vec) { - auto get_value = [](const auto& label) -> label_view { - auto name = label.name(); - auto val = label.value(); - return {std::string_view{name.data(), name.size()}, - std::string_view{val.data(), val.size()}}; - }; - const auto& labels = instance->labels(); - std::transform(labels.begin(), labels.end(), std::back_inserter(vec), - get_value); -} - -const auto* opaque(const ct::metric_family* family) { - return reinterpret_cast(family); -} - -#define OPAQUE_OBJ(type) \ - const auto* opaque(const ct::type* obj) { \ - return reinterpret_cast(obj); \ - } - -OPAQUE_OBJ(dbl_counter) -OPAQUE_OBJ(int_counter) -OPAQUE_OBJ(dbl_gauge) -OPAQUE_OBJ(int_gauge) -OPAQUE_OBJ(dbl_histogram) -OPAQUE_OBJ(int_histogram) - -// -- impl_base - -class impl_base : public metric_registry_impl { -public: - explicit impl_base(ct::metric_registry* reg) : reg_(reg) { - // nop - } - - int_counter_family_hdl* int_counter_fam(std::string_view pre, - std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit, - bool is_sum) override { - return internal::with_native_labels(labels, [=](auto xs) { - auto ptr = reg_->counter_family(pre, name, xs, helptext, unit, is_sum); - return reinterpret_cast(ptr); - }); - } - - dbl_counter_family_hdl* dbl_counter_fam(std::string_view pre, - std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit, - bool is_sum) override { - return internal::with_native_labels(labels, [=](auto xs) { - auto ptr = reg_->counter_family(pre, name, xs, helptext, unit, - is_sum); - return reinterpret_cast(ptr); - }); - } - - int_gauge_family_hdl* - int_gauge_fam(std::string_view pre, std::string_view name, - span labels, std::string_view helptext, - std::string_view unit, bool is_sum) override { - return internal::with_native_labels(labels, [=](auto xs) { - auto ptr = reg_->gauge_family(pre, name, xs, helptext, unit, is_sum); - return reinterpret_cast(ptr); - }); - } - - dbl_gauge_family_hdl* - dbl_gauge_fam(std::string_view pre, std::string_view name, - span labels, std::string_view helptext, - std::string_view unit, bool is_sum) override { - return internal::with_native_labels(labels, [=](auto xs) { - auto ptr = reg_->gauge_family(pre, name, xs, helptext, unit, - is_sum); - return reinterpret_cast(ptr); - }); - } - - int_histogram_family_hdl* - int_histogram_fam(std::string_view pre, std::string_view name, - span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum) override { - return internal::with_native_labels(labels, [=](auto xs) { - auto bounds = caf::span{ubounds.data(), ubounds.size()}; - auto ptr = reg_->histogram_family(pre, name, xs, bounds, helptext, unit, - is_sum); - return reinterpret_cast(ptr); - }); - } - - dbl_histogram_family_hdl* - dbl_histogram_fam(std::string_view pre, std::string_view name, - span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum) override { - return internal::with_native_labels(labels, [=](auto xs) { - auto bounds = caf::span{ubounds.data(), ubounds.size()}; - auto ptr = reg_->histogram_family(pre, name, xs, bounds, helptext, - unit, is_sum); - return reinterpret_cast(ptr); - }); - } - - void collect(metrics_collector& collector) override { - std::vector labels_vec; // Reuse for label extraction - auto fn = [&collector, &labels_vec](const ct::metric_family* family, - const ct::metric* instance, - const auto* obj) { - labels_vec.clear(); - extract_labels(instance, labels_vec); - collector(opaque(family), opaque(obj), labels_vec); - }; - - reg_->collect(fn); - } - -protected: - ct::metric_registry* reg_; -}; - -// Metrics access before initializing the actor system. -class pre_init_impl : public impl_base { -public: - using super = impl_base; - - pre_init_impl() : super(&tmp_) { - // nop - } - - bool merge(endpoint& where) override { - auto& sys = internal::endpoint_access{&where}.sys(); - sys.metrics().merge(tmp_); - return true; - } - -private: - ct::metric_registry tmp_; -}; - -// Metrics access after initializing the actor system. -class post_init_impl : public impl_base { -public: - using super = impl_base; - - post_init_impl(internal::endpoint_context_ptr ctx) - : super(std::addressof(ctx->sys.metrics())), ctx_(std::move(ctx)) { - // nop - } - - bool merge(endpoint&) override { - return false; - } - - internal::endpoint_context_ptr ctx_; -}; - -} // namespace - -metric_registry metric_registry::pre_init_instance() { - return metric_registry{new pre_init_impl, false}; -} - -metric_registry metric_registry::merge(metric_registry what, - broker::endpoint& where) { - if (what.impl_->merge(where)) { - return from(where); - } - return what; -} - -metric_registry metric_registry::from(broker::endpoint& where) { - auto ctx = internal::endpoint_access{&where}.ctx(); - return metric_registry{new post_init_impl(std::move(ctx)), false}; -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/metric_registry.hh b/libbroker/broker/telemetry/metric_registry.hh deleted file mode 100644 index 4b2e6fd5..00000000 --- a/libbroker/broker/telemetry/metric_registry.hh +++ /dev/null @@ -1,393 +0,0 @@ -#pragma once - -#include "broker/fwd.hh" -#include "broker/span.hh" -#include "broker/telemetry/counter.hh" -#include "broker/telemetry/gauge.hh" -#include "broker/telemetry/histogram.hh" -#include "broker/telemetry/metric_registry_impl.hh" - -#include -#include -#include -#include -#include - -namespace broker::telemetry { - -/// Provides access to families. -class metric_registry { -public: - metric_registry() = delete; - - metric_registry(metric_registry&&) noexcept; - - metric_registry(const metric_registry&) noexcept; - - metric_registry& operator=(metric_registry&&) noexcept; - - metric_registry& operator=(const metric_registry&) noexcept; - - explicit metric_registry(metric_registry_impl* impl) noexcept - : metric_registry(impl, true) { - // nop - } - - ~metric_registry(); - - /// Returns a metric registry handle that can be used to get access to metrics - /// even before constructing the @ref endpoint. - static metric_registry pre_init_instance(); - - /// Merges all metrics from @p what into the endpoint @p where. - /// @warning invalidates all other handles to the same pre-init registry! - /// @return A handle to the registry of the endpoint. - static metric_registry merge(metric_registry what, broker::endpoint& where); - - /// @return A handle to the registry of the endpoint. - static metric_registry from(broker::endpoint& where); - - /// @return A counter metric family. Creates the family lazily if necessary. - /// @param pre The prefix (namespace) this family belongs to. - /// @param name The human-readable name of the metric, e.g., `requests`. - /// @param labels Names for all label dimensions of the metric. - /// @param helptext Short explanation of the metric. - /// @param unit Unit of measurement. - /// @param is_sum Indicates whether this metric accumulates something, where - /// only the total value is of interest. - template - auto counter_family(std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, std::string_view unit = "1", - bool is_sum = false) { - if constexpr (std::is_same_v) { - return impl_->int_counter_fam(pre, name, labels, helptext, unit, is_sum); - } else { - static_assert(std::is_same_v, - "metrics only support int64_t and double values"); - return impl_->dbl_counter_fam(pre, name, labels, helptext, unit, is_sum); - } - } - - /// @copydoc counter_family - template - auto counter_family(std::string_view pre, std::string_view name, - std::initializer_list labels, - std::string_view helptext, std::string_view unit = "1", - bool is_sum = false) { - auto lbl_span = span{labels.begin(), labels.size()}; - return counter_family(pre, name, lbl_span, helptext, unit, is_sum); - } - - /** - * Accesses a counter instance. Creates the hosting metric family as well - * as the counter lazily if necessary. - * @param pre The prefix (namespace) this family belongs to. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param labels Values for all label dimensions of the metric. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. - * @param is_sum Indicates whether this metric accumulates something, where - * only the total value is of interest. - */ - template - counter - counter_instance(std::string_view pre, std::string_view name, - span labels, std::string_view helptext, - std::string_view unit = "1", bool is_sum = false) { - return with_label_names(labels, [&, this](auto labelNames) { - auto family = counter_family(pre, name, labelNames, helptext, unit, - is_sum); - return family.getOrAdd(labels); - }); - } - - /// @copydoc counter_instance - template - counter counter_instance(std::string_view pre, std::string_view name, - std::initializer_list labels, - std::string_view helptext, - std::string_view unit = "1", - bool is_sum = false) { - auto lbl_span = span{labels.begin(), labels.size()}; - return counter_instance(pre, name, lbl_span, helptext, unit, is_sum); - } - - /** - * Accesses a counter singleton, i.e., a counter that belongs to a family - * without label dimensions (which thus only has a single member). Creates - * the hosting metric family as well as the counter lazily if necessary. - * @param pre The prefix (namespace) this family belongs to. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. - * @param is_sum Indicates whether this metric accumulates something, where - * only the total value is of interest. - */ - template - counter counter_singleton(std::string_view pre, std::string_view name, - std::string_view helptext, - std::string_view unit = "1", - bool is_sum = false) { - auto labels = span{}; - auto fam = counter_family(pre, name, labels, helptext, unit, is_sum); - return fam.get_or_add({}); - } - - /** - * @return A gauge metric family. Creates the family lazily if necessary. - * @param pre The prefix (namespace) this family belongs to. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param labels Names for all label dimensions of the metric. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. - * @param is_sum Indicates whether this metric accumulates something, where - * only the total value is of interest. - */ - template - auto gauge_family(std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, std::string_view unit = "1", - bool is_sum = false) { - if constexpr (std::is_same_v) { - return impl_->int_gauge_fam(pre, name, labels, helptext, unit, is_sum); - } else { - static_assert(std::is_same_v, - "metrics only support int64_t and double values"); - return impl_->dbl_gauge_fam(pre, name, labels, helptext, unit, is_sum); - } - } - - /// @copydoc gauge_family - template - auto gauge_family(std::string_view pre, std::string_view name, - std::initializer_list labels, - std::string_view helptext, std::string_view unit = "1", - bool is_sum = false) { - auto lbl_span = span{labels.begin(), labels.size()}; - return gauge_family(pre, name, lbl_span, helptext, unit, is_sum); - } - - /** - * Accesses a gauge instance. Creates the hosting metric family as well - * as the gauge lazily if necessary. - * @param pre The prefix (namespace) this family belongs to. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param labels Values for all label dimensions of the metric. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. - * @param is_sum Indicates whether this metric accumulates something, where - * only the total value is of interest. - */ - template - gauge gauge_instance(std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, - std::string_view unit = "1", bool is_sum = false) { - return with_label_names(labels, [&, this](auto labelNames) { - auto family = gauge_family(pre, name, labelNames, helptext, unit, - is_sum); - return family.getOrAdd(labels); - }); - } - - /// @copydoc gauge_instance - template - gauge gauge_instance(std::string_view pre, std::string_view name, - std::initializer_list labels, - std::string_view helptext, - std::string_view unit = "1", bool is_sum = false) { - auto lbl_span = span{labels.begin(), labels.size()}; - return gauge_instance(pre, name, lbl_span, helptext, unit, is_sum); - } - - /** - * Accesses a gauge singleton, i.e., a gauge that belongs to a family - * without label dimensions (which thus only has a single member). Creates - * the hosting metric family as well as the gauge lazily if necessary. - * @param pre The prefix (namespace) this family belongs to. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. - * @param is_sum Indicates whether this metric accumulates something, where - * only the total value is of interest. - */ - template - gauge gauge_singleton(std::string_view pre, std::string_view name, - std::string_view helptext, - std::string_view unit = "1", bool is_sum = false) { - auto labels = span{}; - auto fam = gauge_family(pre, name, labels, helptext, unit, is_sum); - return fam.get_or_add({}); - } - - // Forces the compiler to use the type `span` instead of trying to - // match paremeters to a `span`. - template - struct const_span_oracle { - using type = span; - }; - - // Convenience alias to safe some typing. - template - using const_span = typename const_span_oracle::type; - - /** - * Returns a histogram metric family. Creates the family lazily if - * necessary. - * @param pre The prefix (namespace) this family belongs to. Usually the - * application or protocol name, e.g., `http`. The prefix `caf` - * as well as prefixes starting with an underscore are - * reserved. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param labels Names for all label dimensions of the metric. - * @param default_upper_bounds Upper bounds for the metric buckets. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. Please use base units such as `bytes` or - * `seconds` (prefer lowercase). The pseudo-unit `1` identifies - * dimensionless counts. - * @param is_sum Setting this to `true` indicates that this metric adds - * something up to a total, where only the total value is of - * interest. For example, the total number of HTTP requests. - * @note The first call wins when calling this function multiple times with - * different bucket settings. Users may also override - * @p default_upper_bounds via run-time configuration. - */ - template - auto histogram_family(std::string_view pre, std::string_view name, - span labels, - const_span default_upper_bounds, - std::string_view helptext, std::string_view unit = "1", - bool is_sum = false) { - if constexpr (std::is_same_v) { - auto hdl = impl_->int_histogram_fam(pre, name, labels, - default_upper_bounds, helptext, unit, - is_sum); - return int_histogram_family{hdl}; - } else { - static_assert(std::is_same_v, - "metrics only support int64_t and double values"); - auto hdl = impl_->dbl_histogram_fam(pre, name, labels, - default_upper_bounds, helptext, unit, - is_sum); - return dbl_histogram_family{hdl}; - } - } - - /// @copydoc histogram_family - template - auto histogram_family(std::string_view pre, std::string_view name, - std::initializer_list labels, - const_span default_upper_bounds, - std::string_view helptext, std::string_view unit = "1", - bool is_sum = false) { - auto lbl_span = span{labels.begin(), labels.size()}; - return histogram_family(pre, name, lbl_span, default_upper_bounds, - helptext, unit, is_sum); - } - - /** - * Returns a histogram. Creates the family lazily if necessary. - * @param pre The prefix (namespace) this family belongs to. Usually the - * application or protocol name, e.g., `http`. The prefix `caf` - * as well as prefixes starting with an underscore are reserved. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param labels Names for all label dimensions of the metric. - * @param default_upper_bounds Upper bounds for the metric buckets. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. Please use base units such as `bytes` or - * `seconds` (prefer lowercase). The pseudo-unit `1` identifies - * dimensionless counts. - * @param is_sum Setting this to `true` indicates that this metric adds - * something up to a total, where only the total value is of - * interest. For example, the total number of HTTP requests. - * @note The first call wins when calling this function multiple times with - * different bucket settings. Users may also override - * @p default_upper_bounds via run-time configuration. - */ - template - histogram histogram_instance(std::string_view pre, std::string_view name, - span labels, - const_span default_upper_bounds, - std::string_view helptext, - std::string_view unit = "1", - bool is_sum = false) { - return with_label_names(labels, [&, this](auto labelNames) { - auto family = histogram_family(pre, name, labelNames, - default_upper_bounds, helptext, unit, - is_sum); - return family.getOrAdd(labels); - }); - } - - /// @copdoc histogram_instance - template - histogram histogram_instance(std::string_view pre, std::string_view name, - std::initializer_list labels, - const_span default_upper_bounds, - std::string_view helptext, - std::string_view unit = "1", - bool is_sum = false) { - auto lbls = span{labels.begin(), labels.size()}; - return histogram_instance(pre, name, lbls, default_upper_bounds, helptext, - unit, is_sum); - } - - /** - * Returns a histogram metric singleton, i.e., the single instance of a - * family without label dimensions. Creates all objects lazily if necessary, - * but fails if the full name already belongs to a different family. - * @param pre The prefix (namespace) this family belongs to. Usually the - * application or protocol name, e.g., `http`. The prefix `caf` - * as well as prefixes starting with an underscore are reserved. - * @param name The human-readable name of the metric, e.g., `requests`. - * @param default_upper_bounds Upper bounds for the metric buckets. - * @param helptext Short explanation of the metric. - * @param unit Unit of measurement. Please use base units such as `bytes` or - * `seconds` (prefer lowercase). The pseudo-unit `1` identifies - * dimensionless counts. - * @param is_sum Setting this to `true` indicates that this metric adds - * something up to a total, where only the total value is of - * interest. For example, the total number of HTTP requests. - * @note The first call wins when calling this function multiple times with - * different bucket settings. Users may also override - * @p default_upper_bounds via run-time configuration. - */ - template - histogram histogram_singleton(std::string_view pre, std::string_view name, - const_span default_upper_bounds, - std::string_view helptext, - std::string_view unit = "1", - bool is_sum = false) { - auto lbls = span{}; - auto fam = histogram_family(pre, name, lbls, default_upper_bounds, - helptext, unit, is_sum); - return fam.get_or_add({}); - } - - [[nodiscard]] metric_registry_impl* pimpl() const noexcept { - return impl_; - } - -private: - metric_registry(metric_registry_impl* impl, bool add_ref) noexcept; - - template - static void with_label_names(span xs, F continuation) { - if (xs.size() <= 10) { - std::string_view buf[10]; - for (size_t index = 0; index < xs.size(); ++index) - buf[index] = xs[index].first; - return continuation(span{buf, xs.size()}); - } else { - std::vector buf; - for (auto x : xs) - buf.emplace_back(x.first, x.second); - return continuation(span{buf}); - } - } - - metric_registry_impl* impl_; -}; - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/metric_registry_impl.cc b/libbroker/broker/telemetry/metric_registry_impl.cc deleted file mode 100644 index 250e13f9..00000000 --- a/libbroker/broker/telemetry/metric_registry_impl.cc +++ /dev/null @@ -1,13 +0,0 @@ -#include "broker/telemetry/metric_registry_impl.hh" - -namespace broker::telemetry { - -metric_registry_impl::metric_registry_impl() : rc_(1) { - // nop -} - -metric_registry_impl::~metric_registry_impl() { - // nop -} - -} // namespace broker::telemetry diff --git a/libbroker/broker/telemetry/metric_registry_impl.hh b/libbroker/broker/telemetry/metric_registry_impl.hh deleted file mode 100644 index cd831e4b..00000000 --- a/libbroker/broker/telemetry/metric_registry_impl.hh +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include "broker/config.hh" -#include "broker/fwd.hh" -#include "broker/telemetry/fwd.hh" - -#include -#include - -namespace broker::telemetry { - -// Collector for the broker::telemetry layer. We string through the labels -// of individual metric instances as there aren't direct accessors for those -// from a hdl. -class metrics_collector { -public: - virtual void operator()(const metric_family_hdl* family, - const dbl_counter_hdl* counter, - const_label_list labels) = 0; - virtual void operator()(const metric_family_hdl* family, - const int_counter_hdl* counter, - const_label_list labels) = 0; - virtual void operator()(const metric_family_hdl* family, - const dbl_gauge_hdl* gauge, - const_label_list labels) = 0; - virtual void operator()(const metric_family_hdl* family, - const int_gauge_hdl* gauge, - const_label_list labels) = 0; - virtual void operator()(const metric_family_hdl* family, - const dbl_histogram_hdl* histogram, - const_label_list labels) = 0; - virtual void operator()(const metric_family_hdl* family, - const int_histogram_hdl* histogram, - const_label_list labels) = 0; -}; - -class metric_registry_impl { -public: - using ref_count_type = std::atomic; - - metric_registry_impl(); - - metric_registry_impl(const metric_registry_impl&) = delete; - - metric_registry_impl& operator-(const metric_registry_impl&) = delete; - - virtual ~metric_registry_impl(); - - virtual int_counter_family_hdl* - int_counter_fam(std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, std::string_view unit, - bool is_sum) = 0; - - virtual dbl_counter_family_hdl* - dbl_counter_fam(std::string_view pre, std::string_view name, - span labels, - std::string_view helptext, std::string_view unit, - bool is_sum) = 0; - - virtual int_gauge_family_hdl* - int_gauge_fam(std::string_view pre, std::string_view name, - span labels, std::string_view helptext, - std::string_view unit, bool is_sum) = 0; - - virtual dbl_gauge_family_hdl* - dbl_gauge_fam(std::string_view pre, std::string_view name, - span labels, std::string_view helptext, - std::string_view unit, bool is_sum) = 0; - - virtual int_histogram_family_hdl* - int_histogram_fam(std::string_view pre, std::string_view name, - span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum) = 0; - - virtual dbl_histogram_family_hdl* - dbl_histogram_fam(std::string_view pre, std::string_view name, - span labels, - span ubounds, std::string_view helptext, - std::string_view unit, bool is_sum) = 0; - - // Collect metrics. - virtual void collect(metrics_collector& collector) = 0; - - virtual bool merge(endpoint& where) = 0; - - void ref() const noexcept { - ++rc_; - } - - void deref() const noexcept { - if (--rc_ == 0) - delete this; - } - - bool unique() const noexcept { - return rc_.load() == 1; - } - -private: - alignas(BROKER_CONSTRUCTIVE_INTERFERENCE_SIZE) mutable ref_count_type rc_; -}; - -} // namespace broker::telemetry