From 4d76c28e46179c734619f069cd450b0f7aafd785 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Tue, 3 Dec 2024 11:01:24 +0800 Subject: [PATCH 1/2] sync metric --- include/cinatra/ylt/metric/counter.hpp | 8 +- include/cinatra/ylt/metric/dynamic_metric.hpp | 4 +- include/cinatra/ylt/metric/gauge.hpp | 1 + include/cinatra/ylt/metric/histogram.hpp | 7 +- include/cinatra/ylt/metric/metric.hpp | 11 +- include/cinatra/ylt/metric/metric_manager.hpp | 16 +- include/cinatra/ylt/metric/summary_impl.hpp | 1 - include/cinatra/ylt/metric/system_metric.hpp | 249 +---------- include/cinatra/ylt/util/map_sharded.hpp | 3 + tests/test_metric.cpp | 421 +++++++++++++++++- 10 files changed, 433 insertions(+), 288 deletions(-) diff --git a/include/cinatra/ylt/metric/counter.hpp b/include/cinatra/ylt/metric/counter.hpp index 43dc40b3..e676f940 100644 --- a/include/cinatra/ylt/metric/counter.hpp +++ b/include/cinatra/ylt/metric/counter.hpp @@ -180,6 +180,7 @@ class basic_dynamic_counter // } // else { + size_t count = 0; std::vector vec; for (auto &lb_name : labels_name) { if (auto i = labels.find(lb_name); i != labels.end()) { @@ -187,9 +188,10 @@ class basic_dynamic_counter } else { vec.push_back(""); + count++; } } - if (vec.empty()) { + if (count == labels_name.size()) { return; } Base::erase_if([&](auto &pair) { @@ -238,6 +240,10 @@ class basic_dynamic_counter bool has_label_value(const std::vector &label_value) override { std::array arr{}; size_t size = (std::min)((size_t)N, label_value.size()); + if (label_value.size() > N) { + return false; + } + for (size_t i = 0; i < size; i++) { arr[i] = label_value[i]; } diff --git a/include/cinatra/ylt/metric/dynamic_metric.hpp b/include/cinatra/ylt/metric/dynamic_metric.hpp index df024e54..db1f6509 100644 --- a/include/cinatra/ylt/metric/dynamic_metric.hpp +++ b/include/cinatra/ylt/metric/dynamic_metric.hpp @@ -3,7 +3,7 @@ #include "metric.hpp" #include "thread_local_value.hpp" -#if __has_include("ylt/util/type_traits.h") +#if __has_include("ylt/util/map_sharded.hpp") #include "ylt/util/map_sharded.hpp" #else #include "../util/map_sharded.hpp" @@ -131,4 +131,4 @@ class dynamic_metric_impl : public dynamic_metric { my_hash<137>> map_{std::min(128u, std::thread::hardware_concurrency())}; }; -} // namespace ylt::metric \ No newline at end of file +} // namespace ylt::metric diff --git a/include/cinatra/ylt/metric/gauge.hpp b/include/cinatra/ylt/metric/gauge.hpp index b783ffa1..f2632a90 100644 --- a/include/cinatra/ylt/metric/gauge.hpp +++ b/include/cinatra/ylt/metric/gauge.hpp @@ -3,6 +3,7 @@ #include #include "counter.hpp" +#include "metric.hpp" namespace ylt::metric { diff --git a/include/cinatra/ylt/metric/histogram.hpp b/include/cinatra/ylt/metric/histogram.hpp index 5e3e58f5..bc27fabe 100644 --- a/include/cinatra/ylt/metric/histogram.hpp +++ b/include/cinatra/ylt/metric/histogram.hpp @@ -242,10 +242,6 @@ class basic_dynamic_histogram : public dynamic_metric { value_str.append("\n"); } - if (value_str.empty()) { - return; - } - str.append(value_str); str.append(name_); @@ -262,6 +258,9 @@ class basic_dynamic_histogram : public dynamic_metric { str.append(std::to_string(count)); str.append("\n"); } + if (value_str.empty()) { + str.clear(); + } } #ifdef CINATRA_ENABLE_METRIC_JSON diff --git a/include/cinatra/ylt/metric/metric.hpp b/include/cinatra/ylt/metric/metric.hpp index fddd50f3..67e90d9b 100644 --- a/include/cinatra/ylt/metric/metric.hpp +++ b/include/cinatra/ylt/metric/metric.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -14,7 +13,6 @@ #include "async_simple/coro/Lazy.h" #include "async_simple/coro/SyncAwait.h" #include "cinatra/cinatra_log_wrapper.hpp" -#include "thread_local_value.hpp" #if __has_include("ylt/coro_io/coro_io.hpp") #include "ylt/coro_io/coro_io.hpp" #else @@ -53,6 +51,7 @@ struct metric_filter_options { class metric_t { public: static inline std::atomic g_user_metric_count = 0; + metric_t() = default; metric_t(MetricType type, std::string name, std::string help) : type_(type), @@ -92,8 +91,6 @@ class metric_t { MetricType metric_type() { return type_; } - auto get_created_time() { return metric_created_time_; } - std::string_view metric_name() { switch (type_) { case MetricType::Counter: @@ -197,16 +194,10 @@ inline std::chrono::seconds ylt_label_max_age{0}; inline std::chrono::seconds ylt_label_check_expire_duration{60}; inline std::atomic ylt_metric_capacity = 10000000; -inline int64_t ylt_label_capacity = 20000000; - inline void set_metric_capacity(int64_t max_count) { ylt_metric_capacity = max_count; } -inline void set_label_capacity(int64_t max_label_count) { - ylt_label_capacity = max_label_count; -} - inline void set_label_max_age( std::chrono::seconds max_age, std::chrono::seconds check_duration = std::chrono::seconds{60}) { diff --git a/include/cinatra/ylt/metric/metric_manager.hpp b/include/cinatra/ylt/metric/metric_manager.hpp index ff6011d1..32c5da4d 100644 --- a/include/cinatra/ylt/metric/metric_manager.hpp +++ b/include/cinatra/ylt/metric/metric_manager.hpp @@ -4,6 +4,11 @@ #include #include "metric.hpp" +#if __has_include("ylt/util/map_sharded.hpp") +#include "ylt/util/map_sharded.hpp" +#else +#include "../util/map_sharded.hpp" +#endif namespace ylt::metric { class manager_helper { @@ -36,9 +41,6 @@ class manager_helper { #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) { @@ -49,7 +51,10 @@ class manager_helper { } if (str.size() == 1) { - return ""; + str.append("]"); + } + else { + str.back() = ']'; } str.back() = ']'; @@ -139,6 +144,9 @@ class manager_helper { static void filter_by_label_name( std::vector>& filtered_metrics, std::shared_ptr m, const metric_filter_options& options) { + if (!options.label_regex) { + return; + } const auto& labels_name = m->labels_name(); for (auto& label_name : labels_name) { if (std::regex_match(label_name, *options.label_regex)) { diff --git a/include/cinatra/ylt/metric/summary_impl.hpp b/include/cinatra/ylt/metric/summary_impl.hpp index a6935c78..cf48ed36 100644 --- a/include/cinatra/ylt/metric/summary_impl.hpp +++ b/include/cinatra/ylt/metric/summary_impl.hpp @@ -73,7 +73,6 @@ class summary_impl { fltInt16 |= (fltInt32 >> 15) & 0xff; auto i = fltInt16 >> (8 - frac_bit); - auto j = decode_impl(i); return i; } diff --git a/include/cinatra/ylt/metric/system_metric.hpp b/include/cinatra/ylt/metric/system_metric.hpp index 7d286caf..dd786a9d 100644 --- a/include/cinatra/ylt/metric/system_metric.hpp +++ b/include/cinatra/ylt/metric/system_metric.hpp @@ -2,16 +2,6 @@ #if defined(__GNUC__) #include #include -#endif - -#if defined(WIN32) -#include -#include -#include - -// Link with Psapi.lib -#pragma comment(lib, "Psapi.lib") -#endif #include #include @@ -83,175 +73,6 @@ inline int read_command_output_through_popen(std::ostream& os, } #endif -#if defined(WIN32) -typedef struct timeval { - long tv_sec; - long tv_usec; -} timeval; - -inline int gettimeofday(struct timeval* tp, struct timezone* tzp) { - // Note: some broken versions only have 8 trailing zero's, the correct epoch - // has 9 trailing zero's This magic number is the number of 100 nanosecond - // intervals since January 1, 1601 (UTC) until 00:00:00 January 1, 1970 - static const uint64_t epoch = ((uint64_t)116444736000000000ULL); - - SYSTEMTIME system_time; - FILETIME file_time; - uint64_t time; - - GetSystemTime(&system_time); - SystemTimeToFileTime(&system_time, &file_time); - time = ((uint64_t)file_time.dwLowDateTime); - time += ((uint64_t)file_time.dwHighDateTime) << 32; - - tp->tv_sec = (long)((time - epoch) / 10000000L); - tp->tv_usec = (long)(system_time.wMilliseconds * 1000); - return 0; -} - -#define RUSAGE_SELF 0 -#define RUSAGE_CHILDREN (-1) - -struct rusage { - struct timeval ru_utime; /* user time used */ - struct timeval ru_stime; /* system time used */ -}; - -inline int getrusage(int who, struct rusage* rusage) { - FILETIME starttime; - FILETIME exittime; - FILETIME kerneltime; - FILETIME usertime; - ULARGE_INTEGER li; - - if (who != RUSAGE_SELF) { - /* Only RUSAGE_SELF is supported in this implementation for now */ - errno = EINVAL; - return -1; - } - - if (rusage == (struct rusage*)NULL) { - errno = EFAULT; - return -1; - } - memset(rusage, 0, sizeof(struct rusage)); - if (GetProcessTimes(GetCurrentProcess(), &starttime, &exittime, &kerneltime, - &usertime) == 0) { - return -1; - } - - /* Convert FILETIMEs (0.1 us) to struct timeval */ - memcpy(&li, &kerneltime, sizeof(FILETIME)); - li.QuadPart /= 10L; /* Convert to microseconds */ - rusage->ru_stime.tv_sec = li.QuadPart / 1000000L; - rusage->ru_stime.tv_usec = li.QuadPart % 1000000L; - - memcpy(&li, &usertime, sizeof(FILETIME)); - li.QuadPart /= 10L; /* Convert to microseconds */ - rusage->ru_utime.tv_sec = li.QuadPart / 1000000L; - rusage->ru_utime.tv_usec = li.QuadPart % 1000000L; - - return 0; -} - -inline SIZE_T get_shared_memory_size(HANDLE h_process) { - MEMORY_BASIC_INFORMATION mbi; - SIZE_T base_address = 0; - SIZE_T shared_memory_size = 0; - - while (VirtualQueryEx(h_process, (LPCVOID)base_address, &mbi, sizeof(mbi))) { - if (mbi.State == MEM_COMMIT) { - if (mbi.Type == MEM_MAPPED || mbi.Type == MEM_IMAGE) { - shared_memory_size += mbi.RegionSize; - } - } - base_address = (SIZE_T)mbi.BaseAddress + mbi.RegionSize; - } - - return shared_memory_size; -} - -inline DWORD getppid() { - HANDLE h_snapshot; - PROCESSENTRY32 pe32; - DWORD ppid = 0, pid = GetCurrentProcessId(); - - h_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - try { - if (h_snapshot == INVALID_HANDLE_VALUE) - return ppid; - - ZeroMemory(&pe32, sizeof(pe32)); - pe32.dwSize = sizeof(pe32); - if (!Process32First(h_snapshot, &pe32)) - return ppid; - - do { - if (pe32.th32ProcessID == pid) { - ppid = pe32.th32ParentProcessID; - break; - } - } while (Process32Next(h_snapshot, &pe32)); - - } catch (...) { - if (h_snapshot != INVALID_HANDLE_VALUE) - CloseHandle(h_snapshot); - } - - if (h_snapshot != INVALID_HANDLE_VALUE) - CloseHandle(h_snapshot); - - return ppid; -} - -inline DWORD get_thread_number(DWORD processId) { - DWORD thread_count = 0; - HANDLE snapshot_handle = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); - - if (snapshot_handle == INVALID_HANDLE_VALUE) { - std::cerr << "Failed to create snapshot. Error code: " << GetLastError() - << std::endl; - return 0; - } - - THREADENTRY32 threadEntry; - threadEntry.dwSize = sizeof(THREADENTRY32); - - if (Thread32First(snapshot_handle, &threadEntry)) { - do { - if (threadEntry.th32OwnerProcessID == processId) { - ++thread_count; - } - } while (Thread32Next(snapshot_handle, &threadEntry)); - } - else { - std::cerr << "Failed to retrieve thread information. Error code: " - << GetLastError() << std::endl; - } - - CloseHandle(snapshot_handle); - return thread_count; -} - -inline DWORD get_process_group(HANDLE process_handle) { - DWORD_PTR process_affinity_mask; - DWORD_PTR system_affinity_mask; - - if (GetProcessAffinityMask(process_handle, &process_affinity_mask, - &system_affinity_mask)) { - // Output the processor group information - // Process Affinity Mask - DWORD grop_id = process_affinity_mask; - return grop_id; - } - else { - std::cerr << "Failed to get process affinity mask. Error code: " - << GetLastError() << std::endl; - return 0; - } -} -#endif - inline int64_t last_time_us = 0; inline int64_t last_sys_time_us = 0; inline int64_t last_user_time_us = 0; @@ -324,9 +145,7 @@ inline void stat_memory() { long virtual_size = 0; long resident = 0; long share = 0; -#if defined(__GNUC__) static long page_size = sysconf(_SC_PAGE_SIZE); -#endif #if defined(__APPLE__) static pid_t pid = getpid(); @@ -350,33 +169,9 @@ inline void stat_memory() { file >> virtual_size >> resident >> share; #endif -#if defined(WIN32) - DWORD current_process = GetCurrentProcessId(); - // open process - HANDLE h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, current_process); - if (h_process == NULL) { - virtual_size = 0; - resident = 0; - share = 0; - } - PROCESS_MEMORY_COUNTERS pmc; - if (GetProcessMemoryInfo(h_process, &pmc, sizeof(pmc))) { - virtual_size = pmc.PagefileUsage; - resident = pmc.WorkingSetSize; - } - share = get_shared_memory_size(h_process); - - CloseHandle(h_process); - - process_memory_virtual->update(virtual_size); - process_memory_resident->update(resident); - process_memory_shared->update(share); -#else process_memory_virtual->update(virtual_size * page_size); process_memory_resident->update(resident * page_size); process_memory_shared->update(share * page_size); -#endif } struct ProcIO { @@ -404,9 +199,10 @@ inline void stat_io() { "ylt_process_io_write_second"); ProcIO s{}; -#if defined(__GUNC__) +#if defined(__APPLE__) +#else auto stream_file = - std::shared_ptr(fopen("/proc/self/io", "r"), [](FILE* ptr) { + std::shared_ptr(fopen("/proc/self/io", "r"), [](FILE *ptr) { fclose(ptr); }); if (stream_file == nullptr) { @@ -421,30 +217,6 @@ inline void stat_io() { } #endif -#if defined(WIN32) - DWORD current_process_id = GetCurrentProcessId(); - // open process - HANDLE h_process = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, current_process_id); - if (h_process == NULL) { - s.rchar = 0; - s.wchar = 0; - s.syscr = 0; - s.syscw = 0; - } - else { - IO_COUNTERS io_counters = {0}; - if (GetProcessIoCounters(h_process, &io_counters)) { - s.rchar = io_counters.ReadOperationCount; - s.wchar = io_counters.WriteOperationCount; - s.syscr = io_counters.ReadOperationCount; - s.syscw = io_counters.WriteOperationCount; - } - } - - CloseHandle(h_process); -#endif - process_io_read_bytes_second->update(s.rchar); process_io_write_bytes_second->update(s.wchar); process_io_read_second->update(s.syscr); @@ -564,7 +336,7 @@ inline void process_status() { if (read_command_output_through_popen(oss, cmdbuf) != 0) { return; } - const std::string& result = oss.str(); + const std::string &result = oss.str(); if (sscanf(result.c_str(), "%d %d %d %d" "%d %u %ld %ld", @@ -572,16 +344,6 @@ inline void process_status() { &stat.flags, &stat.priority, &stat.nice) != 8) { return; } -#elif defined(WIN32) - stat.pid = GetCurrentProcessId(); - stat.ppid = getppid(); - - HANDLE h_process = - OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, stat.pid); - stat.priority = GetPriorityClass(h_process); - stat.num_threads = get_thread_number(stat.pid); - stat.pgrp = get_process_group(h_process); - CloseHandle(h_process); #endif process_uptime->inc(); process_priority->update(stat.priority); @@ -689,4 +451,5 @@ inline bool start_system_metric() { return true; } -} // namespace ylt::metric \ No newline at end of file +} // namespace ylt::metric +#endif diff --git a/include/cinatra/ylt/util/map_sharded.hpp b/include/cinatra/ylt/util/map_sharded.hpp index c1c255a3..a48a2bcf 100644 --- a/include/cinatra/ylt/util/map_sharded.hpp +++ b/include/cinatra/ylt/util/map_sharded.hpp @@ -25,6 +25,9 @@ class map_lock_t { return nullptr; } auto it = map_->find(key); + if (it == map_->end()) { + return nullptr; + } return it->second; } diff --git a/tests/test_metric.cpp b/tests/test_metric.cpp index 4e5757bd..15552c9c 100644 --- a/tests/test_metric.cpp +++ b/tests/test_metric.cpp @@ -14,6 +14,7 @@ #include "doctest/doctest.h" using namespace ylt; using namespace ylt::metric; +using namespace std::chrono_literals; struct metrc_tag {}; @@ -82,8 +83,8 @@ TEST_CASE("serialize zero") { CHECK(str.empty()); #ifdef CINATRA_ENABLE_METRIC_JSON h.serialize_to_json(str); -#endif CHECK(str.empty()); +#endif h.observe(23); h.serialize(str); CHECK(!str.empty()); @@ -97,12 +98,10 @@ TEST_CASE("serialize zero") { #ifdef CINATRA_ENABLE_METRIC_JSON summary->serialize_to_json(str); CHECK(str.empty()); -#endif summary->observe(0); summary->serialize(str); CHECK(!str.empty()); str.clear(); -#ifdef CINATRA_ENABLE_METRIC_JSON summary->serialize_to_json(str); CHECK(!str.empty()); str.clear(); @@ -122,6 +121,9 @@ TEST_CASE("test metric manager") { auto v2 = inst_s.get_metric_by_name("test1"); CHECK(v2 != nullptr); + v2 = inst_s.get_metric_by_name("test111"); + CHECK(v2 == nullptr); + c->inc(); g->inc(); @@ -167,7 +169,6 @@ TEST_CASE("test metric manager") { std::string json = inst_d.serialize_to_json_dynamic(); std::cout << json << "\n"; #endif - using root_manager = metric_collector_t, dynamic_metric_manager>; str = root_manager::serialize(); @@ -255,6 +256,68 @@ TEST_CASE("test metric manager") { CHECK(v8.size() == 1); } +struct remove_tag {}; + +TEST_CASE("test metric manager remove") { + auto dc = std::make_shared( + std::string("test3"), std::string(""), + std::array{"url", "code"}); + dc->inc(std::array{"/", "200"}); + dynamic_metric_manager::instance().register_metric(dc); + auto& inst_d = dynamic_metric_manager::instance(); + std::map map{ + {"url", "/"}, {"code", "200"}, {"method", "GET"}}; + CHECK(inst_d.metric_count() == 1); + inst_d.remove_metric_by_label(map); + CHECK(inst_d.metric_count() == 1); + + std::vector> filtered_metrics; + metric_filter_options options; + options.name_regex = ".*counter.*"; + manager_helper::filter_by_label_name(filtered_metrics, dc, options); + CHECK(filtered_metrics.empty()); + + options.label_regex = ".*counter.*"; + manager_helper::filter_by_label_name(filtered_metrics, dc, options); + CHECK(filtered_metrics.empty()); + + options.label_regex = "url"; + manager_helper::filter_by_label_name(filtered_metrics, dc, options); + CHECK(filtered_metrics.size() == 1); + filtered_metrics.clear(); + options.label_regex = "code"; + manager_helper::filter_by_label_name(filtered_metrics, dc, options); + CHECK(filtered_metrics.size() == 1); + + std::map map1{{"url", "/test"}, {"code", "200"}}; + inst_d.remove_metric_by_label(map1); + CHECK(inst_d.metric_count() == 1); + std::map map2{{"url", "/"}, {"code", "400"}}; + inst_d.remove_metric_by_label(map2); + CHECK(inst_d.metric_count() == 1); + std::map map3{{"url1", "/"}, {"code", "200"}}; + inst_d.remove_metric_by_label(map3); + CHECK(inst_d.metric_count() == 1); + std::map map4{{"url", "/"}, {"code", "200"}}; + inst_d.remove_metric_by_label(map4); + CHECK(inst_d.metric_count() == 0); + + inst_d.register_metric(dc); + CHECK(inst_d.metric_count() == 1); + + std::map map5{{"url", "/get"}}; + inst_d.remove_metric_by_label(map5); + CHECK(inst_d.metric_count() == 1); + + std::map map6{{"url1", "/"}}; + inst_d.remove_metric_by_label(map6); + CHECK(inst_d.metric_count() == 1); + + std::map map7{{"url", "/"}}; + inst_d.remove_metric_by_label(map7); + CHECK(inst_d.metric_count() == 0); +} + TEST_CASE("test dynamic counter") { basic_dynamic_counter c("test", "", {"url", "code"}); c.inc({"/", "200"}); @@ -268,7 +331,6 @@ TEST_CASE("test dynamic counter") { std::string str; c.serialize(str); std::cout << str << "\n"; - #ifdef CINATRA_ENABLE_METRIC_JSON std::string json; c.serialize_to_json(json); @@ -285,7 +347,6 @@ TEST_CASE("test dynamic counter") { std::string str; c1.serialize(str); std::cout << str << "\n"; - #ifdef CINATRA_ENABLE_METRIC_JSON std::string json; c1.serialize_to_json(json); @@ -309,6 +370,14 @@ TEST_CASE("test dynamic counter") { dynamic_gauge_t g2("test_g2", "", {"url", "code"}); g2.inc({"/", "200"}); CHECK(g2.value({"/", "200"}) == 1); + + auto count = g2.label_value_count(); + g2.remove_label_value({{"url", "/"}, {"code", "200"}, {"method", "GET"}}); + auto count1 = g2.label_value_count(); + CHECK(count == count1); + g2.remove_label_value({{"url1", "/"}, {"code1", "200"}}); + count1 = g2.label_value_count(); + CHECK(count == count1); } TEST_CASE("test static counter") { @@ -322,7 +391,6 @@ TEST_CASE("test static counter") { std::string str; c.serialize(str); std::cout << str << "\n"; - #ifdef CINATRA_ENABLE_METRIC_JSON std::string json; c.serialize_to_json(json); @@ -342,7 +410,6 @@ TEST_CASE("test static counter") { std::string str; c1.serialize(str); std::cout << str << "\n"; - #ifdef CINATRA_ENABLE_METRIC_JSON std::string json; c1.serialize_to_json(json); @@ -386,7 +453,6 @@ TEST_CASE("test static histogram") { std::string str; h.serialize(str); std::cout << str; - #ifdef CINATRA_ENABLE_METRIC_JSON std::string json; h.serialize_to_json(json); @@ -481,12 +547,49 @@ using my_manager = static_metric_manager; auto g_pair = my_manager::instance().create_metric_static( "test_g_counter", ""); -TEST_CASE("test no lable") { +TEST_CASE("test no label") { { std::map customMap = {}; auto summary = std::make_shared( "test", "help", std::vector{0.5, 0.9, 0.95, 0.99}, customMap); summary->observe(100); + auto result = summary->get_rates(); + for (auto& e : result) { + CHECK(e == 100); + } + + double sum{}; + result = summary->get_rates(sum); + CHECK(sum == 100); + + uint64_t count{}; + result = summary->get_rates(count); + CHECK(count == 1); + } + + { + std::map customMap = {}; + auto summary = std::make_shared( + "test", "help", std::vector{0.95, 0.5, 0.99, 0.9}, customMap); + summary->observe(100); + auto result = summary->get_rates(); + for (auto& e : result) { + CHECK(e == 100); + } + } + { + std::map customMap = {}; + auto summary = std::make_shared( + "test", "help", std::vector{}, customMap); + std::string str; + summary->serialize(str); + CHECK(str.empty()); + +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string str_json; + summary->serialize_to_json(str_json); + CHECK(str_json.empty()); +#endif } auto g_counter = g_pair.second; g_counter->inc(); @@ -518,9 +621,25 @@ TEST_CASE("test no lable") { CHECK(r == 1); CHECK(g.value() == 10); } + { + metric_t m{}; + auto name = m.metric_name(); + CHECK(name == "unknown"); + + m.clean_expired_label(); + + std::string str; + m.serialize(str); + CHECK(str.empty()); +#ifdef CINATRA_ENABLE_METRIC_JSON + m.serialize_to_json(str); + CHECK(str.empty()); +#endif + } { counter_t c("get_count", "get counter"); CHECK(c.metric_type() == MetricType::Counter); + CHECK(c.help() == "get counter"); CHECK(c.labels_name().empty()); c.inc(); CHECK(c.value() == 1); @@ -549,6 +668,21 @@ TEST_CASE("test with atomic") { c.update(10); CHECK(c.value() == 10); + bool r = c.has_label_value("GET"); + CHECK(r); + r = c.has_label_value("POST"); + CHECK(!r); + + r = c.has_label_value(std::vector{"GET", "/"}); + CHECK(r); + r = c.has_label_value(std::vector{"GET"}); + CHECK(!r); + + c.remove_label_value( + std::map{{"method", "GET"}, {"url", "/"}}); + r = c.has_label_value(std::vector{"GET", "/"}); + CHECK(r); + gauge_t g( "get_qps", "get qps", std::map{{"method", "GET"}, {"url", "/"}}); @@ -603,10 +737,8 @@ TEST_CASE("test counter with dynamic labels value") { std::array{"method", "code"}); CHECK(c.labels_name() == std::vector{"method", "code"}); c.inc({"GET", "200"}, 1); - CHECK(c.value({"GET", "200"}) == 1); c.inc({"GET", "200"}, 2); - CHECK(c.value({"GET", "200"}) == 3); std::string str; @@ -651,10 +783,8 @@ TEST_CASE("test gauge") { CHECK(g.labels_name() == std::vector{"method", "code", "url"}); // method, status code, url g.inc({"GET", "200", "/"}, 1); - CHECK(g.value({"GET", "200", "/"}) == 1); g.inc({"GET", "200", "/"}, 2); - CHECK(g.value({"GET", "200", "/"}) == 3); g.inc({"POST", "200", "/"}, 4); @@ -663,7 +793,8 @@ TEST_CASE("test gauge") { std::string str_json; g.serialize_to_json(str_json); std::cout << str_json << "\n"; - CHECK(str_json.find("\"code\":\"200\"") != std::string::npos); + std::cout << str_json.size() << std::endl; + CHECK(str_json.size() == 185); #endif std::string str; @@ -674,10 +805,8 @@ TEST_CASE("test gauge") { std::string::npos); g.dec({"GET", "200", "/"}, 1); - CHECK(g.value({"GET", "200", "/"}) == 2); g.dec({"GET", "200", "/"}, 2); - CHECK(g.value({"GET", "200", "/"}) == 0); } } @@ -712,16 +841,41 @@ TEST_CASE("test histogram") { } TEST_CASE("test summary") { - summary_t summary{"test_summary", "summary help", - std::vector{0.5, 0.9, 0.95, 0.99}}; + summary_t summary{"test_summary", "summary help", {0.5, 0.9, 0.95, 0.99}}; std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution<> distr(1, 100); for (int i = 0; i < 50; i++) { summary.observe(distr(gen)); } + std::string str; + summary.serialize(str); + std::cout << str; + double sum; + uint64_t cnt; + summary.get_rates(sum, cnt); + CHECK(cnt == 50); + CHECK(sum > 0); + CHECK(str.find("test_summary") != std::string::npos); + CHECK(str.find("test_summary_count") != std::string::npos); + CHECK(str.find("test_summary_sum") != std::string::npos); + CHECK(str.find("test_summary{quantile=\"") != std::string::npos); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string str_json; + summary.serialize_to_json(str_json); + std::cout << str_json << "\n"; +#endif +} + +TEST_CASE("test summary with INF") { + summary_t summary{"test_summary", "summary help", {0.5, 0.9, 0.95, 0.99}}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distr(1, 100); + for (int i = 0; i < 50; i++) { + summary.observe(INFINITY); + } std::string str; summary.serialize(str); std::cout << str; @@ -739,10 +893,143 @@ TEST_CASE("test summary") { std::string str_json; summary.serialize_to_json(str_json); std::cout << str_json << "\n"; - CHECK(str_json.find("\"0.9\":") != std::string::npos); + std::cout << str_json.size() << std::endl; + CHECK(str_json.size() == 238); #endif } +TEST_CASE("test summary with NAN") { + summary_t summary{"test_summary", "summary help", {0.5, 0.9, 0.95, 0.99}}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distr(1, 100); + for (int i = 0; i < 50; i++) { + summary.observe(NAN); + } + std::string str; + summary.serialize(str); + std::cout << str; + double sum; + uint64_t cnt; + summary.get_rates(sum, cnt); + CHECK(cnt == 50); + CHECK(sum < 1e99); + CHECK(str.find("test_summary") != std::string::npos); + CHECK(str.find("test_summary_count") != std::string::npos); + CHECK(str.find("test_summary_sum") != std::string::npos); + CHECK(str.find("test_summary{quantile=\"") != std::string::npos); + +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string str_json; + summary.serialize_to_json(str_json); + std::cout << str_json << "\n"; + std::cout << str_json.size() << std::endl; + CHECK(str_json.size() == 238); +#endif +} + +TEST_CASE("test summary with illegal quantities") { + summary_t summary{ + "test_summary", "summary help", {-1, 0.9, 0.5, 0.9, 0, 0.95, 1.1}}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distr(1, 100); + for (int i = 0; i < 100; i++) { + summary.observe(i); + } + std::string str; + summary.serialize(str); + std::cout << str; + double sum; + uint64_t cnt; + auto result = summary.get_rates(sum, cnt); + CHECK(cnt == 100); + CHECK(sum > 0); + CHECK(str.find("test_summary") != std::string::npos); + CHECK(str.find("test_summary_count") != std::string::npos); + CHECK(str.find("test_summary_sum") != std::string::npos); + CHECK(str.find("test_summary{quantile=\"") != std::string::npos); + CHECK(result[0] < 0); + CHECK(result[1] < 0); + CHECK(result[result.size() - 1] > result[result.size() - 2]); + +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string str_json; + summary.serialize_to_json(str_json); + std::cout << str_json << "\n"; + std::cout << str_json.size() << std::endl; + CHECK(str_json.size() == 233); +#endif +} + +TEST_CASE("test summary with many quantities") { + std::vector q; + for (int i = 0; i <= 1000; ++i) { + q.push_back(1.0 * i / 1000); + } + summary_t summary{"test_summary", "summary help", q}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distr(1, 100); + for (int i = 0; i < 50; i++) { + summary.observe(i); + } + std::string str; + summary.serialize(str); + // std::cout << str; + double sum; + uint64_t cnt; + auto result = summary.get_rates(sum, cnt); + CHECK(cnt == 50); + CHECK(sum > 0); + CHECK(str.find("test_summary") != std::string::npos); + CHECK(str.find("test_summary_count") != std::string::npos); + CHECK(str.find("test_summary_sum") != std::string::npos); + CHECK(str.find("test_summary{quantile=\"") != std::string::npos); + CHECK(result[1] == result[2]); + CHECK(result[result.size() - 2] == result[result.size() - 3]); + +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string str_json; + summary.serialize_to_json(str_json); + std::cout << str_json << "\n"; + std::cout << str_json.size() << std::endl; + CHECK(str_json.size() == 8868); +#endif +} + +TEST_CASE("test summary refresh") { + summary_t summary{"test_summary", "summary help", {0.5, 0.9, 0.95, 1.1}, 1s}; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> distr(1, 100); + for (int i = 0; i < 50; i++) { + summary.observe(i); + } + double sum; + uint64_t cnt; + summary.get_rates(sum, cnt); + CHECK(cnt == 50); + std::this_thread::sleep_for(1s); + summary.get_rates(sum, cnt); + CHECK(cnt == 0); + for (int i = 0; i < 50; i++) { + summary.observe(i); + } + std::this_thread::sleep_for(500ms); + for (int i = 0; i < 10; i++) { + summary.observe(i); + } + summary.get_rates(sum, cnt); + CHECK(cnt == 60); + std::this_thread::sleep_for(500ms); + summary.get_rates(sum, cnt); + CHECK(cnt == 10); + std::this_thread::sleep_for(500ms); + summary.get_rates(sum, cnt); + CHECK(cnt == 0); +} + TEST_CASE("test register metric") { auto c = std::make_shared(std::string("get_count"), std::string("get counter")); @@ -782,6 +1069,11 @@ TEST_CASE("test register metric") { default_static_metric_manager::instance().get_metric_static( "get_guage_count"); CHECK(m1->as()->value() == 1); + + auto m2 = + default_static_metric_manager::instance().get_metric_static( + "not_exist"); + CHECK(m2 == nullptr); } template @@ -802,6 +1094,20 @@ TEST_CASE("test remove metric and serialize metrics") { count = metric_mgr::instance().metric_count(); CHECK(count == 1); + metric_mgr::instance().remove_metric(std::vector{}); + count = metric_mgr::instance().metric_count(); + CHECK(count == 1); + + std::shared_ptr ptr = nullptr; + metric_mgr::instance().remove_metric(ptr); + count = metric_mgr::instance().metric_count(); + CHECK(count == 1); + + metric_mgr::instance().remove_metric( + std::vector>{}); + count = metric_mgr::instance().metric_count(); + CHECK(count == 1); + metric_mgr::instance().remove_metric("test_counter2"); count = metric_mgr::instance().metric_count(); CHECK(count == 0); @@ -870,6 +1176,19 @@ TEST_CASE("test filter metrics static") { CHECK(s.empty()); } + { +#ifdef CINATRA_ENABLE_METRIC_JSON + std::vector> vec{}; + auto s = manager_helper::serialize_to_json(vec); + CHECK(s == "[]"); + auto c = std::make_shared(std::string("get_count"), + std::string("get counter")); + vec.push_back(c); + s = manager_helper::serialize_to_json(vec); + CHECK(s == "[]"); +#endif + } + // don't filter options = {}; { @@ -1208,7 +1527,7 @@ TEST_CASE("test histogram serialize with static labels") { TEST_CASE("test summary with dynamic labels") { basic_dynamic_summary<2> summary{"test_summary", "summary help", - std::vector{0.5, 0.9, 0.95, 0.99}, + {0.5, 0.9, 0.95, 0.99}, {"method", "url"}}; std::random_device rd; std::mt19937 gen(rd()); @@ -1222,6 +1541,7 @@ TEST_CASE("test summary with dynamic labels") { double sum; uint64_t count; + auto rates = summary.get_rates({"GET", "/"}, sum, count); std::cout << rates.size() << "\n"; @@ -1234,11 +1554,25 @@ TEST_CASE("test summary with dynamic labels") { summary.serialize_to_json(json_str); std::cout << json_str << "\n"; #endif + + { + basic_dynamic_summary<2> summary{"test_summary", + "summary help", + {0.9, 0.5, 0.95, 0.99}, + {"method", "url"}}; +#ifdef CINATRA_ENABLE_METRIC_JSON + std::string json_str1; + summary.serialize_to_json(json_str1); + CHECK(json_str1.empty()); +#endif + } } TEST_CASE("test summary with static labels") { summary_t summary{ - "test_summary", "summary help", std::vector{0.5, 0.9, 0.95, 0.99}, + "test_summary", + "summary help", + {0.5, 0.9, 0.95, 0.99}, std::map{{"method", "GET"}, {"url", "/"}}}; std::random_device rd; std::mt19937 gen(rd()); @@ -1276,6 +1610,15 @@ TEST_CASE("test serialize with emptry metrics") { std::array{"method"}); h1->serialize(s1); CHECK(s1.empty()); + +#ifdef CINATRA_ENABLE_METRIC_JSON + h1->serialize_to_json(s1); + CHECK(s1.empty()); +#endif + + h1->observe({"GET"}, 0); + h1->serialize(s1); + CHECK(s1.empty()); #ifdef CINATRA_ENABLE_METRIC_JSON h1->serialize_to_json(s1); CHECK(s1.empty()); @@ -1333,6 +1676,14 @@ TEST_CASE("test serialize with emptry metrics") { { std::string str; h1->observe({"POST"}, 1); + bool r = h1->has_label_value("POST"); + CHECK(r); + r = h1->has_label_value("HEAD"); + CHECK(!r); + r = h1->has_label_value(std::vector{"POST"}); + CHECK(r); + r = h1->has_label_value(std::vector{"HEAD"}); + CHECK(!r); h1->serialize(str); CHECK(!str.empty()); str.clear(); @@ -1630,6 +1981,30 @@ TEST_CASE("test metric manager clean expired label") { CHECK(count == 1); } +TEST_CASE("test remove label value") { + dynamic_counter_t counter("test", "", + std::array{"url", "code"}); + counter.inc({"/", "200"}); + counter.inc({"/", "400"}); + counter.inc({"/index", "200"}); + counter.inc({"/test", "404"}); + CHECK(counter.has_label_name("url")); + CHECK(counter.has_label_name(std::vector{"url", "code"})); + + CHECK(counter.has_label_value("/")); + CHECK(counter.has_label_value("/index")); + CHECK(counter.has_label_value("/test")); + + CHECK(!counter.has_label_value(std::vector{"/"})); + bool r = counter.has_label_value(std::vector{"/index"}); + CHECK(!counter.has_label_value(std::vector{"/index"})); + CHECK(!counter.has_label_value(std::vector{"/test"})); + + CHECK(!counter.has_label_value(std::vector{"/", "test"})); + CHECK(!counter.has_label_value(std::vector{"/", "200", "test"})); + CHECK(!counter.has_label_value(std::vector{})); +} + 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 b041b08fe4883ec21b17219382a46295292272a3 Mon Sep 17 00:00:00 2001 From: qicosmos Date: Tue, 3 Dec 2024 11:08:55 +0800 Subject: [PATCH 2/2] add header --- include/cinatra/ylt/metric/metric.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/cinatra/ylt/metric/metric.hpp b/include/cinatra/ylt/metric/metric.hpp index 67e90d9b..dff24284 100644 --- a/include/cinatra/ylt/metric/metric.hpp +++ b/include/cinatra/ylt/metric/metric.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include