diff --git a/include/ylt/metric/metric.hpp b/include/ylt/metric/metric.hpp index f3f2179d4..1ee5a18eb 100644 --- a/include/ylt/metric/metric.hpp +++ b/include/ylt/metric/metric.hpp @@ -46,6 +46,7 @@ enum class MetricType { struct metric_filter_options { std::optional name_regex{}; std::optional label_regex{}; + std::optional label_value_regex{}; bool is_white = true; }; diff --git a/include/ylt/metric/metric_manager.hpp b/include/ylt/metric/metric_manager.hpp index d9b68b7d4..5ae16c2dd 100644 --- a/include/ylt/metric/metric_manager.hpp +++ b/include/ylt/metric/metric_manager.hpp @@ -69,63 +69,74 @@ class manager_helper { } #endif - static std::vector> filter_metrics_by_label_value( - auto& metrics, const std::regex& label_regex, bool is_white) { + static std::vector> filter_metrics_by_name( + auto& metrics, const std::regex& name_regex) { + std::vector> filtered_metrics; + for (auto& m : metrics) { + if (std::regex_match(m->str_name(), name_regex)) { + filtered_metrics.push_back(m); + } + } + return filtered_metrics; + } + + static std::vector> filter_metrics_by_label_name( + auto& metrics, const std::regex& label_name_regex) { std::vector> filtered_metrics; - std::vector indexs; - size_t index = 0; for (auto& m : metrics) { - if (m->has_label_value(label_regex)) { - if (is_white) { + const auto& labels_name = m->labels_name(); + for (auto& label_name : labels_name) { + if (std::regex_match(label_name, label_name_regex)) { filtered_metrics.push_back(m); } - else { - indexs.push_back(index); - } } - - index++; } + return filtered_metrics; + } - if (!is_white) { - for (size_t i : indexs) { - metrics.erase(std::next(metrics.begin(), i)); + static std::vector> filter_metrics_by_label_value( + auto& metrics, const std::regex& label_value_regex) { + std::vector> filtered_metrics; + for (auto& m : metrics) { + if (m->has_label_value(label_value_regex)) { + filtered_metrics.push_back(m); } - return metrics; } - return filtered_metrics; } static std::vector> filter_metrics( auto& metrics, const metric_filter_options& options) { - if (!options.name_regex && !options.label_regex) { + if (!(options.name_regex || options.label_regex || + options.label_value_regex)) { return metrics; } - std::vector> filtered_metrics; - for (auto& m : metrics) { - if (options.name_regex && - !options.label_regex) { // only check metric name - if (std::regex_match(std::string(m->name()), *options.name_regex)) { - filtered_metrics.push_back(m); - } + std::vector> filtered_metrics = metrics; + if (options.name_regex) { + filtered_metrics = filter_metrics_by_name(metrics, *options.name_regex); + if (filtered_metrics.empty()) { + return {}; } - else if (options.label_regex && - !options.name_regex) { // only check label name - filter_by_label_name(filtered_metrics, m, options); + } + + if (options.label_regex) { + filtered_metrics = + filter_metrics_by_label_name(filtered_metrics, *options.label_regex); + if (filtered_metrics.empty()) { + return {}; } - else { - if (std::regex_match( - std::string(m->name()), - *options.name_regex)) { // check label name and label value - filter_by_label_name(filtered_metrics, m, options); - } + } + + if (options.label_value_regex) { + filtered_metrics = filter_metrics_by_label_value( + filtered_metrics, *options.label_value_regex); + if (filtered_metrics.empty()) { + return {}; } } if (!options.is_white) { - auto it = metrics.begin(); for (auto& m : filtered_metrics) { std::erase_if(metrics, [&](auto t) { return t == m; @@ -239,19 +250,18 @@ class static_metric_manager { return metrics; } - std::vector> filter_metrics_by_label_value( - const std::regex& label_regex, bool is_white = true) { - auto metrics = collect(); - return manager_helper::filter_metrics_by_label_value(metrics, label_regex, - is_white); - } - std::vector> filter_metrics_static( const metric_filter_options& options) { auto metrics = collect(); return manager_helper::filter_metrics(metrics, options); } + std::vector> filter_metrics_by_label_value( + const std::regex& label_regex) { + auto metrics = collect(); + return manager_helper::filter_metrics_by_label_value(metrics, label_regex); + } + private: static_metric_manager() = default; @@ -498,34 +508,40 @@ class dynamic_metric_manager { } std::vector> filter_metrics_by_label_value( - const std::regex& label_regex, bool is_white = true) { + const std::regex& label_regex) { auto metrics = collect(); - return manager_helper::filter_metrics_by_label_value(metrics, label_regex, - is_white); + return manager_helper::filter_metrics_by_label_value(metrics, label_regex); } private: void clean_label_expired() { executor_ = coro_io::create_io_context_pool(1); timer_ = std::make_shared(executor_->get_executor()); - check_label_expired().via(executor_->get_executor()).start([](auto&&) { - }); + check_label_expired(timer_) + .via(executor_->get_executor()) + .start([](auto&&) { + }); } - async_simple::coro::Lazy check_label_expired() { - auto timer = timer_; - timer_->expires_after(ylt_label_check_expire_duration); - bool r = co_await timer_->async_await(); - if (!r) { - co_return; - } + async_simple::coro::Lazy check_label_expired( + std::weak_ptr weak) { + while (true) { + auto timer = weak.lock(); + if (timer == nullptr) { + co_return; + } - std::unique_lock lock(mtx_); - for (auto& [_, m] : metric_map_) { - m->clean_expired_label(); + timer->expires_after(ylt_label_check_expire_duration); + bool r = co_await timer->async_await(); + if (!r) { + co_return; + } + + std::unique_lock lock(mtx_); + for (auto& [_, m] : metric_map_) { + m->clean_expired_label(); + } } - lock.unlock(); - co_await check_label_expired(); } dynamic_metric_manager() { @@ -611,13 +627,6 @@ struct metric_collector_t { return manager_helper::filter_metrics(vec, options); } - static std::vector> filter_metrics_by_label_value( - const std::regex& label_regex, bool is_white = true) { - auto vec = get_all_metrics(); - return manager_helper::filter_metrics_by_label_value(vec, label_regex, - is_white); - } - private: template static void append_vector(std::vector>& vec) { diff --git a/src/metric/tests/test_metric.cpp b/src/metric/tests/test_metric.cpp index fbe9794c7..5d283d0c7 100644 --- a/src/metric/tests/test_metric.cpp +++ b/src/metric/tests/test_metric.cpp @@ -11,19 +11,6 @@ struct metrc_tag {}; struct test_tag {}; -TEST_CASE("test metric manager clean expired label") { - set_label_max_age(std::chrono::seconds(1), std::chrono::seconds(1)); - auto& inst = dynamic_metric_manager::instance(); - auto [ec, c] = inst.create_metric_dynamic( - std::string("some_counter"), "", std::array{"url"}); - c->inc({"/"}); - c->inc({"/test"}); - std::this_thread::sleep_for(std::chrono::seconds(2)); - c->inc({"/index"}); - size_t count = c->label_value_count(); - CHECK(count == 1); -} - TEST_CASE("test metric manager") { auto c = std::make_shared("test1", ""); auto g = std::make_shared("test2", ""); @@ -144,7 +131,7 @@ TEST_CASE("test metric manager") { CHECK(inst_d.metric_count() == 0); inst_d.register_metric(dc); - inst_d.create_metric_dynamic( + auto pair2 = inst_d.create_metric_dynamic( "test4", "", std::array{"method", "code"}); metric_filter_options options; @@ -155,8 +142,14 @@ TEST_CASE("test metric manager") { auto v6 = inst_d.filter_metrics_dynamic(options); CHECK(v6.size() == 1); - auto v7 = inst_d.filter_metrics_by_label_value(std::regex("200")); - CHECK(v7.size() == 1); + options.label_value_regex = "200"; + + auto v7 = inst_d.filter_metrics_dynamic(options); + CHECK(v7.size() == 0); + + pair2.second->inc({"200"}); + auto v8 = inst_d.filter_metrics_dynamic(options); + CHECK(v8.size() == 1); } TEST_CASE("test dynamic counter") { @@ -1517,6 +1510,20 @@ TEST_CASE("testFilterMetricsDynamicWithMultiLabel") { } } +TEST_CASE("test metric manager clean expired label") { + set_label_max_age(std::chrono::seconds(1), std::chrono::seconds(1)); + auto& inst = dynamic_metric_manager::instance(); + auto [ec, c] = inst.create_metric_dynamic( + std::string("some_counter"), "", std::array{"url"}); + c->inc({"/"}); + c->inc({"/test"}); + CHECK(c->label_value_count() == 2); + std::this_thread::sleep_for(std::chrono::seconds(2)); + c->inc({"/index"}); + size_t count = c->label_value_count(); + CHECK(count == 1); +} + 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