diff --git a/include/ylt/metric.hpp b/include/ylt/metric.hpp index 89279894d..108113e76 100644 --- a/include/ylt/metric.hpp +++ b/include/ylt/metric.hpp @@ -17,6 +17,6 @@ #define CINATRA_ENABLE_METRIC_JSON #include "metric/gauge.hpp" #include "metric/histogram.hpp" -#include "metric/metric.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 d801722a3..48fbaf824 100644 --- a/include/ylt/metric/counter.hpp +++ b/include/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_; } @@ -82,20 +86,16 @@ 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); + std::string value_str; + serialize_map(map, value_str); + if (!value_str.empty()) { + serialize_head(str); + str.append(value_str); } } @@ -113,18 +113,17 @@ 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 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) { @@ -133,7 +132,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 @@ -164,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/ylt/metric/gauge.hpp b/include/ylt/metric/gauge.hpp index 18693f1af..8ab33f143 100644 --- a/include/ylt/metric/gauge.hpp +++ b/include/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/ylt/metric/histogram.hpp b/include/ylt/metric/histogram.hpp index 1f371b853..a15a350ed 100644 --- a/include/ylt/metric/histogram.hpp +++ b/include/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/include/ylt/metric/metric.hpp b/include/ylt/metric/metric.hpp index 12c25c6b0..73424c094 100644 --- a/include/ylt/metric/metric.hpp +++ b/include/ylt/metric/metric.hpp @@ -201,7 +201,22 @@ class metric_t { std::chrono::system_clock::time_point metric_created_time_{}; }; -template +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 { void lock() {} @@ -263,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(); } @@ -580,5 +604,34 @@ 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 +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/ylt/metric/summary.hpp b/include/ylt/metric/summary.hpp index 58957bc74..cf58b9cc5 100644 --- a/include/ylt/metric/summary.hpp +++ b/include/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/ylt/metric/system_metric.hpp b/include/ylt/metric/system_metric.hpp new file mode 100644 index 000000000..81d1e1b40 --- /dev/null +++ b/include/ylt/metric/system_metric.hpp @@ -0,0 +1,339 @@ +#pragma once +#if defined(__GNUC__) +#include +#include + +#include +#include +#include +#include +#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 + +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/include/ylt/standalone/cinatra/coro_http_server.hpp b/include/ylt/standalone/cinatra/coro_http_server.hpp index 17a057fdb..d34050e17 100644 --- a/include/ylt/standalone/cinatra/coro_http_server.hpp +++ b/include/ylt/standalone/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,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: diff --git a/src/metric/tests/test_metric.cpp b/src/metric/tests/test_metric.cpp index 5608460c8..6242e285d 100644 --- a/src/metric/tests/test_metric.cpp +++ b/src/metric/tests/test_metric.cpp @@ -303,8 +303,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>; + using metric_mgr = metric_manager_t>; metric_mgr::create_metric_dynamic("test_counter", ""); metric_mgr::create_metric_dynamic("test_counter2", ""); @@ -323,7 +326,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 = @@ -349,7 +352,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"}}); @@ -429,7 +432,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( @@ -509,7 +512,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", "/"}}); @@ -567,7 +570,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"}); @@ -771,6 +774,205 @@ 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()); +#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; + h1->observe({"POST"}, 1); + h1->serialize(str); + CHECK(!str.empty()); + str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON + h1->serialize_to_json(str); + CHECK(!str.empty()); +#endif + } + + { + std::string str; + h2->observe(1); + h2->serialize(str); + CHECK(!str.empty()); + str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON + h1->serialize_to_json(str); + CHECK(!str.empty()); +#endif + } + + { + std::string str; + c1->inc(); + c1->serialize(str); + CHECK(!str.empty()); + str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON + c1->serialize_to_json(str); + CHECK(!str.empty()); +#endif + } + + { + std::string str; + c2->inc({"GET"}); + c2->serialize(str); + CHECK(!str.empty()); + str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON + c2->serialize_to_json(str); + CHECK(!str.empty()); +#endif + } + + { + std::string str; + c3->inc({"GET"}); + c3->serialize(str); + CHECK(!str.empty()); + str.clear(); +#ifdef CINATRA_ENABLE_METRIC_JSON + c3->serialize_to_json(str); + CHECK(!str.empty()); +#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>; + + 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 +} + +#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()); + +#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 + 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 diff --git a/website/docs/zh/metric/metrict_introduction.md b/website/docs/zh/metric/metrict_introduction.md index a26d75bac..e7b9cdb1f 100644 --- a/website/docs/zh/metric/metrict_introduction.md +++ b/website/docs/zh/metric/metrict_introduction.md @@ -295,7 +295,7 @@ CHECK(m1->as()->value() == 1); 指标管理器的api ```cpp -template +template struct metric_manager_t { // 创建并注册指标,返回注册的指标对象 template @@ -326,6 +326,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 @@ -374,13 +377,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