include/ylt/metric/metric_manager.hpp (497 lines of code) (raw):

#pragma once #include <shared_mutex> #include <system_error> #include <utility> #include "metric.hpp" #include "ylt/util/map_sharded.hpp" namespace ylt::metric { class manager_helper { public: static bool register_metric(auto& metric_map, auto metric) { if (metric::metric_t::g_user_metric_count > ylt_metric_capacity) { CINATRA_LOG_ERROR << "metric count at capacity size: " << metric::metric_t::g_user_metric_count; return false; } auto&& [it, r] = metric_map.try_emplace(metric->str_name(), metric); if (!r) { CINATRA_LOG_ERROR << "duplicate registered metric name: " << metric->str_name(); return false; } return true; } static std::string serialize( const std::vector<std::shared_ptr<metric_t>>& metrics) { std::string str; for (auto& m : metrics) { m->serialize(str); } return str; } #ifdef CINATRA_ENABLE_METRIC_JSON static std::string serialize_to_json( const std::vector<std::shared_ptr<metric_t>>& metrics) { std::string str; str.append("["); for (auto& m : metrics) { size_t start = str.size(); m->serialize_to_json(str); if (str.size() > start) str.append(","); } if (str.size() == 1) { str.append("]"); } else { str.back() = ']'; } str.back() = ']'; return str; } #endif static std::vector<std::shared_ptr<metric_t>> filter_metrics_by_name( auto& metrics, const std::regex& name_regex) { std::vector<std::shared_ptr<metric_t>> filtered_metrics; for (auto& m : metrics) { if (std::regex_match(m->str_name(), name_regex)) { filtered_metrics.push_back(m); } } return filtered_metrics; } static std::vector<std::shared_ptr<metric_t>> filter_metrics_by_label_name( auto& metrics, const std::regex& label_name_regex) { std::vector<std::shared_ptr<metric_t>> filtered_metrics; for (auto& m : metrics) { const auto& labels_name = m->labels_name(); for (auto& label_name : labels_name) { if (std::regex_match(label_name, label_name_regex)) { filtered_metrics.push_back(m); } } } return filtered_metrics; } static std::vector<std::shared_ptr<metric_t>> filter_metrics_by_label_value( auto& metrics, const std::regex& label_value_regex) { std::vector<std::shared_ptr<metric_t>> filtered_metrics; for (auto& m : metrics) { if (m->has_label_value(label_value_regex)) { filtered_metrics.push_back(m); } } return filtered_metrics; } static std::vector<std::shared_ptr<metric_t>> filter_metrics( auto& metrics, const metric_filter_options& options) { if (!(options.name_regex || options.label_regex || options.label_value_regex)) { return metrics; } std::vector<std::shared_ptr<metric_t>> filtered_metrics = metrics; if (options.name_regex) { filtered_metrics = filter_metrics_by_name(metrics, *options.name_regex); if (filtered_metrics.empty()) { return {}; } } if (options.label_regex) { filtered_metrics = filter_metrics_by_label_name(filtered_metrics, *options.label_regex); if (filtered_metrics.empty()) { return {}; } } if (options.label_value_regex) { filtered_metrics = filter_metrics_by_label_value( filtered_metrics, *options.label_value_regex); if (filtered_metrics.empty()) { return {}; } } if (!options.is_white) { for (auto& m : filtered_metrics) { std::erase_if(metrics, [&](auto t) { return t == m; }); } return metrics; } return filtered_metrics; } static void filter_by_label_name( std::vector<std::shared_ptr<metric_t>>& filtered_metrics, std::shared_ptr<metric_t> m, const metric_filter_options& options) { if (!options.label_regex) { return; } const auto& labels_name = m->labels_name(); for (auto& label_name : labels_name) { if (std::regex_match(label_name, *options.label_regex)) { filtered_metrics.push_back(m); } } } }; template <typename Tag> class static_metric_manager { public: static_metric_manager(static_metric_manager const&) = delete; static_metric_manager(static_metric_manager&&) = delete; static_metric_manager& operator=(static_metric_manager const&) = delete; static_metric_manager& operator=(static_metric_manager&&) = delete; static static_metric_manager<Tag>& instance() { static auto* inst = new static_metric_manager<Tag>(); return *inst; } template <typename T, typename... Args> std::pair<std::error_code, std::shared_ptr<T>> create_metric_static( const std::string& name, const std::string& help, Args&&... args) { auto m = std::make_shared<T>(name, help, std::forward<Args>(args)...); bool r = register_metric(m); if (!r) { return std::make_pair(std::make_error_code(std::errc::invalid_argument), nullptr); } return std::make_pair(std::error_code{}, m); } bool register_metric(std::shared_ptr<static_metric> metric) { return manager_helper::register_metric(metric_map_, metric); } size_t metric_count() { return metric_map_.size(); } auto metric_map() { return metric_map_; } auto collect() { std::vector<std::shared_ptr<metric_t>> metrics; for (auto& pair : metric_map_) { metrics.push_back(pair.second); } return metrics; } std::string serialize(const std::vector<std::shared_ptr<metric_t>>& metrics) { return manager_helper::serialize(metrics); } std::string serialize_static() { return manager_helper::serialize(collect()); } #ifdef CINATRA_ENABLE_METRIC_JSON std::string serialize_to_json_static() { return manager_helper::serialize_to_json(collect()); } #endif template <typename T> std::shared_ptr<T> get_metric_static(const std::string& name) { static_assert(std::is_base_of_v<static_metric, T>, "must be dynamic metric"); auto it = metric_map_.find(name); if (it == metric_map_.end()) { return nullptr; } return std::dynamic_pointer_cast<T>(it->second); } std::shared_ptr<static_metric> get_metric_by_name(const std::string& name) { auto it = metric_map_.find(name); if (it == metric_map_.end()) { return nullptr; } return it->second; } std::vector<std::shared_ptr<static_metric>> get_metric_by_label( const std::map<std::string, std::string>& labels) { std::vector<std::shared_ptr<static_metric>> metrics; for (auto& [key, m] : metric_map_) { if (m->get_static_labels() == labels) { metrics.push_back(m); } } return metrics; } std::vector<std::shared_ptr<metric_t>> filter_metrics_static( const metric_filter_options& options) { auto metrics = collect(); return manager_helper::filter_metrics(metrics, options); } std::vector<std::shared_ptr<metric_t>> filter_metrics_by_label_value( const std::regex& label_regex) { auto metrics = collect(); return manager_helper::filter_metrics_by_label_value(metrics, label_regex); } private: static_metric_manager() = default; std::unordered_map<std::string, std::shared_ptr<static_metric>> metric_map_; }; // using metric_manager_t = static_metric_manager; template <typename Tag> class dynamic_metric_manager { public: dynamic_metric_manager(dynamic_metric_manager const&) = delete; dynamic_metric_manager(dynamic_metric_manager&&) = delete; dynamic_metric_manager& operator=(dynamic_metric_manager const&) = delete; dynamic_metric_manager& operator=(dynamic_metric_manager&&) = delete; static dynamic_metric_manager<Tag>& instance() { static auto* inst = new dynamic_metric_manager<Tag>(); return *inst; } template <typename T, typename... Args> std::pair<std::error_code, std::shared_ptr<T>> create_metric_dynamic( const std::string& name, const std::string& help, Args&&... args) { auto m = std::make_shared<T>(name, help, std::forward<Args>(args)...); bool r = register_metric(m); if (!r) { return std::make_pair(std::make_error_code(std::errc::invalid_argument), nullptr); } return std::make_pair(std::error_code{}, m); } bool register_metric(std::shared_ptr<dynamic_metric> metric) { return manager_helper::register_metric(metric_map_, metric); } bool register_metric(std::vector<std::shared_ptr<dynamic_metric>> metrics) { bool r = false; for (auto& m : metrics) { r = register_metric(m); if (!r) { break; } } return r; } std::string serialize_dynamic() { return manager_helper::serialize(collect()); } std::string serialize(const std::vector<std::shared_ptr<metric_t>>& metrics) { return manager_helper::serialize(metrics); } #ifdef CINATRA_ENABLE_METRIC_JSON std::string serialize_to_json_dynamic() { return manager_helper::serialize_to_json(collect()); } #endif bool remove_metric(const std::string& name) { return metric_map_.erase(name); } bool remove_metric(std::shared_ptr<dynamic_metric> metric) { if (metric == nullptr) { return false; } return remove_metric(metric->str_name()); } void remove_metric(const std::vector<std::string>& names) { if (names.empty()) { return; } for (auto& name : names) { remove_metric(name); } } void remove_metric(std::vector<std::shared_ptr<dynamic_metric>> metrics) { if (metrics.empty()) { return; } for (auto& metric : metrics) { remove_metric(metric); } } void remove_label_value(const std::map<std::string, std::string>& labels) { metric_map_.for_each([&](auto& m) { auto&& [_, metric] = m; metric->remove_label_value(labels); }); } void remove_metric_by_label( const std::map<std::string, std::string>& labels) { metric_map_.erase_if([&](auto& metric) { auto&& [_, m] = metric; const auto& labels_name = m->labels_name(); if (labels.size() > labels_name.size()) { return false; } if (labels.size() == labels_name.size()) { std::vector<std::string> label_value; for (auto& lb_name : labels_name) { if (auto i = labels.find(lb_name); i != labels.end()) { label_value.push_back(i->second); } } return m->has_label_value(label_value); } else { for (auto& label : labels) { if (auto i = std::find(labels_name.begin(), labels_name.end(), label.first); i != labels_name.end()) { if (!m->has_label_value(label.second)) { return false; } } else { return false; } } return true; } }); } void remove_metric_by_label_name( const std::vector<std::string>& labels_name) { metric_map_.erase_one([&](auto& m) { auto&& [name, metric] = m; return metric->labels_name() == labels_name; }); } void remove_metric_by_label_name(std::string_view labels_name) { metric_map_.erase_if([&](auto& m) { auto&& [_, metric] = m; auto& names = metric->labels_name(); return std::find(names.begin(), names.end(), labels_name) != names.end(); }); } size_t metric_count() { return metric_map_.size(); } std::vector<std::shared_ptr<metric_t>> collect() const { return metric_map_.template copy<std::shared_ptr<metric_t>>(); } template <typename T> std::shared_ptr<T> get_metric_dynamic(const std::string& name) { static_assert(std::is_base_of_v<dynamic_metric, T>, "must be dynamic metric"); return std::dynamic_pointer_cast<T>(metric_map_.find(name)); } std::shared_ptr<dynamic_metric> get_metric_by_name(std::string_view name) { return metric_map_.find(name); } std::vector<std::shared_ptr<dynamic_metric>> get_metric_by_label( const std::vector<std::pair<std::string, std::string>>& labels) { std::vector<std::string> label_value; for (auto& [k, v] : labels) { label_value.push_back(v); } return get_metric_by_label_value(label_value); } std::vector<std::shared_ptr<dynamic_metric>> get_metric_by_label_name( const std::vector<std::string>& labels_name) { return metric_map_.template copy<std::shared_ptr<dynamic_metric>>( [&](auto& m) { return m->labels_name() == labels_name; }); } std::vector<std::shared_ptr<metric_t>> filter_metrics_dynamic( const metric_filter_options& options) { auto metrics = collect(); return manager_helper::filter_metrics(metrics, options); } std::vector<std::shared_ptr<metric_t>> filter_metrics_by_label_value( const std::regex& label_regex) { auto metrics = collect(); return manager_helper::filter_metrics_by_label_value(metrics, label_regex); } private: void clean_label_expired() { executor_ = coro_io::create_io_context_pool(1); auto sp = executor_; timer_ = std::make_shared<coro_io::period_timer>(executor_->get_executor()); check_label_expired(timer_) .via(executor_->get_executor()) .start([sp](auto&&) { }); } async_simple::coro::Lazy<void> check_label_expired( std::weak_ptr<coro_io::period_timer> weak) { while (true) { auto timer = weak.lock(); if (timer == nullptr) { co_return; } timer->expires_after(ylt_label_check_expire_duration); bool r = co_await timer->async_await(); if (!r) { co_return; } metric_map_.for_each([](auto& metric) { metric.second->clean_expired_label(); }); } } dynamic_metric_manager() : metric_map_( std::min<unsigned>(std::thread::hardware_concurrency(), 128u)) { if (ylt_label_max_age.count() > 0) { clean_label_expired(); } } std::vector<std::shared_ptr<dynamic_metric>> get_metric_by_label_value( const std::vector<std::string>& label_value) { return metric_map_.template copy<std::shared_ptr<dynamic_metric>>( [&label_value](auto& metric) { return metric->has_label_value(label_value); }); } void remove_metric_by_label_value( const std::vector<std::string>& label_value) { metric_map_.erase_if([&](auto& metric) { return metric.second->has_label_value(label_value); }); } template <size_t seed = 131> struct my_hash { using is_transparent = void; std::size_t operator()(std::string_view s) const noexcept { unsigned int hash = 0; for (auto ch : s) { hash = hash * seed + ch; } return hash; } }; util::map_sharded_t< std::unordered_map<std::string, std::shared_ptr<dynamic_metric>>, my_hash<>> metric_map_; std::shared_ptr<coro_io::period_timer> timer_ = nullptr; std::shared_ptr<coro_io::io_context_pool> executor_ = nullptr; }; struct ylt_default_metric_tag_t {}; using default_static_metric_manager = static_metric_manager<ylt_default_metric_tag_t>; using default_dynamiv_metric_manager = dynamic_metric_manager<ylt_default_metric_tag_t>; template <typename Tag> struct metric_manager_t; struct ylt_system_tag_t {}; using system_metric_manager = static_metric_manager<ylt_system_tag_t>; template <typename... Args> struct metric_collector_t { static std::string serialize() { auto vec = get_all_metrics(); return manager_helper::serialize(vec); } #ifdef CINATRA_ENABLE_METRIC_JSON static std::string serialize_to_json() { auto vec = get_all_metrics(); return manager_helper::serialize_to_json(vec); } static std::string serialize_to_json( const std::vector<std::shared_ptr<metric_t>>& metrics) { return manager_helper::serialize_to_json(metrics); } #endif static std::string serialize( const std::vector<std::shared_ptr<metric_t>>& metrics) { return manager_helper::serialize(metrics); } static std::vector<std::shared_ptr<metric_t>> get_all_metrics() { std::vector<std::shared_ptr<metric_t>> vec; (append_vector<Args>(vec), ...); return vec; } static std::vector<std::shared_ptr<metric_t>> filter_metrics( const metric_filter_options& options) { auto vec = get_all_metrics(); return manager_helper::filter_metrics(vec, options); } private: template <typename T> static void append_vector(std::vector<std::shared_ptr<metric_t>>& vec) { auto v = T::instance().collect(); vec.insert(vec.end(), v.begin(), v.end()); } }; } // namespace ylt::metric