diff --git a/include/seastar/core/metrics_api.hh b/include/seastar/core/metrics_api.hh index d84870b6f5..b77276d876 100644 --- a/include/seastar/core/metrics_api.hh +++ b/include/seastar/core/metrics_api.hh @@ -69,6 +69,8 @@ namespace metrics { SEASTAR_MODULE_EXPORT struct relabel_config; +SEASTAR_MODULE_EXPORT +struct metric_family_config; /*! * \brief result of metric relabeling * @@ -330,10 +332,17 @@ using metric_metadata_fifo = std::deque; * * The meta data of a metric family compose of the * metadata of the family, and a vector of the metadata for - * each of the metric. + * each of the metrics. + * + * The struct is used for two purposes. First, it allows iterating over all metric_families + * and all metrics related to them. Second, it only contains enabled metrics, + * making disabled metrics more efficient. + * The struct is recreated when impl._value_map changes + * Using a pointer to the family_info metadata is an efficient way to get + * from a metric_family to its metadata based on its name. */ struct metric_family_metadata { - metric_family_info mf; + metric_family_info& mf; //This points to impl._value_map metric_metadata_fifo metrics; }; @@ -358,6 +367,7 @@ class impl { std::set _labels; std::vector> _current_metrics; std::vector _relabel_configs; + std::vector _metric_family_configs; public: value_map& get_value_map() { return _value_map; @@ -398,6 +408,13 @@ public: const std::vector& get_relabel_configs() const noexcept { return _relabel_configs; } + const std::vector& get_metric_family_configs() const noexcept { + return _metric_family_configs; + } + + void set_metric_family_configs(const std::vector& metrics_config); + + void update_aggregate(metric_family_info& mf) const noexcept; }; const value_map& get_value_map(); @@ -486,5 +503,29 @@ future set_relabel_configs(const std::vector& get_relabel_configs(); +/* + * \brief change the metrics family config + * + * Family config is a configuration that relates to all metrics with the same name but with different labels. + * set_metric_family_configs allows changing that configuration during run time. + * Specifically, change the label aggregation based on a metric name. + * + * The following is an example for setting the aggregate labels for the metric test_gauge_1 + * and all metrics matching the regex test_gauge1.*: + * + * std::vector fc(2); + * fc[0].name = "test_gauge_1"; + * fc[0].aggregate_labels = { "lb" }; + * fc[1].regex_name = "test_gauge1.*"; + * fc[1].aggregate_labels = { "ll", "aa" }; + * sm::set_metric_family_configs(fc); + */ +void set_metric_family_configs(const std::vector& metrics_config); + +/* + * \brief return the current metric_family_config + * This function returns a vector of the current metrics family config + */ +const std::vector& get_metric_family_configs(); } } diff --git a/include/seastar/core/relabel_config.hh b/include/seastar/core/relabel_config.hh index 1e0e17ee65..d8fa1e89c0 100644 --- a/include/seastar/core/relabel_config.hh +++ b/include/seastar/core/relabel_config.hh @@ -58,6 +58,13 @@ public: _regex = std::regex(_regex_str); return *this; } + bool empty() const noexcept { + return _regex_str.empty(); + } + + bool match(const std::string& str) const noexcept { + return !empty() && std::regex_match(str, _regex); + } }; /*! @@ -103,6 +110,25 @@ struct relabel_config { */ relabel_config::relabel_action relabel_config_action(const std::string& action); +/*! + * \brief metric_family_config allow changing metrics family configuration + * + * Allow changing metrics family configuration + * Supports changing the aggregation labels. + * The metrics family can be identified by a name or by regex; name-matching is + * more efficient and should be used for a single metrics family. + * + * name - optional exact metric name + * regex_name - if set, all the metrics name that match the regular expression + * aggregate_labels - The labels to aggregate the metrics by. + * + */ +struct metric_family_config { + std::string name; + relabel_config_regex regex_name = ""; + std::vector aggregate_labels; +}; + SEASTAR_MODULE_EXPORT_END } diff --git a/src/core/metrics.cc b/src/core/metrics.cc index af2e8937e0..3c703df826 100644 --- a/src/core/metrics.cc +++ b/src/core/metrics.cc @@ -130,7 +130,20 @@ future set_relabel_configs(const std::vector& get_relabel_configs() { return impl::get_local_impl()->get_relabel_configs(); } +void impl::impl::update_aggregate(metric_family_info& mf) const noexcept { + for (const auto& fc : _metric_family_configs) { + if (fc.name == mf.name || fc.regex_name.match(mf.name)) { + mf.aggregate_labels = fc.aggregate_labels; + } + } +} +void set_metric_family_configs(const std::vector& family_config) { + impl::get_local_impl()->set_metric_family_configs(family_config); +} +const std::vector& get_metric_family_configs() { + return impl::get_local_impl()->get_metric_family_configs(); +} static bool apply_relabeling(const relabel_config& rc, impl::metric_info& info) { std::stringstream s; @@ -454,6 +467,7 @@ void impl::add_registration(const metric_id& id, const metric_type& type, metric _value_map[name].info().inherit_type = type.type_name; _value_map[name].info().name = id.full_name(); _value_map[name].info().aggregate_labels = aggregate_labels; + impl::update_aggregate(_value_map[name].info()); _value_map[name][rm->info().id.labels()] = rm; } dirty(); @@ -503,6 +517,26 @@ future impl::set_relabel_configs(const std::vector(conflicts); } + +void impl::set_metric_family_configs(const std::vector& family_config) { + _metric_family_configs = family_config; + bool has_regex = false; + for (const auto& fc : family_config) { + has_regex |= !fc.regex_name.empty(); + if (fc.name != "" && _value_map.find(fc.name) != _value_map.end()) { + _value_map[fc.name].info().aggregate_labels = fc.aggregate_labels; + } + } + if (has_regex) { + for (auto& [name, family] : _value_map) { + for (const auto& fc : family_config) { + if (fc.regex_name.match(name)) { + family.info().aggregate_labels = fc.aggregate_labels; + } + } + } + } +} } const bool metric_disabled = false; diff --git a/tests/unit/metrics_test.cc b/tests/unit/metrics_test.cc index 8d96d54785..0012e56b44 100644 --- a/tests/unit/metrics_test.cc +++ b/tests/unit/metrics_test.cc @@ -224,6 +224,69 @@ SEASTAR_THREAD_TEST_CASE(test_relabel_add_labels) { sm::set_relabel_configs({}).get(); } +SEASTAR_THREAD_TEST_CASE(test_metrics_family_aggregate) { + using namespace seastar::metrics; + namespace sm = seastar::metrics; + sm::metric_groups app_metrics; + sm::label lb("lb"); + app_metrics.add_group("test", { + sm::make_gauge("gauge_1", sm::description("gague 1"), [] { return 1; })(lb("1")), + sm::make_gauge("gauge_1", sm::description("gague 1"), [] { return 2; })(lb("2")), + sm::make_counter("counter_1", sm::description("counter 1"), [] { return 3; })(lb("1")), + sm::make_counter("counter_1", sm::description("counter 1"), [] { return 4; })(lb("2")) + }); + std::vector rl(2); + rl[0].source_labels = {"__name__"}; + rl[0].action = sm::relabel_config::relabel_action::drop; + + rl[1].source_labels = {"lb"}; + rl[1].action = sm::relabel_config::relabel_action::keep; + // Dropping the lev label would cause a conflict, but not crash the system + sm::set_relabel_configs(rl).get(); + + std::vector fc(2); + fc[0].name = "test_gauge_1"; + fc[0].aggregate_labels = { "lb" }; + fc[1].regex_name = "test_gauge1.*"; + fc[1].aggregate_labels = { "ll", "aa" }; + sm::set_metric_family_configs(fc); + seastar::foreign_ptr values = seastar::metrics::impl::get_values(); + int count = 0; + for (auto&& md : (*values->metadata)) { + if (md.mf.name == "test_gauge_1") { + BOOST_CHECK_EQUAL(md.mf.aggregate_labels.size(), 1); + BOOST_CHECK_EQUAL(md.mf.aggregate_labels[0], "lb"); + } else { + BOOST_CHECK_EQUAL(md.mf.aggregate_labels.size(), 0); + } + count++; + } + BOOST_CHECK_EQUAL(count, 2); + app_metrics.add_group("test", { + sm::make_gauge("gauge1_1", sm::description("gague 1"), [] { return 1; })(lb("1")), + sm::make_gauge("gauge1_1", sm::description("gague 1"), [] { return 2; })(lb("2")), + sm::make_counter("counter1_1", sm::description("counter 1"), [] { return 3; })(lb("1")), + sm::make_counter("counter1_1", sm::description("counter 1"), [] { return 4; })(lb("2")) + }); + values = seastar::metrics::impl::get_values(); + count = 0; + for (auto&& md : (*values->metadata)) { + if (md.mf.name == "test_gauge_1") { + BOOST_CHECK_EQUAL(md.mf.aggregate_labels.size(), 1); + BOOST_CHECK_EQUAL(md.mf.aggregate_labels[0], "lb"); + } else if (md.mf.name == "test_gauge1_1") { + BOOST_CHECK_EQUAL(md.mf.aggregate_labels.size(), 2); + BOOST_CHECK_EQUAL(md.mf.aggregate_labels[0], "ll"); + } else { + BOOST_CHECK_EQUAL(md.mf.aggregate_labels.size(), 0); + } + count++; + } + BOOST_CHECK_EQUAL(count, 4); + std::vector rl1; + sm::set_relabel_configs(rl1).get(); +} + SEASTAR_THREAD_TEST_CASE(test_relabel_drop_label_prevent_runtime_conflicts) { using namespace seastar::metrics; namespace sm = seastar::metrics;