From a2709a056c372acc7ec920833f280e30552cebe2 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 9 Aug 2024 16:26:37 +0800 Subject: [PATCH] [metric]Refact metric (#740) --- include/ylt/metric.hpp | 1 + include/ylt/metric/counter.hpp | 538 +++++----- include/ylt/metric/gauge.hpp | 107 +- include/ylt/metric/histogram.hpp | 227 +++-- include/ylt/metric/metric.hpp | 546 +---------- include/ylt/metric/metric_manager.hpp | 552 +++++++++++ include/ylt/metric/summary.hpp | 300 +++--- include/ylt/metric/system_metric.hpp | 2 + include/ylt/metric/thread_local_value.hpp | 1 + .../standalone/cinatra/coro_http_server.hpp | 20 +- .../ylt/standalone/cinatra/metric_conf.hpp | 17 +- src/metric/tests/test_metric.cpp | 919 ++++++++++++------ 12 files changed, 1840 insertions(+), 1390 deletions(-) create mode 100644 include/ylt/metric/metric_manager.hpp diff --git a/include/ylt/metric.hpp b/include/ylt/metric.hpp index 108113e76..0de3a0a82 100644 --- a/include/ylt/metric.hpp +++ b/include/ylt/metric.hpp @@ -17,6 +17,7 @@ #define CINATRA_ENABLE_METRIC_JSON #include "metric/gauge.hpp" #include "metric/histogram.hpp" +#include "metric/metric_manager.hpp" #include "metric/summary.hpp" #include "metric/system_metric.hpp" #include "ylt/struct_json/json_writer.h" diff --git a/include/ylt/metric/counter.hpp b/include/ylt/metric/counter.hpp index d68b49f63..0c1a24f37 100644 --- a/include/ylt/metric/counter.hpp +++ b/include/ylt/metric/counter.hpp @@ -1,4 +1,6 @@ #pragma once +#include +#include #include #include #include @@ -11,7 +13,7 @@ enum class op_type_t { INC, DEC, SET }; #ifdef CINATRA_ENABLE_METRIC_JSON struct json_counter_metric_t { - std::unordered_multimap labels; + std::map labels; std::variant value; }; REFLECTION(json_counter_metric_t, labels, value); @@ -24,376 +26,368 @@ struct json_counter_t { REFLECTION(json_counter_t, name, help, type, metrics); #endif -template -class basic_counter : public metric_t { - public: - // default, no labels, only contains an atomic value. - basic_counter(std::string name, std::string help, size_t dupli_count = 2) - : metric_t(MetricType::Counter, std::move(name), std::move(help)), - dupli_count_(dupli_count), - default_label_value_(dupli_count) { - use_atomic_ = true; - g_user_metric_count++; +template +inline void set_value(T &label_val, value_type value, op_type_t type) { + switch (type) { + case op_type_t::INC: { +#ifdef __APPLE__ + if constexpr (std::is_floating_point_v) { + mac_os_atomic_fetch_add(&label_val.local_value(), value); + } + else { + label_val += value; + } +#else + label_val += value; +#endif + } break; + case op_type_t::DEC: +#ifdef __APPLE__ + if constexpr (std::is_floating_point_v) { + mac_os_atomic_fetch_sub(&label_val, value); + } + else { + label_val -= value; + } +#else + label_val -= value; +#endif + break; + case op_type_t::SET: + label_val = value; + break; } +} - // static labels value, contains a map with atomic value. - basic_counter(std::string name, std::string help, - std::map labels, - size_t dupli_count = 2) - : metric_t(MetricType::Counter, std::move(name), std::move(help), - std::move(labels)), - dupli_count_(dupli_count) { - atomic_value_map_.emplace(labels_value_, - thread_local_value(dupli_count)); - g_user_metric_count++; - use_atomic_ = true; +template +class basic_static_counter : public static_metric { + public: + // static counter, no labels, only contains an atomic value. + basic_static_counter(std::string name, std::string help, + size_t dupli_count = 2) + : static_metric(MetricType::Counter, std::move(name), std::move(help)) { + init_thread_local(dupli_count); } - // dynamic labels value - basic_counter(std::string name, std::string help, - std::vector labels_name, size_t dupli_count = 2) - : metric_t(MetricType::Counter, std::move(name), std::move(help), - std::move(labels_name)), - dupli_count_(dupli_count) { - g_user_metric_count++; + // static counter, contains a static labels with atomic value. + basic_static_counter(std::string name, std::string help, + std::map labels, + size_t dupli_count = 2) + : static_metric(MetricType::Counter, std::move(name), std::move(help), + std::move(labels)) { + init_thread_local(dupli_count); } - virtual ~basic_counter() { g_user_metric_count--; } - - value_type value() { - if (!labels_value_.empty()) { - value_type val = atomic_value_map_[labels_value_].value(); - return val; + void init_thread_local(size_t dupli_count) { + if (dupli_count > 0) { + dupli_count_ = dupli_count; + default_label_value_ = {dupli_count}; } - if (!labels_name_.empty()) { - throw std::invalid_argument("it's not a dynamic counter!"); - } - return default_label_value_.value(); + g_user_metric_count++; } - value_type value(const std::vector &labels_value) { - if (labels_name_.empty()) { - throw std::invalid_argument("it is a default value counter"); - } + virtual ~basic_static_counter() { g_user_metric_count--; } - if (use_atomic_) { - value_type val = atomic_value_map_[labels_value].value(); - return val; - } - else { - std::lock_guard lock(mtx_); - auto [it, r] = value_map_.try_emplace( - labels_value, thread_local_value(dupli_count_)); - return it->second.value(); + void inc(value_type val = 1) { + if (val <= 0) { + return; } - } - metric_hash_map> value_map() { - metric_hash_map> map; - if (use_atomic_) { - map = {atomic_value_map_.begin(), atomic_value_map_.end()}; +#ifdef __APPLE__ + if constexpr (std::is_floating_point_v) { + mac_os_atomic_fetch_add(&default_label_value_.local_value(), val); } else { - std::lock_guard lock(mtx_); - map = value_map_; + default_label_value_.inc(val); } - return map; +#else + default_label_value_.inc(val); +#endif } - bool has_label_value(const std::string &label_val) override { - auto map = value_map(); - auto it = std::find_if(map.begin(), map.end(), [&label_val](auto &pair) { - auto &key = pair.first; - return std::find(key.begin(), key.end(), label_val) != key.end(); - }); - return it != map.end(); + value_type update(value_type value) { + return default_label_value_.update(value); } - void serialize(std::string &str) override { - if (labels_name_.empty()) { - if (default_label_value_.value() == 0) { - return; - } - serialize_head(str); - serialize_default_label(str); - return; - } + value_type reset() { return default_label_value_.reset(); } - auto map = value_map(); - if (map.empty()) { + value_type value() { return default_label_value_.value(); } + + void serialize(std::string &str) override { + auto value = default_label_value_.value(); + if (value == 0) { return; } - std::string value_str; - serialize_map(map, value_str); - if (!value_str.empty()) { - serialize_head(str); - str.append(value_str); - } + serialize_head(str); + serialize_default_label(str, value); } #ifdef CINATRA_ENABLE_METRIC_JSON void serialize_to_json(std::string &str) override { - std::string s; - if (labels_name_.empty()) { - if (default_label_value_.value() == 0) { - return; - } - json_counter_t counter{name_, help_, std::string(metric_name())}; - auto value = default_label_value_.value(); - counter.metrics.push_back({{}, value}); - iguana::to_json(counter, str); + if (default_label_value_.value() == 0) { return; } - auto map = value_map(); json_counter_t counter{name_, help_, std::string(metric_name())}; - to_json(counter, map, str); + auto value = default_label_value_.value(); + counter.metrics.push_back({static_labels_, value}); + iguana::to_json(counter, str); } +#endif - template - void to_json(json_counter_t &counter, T &map, std::string &str) { - for (auto &[k, v] : map) { - if (v.value() == 0) { - continue; - } - json_counter_metric_t metric; - size_t index = 0; - for (auto &label_value : k) { - metric.labels.emplace(labels_name_[index++], label_value); - } - metric.value = (int64_t)v.value(); - counter.metrics.push_back(std::move(metric)); + protected: + void serialize_default_label(std::string &str, value_type value) { + str.append(name_); + if (labels_name_.empty()) { + str.append(" "); } - if (!counter.metrics.empty()) { - iguana::to_json(counter, str); + else { + str.append("{"); + build_string(str, labels_name_, labels_value_); + str.append("} "); } + + str.append(std::to_string(value)); + + str.append("\n"); } -#endif - void inc(value_type val = 1) { - if (!labels_value_.empty()) { - set_value(atomic_value_map_[labels_value_].local_value(), val, - op_type_t::INC); - return; + void build_string(std::string &str, const std::vector &v1, + const std::vector &v2) { + for (size_t i = 0; i < v1.size(); i++) { + str.append(v1[i]).append("=\"").append(v2[i]).append("\"").append(","); } + str.pop_back(); + } - if (!labels_name_.empty()) { - throw std::invalid_argument("it's not a dynamic counter!"); - } + thread_local_value default_label_value_; + size_t dupli_count_ = 2; +}; - if (val < 0) { - throw std::invalid_argument("the value is less than zero"); - } +template +struct array_hash { + size_t operator()(const std::array &arr) const { + unsigned int seed = 131; + unsigned int hash = 0; -#ifdef __APPLE__ - if constexpr (std::is_floating_point_v) { - mac_os_atomic_fetch_add(&default_label_value_.local_value(), val); - } - else { - default_label_value_.inc(val); + for (const auto &str : arr) { + for (auto ch : str) { + hash = hash * seed + ch; + } } -#else - default_label_value_.inc(val); -#endif + + return (hash & 0x7FFFFFFF); } +}; + +using counter_t = basic_static_counter; +using counter_d = basic_static_counter; - void inc(const std::vector &labels_value, value_type value = 1) { +template +using dynamic_metric_hash_map = + std::unordered_map, T, array_hash>; + +template +class basic_dynamic_counter : public dynamic_metric { + public: + // dynamic labels value + basic_dynamic_counter(std::string name, std::string help, + std::array labels_name, + size_t dupli_count = 2) + : dynamic_metric(MetricType::Counter, std::move(name), std::move(help), + std::move(labels_name)), + dupli_count_(dupli_count) { + g_user_metric_count++; + } + + virtual ~basic_dynamic_counter() { g_user_metric_count--; } + + void inc(const std::array &labels_value, + value_type value = 1) { if (value == 0) { return; } - if (labels_name_.empty()) { - throw std::invalid_argument("it's a default value counter!"); + std::lock_guard lock(mtx_); + auto [it, r] = value_map_.try_emplace( + labels_value, thread_local_value(dupli_count_)); + if (r) { + g_user_metric_label_count++; // TODO: use thread local } + set_value(it->second.local_value(), value, op_type_t::INC); + } - validate(labels_value, value); - if (use_atomic_) { - if (labels_value != labels_value_) { - throw std::invalid_argument( - "the given labels_value is not match with origin labels_value"); - } - set_value(atomic_value_map_[labels_value].local_value(), value, - op_type_t::INC); - } - else { - std::lock_guard lock(mtx_); - stat_metric(labels_value); - set_value(value_map_[labels_value].local_value(), value, - op_type_t::INC); - } + value_type update(const std::array &labels_value, + value_type value) { + std::lock_guard lock(mtx_); + auto [it, r] = value_map_.try_emplace( + labels_value, thread_local_value(dupli_count_)); + return it->second.update(value); } - void stat_metric(const std::vector &labels_value) { - if (!value_map_.contains(labels_value)) { - g_user_metric_label_count++; + value_type value(const std::array &labels_value) { + std::lock_guard lock(mtx_); + if (auto it = value_map_.find(labels_value); it != value_map_.end()) { + return it->second.value(); } - } - value_type update(value_type value) { - return default_label_value_.update(value); + return value_type{}; } value_type reset() { value_type val = {}; - if (use_atomic_) { - if (labels_name_.empty()) { - val = default_label_value_.reset(); - return val; - } - for (auto &[key, t] : atomic_value_map_) { - val += t.reset(); - } + std::lock_guard lock(mtx_); + for (auto &[key, t] : value_map_) { + val += t.reset(); } - else { + + return val; + } + + dynamic_metric_hash_map, N> value_map() { + dynamic_metric_hash_map, N> map; + { std::lock_guard lock(mtx_); - for (auto &[key, t] : value_map_) { - val += t.reset(); - } + map = value_map_; } - return val; + + return map; } - void update(const std::vector &labels_value, value_type value) { - if (labels_value.empty() || labels_name_.size() != labels_value.size()) { - throw std::invalid_argument( - "the number of labels_value name and labels_value is not match"); + bool has_label_value(const std::string &value) override { + auto map = value_map(); + for (auto &[label_value, _] : map) { + if (auto it = std::find(label_value.begin(), label_value.end(), value); + it != label_value.end()) { + return true; + } } - if (use_atomic_) { - if (labels_value != labels_value_) { - throw std::invalid_argument( - "the given labels_value is not match with origin labels_value"); + + return false; + } + + bool has_label_value(const std::regex ®ex) override { + auto map = value_map(); + for (auto &[label_value, _] : map) { + if (auto it = std::find_if(label_value.begin(), label_value.end(), + [&](auto &val) { + return std::regex_match(val, regex); + }); + it != label_value.end()) { + return true; } - atomic_value_map_[labels_value].update(value); } - else { - std::lock_guard lock(mtx_); - value_map_[labels_value].update(value); + + return false; + } + + bool has_label_value(const std::vector &label_value) override { + std::array arr{}; + size_t size = (std::min)(N, label_value.size()); + for (size_t i = 0; i < size; i++) { + arr[i] = label_value[i]; } + std::lock_guard lock(mtx_); + return value_map_.contains(arr); } - auto &atomic_value_map() { return atomic_value_map_; } + void serialize(std::string &str) override { + auto map = value_map(); + if (map.empty()) { + return; + } - protected: - void serialize_default_label(std::string &str) { - str.append(name_); - if (labels_name_.empty()) { - str.append(" "); + std::string value_str; + serialize_map(map, value_str); + if (!value_str.empty()) { + serialize_head(str); + str.append(value_str); } + } - str.append(std::to_string(default_label_value_.value())); +#ifdef CINATRA_ENABLE_METRIC_JSON + void serialize_to_json(std::string &str) override { + std::string s; + auto map = value_map(); + json_counter_t counter{name_, help_, std::string(metric_name())}; + to_json(counter, map, str); + } - str.append("\n"); + template + void to_json(json_counter_t &counter, T &map, std::string &str) { + for (auto &[k, v] : map) { + auto val = v.value(); + if (val == 0) { + continue; + } + json_counter_metric_t metric; + size_t index = 0; + for (auto &label_value : k) { + metric.labels.emplace(labels_name_[index++], label_value); + } + metric.value = (int64_t)val; + counter.metrics.push_back(std::move(metric)); + } + if (!counter.metrics.empty()) { + iguana::to_json(counter, str); + } } +#endif + protected: template void serialize_map(T &value_map, std::string &str) { for (auto &[labels_value, value] : value_map) { - if (value.value() == 0) { + auto val = value.value(); + if (val == 0) { continue; } str.append(name_); - str.append("{"); - build_string(str, labels_name_, labels_value); - str.append("} "); + if (labels_name_.empty()) { + str.append(" "); + } + else { + str.append("{"); + build_string(str, labels_name_, labels_value); + str.append("} "); + } - str.append(std::to_string(value.value())); + str.append(std::to_string(val)); str.append("\n"); } } void build_string(std::string &str, const std::vector &v1, - const std::vector &v2) { + const auto &v2) { for (size_t i = 0; i < v1.size(); i++) { str.append(v1[i]).append("=\"").append(v2[i]).append("\"").append(","); } str.pop_back(); } - void validate(const std::vector &labels_value, - value_type value) { - if (value < 0) { - throw std::invalid_argument("the value is less than zero"); - } - if (labels_value.empty() || labels_name_.size() != labels_value.size()) { - throw std::invalid_argument( - "the number of labels_value name and labels_value is not match"); - } - } + std::mutex mtx_; + dynamic_metric_hash_map, N> value_map_; + size_t dupli_count_ = 2; +}; - template - void set_value_static(T &label_val, value_type value, op_type_t type) { - set_value(label_val, value, type); - } +using dynamic_counter_1t = basic_dynamic_counter; +using dynamic_counter_1d = basic_dynamic_counter; - template - void set_value_dynamic(T &label_val, value_type value, op_type_t type) { - set_value(label_val, value, type); - } +using dynamic_counter_2t = basic_dynamic_counter; +using dynamic_counter_2d = basic_dynamic_counter; +using dynamic_counter_t = dynamic_counter_2t; +using dynamic_counter_d = dynamic_counter_2d; - template - void set_value(T &label_val, value_type value, op_type_t type) { - switch (type) { - case op_type_t::INC: { -#ifdef __APPLE__ - if constexpr (is_atomic) { - if constexpr (std::is_floating_point_v) { - mac_os_atomic_fetch_add(&label_val.local_value(), value); - } - else { - label_val += value; - } - } - else { - label_val += value; - } -#else - if constexpr (is_atomic) { - label_val.fetch_add(value, std::memory_order_relaxed); - } - else { - label_val += value; - } -#endif - } break; - case op_type_t::DEC: -#ifdef __APPLE__ - if constexpr (is_atomic) { - if constexpr (std::is_floating_point_v) { - mac_os_atomic_fetch_sub(&label_val, value); - } - else { - label_val -= value; - } - } - else { - label_val -= value; - } -#else - if constexpr (is_atomic) { - label_val.fetch_sub(value, std::memory_order_relaxed); - } - else { - label_val -= value; - } -#endif - break; - case op_type_t::SET: - label_val = value; - break; - } - } +using dynamic_counter_3t = basic_dynamic_counter; +using dynamic_counter_3d = basic_dynamic_counter; - metric_hash_map> atomic_value_map_; - thread_local_value default_label_value_; +using dynamic_counter_4t = basic_dynamic_counter; +using dynamic_counter_4d = basic_dynamic_counter; - std::mutex mtx_; - metric_hash_map> value_map_; - size_t dupli_count_ = 1; -}; -using counter_t = basic_counter; -using counter_d = basic_counter; +using dynamic_counter_5t = basic_dynamic_counter; +using dynamic_counter_5d = basic_dynamic_counter; } // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/gauge.hpp b/include/ylt/metric/gauge.hpp index 5f8bea7ed..80c7913d5 100644 --- a/include/ylt/metric/gauge.hpp +++ b/include/ylt/metric/gauge.hpp @@ -6,50 +6,28 @@ namespace ylt::metric { template -class basic_gauge : public basic_counter { +class basic_static_gauge : public basic_static_counter { using metric_t::set_metric_type; - using basic_counter::validate; - using metric_t::use_atomic_; - using basic_counter::default_label_value_; - using metric_t::labels_name_; + using basic_static_counter::default_label_value_; using metric_t::labels_value_; - using basic_counter::atomic_value_map_; - using basic_counter::value_map_; - using basic_counter::mtx_; - using basic_counter::stat_metric; - using basic_counter::set_value_static; - using basic_counter::set_value_dynamic; + using basic_static_counter::dupli_count_; public: - basic_gauge(std::string name, std::string help, size_t dupli_count = 2) - : basic_counter(std::move(name), std::move(help), - dupli_count) { - set_metric_type(MetricType::Gauge); - } - basic_gauge(std::string name, std::string help, - std::vector labels_name, size_t dupli_count = 2) - : basic_counter(std::move(name), std::move(help), - std::move(labels_name), dupli_count) { + basic_static_gauge(std::string name, std::string help, size_t dupli_count = 2) + : basic_static_counter(std::move(name), std::move(help), + dupli_count) { set_metric_type(MetricType::Gauge); } - basic_gauge(std::string name, std::string help, - std::map labels, size_t dupli_count = 2) - : basic_counter(std::move(name), std::move(help), - std::move(labels), dupli_count) { + basic_static_gauge(std::string name, std::string help, + std::map labels, + size_t dupli_count = 2) + : basic_static_counter(std::move(name), std::move(help), + std::move(labels), dupli_count) { set_metric_type(MetricType::Gauge); } void dec(value_type value = 1) { - if (!labels_value_.empty()) { - set_value_static(atomic_value_map_[labels_value_].local_value(), value, - op_type_t::DEC); - return; - } - - if (!labels_name_.empty()) { - throw std::invalid_argument("it's not a dynamic counter!"); - } #ifdef __APPLE__ if constexpr (std::is_floating_point_v) { mac_os_atomic_fetch_sub(&default_label_value_.local_value(), value); @@ -61,29 +39,58 @@ class basic_gauge : public basic_counter { default_label_value_.dec(value); #endif } +}; +using gauge_t = basic_static_gauge; +using gauge_d = basic_static_gauge; + +template +class basic_dynamic_gauge : public basic_dynamic_counter { + using metric_t::set_metric_type; + using basic_dynamic_counter::value_map_; + using basic_dynamic_counter::mtx_; + using basic_dynamic_counter::dupli_count_; - void dec(const std::vector& labels_value, value_type value = 1) { + public: + basic_dynamic_gauge(std::string name, std::string help, + std::array labels_name, + size_t dupli_count = 2) + : basic_dynamic_counter(std::move(name), std::move(help), + std::move(labels_name), + dupli_count) { + set_metric_type(MetricType::Gauge); + } + + void dec(const std::array& labels_value, + value_type value = 1) { if (value == 0) { return; } - validate(labels_value, value); - if (use_atomic_) { - if (labels_value != labels_value_) { - throw std::invalid_argument( - "the given labels_value is not match with origin labels_value"); - } - set_value_static(atomic_value_map_[labels_value].local_value(), value, - op_type_t::DEC); - } - else { - std::lock_guard lock(mtx_); - stat_metric(labels_value); - set_value_dynamic(value_map_[labels_value].local_value(), value, - op_type_t::DEC); + std::lock_guard lock(mtx_); + auto [it, r] = value_map_.try_emplace( + labels_value, thread_local_value(dupli_count_)); + if (r) { + g_user_metric_label_count++; // TODO: use thread local } + + set_value(it->second.local_value(), value, op_type_t::DEC); } }; -using gauge_t = basic_gauge; -using gauge_d = basic_gauge; + +using dynamic_gauge_1t = basic_dynamic_gauge; +using dynamic_gauge_1d = basic_dynamic_gauge; + +using dynamic_gauge_t = basic_dynamic_gauge; +using dynamic_gauge_d = basic_dynamic_gauge; +using dynamic_gauge_2t = dynamic_gauge_t; +using dynamic_gauge_2d = dynamic_gauge_d; + +using dynamic_gauge_3t = basic_dynamic_gauge; +using dynamic_gauge_3d = basic_dynamic_gauge; + +using dynamic_gauge_4t = basic_dynamic_gauge; +using dynamic_gauge_4d = basic_dynamic_gauge; + +using dynamic_gauge_5t = basic_dynamic_gauge; +using dynamic_gauge_5d = basic_dynamic_gauge; } // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/histogram.hpp b/include/ylt/metric/histogram.hpp index 623215fc8..7da52da4f 100644 --- a/include/ylt/metric/histogram.hpp +++ b/include/ylt/metric/histogram.hpp @@ -27,73 +27,27 @@ REFLECTION(json_histogram_t, name, help, type, metrics); #endif template -class basic_histogram : public metric_t { +class basic_static_histogram : public static_metric { public: - basic_histogram(std::string name, std::string help, - std::vector buckets, size_t dupli_count = 2) - : bucket_boundaries_(buckets), - metric_t(MetricType::Histogram, std::move(name), std::move(help)), + basic_static_histogram(std::string name, std::string help, + std::vector buckets, size_t dupli_count = 2) + : bucket_boundaries_(std::move(buckets)), + static_metric(MetricType::Histogram, std::move(name), std::move(help)), sum_(std::make_shared("", "", dupli_count)) { - if (!is_strict_sorted(begin(bucket_boundaries_), end(bucket_boundaries_))) { - throw std::invalid_argument("Bucket Boundaries must be strictly sorted"); - } - - g_user_metric_count++; - - for (size_t i = 0; i < buckets.size() + 1; i++) { - bucket_counts_.push_back(std::make_shared("", "")); - } - use_atomic_ = true; + init_bucket_counter(dupli_count, bucket_boundaries_.size()); } - basic_histogram(std::string name, std::string help, - std::vector buckets, - std::vector labels_name, size_t dupli_count = 2) - : bucket_boundaries_(buckets), - metric_t(MetricType::Histogram, name, help, labels_name), - sum_(std::make_shared(name, help, labels_name, dupli_count)) { - if (!is_strict_sorted(begin(bucket_boundaries_), end(bucket_boundaries_))) { - throw std::invalid_argument("Bucket Boundaries must be strictly sorted"); - } - - g_user_metric_count++; - - for (size_t i = 0; i < buckets.size() + 1; i++) { - bucket_counts_.push_back( - std::make_shared(name, help, labels_name, dupli_count)); - } - } - - basic_histogram(std::string name, std::string help, - std::vector buckets, - std::map labels, - size_t dupli_count = std::thread::hardware_concurrency()) - : bucket_boundaries_(buckets), - metric_t(MetricType::Histogram, name, help, labels), - sum_(std::make_shared(name, help, labels, dupli_count)) { - if (!is_strict_sorted(begin(bucket_boundaries_), end(bucket_boundaries_))) { - throw std::invalid_argument("Bucket Boundaries must be strictly sorted"); - } - - g_user_metric_count++; - - for (size_t i = 0; i < buckets.size() + 1; i++) { - bucket_counts_.push_back( - std::make_shared(name, help, labels, dupli_count)); - } - use_atomic_ = true; + basic_static_histogram(std::string name, std::string help, + std::vector buckets, + std::map labels, + size_t dupli_count = 2) + : bucket_boundaries_(std::move(buckets)), + static_metric(MetricType::Histogram, name, help, labels), + sum_(std::make_shared("", "", dupli_count)) { + init_bucket_counter(dupli_count, bucket_boundaries_.size()); } void observe(value_type value) { - if (!labels_value_.empty()) { - observe(labels_value_, value); - return; - } - - if (!use_atomic_ || !labels_name_.empty()) { - throw std::invalid_argument("not a default label metric"); - } - const auto bucket_index = static_cast( std::distance(bucket_boundaries_.begin(), std::lower_bound(bucket_boundaries_.begin(), @@ -102,32 +56,12 @@ class basic_histogram : public metric_t { bucket_counts_[bucket_index]->inc(); } - void observe(const std::vector &labels_value, value_type value) { - if (sum_->labels_name().empty()) { - throw std::invalid_argument("not a label metric"); - } - - const auto bucket_index = static_cast( - std::distance(bucket_boundaries_.begin(), - std::lower_bound(bucket_boundaries_.begin(), - bucket_boundaries_.end(), value))); - sum_->inc(labels_value, value); - bucket_counts_[bucket_index]->inc(labels_value); - } - auto get_bucket_counts() { return bucket_counts_; } - bool has_label_value(const std::string &label_val) override { - return sum_->has_label_value(label_val); - } - void serialize(std::string &str) override { - if (!sum_->labels_name().empty()) { - serialize_with_labels(str); - return; - } + auto val = sum_->value(); - if (sum_->value() == 0) { + if (val == 0) { return; } @@ -137,6 +71,12 @@ class basic_histogram : public metric_t { for (size_t i = 0; i < bucket_counts.size(); i++) { auto counter = bucket_counts[i]; str.append(name_).append("_bucket{"); + + if (!labels_name_.empty()) { + build_label_string(str, labels_name_, labels_value_); + str.append(","); + } + if (i == bucket_boundaries_.size()) { str.append("le=\"").append("+Inf").append("\"} "); } @@ -151,10 +91,7 @@ class basic_histogram : public metric_t { str.append("\n"); } - str.append(name_) - .append("_sum ") - .append(std::to_string(sum_->value())) - .append("\n"); + str.append(name_).append("_sum ").append(std::to_string(val)).append("\n"); str.append(name_) .append("_count ") @@ -164,12 +101,8 @@ class basic_histogram : public metric_t { #ifdef CINATRA_ENABLE_METRIC_JSON void serialize_to_json(std::string &str) override { - if (!sum_->labels_name().empty()) { - serialize_to_json_with_labels(str); - return; - } - - if (sum_->value() == 0) { + auto val = sum_->value(); + if (val == 0) { return; } @@ -193,7 +126,11 @@ class basic_histogram : public metric_t { } } metric.count = (int64_t)count; - metric.sum = sum_->value(); + metric.sum = val; + + for (size_t i = 0; i < labels_value_.size(); i++) { + metric.labels[labels_name_[i]] = labels_value_[i]; + } hist.metrics.push_back(std::move(metric)); @@ -202,6 +139,15 @@ class basic_histogram : public metric_t { #endif private: + void init_bucket_counter(size_t dupli_count, size_t bucket_size) { + g_user_metric_count++; + + for (size_t i = 0; i < bucket_size + 1; i++) { + bucket_counts_.push_back( + std::make_shared("", "", dupli_count)); + } + } + template bool is_strict_sorted(ForwardIterator first, ForwardIterator last) { return std::adjacent_find(first, last, @@ -209,12 +155,65 @@ class basic_histogram : public metric_t { ForwardIterator>::value_type>()) == last; } - void serialize_with_labels(std::string &str) { + std::vector bucket_boundaries_; + std::vector> bucket_counts_; // readonly + std::shared_ptr sum_; +}; +using histogram_t = basic_static_histogram; +using histogram_d = basic_static_histogram; + +template +class basic_dynamic_histogram : public dynamic_metric { + public: + basic_dynamic_histogram(std::string name, std::string help, + std::vector buckets, + std::array labels_name, + size_t dupli_count = 2) + : bucket_boundaries_(buckets), + dynamic_metric(MetricType::Histogram, name, help, labels_name), + sum_(std::make_shared>( + name, help, labels_name, dupli_count)) { + g_user_metric_count++; + + for (size_t i = 0; i < buckets.size() + 1; i++) { + bucket_counts_.push_back( + std::make_shared>( + name, help, labels_name, dupli_count)); + } + } + + void observe(const std::array &labels_value, + value_type value) { + const auto bucket_index = static_cast( + std::distance(bucket_boundaries_.begin(), + std::lower_bound(bucket_boundaries_.begin(), + bucket_boundaries_.end(), value))); + sum_->inc(labels_value, value); + bucket_counts_[bucket_index]->inc(labels_value); + } + + auto get_bucket_counts() { return bucket_counts_; } + + bool has_label_value(const std::string &label_val) override { + return sum_->has_label_value(label_val); + } + + bool has_label_value(const std::regex ®ex) override { + return sum_->has_label_value(regex); + } + + bool has_label_value(const std::vector &label_value) override { + return sum_->has_label_value(label_value); + } + + void serialize(std::string &str) override { auto value_map = sum_->value_map(); if (value_map.empty()) { return; } + serialize_head(str); + std::string value_str; auto bucket_counts = get_bucket_counts(); for (auto &[labels_value, value] : value_map) { @@ -226,8 +225,10 @@ class basic_histogram : public metric_t { for (size_t i = 0; i < bucket_counts.size(); i++) { auto counter = bucket_counts[i]; value_str.append(name_).append("_bucket{"); - build_label_string(value_str, sum_->labels_name(), labels_value); - value_str.append(","); + if (!labels_name_.empty()) { + build_label_string(value_str, labels_name_, labels_value); + value_str.append(","); + } if (i == bucket_boundaries_.size()) { value_str.append("le=\"").append("+Inf").append("\"} "); @@ -247,7 +248,6 @@ class basic_histogram : public metric_t { return; } - serialize_head(str); str.append(value_str); str.append(name_); @@ -267,7 +267,7 @@ class basic_histogram : public metric_t { } #ifdef CINATRA_ENABLE_METRIC_JSON - void serialize_to_json_with_labels(std::string &str) { + void serialize_to_json(std::string &str) override { auto value_map = sum_->value_map(); if (value_map.empty()) { return; @@ -313,11 +313,34 @@ class basic_histogram : public metric_t { } #endif + private: + template + bool is_strict_sorted(ForwardIterator first, ForwardIterator last) { + return std::adjacent_find(first, last, + std::greater_equal::value_type>()) == last; + } + std::vector bucket_boundaries_; - std::vector>> + std::vector>> bucket_counts_; // readonly - std::shared_ptr sum_; + std::shared_ptr> sum_; }; -using histogram_t = basic_histogram; -using histogram_d = basic_histogram; + +using dynamic_histogram_1t = basic_dynamic_histogram; +using dynamic_histogram_1d = basic_dynamic_histogram; + +using dynamic_histogram_2t = basic_dynamic_histogram; +using dynamic_histogram_2d = basic_dynamic_histogram; +using dynamic_histogram_t = dynamic_histogram_2t; +using dynamic_histogram_d = dynamic_histogram_2d; + +using dynamic_histogram_3t = basic_dynamic_histogram; +using dynamic_histogram_3d = basic_dynamic_histogram; + +using dynamic_histogram_4t = basic_dynamic_histogram; +using dynamic_histogram_4d = basic_dynamic_histogram; + +using dynamic_histogram_5t = basic_dynamic_histogram; +using dynamic_histogram_5d = basic_dynamic_histogram; } // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/metric.hpp b/include/ylt/metric/metric.hpp index b5caba8f6..923fca18a 100644 --- a/include/ylt/metric/metric.hpp +++ b/include/ylt/metric/metric.hpp @@ -48,25 +48,6 @@ struct metric_filter_options { bool is_white = true; }; -struct vector_hash { - size_t operator()(const std::vector& vec) const { - unsigned int seed = 131; - unsigned int hash = 0; - - for (const auto& str : vec) { - for (auto ch : str) { - hash = hash * seed + ch; - } - } - - return (hash & 0x7FFFFFFF); - } -}; - -template -using metric_hash_map = - std::unordered_map, T, vector_hash>; - class metric_t { public: metric_t() = default; @@ -75,10 +56,14 @@ class metric_t { name_(std::move(name)), help_(std::move(help)), metric_created_time_(std::chrono::system_clock::now()) {} + + template metric_t(MetricType type, std::string name, std::string help, - std::vector labels_name) + std::array labels_name) : metric_t(type, std::move(name), std::move(help)) { - labels_name_ = std::move(labels_name); + for (size_t i = 0; i < N; i++) { + labels_name_.push_back(std::move(labels_name[i])); + } } metric_t(MetricType type, std::string name, std::string help, @@ -94,6 +79,8 @@ class metric_t { std::string_view name() { return name_; } + const std::string& str_name() { return name_; } + std::string_view help() { return help_; } MetricType metric_type() { return type_; } @@ -122,7 +109,29 @@ class metric_t { return static_labels_; } - virtual bool has_label_value(const std::string& label_value) { return false; } + virtual bool has_label_value(const std::string& label_value) { + return std::find(labels_value_.begin(), labels_value_.end(), label_value) != + labels_value_.end(); + } + + virtual bool has_label_value(const std::vector& label_value) { + return labels_value_ == label_value; + } + virtual bool has_label_value(const std::regex& regex) { + auto it = std::find_if(labels_value_.begin(), labels_value_.end(), + [&](auto& value) { + return std::regex_match(value, regex); + }); + + return it != labels_value_.end(); + } + bool has_label_name(const std::vector& label_name) { + return labels_name_ == label_name; + } + bool has_label_name(const std::string& label_name) { + return std::find(labels_name_.begin(), labels_name_.end(), label_name) != + labels_name_.end(); + } virtual void serialize(std::string& str) {} @@ -143,8 +152,6 @@ class metric_t { } #endif - bool is_atomic() const { return use_atomic_; } - template T* as() { return dynamic_cast(this); @@ -163,7 +170,7 @@ class metric_t { void build_label_string(std::string& str, const std::vector& label_name, - const std::vector& label_value) { + const auto& label_value) { for (size_t i = 0; i < label_name.size(); i++) { str.append(label_name[i]) .append("=\"") @@ -198,15 +205,16 @@ class metric_t { std::map static_labels_; std::vector labels_name_; // read only std::vector labels_value_; // read only - bool use_atomic_ = false; std::chrono::system_clock::time_point metric_created_time_{}; }; -template -struct metric_manager_t; +class static_metric : public metric_t { + using metric_t::metric_t; +}; -struct ylt_system_tag_t {}; -using system_metric_manager = metric_manager_t; +class dynamic_metric : public metric_t { + using metric_t::metric_t; +}; inline std::atomic g_user_metric_label_count = 0; inline std::atomic g_summary_failed_count = 0; @@ -217,482 +225,4 @@ inline std::atomic ylt_metric_capacity = 10000000; inline void set_metric_capacity(int64_t max_count) { ylt_metric_capacity = max_count; } - -template -struct metric_manager_t { - metric_manager_t(metric_manager_t const&) = delete; - metric_manager_t(metric_manager_t&&) = delete; - metric_manager_t& operator=(metric_manager_t const&) = delete; - metric_manager_t& operator=(metric_manager_t&&) = delete; - - struct null_mutex_t { - void lock() {} - void unlock() {} - }; - - static metric_manager_t& instance() { - static metric_manager_t inst; - return inst; - } - - // create and register metric - template - std::shared_ptr create_metric_static(const std::string& name, - const std::string& help, - Args&&... args) { - auto m = std::make_shared(name, help, std::forward(args)...); - bool r = register_metric_static(m); - if (!r) { - return nullptr; - } - return m; - } - - template - std::shared_ptr create_metric_dynamic(const std::string& name, - const std::string& help, - Args&&... args) { - auto& inst = metric_manager_t::instance(); - auto m = std::make_shared(name, help, std::forward(args)...); - bool r = inst.register_metric_dynamic(m); - if (!r) { - return nullptr; - } - return m; - } - - bool register_metric_static(std::shared_ptr metric) { - return register_metric_impl(metric); - } - - bool register_metric_dynamic(std::shared_ptr metric) { - return register_metric_impl(metric); - } - - bool remove_metric_static(const std::string& name) { - return remove_metric_impl(name); - } - - bool remove_metric_dynamic(const std::string& name) { - return remove_metric_impl(name); - } - - bool remove_metric_dynamic(std::shared_ptr metric) { - return remove_metric_impl(std::string(metric->name())); - } - - void remove_metric_dynamic(const std::vector& names) { - if (names.empty()) { - return; - } - auto lock = get_lock(); - for (auto& name : names) { - metric_map_.erase(name); - } - } - - void remove_metric_dynamic(std::vector> metrics) { - if (metrics.empty()) { - return; - } - auto lock = get_lock(); - for (auto& metric : metrics) { - metric_map_.erase(std::string(metric->name())); - } - } - - template - bool register_metric_dynamic(Metrics... metrics) { - bool r = true; - ((void)(r && (r = register_metric_impl(metrics), true)), ...); - return r; - } - - bool register_metric_dynamic(std::vector> metrics) { - bool r = true; - std::vector> vec; - for (auto& metric : metrics) { - r = register_metric_impl(metric); - if (!r) { - r = false; - break; - } - - vec.push_back(metric); - } - - if (!r) { - remove_metric_dynamic(vec); - } - - return r; - } - - template - bool register_metric_static(Metrics... metrics) { - bool r = true; - ((void)(r && (r = register_metric_impl(metrics), true)), ...); - return r; - } - - auto get_metrics() { - if (need_lock_) { - return collect(); - } - else { - return collect(); - } - } - - auto metric_map_static() { return metric_map_impl(); } - auto metric_map_dynamic() { return metric_map_impl(); } - - size_t metric_count_static() { return metric_count_impl(); } - - size_t metric_count_dynamic() { return metric_count_impl(); } - - std::vector metric_keys_static() { - return metric_keys_impl(); - } - - std::vector metric_keys_dynamic() { - return metric_keys_impl(); - } - - // static labels: {{"method", "GET"}, {"url", "/"}} - std::vector> get_metric_by_labels_static( - const std::map& labels) { - std::vector> vec; - auto map = metric_map_static(); - for (auto& [name, m] : map) { - const auto& static_labels = m->get_static_labels(); - if (static_labels == labels) { - vec.push_back(m); - } - } - return vec; - } - - // static label: {"method", "GET"} - std::vector> get_metric_by_label_static( - const std::pair& label) { - std::vector> vec; - auto map = metric_map_static(); - for (auto& [name, t] : map) { - const auto& static_labels = t->get_static_labels(); - for (const auto& pair : static_labels) { - if (pair.first == label.first && pair.second == label.second) { - vec.push_back(t); - } - } - } - return vec; - } - - // labels: {{"method", "POST"}, {"code", "200"}} - std::vector> get_metric_by_labels_dynamic( - const std::map& labels) { - std::vector> vec; - auto map = metric_map_dynamic(); - for (auto& [name, t] : map) { - auto labels_name = t->labels_name(); - - for (auto& [k, v] : labels) { - if (auto it = std::find(labels_name.begin(), labels_name.end(), k); - it != labels_name.end()) { - if (t->has_label_value(v)) { - vec.push_back(t); - } - } - } - } - - return vec; - } - - template - std::shared_ptr get_metric_static(const std::string& name) { - auto m = get_metric_impl(name); - if (m == nullptr) { - return nullptr; - } - return std::dynamic_pointer_cast(m); - } - - template - std::shared_ptr get_metric_dynamic(const std::string& name) { - auto m = get_metric_impl(name); - if (m == nullptr) { - return nullptr; - } - return std::dynamic_pointer_cast(m); - } - - std::string serialize(const std::vector>& metrics) { - std::string str; - for (auto& m : metrics) { - if (m->metric_type() == MetricType::Summary) { - async_simple::coro::syncAwait(m->serialize_async(str)); - } - else { - m->serialize(str); - } - } - - return str; - } - - std::string serialize_static() { return serialize(collect()); } - - std::string serialize_dynamic() { return serialize(collect()); } - -#ifdef CINATRA_ENABLE_METRIC_JSON - std::string serialize_to_json_static() { - auto metrics = collect(); - return serialize_to_json(metrics); - } - - std::string serialize_to_json_dynamic() { - auto metrics = collect(); - return serialize_to_json(metrics); - } - - std::string serialize_to_json( - const std::vector>& metrics) { - if (metrics.empty()) { - return ""; - } - std::string str; - str.append("["); - for (auto& m : metrics) { - size_t start = str.size(); - if (m->metric_type() == MetricType::Summary) { - async_simple::coro::syncAwait(m->serialize_to_json_async(str)); - } - else { - m->serialize_to_json(str); - } - - if (str.size() > start) - str.append(","); - } - - if (str.size() == 1) { - return ""; - } - - str.back() = ']'; - return str; - } -#endif - - std::vector> filter_metrics_static( - const metric_filter_options& options) { - return filter_metrics(options); - } - - std::vector> filter_metrics_dynamic( - const metric_filter_options& options) { - return filter_metrics(options); - } - - private: - metric_manager_t() = default; - - template - void check_lock() { - if (need_lock_ != need_lock) { - std::string str = "need lock "; - std::string s = need_lock_ ? "true" : "false"; - std::string r = need_lock ? "true" : "false"; - str.append(s).append(" but set as ").append(r); - throw std::invalid_argument(str); - } - } - - template - auto get_lock() { - check_lock(); - if constexpr (need_lock) { - return std::scoped_lock(mtx_); - } - else { - return std::scoped_lock(null_mtx_); - } - } - - template - bool register_metric_impl(std::shared_ptr metric) { - // the first time regiter_metric will set metric_manager_t lock or not lock. - // visit metric_manager_t with different lock strategy will cause throw - // exception. - std::call_once(flag_, [this] { - need_lock_ = need_lock; - }); - - std::string name(metric->name()); - auto lock = get_lock(); - if (g_user_metric_count > ylt_metric_capacity) { - CINATRA_LOG_ERROR << "metric count at capacity size: " - << g_user_metric_count; - return false; - } - bool r = metric_map_.emplace(name, std::move(metric)).second; - if (!r) { - CINATRA_LOG_ERROR << "duplicate registered metric name: " << name; - } - return r; - } - - template - size_t remove_metric_impl(const std::string& name) { - auto lock = get_lock(); - return metric_map_.erase(name); - } - - template - auto metric_map_impl() { - auto lock = get_lock(); - return metric_map_; - } - - template - size_t metric_count_impl() { - auto lock = get_lock(); - return metric_map_.size(); - } - - template - std::vector metric_keys_impl() { - std::vector keys; - { - auto lock = get_lock(); - for (auto& pair : metric_map_) { - keys.push_back(pair.first); - } - } - - return keys; - } - - template - std::shared_ptr get_metric_impl(const std::string& name) { - auto lock = get_lock(); - auto it = metric_map_.find(name); - if (it == metric_map_.end()) { - return nullptr; - } - return it->second; - } - - template - auto collect() { - std::vector> metrics; - { - auto lock = get_lock(); - for (auto& pair : metric_map_) { - metrics.push_back(pair.second); - } - } - return metrics; - } - - void filter_by_label_name( - std::vector>& filtered_metrics, - std::shared_ptr m, const metric_filter_options& options, - std::vector& indexs, size_t index) { - const auto& labels_name = m->labels_name(); - for (auto& label_name : labels_name) { - if (std::regex_match(label_name, *options.label_regex)) { - if (options.is_white) { - filtered_metrics.push_back(m); - } - else { - indexs.push_back(index); - } - } - } - } - - template - std::vector> filter_metrics( - const metric_filter_options& options) { - auto metrics = collect(); - if (!options.name_regex && !options.label_regex) { - return metrics; - } - - std::vector> filtered_metrics; - std::vector indexs; - size_t index = 0; - for (auto& m : metrics) { - if (options.name_regex && !options.label_regex) { - if (std::regex_match(std::string(m->name()), *options.name_regex)) { - if (options.is_white) { - filtered_metrics.push_back(m); - } - else { - indexs.push_back(index); - } - } - } - else if (options.label_regex && !options.name_regex) { - filter_by_label_name(filtered_metrics, m, options, indexs, index); - } - else { - if (std::regex_match(std::string(m->name()), *options.name_regex)) { - filter_by_label_name(filtered_metrics, m, options, indexs, index); - } - } - index++; - } - - if (!options.is_white) { - for (size_t i : indexs) { - metrics.erase(std::next(metrics.begin(), i)); - } - return metrics; - } - - return filtered_metrics; - } - - std::mutex mtx_; - std::unordered_map> metric_map_; - - null_mutex_t null_mtx_; - std::atomic_bool need_lock_ = true; - std::once_flag flag_; -}; - -struct ylt_default_metric_tag_t {}; -using default_metric_manager = metric_manager_t; - -template -struct metric_collector_t { - static std::string serialize() { - auto vec = get_all_metrics(); - return default_metric_manager::instance().serialize(vec); - } - -#ifdef CINATRA_ENABLE_METRIC_JSON - static std::string serialize_to_json() { - auto vec = get_all_metrics(); - return default_metric_manager::instance().serialize_to_json(vec); - } -#endif - - static std::vector> get_all_metrics() { - std::vector> vec; - (append_vector(vec), ...); - return vec; - } - - private: - template - static void append_vector(std::vector>& vec) { - auto v = T::instance().get_metrics(); - vec.insert(vec.end(), v.begin(), v.end()); - } -}; } // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/metric_manager.hpp b/include/ylt/metric/metric_manager.hpp new file mode 100644 index 000000000..d198bbc38 --- /dev/null +++ b/include/ylt/metric/metric_manager.hpp @@ -0,0 +1,552 @@ +#pragma once +#include +#include +#include + +#include "metric.hpp" + +namespace ylt::metric { +class manager_helper { + public: + static bool register_metric(auto& metric_map, auto metric) { + if (g_user_metric_count > ylt_metric_capacity) { + CINATRA_LOG_ERROR << "metric count at capacity size: " + << 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>& metrics) { + std::string str; + for (auto& m : metrics) { + if (m->metric_type() == MetricType::Summary) { + async_simple::coro::syncAwait(m->serialize_async(str)); + } + else { + m->serialize(str); + } + } + + return str; + } + +#ifdef CINATRA_ENABLE_METRIC_JSON + static std::string serialize_to_json( + const std::vector>& metrics) { + if (metrics.empty()) { + return ""; + } + std::string str; + str.append("["); + for (auto& m : metrics) { + size_t start = str.size(); + if (m->metric_type() == MetricType::Summary) { + async_simple::coro::syncAwait(m->serialize_to_json_async(str)); + } + else { + m->serialize_to_json(str); + } + + if (str.size() > start) + str.append(","); + } + + if (str.size() == 1) { + return ""; + } + + str.back() = ']'; + return str; + } +#endif + + static std::vector> filter_metrics_by_label_value( + auto& metrics, const std::regex& label_regex, bool is_white) { + std::vector> filtered_metrics; + std::vector indexs; + size_t index = 0; + for (auto& m : metrics) { + if (m->has_label_value(label_regex)) { + if (is_white) { + filtered_metrics.push_back(m); + } + else { + indexs.push_back(index); + } + } + + index++; + } + + if (!is_white) { + for (size_t i : indexs) { + metrics.erase(std::next(metrics.begin(), i)); + } + return metrics; + } + + return filtered_metrics; + } + + static std::vector> filter_metrics( + auto& metrics, const metric_filter_options& options) { + if (!options.name_regex && !options.label_regex) { + return metrics; + } + + std::vector> filtered_metrics; + for (auto& m : metrics) { + if (options.name_regex && + !options.label_regex) { // only check metric name + if (std::regex_match(std::string(m->name()), *options.name_regex)) { + filtered_metrics.push_back(m); + } + } + else if (options.label_regex && + !options.name_regex) { // only check label name + filter_by_label_name(filtered_metrics, m, options); + } + else { + if (std::regex_match( + std::string(m->name()), + *options.name_regex)) { // check label name and label value + filter_by_label_name(filtered_metrics, m, options); + } + } + } + + if (!options.is_white) { + auto it = metrics.begin(); + 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>& filtered_metrics, + std::shared_ptr m, const metric_filter_options& options) { + 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 +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& instance() { + static static_metric_manager inst; + return inst; + } + + template + std::pair> create_metric_static( + const std::string& name, const std::string& help, Args&&... args) { + auto m = std::make_shared(name, help, std::forward(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 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> metrics; + + for (auto& pair : metric_map_) { + metrics.push_back(pair.second); + } + + return metrics; + } + + std::string serialize(const std::vector>& 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 + std::shared_ptr get_metric_static(const std::string& name) { + static_assert(std::is_base_of_v, + "must be dynamic metric"); + auto it = metric_map_.find(name); + if (it == metric_map_.end()) { + return nullptr; + } + return std::dynamic_pointer_cast(it->second); + } + + std::shared_ptr 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> get_metric_by_label( + const std::map& labels) { + std::vector> metrics; + + for (auto& [key, m] : metric_map_) { + if (m->get_static_labels() == labels) { + metrics.push_back(m); + } + } + + return metrics; + } + + std::vector> filter_metrics_by_label_value( + const std::regex& label_regex, bool is_white = true) { + auto metrics = collect(); + return manager_helper::filter_metrics_by_label_value(metrics, label_regex, + is_white); + } + + std::vector> filter_metrics_static( + const metric_filter_options& options) { + auto metrics = collect(); + return manager_helper::filter_metrics(metrics, options); + } + + private: + static_metric_manager() = default; + + std::unordered_map> metric_map_; +}; + +// using metric_manager_t = static_metric_manager; + +template +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& instance() { + static dynamic_metric_manager inst; + return inst; + } + + template + std::pair> create_metric_dynamic( + const std::string& name, const std::string& help, Args&&... args) { + auto m = std::make_shared(name, help, std::forward(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 metric) { + std::unique_lock lock(mtx_); + return manager_helper::register_metric(metric_map_, metric); + } + + bool register_metric(std::vector> 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>& 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) { + std::unique_lock lock(mtx_); + return metric_map_.erase(name); + } + + bool remove_metric(std::shared_ptr metric) { + if (metric == nullptr) { + return false; + } + + return remove_metric(metric->str_name()); + } + + void remove_metric(const std::vector& names) { + if (names.empty()) { + return; + } + + for (auto& name : names) { + remove_metric(name); + } + } + + void remove_metric(std::vector> metrics) { + if (metrics.empty()) { + return; + } + + for (auto& metric : metrics) { + remove_metric(metric); + } + } + + void remove_metric_by_label( + const std::vector>& labels) { + std::vector label_value; + for (auto& [k, v] : labels) { + label_value.push_back(v); + } + + remove_metric_by_label_value(label_value); + } + + void remove_metric_by_label_name( + const std::vector& labels_name) { + std::unique_lock lock(mtx_); + for (auto& [name, m] : metric_map_) { + if (m->labels_name() == labels_name) { + metric_map_.erase(name); + break; + } + } + } + + void remove_metric_by_label_name(std::string_view labels_name) { + std::unique_lock lock(mtx_); + for (auto it = metric_map_.cbegin(); it != metric_map_.cend();) { + auto& names = it->second->labels_name(); + if (auto sit = std::find(names.begin(), names.end(), labels_name); + sit != names.end()) { + metric_map_.erase(it++); + } + else { + ++it; + } + } + } + + size_t metric_count() { + std::unique_lock lock(mtx_); + return metric_map_.size(); + } + + auto metric_map() { + std::unique_lock lock(mtx_); + return metric_map_; + } + + auto collect() { + std::vector> metrics; + { + std::unique_lock lock(mtx_); + for (auto& pair : metric_map_) { + metrics.push_back(pair.second); + } + } + return metrics; + } + + template + std::shared_ptr get_metric_dynamic(const std::string& name) { + static_assert(std::is_base_of_v, + "must be dynamic metric"); + auto map = metric_map(); + auto it = map.find(name); + if (it == map.end()) { + return nullptr; + } + return std::dynamic_pointer_cast(it->second); + } + + std::shared_ptr get_metric_by_name(std::string_view name) { + auto map = metric_map(); + auto it = map.find(name); + if (it == map.end()) { + return nullptr; + } + + return it->second; + } + + std::vector> get_metric_by_label( + const std::vector>& labels) { + std::vector label_value; + for (auto& [k, v] : labels) { + label_value.push_back(v); + } + + return get_metric_by_label_value(label_value); + } + + std::vector> get_metric_by_label_name( + const std::vector& labels_name) { + auto map = metric_map(); + std::vector> vec; + for (auto& [name, m] : map) { + if (m->labels_name() == labels_name) { + vec.push_back(m); + } + } + return vec; + } + + std::vector> filter_metrics_dynamic( + const metric_filter_options& options) { + auto metrics = collect(); + return manager_helper::filter_metrics(metrics, options); + } + + std::vector> filter_metrics_by_label_value( + const std::regex& label_regex, bool is_white = true) { + auto metrics = collect(); + return manager_helper::filter_metrics_by_label_value(metrics, label_regex, + is_white); + } + + private: + dynamic_metric_manager() = default; + + std::vector> get_metric_by_label_value( + const std::vector& label_value) { + auto map = metric_map(); + std::vector> vec; + for (auto& [name, m] : map) { + if (m->has_label_value(label_value)) { + vec.push_back(m); + } + } + return vec; + } + + void remove_metric_by_label_value( + const std::vector& label_value) { + std::unique_lock lock(mtx_); + for (auto& [name, m] : metric_map_) { + if (m->has_label_value(label_value)) { + metric_map_.erase(name); + break; + } + } + } + + std::shared_mutex mtx_; + std::unordered_map> metric_map_; +}; + +struct ylt_default_metric_tag_t {}; +using default_static_metric_manager = + static_metric_manager; +using default_dynamiv_metric_manager = + dynamic_metric_manager; + +template +struct metric_manager_t; + +struct ylt_system_tag_t {}; +using system_metric_manager = static_metric_manager; + +template +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); + } +#endif + + static std::string serialize(std::vector> metrics) { + return manager_helper::serialize(metrics); + } + + static std::vector> get_all_metrics() { + std::vector> vec; + (append_vector(vec), ...); + return vec; + } + + static std::vector> filter_metrics( + const metric_filter_options& options) { + auto vec = get_all_metrics(); + return manager_helper::filter_metrics(vec, options); + } + + static std::vector> filter_metrics_by_label_value( + const std::regex& label_regex, bool is_white = true) { + auto vec = get_all_metrics(); + return manager_helper::filter_metrics_by_label_value(vec, label_regex, + is_white); + } + + private: + template + static void append_vector(std::vector>& vec) { + auto v = T::instance().collect(); + vec.insert(vec.end(), v.begin(), v.end()); + } +}; +} // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/summary.hpp b/include/ylt/metric/summary.hpp index ac0910525..bf013980e 100644 --- a/include/ylt/metric/summary.hpp +++ b/include/ylt/metric/summary.hpp @@ -27,97 +27,46 @@ struct json_summary_t { REFLECTION(json_summary_t, name, help, type, metrics); #endif -struct summary_label_sample { - std::vector labels_value; - double value; +struct block_t { + std::atomic stop_ = false; + ylt::detail::moodycamel::ConcurrentQueue sample_queue_; + std::shared_ptr quantile_values_; + std::uint64_t count_; + double sum_; }; -class summary_t : public metric_t { +class summary_t : public static_metric { public: using Quantiles = std::vector; summary_t(std::string name, std::string help, Quantiles quantiles, std::chrono::milliseconds max_age = std::chrono::seconds{60}, int age_buckets = 5) : quantiles_{std::move(quantiles)}, - metric_t(MetricType::Summary, std::move(name), std::move(help)), + static_metric(MetricType::Summary, std::move(name), std::move(help)), max_age_(max_age), age_buckets_(age_buckets) { init_no_label(max_age, age_buckets); } - summary_t(std::string name, std::string help, Quantiles quantiles, - std::vector labels_name, - std::chrono::milliseconds max_age = std::chrono::seconds{60}, - int age_buckets = 5) - : quantiles_{std::move(quantiles)}, - metric_t(MetricType::Summary, std::move(name), std::move(help), - std::move(labels_name)), - max_age_(max_age), - age_buckets_(age_buckets) { - init_block(labels_block_); - g_user_metric_count++; - } - summary_t(std::string name, std::string help, Quantiles quantiles, std::map static_labels, std::chrono::milliseconds max_age = std::chrono::seconds{60}, int age_buckets = 5) : quantiles_{std::move(quantiles)}, - metric_t(MetricType::Summary, std::move(name), std::move(help), - std::move(static_labels)), + static_metric(MetricType::Summary, std::move(name), std::move(help), + std::move(static_labels)), max_age_(max_age), age_buckets_(age_buckets) { - if (static_labels_.empty()) { - init_no_label(max_age, age_buckets); - return; - } - - init_block(labels_block_); - labels_block_->label_quantile_values_[labels_value_] = - std::make_shared(quantiles_, max_age, age_buckets); - labels_block_->label_count_.emplace(labels_value_, 0); - labels_block_->label_sum_.emplace(labels_value_, 0); - use_atomic_ = true; - g_user_metric_count++; + init_no_label(max_age, age_buckets); } ~summary_t() { if (block_) { block_->stop_ = true; } - - if (labels_block_) { - labels_block_->stop_ = true; - } } - struct block_t { - std::atomic stop_ = false; - ylt::detail::moodycamel::ConcurrentQueue sample_queue_; - std::shared_ptr quantile_values_; - std::uint64_t count_; - double sum_; - }; - - struct labels_block_t { - std::atomic stop_ = false; - ylt::detail::moodycamel::ConcurrentQueue - sample_queue_; - metric_hash_map> - label_quantile_values_; - metric_hash_map label_count_; - metric_hash_map label_sum_; - }; - void observe(double value) { - if (!labels_value_.empty()) { - observe(labels_value_, value); - return; - } - - if (!labels_name_.empty()) { - throw std::invalid_argument("not a default label metric"); - } if (block_->sample_queue_.size_approx() >= 20000000) { g_summary_failed_count++; return; @@ -131,34 +80,8 @@ class summary_t : public metric_t { } } - void observe(std::vector labels_value, double value) { - if (labels_value.empty()) { - throw std::invalid_argument("not a label metric"); - } - if (use_atomic_) { - if (labels_value != labels_value_) { - throw std::invalid_argument("not equal with static label"); - } - } - if (labels_block_->sample_queue_.size_approx() >= 20000000) { - g_summary_failed_count++; - return; - } - labels_block_->sample_queue_.enqueue({std::move(labels_value), value}); - - bool expected = false; - if (is_coro_started_.compare_exchange_strong(expected, true)) { - start(labels_block_).via(excutor_->get_executor()).start([](auto &&) { - }); - } - } - async_simple::coro::Lazy> get_rates(double &sum, uint64_t &count) { - if (!labels_value_.empty()) { - co_return co_await get_rates(labels_value_, sum, count); - } - std::vector vec; if (quantiles_.empty()) { co_return std::vector{}; @@ -177,55 +100,6 @@ class summary_t : public metric_t { co_return vec; } - async_simple::coro::Lazy> get_rates( - const std::vector &labels_value, double &sum, - uint64_t &count) { - std::vector vec; - if (quantiles_.empty()) { - co_return std::vector{}; - } - - if (use_atomic_) { - if (labels_value != labels_value_) { - throw std::invalid_argument("not equal with static label"); - } - } - - co_await coro_io::post( - [this, &vec, &sum, &count, &labels_value] { - auto it = labels_block_->label_quantile_values_.find(labels_value); - if (it == labels_block_->label_quantile_values_.end()) { - return; - } - sum = labels_block_->label_sum_[labels_value]; - count = labels_block_->label_count_[labels_value]; - for (const auto &quantile : quantiles_) { - vec.push_back(it->second->get(quantile.quantile)); - } - }, - excutor_->get_executor()); - - co_return vec; - } - - metric_hash_map value_map() { - auto ret = async_simple::coro::syncAwait(coro_io::post( - [this] { - return labels_block_->label_sum_; - }, - excutor_->get_executor())); - return ret.value(); - } - - bool has_label_value(const std::string &label_val) override { - auto map = value_map(); - auto it = std::find_if(map.begin(), map.end(), [&label_val](auto &pair) { - auto &key = pair.first; - return std::find(key.begin(), key.end(), label_val) != key.end(); - }); - return it != map.end(); - } - async_simple::coro::Lazy get_sum() { auto ret = co_await coro_io::post( [this] { @@ -247,10 +121,6 @@ class summary_t : public metric_t { size_t size_approx() { return block_->sample_queue_.size_approx(); } async_simple::coro::Lazy serialize_async(std::string &str) override { - if (block_ == nullptr) { - co_await serialize_async_with_label(str); - co_return; - } if (quantiles_.empty()) { co_return; } @@ -263,7 +133,13 @@ class summary_t : public metric_t { for (size_t i = 0; i < quantiles_.size(); i++) { str.append(name_); - str.append("{quantile=\""); + str.append("{"); + if (!labels_name_.empty()) { + build_label_string(str, labels_name_, labels_value_); + str.append(","); + } + + str.append("quantile=\""); str.append(std::to_string(quantiles_[i].quantile)).append("\"} "); str.append(std::to_string(rates[i])).append("\n"); } @@ -278,11 +154,6 @@ class summary_t : public metric_t { #ifdef CINATRA_ENABLE_METRIC_JSON async_simple::coro::Lazy serialize_to_json_async( std::string &str) override { - if (block_ == nullptr) { - co_await serialize_to_json_with_label_async(str); - co_return; - } - if (quantiles_.empty()) { co_return; } @@ -295,6 +166,9 @@ class summary_t : public metric_t { json_summary_metric_t metric; for (size_t i = 0; i < quantiles_.size(); i++) { + for (size_t i = 0; i < labels_name_.size(); i++) { + metric.labels[labels_name_[i]] = labels_value_[i]; + } metric.quantiles.emplace(quantiles_[i].quantile, rates[i]); } @@ -318,7 +192,6 @@ class summary_t : public metric_t { init_block(block_); block_->quantile_values_ = std::make_shared(quantiles_, max_age, age_buckets); - use_atomic_ = true; g_user_metric_count++; } @@ -357,9 +230,126 @@ class summary_t : public metric_t { co_return; } + Quantiles quantiles_; // readonly + std::shared_ptr block_; + static inline std::shared_ptr excutor_ = + coro_io::create_io_context_pool(1); + std::chrono::milliseconds max_age_; + int age_buckets_; + std::atomic is_coro_started_ = false; +}; + +template +struct summary_label_sample { + std::array labels_value; + double value; +}; + +template +struct labels_block_t { + std::atomic stop_ = false; + ylt::detail::moodycamel::ConcurrentQueue> + sample_queue_; + dynamic_metric_hash_map, N> + label_quantile_values_; + dynamic_metric_hash_map label_count_; + dynamic_metric_hash_map label_sum_; +}; + +template +class basic_dynamic_summary : public dynamic_metric { + public: + using Quantiles = std::vector; + + basic_dynamic_summary( + std::string name, std::string help, Quantiles quantiles, + std::array labels_name, + std::chrono::milliseconds max_age = std::chrono::seconds{60}, + int age_buckets = 5) + : quantiles_{std::move(quantiles)}, + dynamic_metric(MetricType::Summary, std::move(name), std::move(help), + std::move(labels_name)), + max_age_(max_age), + age_buckets_(age_buckets) { + init_block(labels_block_); + g_user_metric_count++; + } + + ~basic_dynamic_summary() { + if (labels_block_) { + labels_block_->stop_ = true; + } + } + + void observe(std::array labels_value, double value) { + if (labels_block_->sample_queue_.size_approx() >= 20000000) { + g_summary_failed_count++; + return; + } + labels_block_->sample_queue_.enqueue({std::move(labels_value), value}); + + bool expected = false; + if (is_coro_started_.compare_exchange_strong(expected, true)) { + start(labels_block_).via(excutor_->get_executor()).start([](auto &&) { + }); + } + } + + async_simple::coro::Lazy> get_rates( + const std::array &labels_value, double &sum, + uint64_t &count) { + std::vector vec; + if (quantiles_.empty()) { + co_return std::vector{}; + } + + co_await coro_io::post( + [this, &vec, &sum, &count, &labels_value] { + auto it = labels_block_->label_quantile_values_.find(labels_value); + if (it == labels_block_->label_quantile_values_.end()) { + return; + } + sum = labels_block_->label_sum_[labels_value]; + count = labels_block_->label_count_[labels_value]; + for (const auto &quantile : quantiles_) { + vec.push_back(it->second->get(quantile.quantile)); + } + }, + excutor_->get_executor()); + + co_return vec; + } + + dynamic_metric_hash_map value_map() { + auto ret = async_simple::coro::syncAwait(coro_io::post( + [this] { + return labels_block_->label_sum_; + }, + excutor_->get_executor())); + return ret.value(); + } + + async_simple::coro::Lazy serialize_async(std::string &str) override { + co_await serialize_async_with_label(str); + } + +#ifdef CINATRA_ENABLE_METRIC_JSON + async_simple::coro::Lazy serialize_to_json_async( + std::string &str) override { + co_await serialize_to_json_with_label_async(str); + } +#endif + private: + template + void init_block(std::shared_ptr &block) { + block = std::make_shared(); + start(block).via(excutor_->get_executor()).start([](auto &&) { + }); + } + async_simple::coro::Lazy start( - std::shared_ptr label_block) { - summary_label_sample sample; + std::shared_ptr> label_block) { + summary_label_sample sample; size_t count = 1000000; while (!label_block->stop_) { size_t index = 0; @@ -479,12 +469,18 @@ class summary_t : public metric_t { #endif Quantiles quantiles_; // readonly - std::shared_ptr block_; - std::shared_ptr labels_block_; + std::shared_ptr> labels_block_; static inline std::shared_ptr excutor_ = coro_io::create_io_context_pool(1); std::chrono::milliseconds max_age_; int age_buckets_; std::atomic is_coro_started_ = false; }; + +using dynamic_summary_1 = basic_dynamic_summary<1>; +using dynamic_summary_2 = basic_dynamic_summary<2>; +using dynamic_summary = dynamic_summary_2; +using dynamic_summary_3 = basic_dynamic_summary<3>; +using dynamic_summary_4 = basic_dynamic_summary<4>; +using dynamic_summary_5 = basic_dynamic_summary<5>; } // namespace ylt::metric \ No newline at end of file diff --git a/include/ylt/metric/system_metric.hpp b/include/ylt/metric/system_metric.hpp index f224daf42..a56bf4ed1 100644 --- a/include/ylt/metric/system_metric.hpp +++ b/include/ylt/metric/system_metric.hpp @@ -16,12 +16,14 @@ #include "ylt/metric/counter.hpp" #include "ylt/metric/gauge.hpp" #include "ylt/metric/metric.hpp" +#include "ylt/metric/metric_manager.hpp" #else #include "cinatra/ylt/coro_io/coro_io.hpp" #include "cinatra/ylt/coro_io/io_context_pool.hpp" #include "cinatra/ylt/metric/counter.hpp" #include "cinatra/ylt/metric/gauge.hpp" #include "cinatra/ylt/metric/metric.hpp" +#include "cinatra/ylt/metric/metric_manager.hpp" #endif // modified based on: brpc/src/bvar/default_variables.cpp diff --git a/include/ylt/metric/thread_local_value.hpp b/include/ylt/metric/thread_local_value.hpp index db9b4b8aa..3d554b8bb 100644 --- a/include/ylt/metric/thread_local_value.hpp +++ b/include/ylt/metric/thread_local_value.hpp @@ -52,6 +52,7 @@ class thread_local_value { thread_local_value &operator=(thread_local_value &&other) { duplicates_ = std::move(other.duplicates_); + return *this; } void inc(value_type value = 1) { local_value() += value; } diff --git a/include/ylt/standalone/cinatra/coro_http_server.hpp b/include/ylt/standalone/cinatra/coro_http_server.hpp index 803a62176..1bc287b0b 100644 --- a/include/ylt/standalone/cinatra/coro_http_server.hpp +++ b/include/ylt/standalone/cinatra/coro_http_server.hpp @@ -185,9 +185,9 @@ class coro_http_server { void use_metrics(bool enable_json = false, std::string url_path = "/metrics") { init_metrics(); - using root = - ylt::metric::metric_collector_t; + using root = ylt::metric::metric_collector_t< + ylt::metric::default_static_metric_manager, + ylt::metric::system_metric_manager>; set_http_handler( url_path, [enable_json](coro_http_request &req, coro_http_response &res) { @@ -930,20 +930,20 @@ class coro_http_server { using namespace ylt::metric; cinatra_metric_conf::enable_metric = true; - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_total_req, ""); - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_failed_req, ""); - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_total_recv_bytes, ""); - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_total_send_bytes, ""); - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_total_fd, ""); - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_req_latency, "", std::vector{30, 40, 50, 60, 70, 80, 90, 100, 150}); - default_metric_manager::instance().create_metric_static( + default_static_metric_manager::instance().create_metric_static( cinatra_metric_conf::server_read_latency, "", std::vector{3, 5, 7, 9, 13, 18, 23, 35, 50}); #if defined(__GNUC__) diff --git a/include/ylt/standalone/cinatra/metric_conf.hpp b/include/ylt/standalone/cinatra/metric_conf.hpp index 1c0bc2015..30d81ad1b 100644 --- a/include/ylt/standalone/cinatra/metric_conf.hpp +++ b/include/ylt/standalone/cinatra/metric_conf.hpp @@ -7,6 +7,7 @@ #include "ylt/metric/histogram.hpp" #include "ylt/metric/metric.hpp" #include "ylt/metric/summary.hpp" +#include "ylt/metric/system_metric.hpp" namespace cinatra { struct cinatra_metric_conf { @@ -26,7 +27,7 @@ struct cinatra_metric_conf { } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_total_req); if (m == nullptr) { return; @@ -39,7 +40,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_failed_req); if (m == nullptr) { return; @@ -52,7 +53,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_total_fd); if (m == nullptr) { return; @@ -65,7 +66,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_total_fd); if (m == nullptr) { return; @@ -78,7 +79,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_total_recv_bytes); if (m == nullptr) { return; @@ -91,7 +92,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_total_send_bytes); if (m == nullptr) { return; @@ -104,7 +105,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_req_latency); if (m == nullptr) { return; @@ -117,7 +118,7 @@ struct cinatra_metric_conf { return; } static auto m = - ylt::metric::default_metric_manager::instance() + ylt::metric::default_static_metric_manager::instance() .get_metric_static(server_read_latency); if (m == nullptr) { return; diff --git a/src/metric/tests/test_metric.cpp b/src/metric/tests/test_metric.cpp index a46ca6b86..6e7814de1 100644 --- a/src/metric/tests/test_metric.cpp +++ b/src/metric/tests/test_metric.cpp @@ -7,10 +7,334 @@ using namespace ylt; using namespace ylt::metric; +struct metrc_tag {}; +TEST_CASE("test metric manager") { + auto c = std::make_shared("test1", ""); + auto g = std::make_shared("test2", ""); + auto& inst_s = static_metric_manager::instance(); + static_metric_manager::instance().register_metric(c); + static_metric_manager::instance().register_metric(g); + auto pair = inst_s.create_metric_static("test1", ""); + CHECK(pair.first == std::errc::invalid_argument); + auto v1 = inst_s.get_metric_by_label({}); + CHECK(v1.size() == 2); + auto v2 = inst_s.get_metric_by_name("test1"); + CHECK(v2 != nullptr); + + c->inc(); + g->inc(); + + inst_s.create_metric_static( + "test_counter", "", std::map{{"url", "/"}}); + auto ms = inst_s.filter_metrics_by_label_value(std::regex("/")); + CHECK(ms.size() == 1); + + { + std::string str = inst_s.serialize_static(); + std::cout << str << "\n"; + + std::string json = inst_s.serialize_to_json_static(); + std::cout << json << "\n"; + } + + { + metric_filter_options options; + options.name_regex = ".*test.*"; + auto v5 = inst_s.filter_metrics_static(options); + CHECK(v5.size() == 3); + options.label_regex = "url"; + auto v6 = inst_s.filter_metrics_static(options); + CHECK(v6.size() == 1); + } + + auto dc = std::make_shared( + std::string("test3"), std::string(""), + std::array{"url", "code"}); + dynamic_metric_manager::instance().register_metric(dc); + auto& inst_d = dynamic_metric_manager::instance(); + auto pair1 = inst_d.create_metric_dynamic( + std::string("test3"), std::string(""), std::array{}); + CHECK(pair1.first == std::errc::invalid_argument); + dc->inc({"/", "200"}); + + { + std::string str = inst_d.serialize_dynamic(); + std::cout << str << "\n"; + + std::string json = inst_d.serialize_to_json_dynamic(); + std::cout << json << "\n"; + + using root_manager = metric_collector_t, + dynamic_metric_manager>; + str = root_manager::serialize(); + std::cout << str << "\n"; + json = root_manager::serialize_to_json(); + std::cout << json << "\n"; + } + + auto v3 = inst_d.get_metric_by_label({{"url", "/"}, {"code", "200"}}); + CHECK(v3.size() == 1); + + auto v4 = inst_d.get_metric_by_label_name({"url", "code"}); + CHECK(v4.size() == 1); + + inst_d.remove_metric(dc); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric(dc->str_name()); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric(std::vector>{dc}); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric({dc->str_name()}); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric_by_label({{"url", "/"}, {"code", "200"}}); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric_by_label_name(std::vector{"url", "code"}); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric_by_label_name("url"); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.remove_metric_by_label_name("code"); + CHECK(inst_d.metric_count() == 0); + inst_d.register_metric(dc); + + inst_d.create_metric_dynamic( + "test4", "", std::array{"method", "code"}); + + metric_filter_options options; + options.name_regex = ".*test.*"; + auto v5 = inst_d.filter_metrics_dynamic(options); + CHECK(v5.size() == 2); + options.label_regex = "method"; + auto v6 = inst_d.filter_metrics_dynamic(options); + CHECK(v6.size() == 1); + + auto v7 = inst_d.filter_metrics_by_label_value(std::regex("200")); + CHECK(v7.size() == 1); +} + +TEST_CASE("test dynamic counter") { + basic_dynamic_counter c("test", "", {"url", "code"}); + c.inc({"/", "200"}); + c.inc({"/test", "200"}); + auto v1 = c.value({"/", "200"}); + auto v2 = c.value({"/test", "200"}); + CHECK(v1 == 1); + CHECK(v2 == 1); + + { + std::string str; + c.serialize(str); + std::cout << str << "\n"; + + std::string json; + c.serialize_to_json(json); + std::cout << json << "\n"; + } + + basic_dynamic_counter c1("test1", "", {}); + c1.inc({}); + auto v3 = c1.value({}); + CHECK(v3 == 1); + + { + std::string str; + c1.serialize(str); + std::cout << str << "\n"; + + std::string json; + c1.serialize_to_json(json); + std::cout << json << "\n"; + } + + basic_dynamic_gauge g("test_gauge", "", {"url"}); + g.inc({"/"}); + CHECK(g.value({"/"}) == 1); + + g.dec({"/"}); + CHECK(g.value({"/"}) == 0); + + basic_dynamic_gauge g1("test_gauge1", "", {}); + g1.inc({}); + CHECK(g1.value({}) == 1); + g1.dec({}); + CHECK(g1.value({}) == 0); + + dynamic_gauge_t g2("test_g2", "", {"url", "code"}); + g2.inc({"/", "200"}); + CHECK(g2.value({"/", "200"}) == 1); +} + +TEST_CASE("test static counter") { + basic_static_counter c("test", ""); + c.inc(); + c.inc(); + auto v = c.value(); + CHECK(v == 2); + + { + std::string str; + c.serialize(str); + std::cout << str << "\n"; + + std::string json; + c.serialize_to_json(json); + std::cout << json << "\n"; + } + + basic_static_counter c1( + "test", "", + std::map{{"method", "GET"}, {"url", "/"}}); + c1.inc(); + c1.inc(); + auto v1 = c1.value(); + CHECK(v1 == 2); + + { + std::string str; + c1.serialize(str); + std::cout << str << "\n"; + + std::string json; + c1.serialize_to_json(json); + std::cout << json << "\n"; + } + + basic_static_gauge g("test", ""); + g.inc(); + g.inc(); + auto v3 = g.value(); + CHECK(v3 == 2); + g.dec(); + CHECK(g.value() == 1); + + basic_static_gauge g1("test", "", + std::map{}); + g1.inc(); + g1.inc(); + auto v4 = g1.value(); + CHECK(v4 == 2); + g1.dec(); + CHECK(g1.value() == 1); +} + +TEST_CASE("test static histogram") { + { + histogram_t h("test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}); + h.observe(23); + auto counts = h.get_bucket_counts(); + CHECK(counts[3]->value() == 1); + h.observe(42); + CHECK(counts[3]->value() == 2); + h.observe(60); + CHECK(counts[4]->value() == 1); + h.observe(120); + CHECK(counts[5]->value() == 1); + h.observe(1); + CHECK(counts[0]->value() == 1); + + std::string str; + h.serialize(str); + std::cout << str; + + std::string json; + h.serialize_to_json(json); + std::cout << json << "\n"; + } + + { + histogram_t h( + "test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}, + std::map{{"method", "GET"}, {"url", "/"}}); + h.observe(23); + auto counts = h.get_bucket_counts(); + CHECK(counts[3]->value() == 1); + h.observe(42); + CHECK(counts[3]->value() == 2); + h.observe(60); + CHECK(counts[4]->value() == 1); + h.observe(120); + CHECK(counts[5]->value() == 1); + h.observe(1); + CHECK(counts[0]->value() == 1); + + std::string str; + h.serialize(str); + std::cout << str; + + std::string json; + h.serialize_to_json(json); + std::cout << json << "\n"; + } + + { + histogram_t h( + "test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}, + std::map{{"method", "GET"}, {"url", "/"}}); + + std::string str; + h.serialize(str); + std::cout << str; + + std::string json; + h.serialize_to_json(json); + std::cout << json << "\n"; + } +} + +TEST_CASE("test dynamic histogram") { + dynamic_histogram_t h("test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}, + {"method", "url"}); + h.observe({"GET", "/"}, 23); + auto counts = h.get_bucket_counts(); + CHECK(counts[3]->value({"GET", "/"}) == 1); + h.observe({"GET", "/"}, 42); + CHECK(counts[3]->value({"GET", "/"}) == 2); + h.observe({"GET", "/"}, 60); + CHECK(counts[4]->value({"GET", "/"}) == 1); + h.observe({"GET", "/"}, 120); + CHECK(counts[5]->value({"GET", "/"}) == 1); + h.observe({"GET", "/"}, 1); + CHECK(counts[0]->value({"GET", "/"}) == 1); + + h.observe({"POST", "/"}, 23); + CHECK(counts[3]->value({"POST", "/"}) == 1); + h.observe({"POST", "/"}, 42); + CHECK(counts[3]->value({"POST", "/"}) == 2); + h.observe({"POST", "/"}, 60); + CHECK(counts[4]->value({"POST", "/"}) == 1); + h.observe({"POST", "/"}, 120); + CHECK(counts[5]->value({"POST", "/"}) == 1); + h.observe({"POST", "/"}, 1); + CHECK(counts[0]->value({"POST", "/"}) == 1); + + std::string str; + h.serialize(str); + std::cout << str; + +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string str_json; + h.serialize_to_json(str_json); + std::cout << str_json << "\n"; +#endif +} + struct my_tag {}; -using my_manager = metric_manager_t; +using my_manager = static_metric_manager; -auto g_counter = my_manager::instance().create_metric_dynamic( +auto g_pair = my_manager::instance().create_metric_static( "test_g_counter", ""); TEST_CASE("test no lable") { @@ -23,6 +347,7 @@ TEST_CASE("test no lable") { customMap); summary->observe(100); } + auto g_counter = g_pair.second; g_counter->inc(); CHECK(g_counter->value() == 1); { @@ -36,9 +361,6 @@ TEST_CASE("test no lable") { g.dec(); CHECK(g.value() == 1); - CHECK_THROWS_AS(g.dec({}, 1), std::invalid_argument); - CHECK_THROWS_AS(g.inc({}, 1), std::invalid_argument); - CHECK_THROWS_AS(g.update({}, 1), std::invalid_argument); counter_t c{"test_counter", "help", 10}; c.inc(); @@ -67,10 +389,6 @@ TEST_CASE("test no lable") { CHECK(c.value() == 2); - CHECK_THROWS_AS(c.inc(-2), std::invalid_argument); - CHECK_THROWS_AS(c.inc({}, 1), std::invalid_argument); - CHECK_THROWS_AS(c.update({}, 1), std::invalid_argument); - c.update(10); CHECK(c.value() == 10); @@ -83,26 +401,22 @@ TEST_CASE("test with atomic") { counter_t c( "get_count", "get counter", std::map{{"method", "GET"}, {"url", "/"}}); - std::vector labels_value{"GET", "/"}; - c.inc(labels_value); - c.inc(labels_value, 2); - CHECK(c.value(labels_value) == 3); - CHECK_THROWS_AS(c.inc({"GET", "/test"}), std::invalid_argument); - CHECK_THROWS_AS(c.inc({"POST", "/"}), std::invalid_argument); - c.update(labels_value, 10); - CHECK(c.value(labels_value) == 10); + + c.inc(); + c.inc(2); + CHECK(c.value() == 3); + c.update(10); + CHECK(c.value() == 10); gauge_t g( "get_qps", "get qps", std::map{{"method", "GET"}, {"url", "/"}}); - g.inc(labels_value); - g.inc(labels_value, 2); - CHECK(g.value(labels_value) == 3); - CHECK_THROWS_AS(g.inc({"GET", "/test"}), std::invalid_argument); - CHECK_THROWS_AS(g.inc({"POST", "/"}), std::invalid_argument); - g.dec(labels_value); - g.dec(labels_value, 1); - CHECK(g.value(labels_value) == 1); + g.inc(); + g.inc(2); + CHECK(g.value() == 3); + g.dec(); + g.dec(1); + CHECK(g.value() == 1); std::string str; c.serialize(str); @@ -118,32 +432,34 @@ TEST_CASE("test with atomic") { std::map{{"method", "POST"}, {"url", "/test"}}); g.inc(); - g.inc({"POST", "/test"}); + g.inc(); + CHECK(g.value() == 2); CHECK(g.value() == 2); - CHECK(g.value({"POST", "/test"}) == 2); g.dec(); CHECK(g.value() == 1); - CHECK(g.value({"POST", "/test"}) == 1); - g.dec({"POST", "/test"}); + CHECK(g.value() == 1); + g.dec(); + CHECK(g.value() == 0); CHECK(g.value() == 0); - CHECK(g.value({"POST", "/test"}) == 0); } } TEST_CASE("test counter with dynamic labels value") { { - auto c = std::make_shared( - "get_count", "get counter", std::vector{"method", "code"}); + auto c = std::make_shared( + "get_count", "get counter", + std::array{"method", "code"}); CHECK(c->name() == "get_count"); - auto g = std::make_shared( - "get_count", "get counter", std::vector{"method", "code"}); + auto g = std::make_shared( + std::string("get_count"), std::string("get counter"), + std::array{"method", "code"}); CHECK(g->name() == "get_count"); CHECK(g->metric_name() == "gauge"); } { - counter_t c("get_count", "get counter", - std::vector{"method", "code"}); + dynamic_counter_t c(std::string("get_count"), std::string("get counter"), + std::array{"method", "code"}); CHECK(c.labels_name() == std::vector{"method", "code"}); c.inc({"GET", "200"}, 1); auto values = c.value_map(); @@ -159,8 +475,6 @@ TEST_CASE("test counter with dynamic labels value") { CHECK(str.find("get_count{method=\"GET\",code=\"200\"} 3") != std::string::npos); - CHECK_THROWS_AS(c.inc({"GET", "200", "/"}, 2), std::invalid_argument); - c.update({"GET", "200"}, 20); std::this_thread::sleep_for(std::chrono::milliseconds(10)); values = c.value_map(); @@ -193,7 +507,7 @@ TEST_CASE("test gauge") { } { - gauge_t g("get_count", "get counter", {"method", "code", "url"}); + dynamic_gauge_3t g("get_count", "get counter", {"method", "code", "url"}); CHECK(g.labels_name() == std::vector{"method", "code", "url"}); // method, status code, url g.inc({"GET", "200", "/"}, 1); @@ -219,8 +533,6 @@ TEST_CASE("test gauge") { CHECK(str.find("get_count{method=\"GET\",code=\"200\",url=\"/\"} 3") != std::string::npos); - CHECK_THROWS_AS(g.dec({"GET", "200"}, 1), std::invalid_argument); - g.dec({"GET", "200", "/"}, 1); values = g.value_map(); CHECK(values[{"GET", "200", "/"}].value() == 2); @@ -292,122 +604,92 @@ TEST_CASE("test summary") { TEST_CASE("test register metric") { auto c = std::make_shared(std::string("get_count"), std::string("get counter")); - default_metric_manager::instance().register_metric_static(c); - CHECK_FALSE(default_metric_manager::instance().register_metric_static(c)); + default_static_metric_manager::instance().register_metric(c); + CHECK_FALSE(default_static_metric_manager::instance().register_metric(c)); auto g = std::make_shared(std::string("get_guage_count"), std::string("get counter")); - default_metric_manager::instance().register_metric_static(g); + default_static_metric_manager::instance().register_metric(g); - auto map1 = default_metric_manager::instance().metric_map_static(); + auto map1 = default_static_metric_manager::instance().metric_map(); for (auto& [k, v] : map1) { bool r = k == "get_count" || k == "get_guage_count"; break; } - CHECK(default_metric_manager::instance().metric_count_static() >= 2); - CHECK(default_metric_manager::instance().metric_keys_static().size() >= 2); + CHECK(default_static_metric_manager::instance().metric_count() >= 2); c->inc(); g->inc(); - auto map = default_metric_manager::instance().metric_map_static(); + auto map = default_static_metric_manager::instance().metric_map(); CHECK(map["get_count"]->as()->value() == 1); CHECK(map["get_guage_count"]->as()->value() == 1); - auto s = default_metric_manager::instance().serialize_static(); + auto s = default_static_metric_manager::instance().serialize_static(); std::cout << s << "\n"; CHECK(s.find("get_count 1") != std::string::npos); CHECK(s.find("get_guage_count 1") != std::string::npos); - auto m = default_metric_manager::instance().get_metric_static( - "get_count"); + auto m = + default_static_metric_manager::instance().get_metric_static( + "get_count"); CHECK(m->as()->value() == 1); - auto m1 = default_metric_manager::instance().get_metric_static( - "get_guage_count"); + auto m1 = + default_static_metric_manager::instance().get_metric_static( + "get_guage_count"); CHECK(m1->as()->value() == 1); - - { - // because the first regiter_metric is set no lock, so visit - // default_metric_manager with lock will throw. - auto c1 = std::make_shared(std::string(""), std::string("")); - CHECK_THROWS_AS( - default_metric_manager::instance().register_metric_dynamic(c1), - std::invalid_argument); - CHECK_THROWS_AS(default_metric_manager::instance().metric_count_dynamic(), - std::invalid_argument); - CHECK_THROWS_AS(default_metric_manager::instance().metric_keys_dynamic(), - std::invalid_argument); - CHECK_THROWS_AS(default_metric_manager::instance().metric_map_dynamic(), - std::invalid_argument); - CHECK_THROWS_AS( - default_metric_manager::instance().get_metric_dynamic(""), - std::invalid_argument); - } } template struct test_id_t {}; TEST_CASE("test remove metric and serialize metrics") { - using metric_mgr = metric_manager_t>; + using metric_mgr = dynamic_metric_manager>; - metric_mgr::instance().create_metric_dynamic("test_counter", ""); - metric_mgr::instance().create_metric_dynamic("test_counter2", ""); + metric_mgr::instance().create_metric_dynamic( + "test_counter", "", std::array{}); + metric_mgr::instance().create_metric_dynamic( + "test_counter2", "", std::array{}); - size_t count = metric_mgr::instance().metric_count_dynamic(); + size_t count = metric_mgr::instance().metric_count(); CHECK(count == 2); - metric_mgr::instance().remove_metric_dynamic("test_counter"); - count = metric_mgr::instance().metric_count_dynamic(); + metric_mgr::instance().remove_metric("test_counter"); + count = metric_mgr::instance().metric_count(); CHECK(count == 1); - metric_mgr::instance().remove_metric_dynamic("test_counter2"); - count = metric_mgr::instance().metric_count_dynamic(); + metric_mgr::instance().remove_metric("test_counter2"); + count = metric_mgr::instance().metric_count(); CHECK(count == 0); - CHECK_THROWS_AS(metric_mgr::instance().create_metric_static( - "test_static_counter", ""), - std::invalid_argument); - - using metric_mgr2 = metric_manager_t>; + using metric_mgr2 = static_metric_manager>; auto c = metric_mgr2::instance().create_metric_static( "test_static_counter", ""); auto c2 = metric_mgr2::instance().create_metric_static( "test_static_counter2", ""); - c->inc(); - c2->inc(); + c.second->inc(); + c2.second->inc(); #ifdef CINATRA_ENABLE_METRIC_JSON auto s = metric_mgr2::instance().serialize_to_json_static(); std::cout << s << "\n"; - auto s1 = metric_mgr2::instance().serialize_to_json({c, c2}); - CHECK(s.size() == s1.size()); #endif - CHECK_THROWS_AS(metric_mgr2::instance().metric_count_dynamic(), - std::invalid_argument); - count = metric_mgr2::instance().metric_count_static(); + count = metric_mgr2::instance().metric_count(); CHECK(count == 2); - CHECK_THROWS_AS( - metric_mgr2::instance().remove_metric_dynamic("test_static_counter"), - std::invalid_argument); - - metric_mgr2::instance().remove_metric_static("test_static_counter"); - count = metric_mgr2::instance().metric_count_static(); - CHECK(count == 1); } TEST_CASE("test filter metrics static") { - using metric_mgr = metric_manager_t>; + using metric_mgr = static_metric_manager>; auto c = metric_mgr::instance().create_metric_static( "test_static_counter", "", std::map{{"method", "GET"}}); auto c2 = metric_mgr::instance().create_metric_static( "test_static_counter2", "", std::map{{"url", "/"}}); - c->inc({"GET"}); - c2->inc({"/"}); + c.second->inc(); + c2.second->inc(); metric_filter_options options; options.name_regex = ".*counter.*"; @@ -415,7 +697,7 @@ TEST_CASE("test filter metrics static") { auto metrics = metric_mgr::instance().filter_metrics_static(options); CHECK(metrics.size() == 2); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_static_counter") != std::string::npos); std::cout << s << "\n"; } @@ -424,7 +706,7 @@ TEST_CASE("test filter metrics static") { { auto metrics = metric_mgr::instance().filter_metrics_static(options); CHECK(metrics.size() == 1); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_static_counter2") != std::string::npos); std::cout << s << "\n"; } @@ -433,7 +715,7 @@ TEST_CASE("test filter metrics static") { { auto metrics = metric_mgr::instance().filter_metrics_static(options); CHECK(metrics.empty()); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.empty()); } @@ -442,7 +724,7 @@ TEST_CASE("test filter metrics static") { { auto metrics = metric_mgr::instance().filter_metrics_static(options); CHECK(metrics.empty()); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.empty()); } @@ -459,7 +741,7 @@ TEST_CASE("test filter metrics static") { { auto metrics = metric_mgr::instance().filter_metrics_static(options); CHECK(metrics.size() == 1); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_static_counter") != std::string::npos); CHECK(s.find("test_static_counter2") == std::string::npos); } @@ -470,7 +752,7 @@ TEST_CASE("test filter metrics static") { { auto metrics = metric_mgr::instance().filter_metrics_static(options); CHECK(metrics.size() == 1); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_static_counter") != std::string::npos); CHECK(s.find("method") != std::string::npos); CHECK(s.find("test_static_counter2") == std::string::npos); @@ -479,11 +761,13 @@ TEST_CASE("test filter metrics static") { } TEST_CASE("test filter metrics dynamic") { - using metric_mgr = metric_manager_t>; - auto c = metric_mgr::instance().create_metric_dynamic( - "test_dynamic_counter", "", std::vector{{"method"}}); - auto c2 = metric_mgr::instance().create_metric_dynamic( - "test_dynamic_counter2", "", std::vector{{"url"}}); + using metric_mgr = dynamic_metric_manager>; + auto [ec, c] = + metric_mgr::instance().create_metric_dynamic( + "test_dynamic_counter", "", std::array{"method"}); + auto [ec2, c2] = + metric_mgr::instance().create_metric_dynamic( + "test_dynamic_counter2", "", std::array{"url"}); c->inc({"GET"}); c->inc({"POST"}); c2->inc({"/"}); @@ -495,7 +779,7 @@ TEST_CASE("test filter metrics dynamic") { auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); CHECK(metrics.size() == 2); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_dynamic_counter") != std::string::npos); std::cout << s << "\n"; } @@ -504,7 +788,7 @@ TEST_CASE("test filter metrics dynamic") { { auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); CHECK(metrics.size() == 1); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_dynamic_counter2") != std::string::npos); std::cout << s << "\n"; } @@ -513,7 +797,7 @@ TEST_CASE("test filter metrics dynamic") { { auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); CHECK(metrics.empty()); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.empty()); } @@ -522,7 +806,7 @@ TEST_CASE("test filter metrics dynamic") { { auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); CHECK(metrics.empty()); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.empty()); } @@ -539,7 +823,7 @@ TEST_CASE("test filter metrics dynamic") { { auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); CHECK(metrics.size() == 1); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_dynamic_counter") != std::string::npos); CHECK(s.find("test_dynamic_counter2") == std::string::npos); } @@ -550,7 +834,7 @@ TEST_CASE("test filter metrics dynamic") { { auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); CHECK(metrics.size() == 1); - auto s = metric_mgr::instance().serialize(metrics); + auto s = manager_helper::serialize(metrics); CHECK(s.find("test_dynamic_counter") != std::string::npos); CHECK(s.find("method") != std::string::npos); CHECK(s.find("test_dynamic_counter2") == std::string::npos); @@ -559,7 +843,7 @@ TEST_CASE("test filter metrics dynamic") { } TEST_CASE("test get metric by static labels and label") { - using metric_mgr = metric_manager_t>; + using metric_mgr = static_metric_manager>; metric_mgr::instance().create_metric_static( "http_req_test", "", std::map{{"method", "GET"}, {"url", "/"}}); @@ -570,155 +854,143 @@ TEST_CASE("test get metric by static labels and label") { "http_req_test2", "", std::map{{"method", "GET"}, {"url", "/test"}}); - auto v = metric_mgr::instance().get_metric_by_labels_static( + auto v = metric_mgr::instance().get_metric_by_label( std::map{{"method", "GET"}, {"url", "/test"}}); CHECK(v[0]->name() == "http_req_test2"); - v = metric_mgr::instance().get_metric_by_labels_static( + v = metric_mgr::instance().get_metric_by_label( std::map{{"method", "GET"}, {"url", "/"}}); CHECK(v[0]->name() == "http_req_test"); - auto h1 = metric_mgr::instance().create_metric_static( + auto [ec, h1] = metric_mgr::instance().create_metric_static( "http_req_static_hist", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, std::map{{"method", "GET"}, {"url", "/"}}); - h1->observe({"GET", "/"}, 23); + h1->observe(23); std::string str1; h1->serialize(str1); std::cout << str1; CHECK(str1.find("method=\"GET\",url=\"/\",le=") != std::string::npos); - auto s1 = metric_mgr::instance().create_metric_static( + auto map = + std::map{{"method", "GET"}, {"url", "/"}}; + auto [ec1, s1] = metric_mgr::instance().create_metric_static( "http_req_static_summary", "help", summary_t::Quantiles{ {0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}}, std::map{{"method", "GET"}, {"url", "/"}}); - s1->observe({"GET", "/"}, 23); + s1->observe(23); - auto vec = - metric_mgr::instance().get_metric_by_label_static({"method", "GET"}); - CHECK(vec.size() == 4); + auto vec = metric_mgr::instance().get_metric_by_label(map); + CHECK(vec.size() == 3); { - using metric_mgr2 = metric_manager_t>; - auto s2 = metric_mgr2::instance().create_metric_static( + using metric_mgr2 = static_metric_manager>; + auto [ec, s2] = metric_mgr2::instance().create_metric_static( "http_req_static_summary2", "help", summary_t::Quantiles{ {0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}}, - std::map{{"method", "GET"}, {"url", "/"}}); + map); s2->observe(23); - auto vec = - metric_mgr2::instance().get_metric_by_label_static({"method", "GET"}); + auto vec = metric_mgr2::instance().get_metric_by_label(map); CHECK(vec.size() == 1); } - - vec = metric_mgr::instance().get_metric_by_label_static({"url", "/"}); - CHECK(vec.size() == 4); - - vec = metric_mgr::instance().get_metric_by_label_static({"url", "/test"}); - CHECK(vec.size() == 1); - - vec = metric_mgr::instance().get_metric_by_label_static({"method", "POST"}); - CHECK(vec.size() == 1); - - vec = metric_mgr::instance().get_metric_by_labels_static( - std::map{{"method", "HEAD"}, {"url", "/test"}}); - CHECK(vec.empty()); - - vec = metric_mgr::instance().get_metric_by_labels_static( - std::map{{"method", "GET"}}); - CHECK(vec.empty()); - - vec = metric_mgr::instance().get_metric_by_label_static({"url", "/index"}); - CHECK(vec.empty()); } TEST_CASE("test get metric by dynamic labels") { - using metric_mgr = metric_manager_t>; - auto c = metric_mgr::instance().create_metric_dynamic( - "http_req_static", "", std::vector{"method", "code"}); + using metric_mgr = dynamic_metric_manager>; + auto [ec, c] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static", "", std::array{"method", "code"}); - auto c1 = metric_mgr::instance().create_metric_dynamic( - "http_req_static1", "", std::vector{"method", "code"}); + auto [ec1, c1] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static1", "", std::array{"method", "code"}); - auto c2 = metric_mgr::instance().create_metric_dynamic( - "http_req_static2", "", std::vector{"method", "code"}); + auto [ec2, c2] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static2", "", std::array{"method", "code"}); - auto c3 = metric_mgr::instance().create_metric_dynamic( - "http_req_static3", "", std::vector{"method", "code"}); + auto [ec3, c3] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static3", "", std::array{"method", "code"}); c->inc({"POST", "200"}); c1->inc({"GET", "200"}); c2->inc({"POST", "301"}); c3->inc({"POST", "400"}); - auto c4 = metric_mgr::instance().create_metric_dynamic( - "http_req_static4", "", std::vector{"host", "url"}); + auto [ec4, c4] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static4", "", std::array{"host", "url"}); - auto c5 = metric_mgr::instance().create_metric_dynamic( - "http_req_static5", "", std::vector{"host", "url"}); + auto [ec5, c5] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static5", "", std::array{"host", "url"}); c4->inc({"shanghai", "/"}); c5->inc({"shanghai", "/test"}); auto vec = - metric_mgr::instance().get_metric_by_labels_dynamic({{"method", "POST"}}); + metric_mgr::instance().filter_metrics_by_label_value(std::regex("POST")); CHECK(vec.size() == 3); - vec = - metric_mgr::instance().get_metric_by_labels_dynamic({{"method", "GET"}}); + vec = metric_mgr::instance().filter_metrics_by_label_value(std::regex("GET")); CHECK(vec.size() == 1); - vec = metric_mgr::instance().get_metric_by_labels_dynamic( - {{"host", "shanghai"}}); + vec = metric_mgr::instance().filter_metrics_by_label_value( + std::regex("shanghai")); CHECK(vec.size() == 2); - vec = metric_mgr::instance().get_metric_by_labels_dynamic({{"url", "/"}}); + vec = metric_mgr::instance().filter_metrics_by_label_value(std::regex("/")); CHECK(vec.size() == 1); - vec = metric_mgr::instance().get_metric_by_labels_dynamic({{"url", "/test"}}); + vec = + metric_mgr::instance().filter_metrics_by_label_value(std::regex("/test")); CHECK(vec.size() == 1); - vec = metric_mgr::instance().get_metric_by_labels_dynamic({{"url", "/none"}}); + vec = + metric_mgr::instance().filter_metrics_by_label_value(std::regex("/none")); CHECK(vec.size() == 0); vec = - metric_mgr::instance().get_metric_by_labels_dynamic({{"method", "HEAD"}}); + metric_mgr::instance().filter_metrics_by_label_value(std::regex("HEAD")); CHECK(vec.size() == 0); - auto h1 = metric_mgr::instance().create_metric_dynamic( - "http_req_static_hist", "help", - std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, - std::vector{"method", "url"}); + auto [ec6, h1] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static_hist", "help", + std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, + std::array{"method", "url"}); h1->observe({"GET", "/"}, 23); - auto s1 = metric_mgr::instance().create_metric_dynamic( - "http_req_static_summary", "help", - summary_t::Quantiles{ - {0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}}, - std::vector{"method", "url"}); + auto [ec7, s1] = + metric_mgr::instance().create_metric_dynamic( + "http_req_static_summary", "help", + summary_t::Quantiles{ + {0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}}, + std::array{"method", "url"}); s1->observe({"GET", "/"}, 23); - vec = - metric_mgr::instance().get_metric_by_labels_dynamic({{"method", "GET"}}); + vec = metric_mgr::instance().filter_metrics_by_label_value(std::regex("GET")); CHECK(vec.size() >= 2); - auto str = metric_mgr::instance().serialize(vec); + auto str = metric_mgr::instance().serialize_dynamic(); std::cout << str; #ifdef CINATRA_ENABLE_METRIC_JSON - auto json_str = metric_mgr::instance().serialize_to_json(vec); + auto json_str = metric_mgr::instance().serialize_to_json_dynamic(); std::cout << json_str << "\n"; #endif } TEST_CASE("test histogram serialize with dynamic labels") { - histogram_t h("test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}, - std::vector{"method", "url"}); + dynamic_histogram_2t h("test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}, + std::array{"method", "url"}); h.observe({"GET", "/"}, 23); auto counts = h.get_bucket_counts(); CHECK(counts[3]->value({"GET", "/"}) == 1); @@ -759,32 +1031,32 @@ TEST_CASE("test histogram serialize with static labels default") { std::map{{"method", "GET"}, {"url", "/"}}); h.observe(23); auto counts = h.get_bucket_counts(); - CHECK(counts[3]->value({"GET", "/"}) == 1); + CHECK(counts[3]->value() == 1); h.observe(42); - CHECK(counts[3]->value({"GET", "/"}) == 2); - h.observe({"GET", "/"}, 60); - CHECK(counts[4]->value({"GET", "/"}) == 1); - h.observe({"GET", "/"}, 120); - CHECK(counts[5]->value({"GET", "/"}) == 1); + CHECK(counts[3]->value() == 2); + h.observe(60); + CHECK(counts[4]->value() == 1); + h.observe(120); + CHECK(counts[5]->value() == 1); h.observe(1); - CHECK(counts[0]->value({"GET", "/"}) == 1); + CHECK(counts[0]->value() == 1); } TEST_CASE("test histogram serialize with static labels") { histogram_t h( "test", "help", {5.23, 10.54, 20.0, 50.0, 100.0}, std::map{{"method", "GET"}, {"url", "/"}}); - h.observe({"GET", "/"}, 23); + h.observe(23); auto counts = h.get_bucket_counts(); - CHECK(counts[3]->value({"GET", "/"}) == 1); - h.observe({"GET", "/"}, 42); - CHECK(counts[3]->value({"GET", "/"}) == 2); - h.observe({"GET", "/"}, 60); - CHECK(counts[4]->value({"GET", "/"}) == 1); - h.observe({"GET", "/"}, 120); - CHECK(counts[5]->value({"GET", "/"}) == 1); - h.observe({"GET", "/"}, 1); - CHECK(counts[0]->value({"GET", "/"}) == 1); + CHECK(counts[3]->value() == 1); + h.observe(42); + CHECK(counts[3]->value() == 2); + h.observe(60); + CHECK(counts[4]->value() == 1); + h.observe(120); + CHECK(counts[5]->value() == 1); + h.observe(1); + CHECK(counts[0]->value() == 1); std::string str; h.serialize(str); @@ -798,10 +1070,11 @@ TEST_CASE("test histogram serialize with static labels") { } TEST_CASE("test summary with dynamic labels") { - summary_t summary{"test_summary", - "summary help", - {{0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}}, - std::vector{"method", "url"}}; + basic_dynamic_summary<2> summary{ + "test_summary", + "summary help", + {{0.5, 0.05}, {0.9, 0.01}, {0.95, 0.005}, {0.99, 0.001}}, + {"method", "url"}}; std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> distr(1, 100); @@ -839,17 +1112,14 @@ TEST_CASE("test summary with static labels") { std::mt19937 gen(rd()); std::uniform_int_distribution<> distr(1, 100); for (int i = 0; i < 50; i++) { - summary.observe({"GET", "/"}, distr(gen)); + summary.observe(distr(gen)); } std::this_thread::sleep_for(std::chrono::milliseconds(100)); - CHECK_THROWS_AS(summary.observe({"POST", "/"}, 1), std::invalid_argument); - double sum; uint64_t count; - auto rates = async_simple::coro::syncAwait( - summary.get_rates({"GET", "/"}, sum, count)); + auto rates = async_simple::coro::syncAwait(summary.get_rates(sum, count)); std::cout << rates.size() << "\n"; auto rates1 = async_simple::coro::syncAwait(summary.get_rates(sum, count)); @@ -869,9 +1139,9 @@ TEST_CASE("test summary with static labels") { TEST_CASE("test serialize with emptry metrics") { std::string s1; - auto h1 = std::make_shared( + auto h1 = std::make_shared( "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, - std::vector{"method"}); + std::array{"method"}); h1->serialize(s1); CHECK(s1.empty()); #ifdef CINATRA_ENABLE_METRIC_JSON @@ -918,9 +1188,9 @@ TEST_CASE("test serialize with emptry metrics") { CHECK(s1.empty()); #endif - auto c3 = std::make_shared(std::string("get_count"), - std::string("get counter"), - std::vector{"method"}); + auto c3 = std::make_shared( + std::string("get_count"), std::string("get counter"), + std::array{"method"}); c3->serialize(s1); CHECK(s1.empty()); #ifdef CINATRA_ENABLE_METRIC_JSON @@ -966,7 +1236,7 @@ TEST_CASE("test serialize with emptry metrics") { { std::string str; - c2->inc({"GET"}); + c2->inc(); c2->serialize(str); CHECK(!str.empty()); str.clear(); @@ -978,7 +1248,7 @@ TEST_CASE("test serialize with emptry metrics") { { std::string str; - c3->inc({"GET"}); + c3->inc({"POST"}); c3->serialize(str); CHECK(!str.empty()); str.clear(); @@ -990,24 +1260,24 @@ TEST_CASE("test serialize with emptry metrics") { } TEST_CASE("test serialize with multiple threads") { - auto c = std::make_shared(std::string("get_count"), - std::string("get counter"), - std::vector{"method"}); - auto g = std::make_shared(std::string("get_count1"), - std::string("get counter"), - std::vector{"method"}); + auto c = std::make_shared( + std::string("get_count"), std::string("get counter"), + std::array{"method"}); + auto g = std::make_shared( + std::string("get_count1"), std::string("get counter"), + std::array{"method"}); - auto h1 = std::make_shared( + auto h1 = std::make_shared( "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, - std::vector{"method"}); + std::array{"method"}); - auto c1 = std::make_shared(std::string("get_count3"), - std::string("get counter"), - std::vector{"method"}); + auto c1 = std::make_shared( + std::string("get_count3"), std::string("get counter"), + std::array{"method"}); - using test_metric_manager = metric_manager_t>; + using test_metric_manager = dynamic_metric_manager>; - test_metric_manager::instance().register_metric_dynamic(c, g, h1, c1); + test_metric_manager::instance().register_metric({c, g, h1, c1}); c->inc({"POST"}, 1); g->inc({"GET"}, 1); @@ -1046,11 +1316,11 @@ TEST_CASE("test system metric") { CHECK(!json.empty()); #endif - using metric_manager = metric_manager_t>; - auto c = metric_manager::instance().create_metric_dynamic( - "test_qps", ""); - c->inc(42); - using root = metric_collector_t>; + auto c = metric_manager::instance().create_metric_dynamic( + std::string("test_qps"), "", std::array{"url"}); + c.second->inc({"/"}, 42); + using root = metric_collector_t; s.clear(); s = root::serialize(); @@ -1064,21 +1334,23 @@ TEST_CASE("test system metric") { CHECK(!json.empty()); #endif } -#endif TEST_CASE("test metric capacity") { std::cout << g_user_metric_count << "\n"; - using test_metric_manager = metric_manager_t>; + using test_metric_manager = dynamic_metric_manager>; set_metric_capacity(g_user_metric_count + 2); - auto c = test_metric_manager::instance().create_metric_dynamic( - "counter", ""); - CHECK(c != nullptr); - auto c1 = test_metric_manager::instance().create_metric_dynamic( - "counter1", ""); - CHECK(c1 != nullptr); - auto c2 = test_metric_manager::instance().create_metric_dynamic( - "counter2", ""); - CHECK(c2 == nullptr); + auto c = + test_metric_manager::instance().create_metric_dynamic( + std::string("counter"), "", std::array{}); + CHECK(c.second != nullptr); + auto c1 = + test_metric_manager::instance().create_metric_dynamic( + std::string("counter1"), "", std::array{}); + CHECK(c1.second != nullptr); + auto c2 = + test_metric_manager::instance().create_metric_dynamic( + std::string("counter2"), "", std::array{}); + CHECK(c2.second == nullptr); set_metric_capacity(10000000); auto process_memory_resident = @@ -1091,48 +1363,119 @@ TEST_CASE("test metric capacity") { "ylt_process_memory_virtual"); std::cout << (int64_t)process_memory_virtual->value() << "\n"; } +#endif TEST_CASE("test remove dynamic metric") { - using test_metric_manager = metric_manager_t>; - auto c = test_metric_manager::instance().create_metric_dynamic( - "counter", ""); - CHECK(c != nullptr); - auto c1 = test_metric_manager::instance().create_metric_dynamic( - "counter1", ""); - CHECK(c1 != nullptr); - auto c2 = test_metric_manager::instance().create_metric_dynamic( - "counter2", ""); - CHECK(c2 != nullptr); - - test_metric_manager::instance().remove_metric_dynamic(c); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 2); - test_metric_manager::instance().remove_metric_dynamic(c1); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 1); - test_metric_manager::instance().remove_metric_dynamic(c2); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 0); - - test_metric_manager::instance().register_metric_dynamic(c, c1, c2); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 3); - test_metric_manager::instance().remove_metric_dynamic("counter"); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 2); - test_metric_manager::instance().remove_metric_dynamic( + using test_metric_manager = dynamic_metric_manager>; + auto pair = + test_metric_manager::instance().create_metric_dynamic( + std::string("counter"), "", std::array{}); + CHECK(pair.second != nullptr); + auto pair1 = + test_metric_manager::instance().create_metric_dynamic( + std::string("counter1"), "", std::array{}); + CHECK(pair1.second != nullptr); + auto pair2 = + test_metric_manager::instance().create_metric_dynamic( + std::string("counter2"), "", std::array{}); + CHECK(pair2.second != nullptr); + + auto c = pair.second; + auto c1 = pair1.second; + auto c2 = pair2.second; + + test_metric_manager::instance().remove_metric(c); + CHECK(test_metric_manager::instance().metric_count() == 2); + test_metric_manager::instance().remove_metric(c1); + CHECK(test_metric_manager::instance().metric_count() == 1); + test_metric_manager::instance().remove_metric(c2); + CHECK(test_metric_manager::instance().metric_count() == 0); + + test_metric_manager::instance().register_metric({c, c1, c2}); + CHECK(test_metric_manager::instance().metric_count() == 3); + test_metric_manager::instance().remove_metric("counter"); + CHECK(test_metric_manager::instance().metric_count() == 2); + test_metric_manager::instance().remove_metric( std::vector{"counter1", "counter2"}); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 0); - - test_metric_manager::instance().register_metric_dynamic( - std::vector>{c, c1, c2}); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 3); - test_metric_manager::instance().remove_metric_dynamic({c1, c2}); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 1); - auto r = test_metric_manager::instance().register_metric_dynamic( - std::vector>{c, c1}); + CHECK(test_metric_manager::instance().metric_count() == 0); + + test_metric_manager::instance().register_metric({c, c1, c2}); + CHECK(test_metric_manager::instance().metric_count() == 3); + test_metric_manager::instance().remove_metric({c1, c2}); + CHECK(test_metric_manager::instance().metric_count() == 1); + auto r = test_metric_manager::instance().register_metric({c, c1}); CHECK(!r); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 1); + CHECK(test_metric_manager::instance().metric_count() == 1); - r = test_metric_manager::instance().register_metric_dynamic( - std::vector>{c1, c}); + r = test_metric_manager::instance().register_metric({c1, c}); CHECK(!r); - CHECK(test_metric_manager::instance().metric_count_dynamic() == 1); + CHECK(test_metric_manager::instance().metric_count() == 2); +} + +TEST_CASE("testFilterMetricsDynamicWithMultiLabel") { + using metric_mgr = dynamic_metric_manager>; + auto [ec, c] = + metric_mgr::instance().create_metric_dynamic( + "test_dynamic_counter", "", + std::array{"method", "bucket"}); + auto [ec2, c2] = + metric_mgr::instance().create_metric_dynamic( + "test_dynamic_counter2", "", + std::array{"url", "bucket"}); + c->inc({"GET", "bucket1"}); + c->inc({"POST", "bucket2"}); + c2->inc({"/", "bucket1"}); + c2->inc({"/test", "bucket3"}); + + auto counter = metric_mgr::instance().get_metric_dynamic( + "test_dynamic_counter"); + CHECK(counter != nullptr); + + metric_filter_options options; + options.name_regex = ".*counter.*"; + { + auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); + CHECK(metrics.size() == 2); + + auto s = metric_mgr::instance().serialize(metrics); + CHECK(s.find("test_dynamic_counter") != std::string::npos); + std::cout << s << "\n"; + } + + options.label_regex = "bucket"; + { + auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); + CHECK(metrics.size() == 2); + auto s = metric_mgr::instance().serialize(metrics); + CHECK(s.find("test_dynamic_counter2") != std::string::npos); + std::cout << s << "\n"; + } + + options.label_regex = "method"; + { + auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); + CHECK(metrics.size() == 1); + auto s = metric_mgr::instance().serialize(metrics); + CHECK(s.find("test_dynamic_counter") != std::string::npos); + std::cout << s << "\n"; + } + + options.label_regex = "url"; + { + auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); + CHECK(metrics.size() == 1); + auto s = metric_mgr::instance().serialize(metrics); + CHECK(s.find("test_dynamic_counter2") != std::string::npos); + std::cout << s << "\n"; + } + + // black + options.label_regex = ".*bucket.*"; + options.is_white = false; + { + auto metrics = metric_mgr::instance().filter_metrics_dynamic(options); + CHECK(metrics.size() == 0); + } } DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007)