From eee6b6d48d41ac9413c50049106d14537a65d7ad Mon Sep 17 00:00:00 2001 From: ohadvano <49730675+ohadvano@users.noreply.github.com> Date: Tue, 9 May 2023 07:31:00 +0300 Subject: [PATCH] stats: allow combining configured tags with custom tags (#27215) Adds a way to create a StatNameTagVector that contains extracted tags names from both the configured and well defined tags, and custom provided tags. Fixes #27030 Risk Level: Low Testing: Unit tests Signed-off-by: ohadvano --- envoy/stats/store.h | 21 ++++++ source/common/stats/isolated_store_impl.h | 8 ++ source/common/stats/thread_local_store.cc | 14 ++++ source/common/stats/thread_local_store.h | 3 + test/common/stats/thread_local_store_test.cc | 78 ++++++++++++++++++++ test/integration/server.h | 8 ++ 6 files changed, 132 insertions(+) diff --git a/envoy/stats/store.h b/envoy/stats/store.h index c44f51debca0..5af35defd9c6 100644 --- a/envoy/stats/store.h +++ b/envoy/stats/store.h @@ -26,6 +26,7 @@ namespace Stats { class Sink; class SinkPredicates; +class StatNamePool; /** * Store keeps track of all Scopes created in it, and the Scopes manage @@ -172,6 +173,26 @@ class Store { * @return a scope of the given name. */ ScopeSharedPtr createScope(const std::string& name) { return rootScope()->createScope(name); } + + /** + * Extracts tags from the name and appends them to the provided StatNameTagVector. + * The StatName for the extracted tags will be saved in the provided pool. + * @param name The stat name. + * @param pool The pool to create the tags in. + * @param stat_tags The stat name tags vector to append the tags to. + */ + virtual void extractAndAppendTags(StatName name, StatNamePool& pool, + StatNameTagVector& stat_tags) PURE; + + /** + * Extracts tags from the name and appends them to the provided StatNameTagVector. + * The StatName for the extracted tags will be saved in the provided pool. + * @param name The stat name. + * @param pool The pool to create the tags in. + * @param stat_tags The stat name tags vector to append the tags to. + */ + virtual void extractAndAppendTags(absl::string_view name, StatNamePool& pool, + StatNameTagVector& stat_tags) PURE; }; using StorePtr = std::unique_ptr; diff --git a/source/common/stats/isolated_store_impl.h b/source/common/stats/isolated_store_impl.h index e337e20d67f7..3e642b88888d 100644 --- a/source/common/stats/isolated_store_impl.h +++ b/source/common/stats/isolated_store_impl.h @@ -234,6 +234,14 @@ class IsolatedStoreImpl : public Store { return constRootScope()->iterate(fn); } + void extractAndAppendTags(StatName, StatNamePool&, StatNameTagVector&) override { + IS_ENVOY_BUG("Unexpected call to a function that is not yet implemented"); + } + + void extractAndAppendTags(absl::string_view, StatNamePool&, StatNameTagVector&) override { + IS_ENVOY_BUG("Unexpected call to a function that is not yet implemented"); + } + protected: /** * Provides a hook for sub-classes to define how to create new scopes. When diff --git a/source/common/stats/thread_local_store.cc b/source/common/stats/thread_local_store.cc index edbb1f595762..cc5658d71ae2 100644 --- a/source/common/stats/thread_local_store.cc +++ b/source/common/stats/thread_local_store.cc @@ -1075,5 +1075,19 @@ void ThreadLocalStoreImpl::setSinkPredicates(std::unique_ptr&& s } } +void ThreadLocalStoreImpl::extractAndAppendTags(StatName name, StatNamePool& pool, + StatNameTagVector& stat_tags) { + extractAndAppendTags(symbolTable().toString(name), pool, stat_tags); +} + +void ThreadLocalStoreImpl::extractAndAppendTags(absl::string_view name, StatNamePool& pool, + StatNameTagVector& stat_tags) { + TagVector tags; + tagProducer().produceTags(name, tags); + for (const auto& tag : tags) { + stat_tags.emplace_back(pool.add(tag.name_), pool.add(tag.value_)); + } +} + } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/thread_local_store.h b/source/common/stats/thread_local_store.h index 04d0c216b5d1..a205f6529565 100644 --- a/source/common/stats/thread_local_store.h +++ b/source/common/stats/thread_local_store.h @@ -211,6 +211,9 @@ class ThreadLocalStoreImpl : Logger::Loggable, public StoreRo void releaseHistogramCrossThread(uint64_t histogram_id); const TagProducer& tagProducer() const { return *tag_producer_; } + void extractAndAppendTags(StatName name, StatNamePool& pool, StatNameTagVector& tags) override; + void extractAndAppendTags(absl::string_view name, StatNamePool& pool, + StatNameTagVector& tags) override; private: friend class ThreadLocalStoreTestingPeer; diff --git a/test/common/stats/thread_local_store_test.cc b/test/common/stats/thread_local_store_test.cc index 78c2ae0d25bb..1d43ed663eb6 100644 --- a/test/common/stats/thread_local_store_test.cc +++ b/test/common/stats/thread_local_store_test.cc @@ -775,6 +775,84 @@ TEST_F(StatsThreadLocalStoreTest, SharedScopes) { tls_.shutdownThread(); } +TEST_F(StatsThreadLocalStoreTest, ExtractAndAppendTagsFixedValue) { + store_->initializeThreading(main_thread_dispatcher_, tls_); + + envoy::config::metrics::v3::StatsConfig stats_config; + auto* tag_specifier = stats_config.add_stats_tags(); + tag_specifier->set_tag_name("foo"); + tag_specifier->set_fixed_value("bar"); + + store_->setTagProducer(std::make_unique(stats_config)); + + StatNamePool pool(symbol_table_); + StatNameTagVector tags{{pool.add("a"), pool.add("b")}}; + store_->extractAndAppendTags(pool.add("c1"), pool, tags); + + ASSERT_EQ(2, tags.size()); + EXPECT_EQ("a", symbol_table_.toString(tags[0].first)); + EXPECT_EQ("b", symbol_table_.toString(tags[0].second)); + EXPECT_EQ("foo", symbol_table_.toString(tags[1].first)); + EXPECT_EQ("bar", symbol_table_.toString(tags[1].second)); +} + +TEST_F(StatsThreadLocalStoreTest, ExtractAndAppendTagsRegexValueNoMatch) { + store_->initializeThreading(main_thread_dispatcher_, tls_); + + envoy::config::metrics::v3::StatsConfig stats_config; + auto* tag_specifier = stats_config.add_stats_tags(); + tag_specifier->set_tag_name("foo"); + tag_specifier->set_regex("bar"); + + store_->setTagProducer(std::make_unique(stats_config)); + + StatNamePool pool(symbol_table_); + StatNameTagVector tags{{pool.add("a"), pool.add("b")}}; + store_->extractAndAppendTags(pool.add("c1"), pool, tags); + + ASSERT_EQ(1, tags.size()); + EXPECT_EQ("a", symbol_table_.toString(tags[0].first)); + EXPECT_EQ("b", symbol_table_.toString(tags[0].second)); +} + +TEST_F(StatsThreadLocalStoreTest, ExtractAndAppendTagsRegexValueWithMatch) { + store_->initializeThreading(main_thread_dispatcher_, tls_); + + envoy::config::metrics::v3::StatsConfig stats_config; + auto* tag_specifier = stats_config.add_stats_tags(); + tag_specifier->set_tag_name("foo_tag"); + tag_specifier->set_regex("^foo.(.+)"); + + store_->setTagProducer(std::make_unique(stats_config)); + + StatNamePool pool(symbol_table_); + StatNameTagVector tags{{pool.add("a"), pool.add("b")}}; + store_->extractAndAppendTags(pool.add("foo.bar"), pool, tags); + + ASSERT_EQ(2, tags.size()); + EXPECT_EQ("a", symbol_table_.toString(tags[0].first)); + EXPECT_EQ("b", symbol_table_.toString(tags[0].second)); + EXPECT_EQ("foo_tag", symbol_table_.toString(tags[1].first)); + EXPECT_EQ("bar", symbol_table_.toString(tags[1].second)); +} + +TEST_F(StatsThreadLocalStoreTest, ExtractAndAppendTagsRegexBuiltinExpression) { + store_->initializeThreading(main_thread_dispatcher_, tls_); + + envoy::config::metrics::v3::StatsConfig stats_config; + store_->setTagProducer(std::make_unique(stats_config)); + + StatNamePool pool(symbol_table_); + StatNameTagVector tags{{pool.add("a"), pool.add("b")}}; + store_->extractAndAppendTags(pool.add("cluster.foo.bar"), pool, tags); + + ASSERT_EQ(2, tags.size()); + EXPECT_EQ("a", symbol_table_.toString(tags[0].first)); + EXPECT_EQ("b", symbol_table_.toString(tags[0].second)); + EXPECT_EQ("envoy.cluster_name", symbol_table_.toString(tags[1].first)); + EXPECT_EQ("foo", symbol_table_.toString(tags[1].second)); +} + class LookupWithStatNameTest : public ThreadLocalStoreNoMocksMixin, public testing::Test {}; TEST_F(LookupWithStatNameTest, All) { diff --git a/test/integration/server.h b/test/integration/server.h index 17301e6a5e60..6fefb7f76cba 100644 --- a/test/integration/server.h +++ b/test/integration/server.h @@ -357,6 +357,14 @@ class TestIsolatedStoreImpl : public StoreRoot { bool iterate(const IterateFn& fn) const override { return store_.iterate(fn); } bool iterate(const IterateFn& fn) const override { return store_.iterate(fn); } + void extractAndAppendTags(StatName, StatNamePool&, StatNameTagVector&) override { + IS_ENVOY_BUG("Unexpected call to a function that is not yet implemented"); + }; + + void extractAndAppendTags(absl::string_view, StatNamePool&, StatNameTagVector&) override { + IS_ENVOY_BUG("Unexpected call to a function that is not yet implemented"); + }; + // Stats::StoreRoot void addSink(Sink&) override {} void setTagProducer(TagProducerPtr&&) override {}