From 029d1a8716ff88d7ff4bdea11a1b0e8f5e66f8fe Mon Sep 17 00:00:00 2001 From: qicosmos Date: Wed, 19 Jun 2024 14:51:09 +0800 Subject: [PATCH 01/10] fix serialize --- include/cinatra/ylt/metric/counter.hpp | 26 +++++----------- tests/test_metric.cpp | 42 ++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 19 deletions(-) diff --git a/include/cinatra/ylt/metric/counter.hpp b/include/cinatra/ylt/metric/counter.hpp index d801722a..b43ea08a 100644 --- a/include/cinatra/ylt/metric/counter.hpp +++ b/include/cinatra/ylt/metric/counter.hpp @@ -82,21 +82,13 @@ class counter_t : public metric_t { return; } - serialize_head(str); - std::string s; - if (use_atomic_) { - serialize_map(atomic_value_map_, s); - } - else { - serialize_map(value_map_, s); + auto map = value_map(); + if (map.empty()) { + return; } - if (s.empty()) { - str.clear(); - } - else { - str.append(s); - } + serialize_head(str); + serialize_map(map, str); } #ifdef CINATRA_ENABLE_METRIC_JSON @@ -113,13 +105,9 @@ class counter_t : public metric_t { return; } + auto map = value_map(); json_counter_t counter{name_, help_, std::string(metric_name())}; - if (use_atomic_) { - to_json(counter, atomic_value_map_, str); - } - else { - to_json(counter, value_map_, str); - } + to_json(counter, map, str); } template diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index 86a6039d..b5f092c5 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -777,6 +777,48 @@ TEST_CASE("test summary with static labels") { #endif } +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 h1 = std::make_shared( + "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, + std::vector{"method"}); + + auto c1 = std::make_shared(std::string("get_count3"), + std::string("get counter"), + std::vector{"method"}); + + using test_metric_manager = metric_manager_t<20>; + + test_metric_manager::register_metric_dynamic(c, g, h1, c1); + + c->inc({"POST"}, 1); + g->inc({"GET"}, 1); + h1->observe({"HEAD"}, 1); + + auto s = test_metric_manager::serialize_dynamic(); + std::cout << s; + CHECK(!s.empty()); + CHECK(s.find("get_count") != std::string::npos); + CHECK(s.find("get_count1") != std::string::npos); + CHECK(s.find("get_count2") != std::string::npos); + CHECK(s.find("get_count3") == std::string::npos); + +#ifdef CINATRA_ENABLE_METRIC_JSON + auto json = test_metric_manager::serialize_to_json_dynamic(); + std::cout << json << "\n"; + CHECK(!json.empty()); + CHECK(json.find("get_count") != std::string::npos); + CHECK(json.find("get_count1") != std::string::npos); + CHECK(json.find("get_count2") != std::string::npos); +#endif +} + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } DOCTEST_MSVC_SUPPRESS_WARNING_POP \ No newline at end of file From 11a05895717350c5751b50b2c1bb793953f05288 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Thu, 20 Jun 2024 15:45:58 +0800 Subject: [PATCH 02/10] serialize empty metric --- include/cinatra/ylt/metric/counter.hpp | 15 +++- include/cinatra/ylt/metric/histogram.hpp | 45 +++++++--- tests/test_metric.cpp | 101 +++++++++++++++++++++++ 3 files changed, 147 insertions(+), 14 deletions(-) diff --git a/include/cinatra/ylt/metric/counter.hpp b/include/cinatra/ylt/metric/counter.hpp index b43ea08a..6345571e 100644 --- a/include/cinatra/ylt/metric/counter.hpp +++ b/include/cinatra/ylt/metric/counter.hpp @@ -87,8 +87,12 @@ class counter_t : public metric_t { return; } - serialize_head(str); - serialize_map(map, str); + std::string value_str; + serialize_map(map, value_str); + if (!value_str.empty()) { + serialize_head(str); + str.append(value_str); + } } #ifdef CINATRA_ENABLE_METRIC_JSON @@ -113,6 +117,9 @@ class counter_t : public metric_t { template void to_json(json_counter_t &counter, T &map, std::string &str) { for (auto &[k, v] : map) { + if (v == 0) { + continue; + } json_counter_metric_t metric; size_t index = 0; for (auto &label_value : k) { @@ -121,7 +128,9 @@ class counter_t : public metric_t { metric.value = (int64_t)v; counter.metrics.push_back(std::move(metric)); } - iguana::to_json(counter, str); + if (!counter.metrics.empty()) { + iguana::to_json(counter, str); + } } #endif diff --git a/include/cinatra/ylt/metric/histogram.hpp b/include/cinatra/ylt/metric/histogram.hpp index 1f371b85..a15a350e 100644 --- a/include/cinatra/ylt/metric/histogram.hpp +++ b/include/cinatra/ylt/metric/histogram.hpp @@ -108,6 +108,10 @@ class histogram_t : public metric_t { return; } + if (sum_->value() == 0) { + return; + } + serialize_head(str); double count = 0; auto bucket_counts = get_bucket_counts(); @@ -146,6 +150,10 @@ class histogram_t : public metric_t { return; } + if (sum_->value() == 0) { + return; + } + json_histogram_t hist{name_, help_, std::string(metric_name())}; double count = 0; @@ -183,11 +191,13 @@ class histogram_t : public metric_t { } void serialize_with_labels(std::string &str) { - serialize_head(str); + auto value_map = sum_->value_map(); + if (value_map.empty()) { + return; + } + std::string value_str; auto bucket_counts = get_bucket_counts(); - - auto value_map = sum_->value_map(); for (auto &[labels_value, value] : value_map) { if (value == 0) { continue; @@ -196,24 +206,31 @@ class histogram_t : public metric_t { double count = 0; for (size_t i = 0; i < bucket_counts.size(); i++) { auto counter = bucket_counts[i]; - str.append(name_).append("_bucket{"); + value_str.append(name_).append("_bucket{"); build_label_string(str, sum_->labels_name(), labels_value); - str.append(","); + value_str.append(","); if (i == bucket_boundaries_.size()) { - str.append("le=\"").append("+Inf").append("\"} "); + value_str.append("le=\"").append("+Inf").append("\"} "); } else { - str.append("le=\"") + value_str.append("le=\"") .append(std::to_string(bucket_boundaries_[i])) .append("\"} "); } count += counter->value(labels_value); - str.append(std::to_string(count)); - str.append("\n"); + value_str.append(std::to_string(count)); + value_str.append("\n"); + } + + if (value_str.empty()) { + return; } + serialize_head(str); + str.append(value_str); + str.append(name_); str.append("_sum{"); build_label_string(str, sum_->labels_name(), labels_value); @@ -237,10 +254,14 @@ class histogram_t : public metric_t { #ifdef CINATRA_ENABLE_METRIC_JSON void serialize_to_json_with_labels(std::string &str) { + auto value_map = sum_->value_map(); + if (value_map.empty()) { + return; + } + json_histogram_t hist{name_, help_, std::string(metric_name())}; auto bucket_counts = get_bucket_counts(); - auto value_map = sum_->value_map(); for (auto &[labels_value, value] : value_map) { if (value == 0) { continue; @@ -272,7 +293,9 @@ class histogram_t : public metric_t { hist.metrics.push_back(std::move(metric)); } - iguana::to_json(hist, str); + if (!hist.metrics.empty()) { + iguana::to_json(hist, str); + } } #endif diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index b5f092c5..a0ada5e8 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -777,6 +777,107 @@ TEST_CASE("test summary with static labels") { #endif } +TEST_CASE("test serialize with emptry metrics") { + std::string s1; + + auto h1 = std::make_shared( + "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, + std::vector{"method"}); + h1->serialize(s1); + CHECK(s1.empty()); + h1->serialize_to_json(s1); + CHECK(s1.empty()); + + auto h2 = std::make_shared( + "get_count2", "help", + std::vector{5.23, 10.54, 20.0, 50.0, 100.0}); + h2->serialize(s1); + CHECK(s1.empty()); + h2->serialize_to_json(s1); + CHECK(s1.empty()); + + auto h3 = std::make_shared( + "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, + std::map{{"method", "/"}}); + h3->serialize(s1); + CHECK(s1.empty()); + h3->serialize_to_json(s1); + CHECK(s1.empty()); + + auto c1 = std::make_shared(std::string("get_count"), + std::string("get counter")); + c1->serialize(s1); + CHECK(s1.empty()); + c1->serialize_to_json(s1); + CHECK(s1.empty()); + + auto c2 = std::make_shared( + std::string("get_count"), std::string("get counter"), + std::map{{"method", "GET"}}); + c2->serialize(s1); + CHECK(s1.empty()); + c2->serialize_to_json(s1); + CHECK(s1.empty()); + + auto c3 = std::make_shared(std::string("get_count"), + std::string("get counter"), + std::vector{"method"}); + c3->serialize(s1); + CHECK(s1.empty()); + c3->serialize_to_json(s1); + CHECK(s1.empty()); + + { + std::string str; + h1->observe({"POST"}, 1); + h1->serialize(str); + CHECK(!str.empty()); + str.clear(); + h1->serialize_to_json(str); + CHECK(!str.empty()); + } + + { + std::string str; + h2->observe(1); + h2->serialize(str); + CHECK(!str.empty()); + str.clear(); + h1->serialize_to_json(str); + CHECK(!str.empty()); + } + + { + std::string str; + c1->inc(); + c1->serialize(str); + CHECK(!str.empty()); + str.clear(); + c1->serialize_to_json(str); + CHECK(!str.empty()); + } + + { + std::string str; + c2->inc({"GET"}); + c2->serialize(str); + CHECK(!str.empty()); + str.clear(); + c2->serialize_to_json(str); + CHECK(!str.empty()); + } + + { + std::string str; + c3->inc({"GET"}); + c3->serialize(str); + CHECK(!str.empty()); + str.clear(); + c3->serialize_to_json(str); + CHECK(!str.empty()); + } +} + TEST_CASE("test serialize with multiple threads") { auto c = std::make_shared(std::string("get_count"), std::string("get counter"), From 05ff67ea83bad2f6dcd064ff68ab368efa48301a Mon Sep 17 00:00:00 2001 From: qicosmos Date: Thu, 20 Jun 2024 18:27:28 +0800 Subject: [PATCH 03/10] for compile --- tests/test_metric.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index a0ada5e8..12b436fa 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -311,6 +311,7 @@ TEST_CASE("test register metric") { TEST_CASE("test remove metric and serialize metrics") { using metric_mgr = metric_manager_t<1>; + std::cout << sizeof(metric_mgr) << "\n"; metric_mgr::create_metric_dynamic("test_counter", ""); metric_mgr::create_metric_dynamic("test_counter2", ""); @@ -785,47 +786,59 @@ TEST_CASE("test serialize with emptry metrics") { std::vector{"method"}); h1->serialize(s1); CHECK(s1.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON h1->serialize_to_json(s1); CHECK(s1.empty()); +#endif auto h2 = std::make_shared( "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}); h2->serialize(s1); CHECK(s1.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON h2->serialize_to_json(s1); CHECK(s1.empty()); +#endif auto h3 = std::make_shared( "get_count2", "help", std::vector{5.23, 10.54, 20.0, 50.0, 100.0}, std::map{{"method", "/"}}); h3->serialize(s1); CHECK(s1.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON h3->serialize_to_json(s1); CHECK(s1.empty()); +#endif auto c1 = std::make_shared(std::string("get_count"), std::string("get counter")); c1->serialize(s1); CHECK(s1.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON c1->serialize_to_json(s1); CHECK(s1.empty()); +#endif auto c2 = std::make_shared( std::string("get_count"), std::string("get counter"), std::map{{"method", "GET"}}); c2->serialize(s1); CHECK(s1.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON c2->serialize_to_json(s1); CHECK(s1.empty()); +#endif auto c3 = std::make_shared(std::string("get_count"), std::string("get counter"), std::vector{"method"}); c3->serialize(s1); CHECK(s1.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON c3->serialize_to_json(s1); CHECK(s1.empty()); +#endif { std::string str; @@ -833,8 +846,10 @@ TEST_CASE("test serialize with emptry metrics") { h1->serialize(str); CHECK(!str.empty()); str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON h1->serialize_to_json(str); CHECK(!str.empty()); +#endif } { @@ -843,8 +858,10 @@ TEST_CASE("test serialize with emptry metrics") { h2->serialize(str); CHECK(!str.empty()); str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON h1->serialize_to_json(str); CHECK(!str.empty()); +#endif } { @@ -853,8 +870,10 @@ TEST_CASE("test serialize with emptry metrics") { c1->serialize(str); CHECK(!str.empty()); str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON c1->serialize_to_json(str); CHECK(!str.empty()); +#endif } { @@ -863,8 +882,10 @@ TEST_CASE("test serialize with emptry metrics") { c2->serialize(str); CHECK(!str.empty()); str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON c2->serialize_to_json(str); CHECK(!str.empty()); +#endif } { @@ -873,8 +894,10 @@ TEST_CASE("test serialize with emptry metrics") { c3->serialize(str); CHECK(!str.empty()); str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON c3->serialize_to_json(str); CHECK(!str.empty()); +#endif } } From 41ec2437025347ac30ce98e5a1e389120f33b46b Mon Sep 17 00:00:00 2001 From: qicosmos Date: Thu, 20 Jun 2024 19:07:43 +0800 Subject: [PATCH 04/10] change to type --- include/cinatra/ylt/metric/metric.hpp | 11 +++++++++-- tests/test_metric.cpp | 18 ++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/include/cinatra/ylt/metric/metric.hpp b/include/cinatra/ylt/metric/metric.hpp index 12c25c6b..8060ce2d 100644 --- a/include/cinatra/ylt/metric/metric.hpp +++ b/include/cinatra/ylt/metric/metric.hpp @@ -201,7 +201,7 @@ class metric_t { std::chrono::system_clock::time_point metric_created_time_{}; }; -template +template struct metric_manager_t { struct null_mutex_t { void lock() {} @@ -580,5 +580,12 @@ struct metric_manager_t { static inline std::once_flag flag_; }; -using default_metric_manager = metric_manager_t<0>; +struct ylt_default_metric_tag_t {}; +using default_metric_manager = metric_manager_t; + +template +constexpr inline auto get_root_manager() { + static_assert(sizeof...(Args) > 0, "must fill metric manager"); + return std::tuple{}; +} } // namespace ylt::metric \ No newline at end of file diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index 12b436fa..5c97cbc6 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -309,9 +309,11 @@ TEST_CASE("test register metric") { } } +template +struct test_id_t {}; + TEST_CASE("test remove metric and serialize metrics") { - using metric_mgr = metric_manager_t<1>; - std::cout << sizeof(metric_mgr) << "\n"; + using metric_mgr = metric_manager_t>; metric_mgr::create_metric_dynamic("test_counter", ""); metric_mgr::create_metric_dynamic("test_counter2", ""); @@ -330,7 +332,7 @@ TEST_CASE("test remove metric and serialize metrics") { metric_mgr::create_metric_static("test_static_counter", ""), std::invalid_argument); - using metric_mgr2 = metric_manager_t<2>; + using metric_mgr2 = metric_manager_t>; auto c = metric_mgr2::create_metric_static("test_static_counter", ""); auto c2 = @@ -356,7 +358,7 @@ TEST_CASE("test remove metric and serialize metrics") { } TEST_CASE("test filter metrics static") { - using metric_mgr = metric_manager_t<3>; + using metric_mgr = metric_manager_t>; auto c = metric_mgr::create_metric_static( "test_static_counter", "", std::map{{"method", "GET"}}); @@ -436,7 +438,7 @@ TEST_CASE("test filter metrics static") { } TEST_CASE("test filter metrics dynamic") { - using metric_mgr = metric_manager_t<4>; + using metric_mgr = metric_manager_t>; auto c = metric_mgr::create_metric_dynamic( "test_dynamic_counter", "", std::vector{{"method"}}); auto c2 = metric_mgr::create_metric_dynamic( @@ -516,7 +518,7 @@ TEST_CASE("test filter metrics dynamic") { } TEST_CASE("test get metric by static labels and label") { - using metric_mgr = metric_manager_t<9>; + using metric_mgr = metric_manager_t>; metric_mgr::create_metric_static( "http_req_test", "", std::map{{"method", "GET"}, {"url", "/"}}); @@ -574,7 +576,7 @@ TEST_CASE("test get metric by static labels and label") { } TEST_CASE("test get metric by dynamic labels") { - using metric_mgr = metric_manager_t<10>; + using metric_mgr = metric_manager_t>; auto c = metric_mgr::create_metric_dynamic( "http_req_static", "", std::vector{"method", "code"}); @@ -917,7 +919,7 @@ TEST_CASE("test serialize with multiple threads") { std::string("get counter"), std::vector{"method"}); - using test_metric_manager = metric_manager_t<20>; + using test_metric_manager = metric_manager_t>; test_metric_manager::register_metric_dynamic(c, g, h1, c1); From fe58580a6d4d4b9475751d904f0aca7d32ce7a1a Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 21 Jun 2024 15:44:46 +0800 Subject: [PATCH 05/10] linux inner metrics --- include/cinatra/ylt/metric/counter.hpp | 19 +- include/cinatra/ylt/metric/gauge.hpp | 1 + include/cinatra/ylt/metric/metric.hpp | 15 + include/cinatra/ylt/metric/summary.hpp | 4 +- include/cinatra/ylt/metric/system_metric.hpp | 331 +++++++++++++++++++ tests/test_metric.cpp | 14 +- 6 files changed, 379 insertions(+), 5 deletions(-) create mode 100644 include/cinatra/ylt/metric/system_metric.hpp diff --git a/include/cinatra/ylt/metric/counter.hpp b/include/cinatra/ylt/metric/counter.hpp index 6345571e..48fbaf82 100644 --- a/include/cinatra/ylt/metric/counter.hpp +++ b/include/cinatra/ylt/metric/counter.hpp @@ -28,6 +28,7 @@ class counter_t : public metric_t { counter_t(std::string name, std::string help) : metric_t(MetricType::Counter, std::move(name), std::move(help)) { use_atomic_ = true; + g_user_metric_count++; } // static labels value, contains a map with atomic value. @@ -36,6 +37,7 @@ class counter_t : public metric_t { : metric_t(MetricType::Counter, std::move(name), std::move(help), std::move(labels)) { atomic_value_map_.emplace(labels_value_, 0); + g_user_metric_count++; use_atomic_ = true; } @@ -43,9 +45,11 @@ class counter_t : public metric_t { counter_t(std::string name, std::string help, std::vector labels_name) : metric_t(MetricType::Counter, std::move(name), std::move(help), - std::move(labels_name)) {} + std::move(labels_name)) { + g_user_metric_count++; + } - virtual ~counter_t() {} + virtual ~counter_t() { g_user_metric_count--; } double value() { return default_lable_value_; } @@ -161,10 +165,21 @@ class counter_t : public metric_t { } else { std::lock_guard lock(mtx_); + stat_metric(labels_value); set_value(value_map_[labels_value], value, op_type_t::INC); } } + void stat_metric(const std::vector &labels_value) { + if (!value_map_.contains(labels_value)) { + for (auto &key : labels_value) { + g_user_metric_memory->inc(key.size()); + } + g_user_metric_memory->inc(8); + g_user_metric_labels->inc(); + } + } + void update(double value) { default_lable_value_ = value; } void update(const std::vector &labels_value, double value) { diff --git a/include/cinatra/ylt/metric/gauge.hpp b/include/cinatra/ylt/metric/gauge.hpp index 18693f1a..8ab33f14 100644 --- a/include/cinatra/ylt/metric/gauge.hpp +++ b/include/cinatra/ylt/metric/gauge.hpp @@ -45,6 +45,7 @@ class gauge_t : public counter_t { } else { std::lock_guard lock(mtx_); + stat_metric(labels_value); set_value(value_map_[labels_value], value, op_type_t::DEC); } } diff --git a/include/cinatra/ylt/metric/metric.hpp b/include/cinatra/ylt/metric/metric.hpp index 8060ce2d..5e4b4da5 100644 --- a/include/cinatra/ylt/metric/metric.hpp +++ b/include/cinatra/ylt/metric/metric.hpp @@ -201,6 +201,21 @@ class metric_t { std::chrono::system_clock::time_point metric_created_time_{}; }; +template +struct metric_manager_t; + +struct ylt_system_tag_t {}; +using system_metric_manager = metric_manager_t; + +class counter_t; +inline auto g_user_metric_memory = + std::make_shared("ylt_user_metric_memory", ""); +inline auto g_user_metric_labels = + std::make_shared("ylt_user_metric_labels", ""); +inline auto g_summary_failed_count = + std::make_shared("ylt_summary_failed_count", ""); +inline std::atomic g_user_metric_count = 0; + template struct metric_manager_t { struct null_mutex_t { diff --git a/include/cinatra/ylt/metric/summary.hpp b/include/cinatra/ylt/metric/summary.hpp index 58957bc7..cf58b9cc 100644 --- a/include/cinatra/ylt/metric/summary.hpp +++ b/include/cinatra/ylt/metric/summary.hpp @@ -109,7 +109,7 @@ class summary_t : public metric_t { throw std::invalid_argument("not a default label metric"); } if (block_->sample_queue_.size_approx() >= 20000000) { - // TODO: record failed count. + g_summary_failed_count->inc(); return; } block_->sample_queue_.enqueue(value); @@ -131,7 +131,7 @@ class summary_t : public metric_t { } } if (labels_block_->sample_queue_.size_approx() >= 20000000) { - // TODO: record failed count. + g_summary_failed_count->inc(); return; } labels_block_->sample_queue_.enqueue({std::move(labels_value), value}); diff --git a/include/cinatra/ylt/metric/system_metric.hpp b/include/cinatra/ylt/metric/system_metric.hpp new file mode 100644 index 00000000..3e87a832 --- /dev/null +++ b/include/cinatra/ylt/metric/system_metric.hpp @@ -0,0 +1,331 @@ +#pragma once +#if defined(__GNUC__) +#include +#include + +#include +#include +#include +#include +#include +#include + +#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" + +// reference: brpc/src/bvar/default_variables.cpp + +namespace ylt::metric { +namespace detail { + +inline int64_t last_time_us = 0; +inline int64_t last_sys_time_us = 0; +inline int64_t last_user_time_us = 0; + +inline int64_t gettimeofday_us() { + timeval now; + gettimeofday(&now, NULL); + return now.tv_sec * 1000000L + now.tv_usec; +} + +inline int64_t timeval_to_microseconds(const timeval &tv) { + return tv.tv_sec * 1000000L + tv.tv_usec; +} + +inline void stat_cpu() { + static auto process_cpu_usage = + system_metric_manager::get_metric_static( + "ylt_process_cpu_usage"); + static auto process_cpu_usage_system = + system_metric_manager::get_metric_static( + "ylt_process_cpu_usage_system"); + static auto process_cpu_usage_user = + system_metric_manager::get_metric_static( + "ylt_process_cpu_usage_user"); + + rusage usage{}; + getrusage(RUSAGE_SELF, &usage); + int64_t utime = timeval_to_microseconds(usage.ru_utime); + int64_t stime = timeval_to_microseconds(usage.ru_stime); + int64_t time_total = utime + stime; + int64_t now = gettimeofday_us(); + if (last_time_us == 0) { + last_time_us = now; + last_sys_time_us = stime; + last_user_time_us = utime; + return; + } + + int64_t elapsed = now - last_time_us; + if (elapsed == 0) { + return; + } + + double cpu_usage = + double(time_total - (last_sys_time_us + last_user_time_us)) / + (now - last_time_us); + double sys_cpu_usage = + double(stime - last_sys_time_us) / (now - last_time_us); + double usr_cpu_usage = + double(utime - last_user_time_us) / (now - last_time_us); + process_cpu_usage->update(cpu_usage); + process_cpu_usage_system->update(sys_cpu_usage); + process_cpu_usage_user->update(usr_cpu_usage); + + last_time_us = now; + last_sys_time_us = stime; + last_user_time_us = utime; +} + +inline void stat_memory() { + static auto process_memory_virtual = + system_metric_manager::get_metric_static( + "ylt_process_memory_virtual"); + static auto process_memory_resident = + system_metric_manager::get_metric_static( + "ylt_process_memory_resident"); + static auto process_memory_shared = + system_metric_manager::get_metric_static( + "ylt_process_memory_shared"); + long virtual_size = 0; + long resident = 0; + long share = 0; + std::ifstream file("/proc/self/statm"); + if (!file) { + return; + } + + file >> virtual_size >> resident >> share; + static long page_size = sysconf(_SC_PAGE_SIZE); + + process_memory_virtual->update(virtual_size); + process_memory_resident->update(resident * page_size); + process_memory_shared->update(share * page_size); +} + +struct ProcIO { + size_t rchar; + size_t wchar; + size_t syscr; + size_t syscw; + size_t read_bytes; + size_t write_bytes; + size_t cancelled_write_bytes; +}; + +inline void stat_io() { + static auto process_io_read_bytes_second = + system_metric_manager::get_metric_static( + "ylt_process_io_read_bytes_second"); + static auto process_io_write_bytes_second = + system_metric_manager::get_metric_static( + "ylt_process_io_write_bytes_second"); + static auto process_io_read_second = + system_metric_manager::get_metric_static( + "ylt_process_io_read_second"); + static auto process_io_write_second = + system_metric_manager::get_metric_static( + "ylt_process_io_write_second"); + + auto stream_file = + std::shared_ptr(fopen("/proc/self/io", "r"), [](FILE *ptr) { + fclose(ptr); + }); + if (stream_file == nullptr) { + return; + } + + ProcIO s{}; + if (fscanf(stream_file.get(), + "%*s %lu %*s %lu %*s %lu %*s %lu %*s %lu %*s %lu %*s %lu", + &s.rchar, &s.wchar, &s.syscr, &s.syscw, &s.read_bytes, + &s.write_bytes, &s.cancelled_write_bytes) != 7) { + return; + } + + process_io_read_bytes_second->update(s.rchar); + process_io_write_bytes_second->update(s.wchar); + process_io_read_second->update(s.syscr); + process_io_write_second->update(s.syscw); +} + +inline void stat_avg_load() { + static auto system_loadavg_1m = + system_metric_manager::get_metric_static( + "ylt_system_loadavg_1m"); + static auto system_loadavg_5m = + system_metric_manager::get_metric_static( + "ylt_system_loadavg_5m"); + static auto system_loadavg_15m = + system_metric_manager::get_metric_static( + "ylt_system_loadavg_15m"); + std::ifstream file("/proc/loadavg"); + if (!file) { + return; + } + + double loadavg_1m = 0; + double loadavg_5m = 0; + double loadavg_15m = 0; + file >> loadavg_1m >> loadavg_5m >> loadavg_15m; + system_loadavg_1m->update(loadavg_1m); + system_loadavg_1m->update(loadavg_5m); + system_loadavg_1m->update(loadavg_15m); +} + +struct ProcStat { + int pid; + // std::string comm; + char state; + int ppid; + int pgrp; + int session; + int tty_nr; + int tpgid; + unsigned flags; + unsigned long minflt; + unsigned long cminflt; + unsigned long majflt; + unsigned long cmajflt; + unsigned long utime; + unsigned long stime; + unsigned long cutime; + unsigned long cstime; + long priority; + long nice; + long num_threads; +}; + +inline void process_status() { + static auto process_uptime = + system_metric_manager::get_metric_static("ylt_process_uptime"); + static auto process_priority = + system_metric_manager::get_metric_static("ylt_process_priority"); + static auto pid = + system_metric_manager::get_metric_static("ylt_pid"); + static auto ppid = + system_metric_manager::get_metric_static("ylt_ppid"); + static auto pgrp = + system_metric_manager::get_metric_static("ylt_pgrp"); + static auto thread_count = + system_metric_manager::get_metric_static("ylt_thread_count"); + + auto stream_file = + std::shared_ptr(fopen("/proc/self/stat", "r"), [](FILE *ptr) { + fclose(ptr); + }); + if (stream_file == nullptr) { + return; + } + + ProcStat stat{}; + if (fscanf(stream_file.get(), + "%d %*s %c " + "%d %d %d %d %d " + "%u %lu %lu %lu " + "%lu %lu %lu %lu %lu " + "%ld %ld %ld", + &stat.pid, &stat.state, &stat.ppid, &stat.pgrp, &stat.session, + &stat.tty_nr, &stat.tpgid, &stat.flags, &stat.minflt, + &stat.cminflt, &stat.majflt, &stat.cmajflt, &stat.utime, + &stat.stime, &stat.cutime, &stat.cstime, &stat.priority, + &stat.nice, &stat.num_threads) != 19) { + return; + } + + process_uptime->inc(); + process_priority->update(stat.priority); + pid->update(stat.pid); + ppid->update(stat.ppid); + pgrp->update(stat.pgrp); + thread_count->update(stat.num_threads); +} + +inline void stat_metric() { + static auto user_metric_count = + system_metric_manager::get_metric_static( + "ylt_user_metric_count"); + user_metric_count->update(g_user_metric_count); +} + +inline void ylt_stat() { + stat_cpu(); + stat_memory(); + stat_io(); + stat_avg_load(); + process_status(); + stat_metric(); +} + +inline void start_stat(std::shared_ptr timer) { + timer->expires_after(std::chrono::seconds(1)); + timer->async_wait([timer](std::error_code ec) { + if (ec) { + return; + } + + ylt_stat(); + start_stat(timer); + }); +} +} // namespace detail + +inline bool start_system_metric() { + system_metric_manager::create_metric_static("ylt_process_cpu_usage", + ""); + system_metric_manager::create_metric_static( + "ylt_process_cpu_usage_system", ""); + system_metric_manager::create_metric_static( + "ylt_process_cpu_usage_user", ""); + + system_metric_manager::create_metric_static( + "ylt_process_memory_virtual", ""); + system_metric_manager::create_metric_static( + "ylt_process_memory_resident", ""); + system_metric_manager::create_metric_static( + "ylt_process_memory_shared", ""); + + system_metric_manager::create_metric_static("ylt_process_uptime", + ""); + system_metric_manager::create_metric_static("ylt_pid", ""); + system_metric_manager::create_metric_static("ylt_ppid", ""); + system_metric_manager::create_metric_static("ylt_pgrp", ""); + system_metric_manager::create_metric_static("ylt_thread_count", ""); + system_metric_manager::create_metric_static("ylt_process_priority", + ""); + + system_metric_manager::create_metric_static("ylt_user_metric_count", + ""); + + system_metric_manager::create_metric_static("ylt_system_loadavg_1m", + ""); + system_metric_manager::create_metric_static("ylt_system_loadavg_5m", + ""); + system_metric_manager::create_metric_static("ylt_system_loadavg_15m", + ""); + + system_metric_manager::create_metric_static( + "ylt_process_io_read_bytes_second", ""); + system_metric_manager::create_metric_static( + "ylt_process_io_write_bytes_second", ""); + system_metric_manager::create_metric_static( + "ylt_process_io_read_second", ""); + system_metric_manager::create_metric_static( + "ylt_process_io_write_second", ""); + + system_metric_manager::register_metric_static(g_user_metric_memory); + system_metric_manager::register_metric_static(g_user_metric_labels); + system_metric_manager::register_metric_static(g_summary_failed_count); + + static auto exucutor = coro_io::create_io_context_pool(1); + auto timer = + std::make_shared(exucutor->get_executor()); + detail::start_stat(timer); + + return true; +} +} // namespace ylt::metric +#endif \ No newline at end of file diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index 5c97cbc6..76260e41 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -9,6 +9,7 @@ #include "cinatra/ylt/metric/counter.hpp" #include "cinatra/ylt/metric/histogram.hpp" #include "cinatra/ylt/metric/summary.hpp" +#include "cinatra/ylt/metric/system_metric.hpp" #include "doctest/doctest.h" using namespace ylt; using namespace ylt::metric; @@ -576,7 +577,7 @@ TEST_CASE("test get metric by static labels and label") { } TEST_CASE("test get metric by dynamic labels") { - using metric_mgr = metric_manager_t>; + using metric_mgr = metric_manager_t>; auto c = metric_mgr::create_metric_dynamic( "http_req_static", "", std::vector{"method", "code"}); @@ -945,6 +946,17 @@ TEST_CASE("test serialize with multiple threads") { #endif } +#if defined(__GNUC__) +TEST_CASE("test system metric") { + start_system_metric(); + detail::ylt_stat(); + + auto s = system_metric_manager::serialize_static(); + std::cout << s; + CHECK(!s.empty()); +} +#endif + DOCTEST_MSVC_SUPPRESS_WARNING_WITH_PUSH(4007) int main(int argc, char** argv) { return doctest::Context(argc, argv).run(); } DOCTEST_MSVC_SUPPRESS_WARNING_POP \ No newline at end of file From 6d8b4a8c27bb85a5b4cae2f89c766fe9d3b254cf Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 21 Jun 2024 16:33:12 +0800 Subject: [PATCH 06/10] metric_collector --- include/cinatra/ylt/metric/metric.hpp | 37 ++++++++++++++++++++ include/cinatra/ylt/metric/system_metric.hpp | 2 +- tests/test_metric.cpp | 23 ++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/include/cinatra/ylt/metric/metric.hpp b/include/cinatra/ylt/metric/metric.hpp index 5e4b4da5..df9bff62 100644 --- a/include/cinatra/ylt/metric/metric.hpp +++ b/include/cinatra/ylt/metric/metric.hpp @@ -278,6 +278,15 @@ struct metric_manager_t { return r; } + static auto get_metrics() { + if (need_lock_) { + return collect(); + } + else { + return collect(); + } + } + static auto metric_map_static() { return metric_map_impl(); } static auto metric_map_dynamic() { return metric_map_impl(); } @@ -603,4 +612,32 @@ constexpr inline auto get_root_manager() { static_assert(sizeof...(Args) > 0, "must fill metric manager"); return std::tuple{}; } + +template +struct metric_collector_t { + static std::string serialize() { + auto vec = get_all_metrics(); + return default_metric_manager::serialize(vec); + } + +#ifdef CINATRA_ENABLE_METRIC_JSON + static std::string serialize_to_json() { + auto vec = get_all_metrics(); + return default_metric_manager::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::get_metrics(); + vec.insert(vec.end(), v.begin(), v.end()); + } +}; } // namespace ylt::metric \ No newline at end of file diff --git a/include/cinatra/ylt/metric/system_metric.hpp b/include/cinatra/ylt/metric/system_metric.hpp index 3e87a832..a8f1a071 100644 --- a/include/cinatra/ylt/metric/system_metric.hpp +++ b/include/cinatra/ylt/metric/system_metric.hpp @@ -37,7 +37,7 @@ inline int64_t timeval_to_microseconds(const timeval &tv) { inline void stat_cpu() { static auto process_cpu_usage = - system_metric_manager::get_metric_static( + system_metric_manager::get_metric_static( "ylt_process_cpu_usage"); static auto process_cpu_usage_system = system_metric_manager::get_metric_static( diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index 76260e41..9c312d05 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -954,6 +954,29 @@ TEST_CASE("test system metric") { auto s = system_metric_manager::serialize_static(); std::cout << s; CHECK(!s.empty()); + +#ifdef CINATRA_ENABLE_METRIC_JSON + auto json = system_metric_manager::serialize_to_json_static(); + std::cout << json << "\n"; + CHECK(!json.empty()); +#endif + + using metric_manager = metric_manager_t>; + auto c = metric_manager::create_metric_dynamic("test_qps", ""); + c->inc(42); + using root = metric_collector_t; + s.clear(); + s = root::serialize(); + std::cout << s; + CHECK(!s.empty()); + +#ifdef CINATRA_ENABLE_METRIC_JSON + json.clear(); + json = root::serialize_to_json(); + std::cout << json << "\n"; + CHECK(!json.empty()); +#endif } #endif From 241c3237372fc995285baa7ebcb0e03f2e0a0206 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 21 Jun 2024 16:46:18 +0800 Subject: [PATCH 07/10] inner metrics for http server --- example/main.cpp | 4 ++-- include/cinatra/coro_http_server.hpp | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/example/main.cpp b/example/main.cpp index 3a1ed04d..bf65e766 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -71,7 +71,7 @@ async_simple::coro::Lazy chunked_upload1(coro_http_client &client) { co_await file.async_open(filename, coro_io::flags::read_only); std::string buf; - detail::resize(buf, 100); + cinatra::detail::resize(buf, 100); auto fn = [&file, &buf]() -> async_simple::coro::Lazy { auto [ec, size] = co_await file.async_read(buf.data(), buf.size()); @@ -607,7 +607,7 @@ async_simple::coro::Lazy use_pool() { int main() { // use_metric(); - // metrics_example(); + metrics_example(); async_simple::coro::syncAwait(use_channel()); async_simple::coro::syncAwait(use_pool()); async_simple::coro::syncAwait(basic_usage()); diff --git a/include/cinatra/coro_http_server.hpp b/include/cinatra/coro_http_server.hpp index 17a057fd..60c5670e 100644 --- a/include/cinatra/coro_http_server.hpp +++ b/include/cinatra/coro_http_server.hpp @@ -11,7 +11,7 @@ #include "ylt/coro_io/coro_file.hpp" #include "ylt/coro_io/coro_io.hpp" #include "ylt/coro_io/io_context_pool.hpp" -#include "ylt/metric/metric.hpp" +#include "ylt/metric/system_metric.hpp" namespace cinatra { enum class file_resp_format_type { @@ -185,19 +185,21 @@ 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; set_http_handler( url_path, [enable_json](coro_http_request &req, coro_http_response &res) { std::string str; #ifdef CINATRA_ENABLE_METRIC_JSON if (enable_json) { - str = - ylt::metric::default_metric_manager::serialize_to_json_static(); + str = root::serialize_to_json(); res.set_content_type(); } else #endif - str = ylt::metric::default_metric_manager::serialize_static(); + str = root::serialize(); res.set_status_and_content(status_type::ok, std::move(str)); }); @@ -930,6 +932,7 @@ class coro_http_server { default_metric_manager::create_metric_static( cinatra_metric_conf::server_read_latency, "", std::vector{3, 5, 7, 9, 13, 18, 23, 35, 50}); + ylt::metric::start_system_metric(); } private: From 008bf3c7e2012eecc30251a7325fcbc435f47959 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 21 Jun 2024 17:00:25 +0800 Subject: [PATCH 08/10] for msvc --- example/main.cpp | 2 +- include/cinatra/coro_http_server.hpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/example/main.cpp b/example/main.cpp index bf65e766..d81eebf3 100644 --- a/example/main.cpp +++ b/example/main.cpp @@ -607,7 +607,7 @@ async_simple::coro::Lazy use_pool() { int main() { // use_metric(); - metrics_example(); + // metrics_example(); async_simple::coro::syncAwait(use_channel()); async_simple::coro::syncAwait(use_pool()); async_simple::coro::syncAwait(basic_usage()); diff --git a/include/cinatra/coro_http_server.hpp b/include/cinatra/coro_http_server.hpp index 60c5670e..d34050e1 100644 --- a/include/cinatra/coro_http_server.hpp +++ b/include/cinatra/coro_http_server.hpp @@ -932,7 +932,9 @@ class coro_http_server { default_metric_manager::create_metric_static( cinatra_metric_conf::server_read_latency, "", std::vector{3, 5, 7, 9, 13, 18, 23, 35, 50}); +#if defined(__GNUC__) ylt::metric::start_system_metric(); +#endif } private: From c1d5582d15e2baa1795823696f60e76238d9c7ec Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 21 Jun 2024 17:19:27 +0800 Subject: [PATCH 09/10] headers --- include/cinatra/ylt/metric/system_metric.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/cinatra/ylt/metric/system_metric.hpp b/include/cinatra/ylt/metric/system_metric.hpp index a8f1a071..81d1e1b4 100644 --- a/include/cinatra/ylt/metric/system_metric.hpp +++ b/include/cinatra/ylt/metric/system_metric.hpp @@ -10,11 +10,19 @@ #include #include +#if __has_include("ylt/coro_io/coro_io.hpp") +#include "ylt/coro_io/coro_io.hpp" +#include "ylt/coro_io/io_context_pool.hpp" +#include "ylt/metric/counter.hpp" +#include "ylt/metric/gauge.hpp" +#include "ylt/metric/metric.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" +#endif // reference: brpc/src/bvar/default_variables.cpp From 0c9d16f22820ce9b926c43ed834dc04698edcb10 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Fri, 21 Jun 2024 17:45:21 +0800 Subject: [PATCH 10/10] update doc --- include/cinatra/ylt/metric/metric.hpp | 6 ---- lang/how_to_use_metrics.md | 47 ++++++++++++++++++++++++--- 2 files changed, 43 insertions(+), 10 deletions(-) diff --git a/include/cinatra/ylt/metric/metric.hpp b/include/cinatra/ylt/metric/metric.hpp index df9bff62..73424c09 100644 --- a/include/cinatra/ylt/metric/metric.hpp +++ b/include/cinatra/ylt/metric/metric.hpp @@ -607,12 +607,6 @@ struct metric_manager_t { struct ylt_default_metric_tag_t {}; using default_metric_manager = metric_manager_t; -template -constexpr inline auto get_root_manager() { - static_assert(sizeof...(Args) > 0, "must fill metric manager"); - return std::tuple{}; -} - template struct metric_collector_t { static std::string serialize() { diff --git a/lang/how_to_use_metrics.md b/lang/how_to_use_metrics.md index 471a6b5a..a1983c39 100644 --- a/lang/how_to_use_metrics.md +++ b/lang/how_to_use_metrics.md @@ -218,7 +218,7 @@ CHECK(m1->as()->value() == 1); 指标管理器的api ```cpp -template +template struct metric_manager_t { // 创建并注册指标,返回注册的指标对象 template @@ -249,6 +249,9 @@ struct metric_manager_t { static std::vector metric_keys_static(); static std::vector metric_keys_dynamic(); + // 获取管理器的所有指标 + static std::shared_ptr get_metrics(); + // 根据名称获取指标对象,T为具体指标的类型,如 get_metric_static(); // 如果找不到则返回nullptr template @@ -297,13 +300,49 @@ struct metric_manager_t { static std::vector> filter_metrics_dynamic( const metric_filter_options& options); }; -using default_metric_manager = metric_manager_t<0>; + +struct ylt_default_metric_tag_t {}; +using default_metric_manager = metric_manager_t; ``` metric_manager_t默认为default_metric_manager,如果希望有多个metric manager,用户可以自定义新的metric manager,如: ```cpp -constexpr size_t metric_id = 100; -using my_metric_manager = metric_manager_t; +struct my_tag{}; +using my_metric_manager = metric_manager_t; +``` + +# system_metric_manager +系统metric 管理器,需要调用ylt::metric::start_system_metric(),内部会每秒钟采集系统指标,系统指标: +有进程的cpu,内存,io,平均负载,线程数,指标的指标等指标。 + +指标的指标: +- 总的metric数量 +- 总的label数量 +- metric的内存大小 +- summary失败的数量 + + +# metric_collector_t +metric_collector_t 集中管理所有的metric_manager,如 +```cpp +template +struct metric_collector_t { + // 序列化所有指标管理器中的指标 + static std::string serialize(); + + // 序列化所有指标管理器中的指标为json + static std::string serialize_to_json(); + + // 获取所有指标管理器中的指标 + static std::vector> get_all_metrics(); +}; +``` + +使用metric_collector_t,将所有的指标管理器作为参数传入: +```cpp +using root_manager = metric_collector_t; + +std::string str = root_manager::serialize(); ``` # histogram