From 56d8da125b6b13ef5743262169f8eb081b5c3668 Mon Sep 17 00:00:00 2001 From: wbpcode/wangbaiping Date: Sat, 4 Jul 2026 05:49:00 +0000 Subject: [PATCH 1/5] stats: common tools that used for stats API migration Signed-off-by: wbpcode/wangbaiping --- envoy/stats/stats_macros.h | 32 +++++ source/common/stats/BUILD | 13 ++ source/common/stats/prefix_utility.cc | 82 ++++++++++++ source/common/stats/prefix_utility.h | 44 +++++++ source/common/stats/symbol_table.h | 7 + source/common/stats/utility.cc | 102 +++++++++++++++ source/common/stats/utility.h | 78 +++++++++++ test/common/stats/BUILD | 12 ++ test/common/stats/prefix_utility_test.cc | 158 +++++++++++++++++++++++ test/common/stats/utility_test.cc | 92 +++++++++++++ 10 files changed, 620 insertions(+) create mode 100644 source/common/stats/prefix_utility.cc create mode 100644 source/common/stats/prefix_utility.h create mode 100644 test/common/stats/prefix_utility_test.cc diff --git a/envoy/stats/stats_macros.h b/envoy/stats/stats_macros.h index 123bb7983624e..34cfcec35cf36 100644 --- a/envoy/stats/stats_macros.h +++ b/envoy/stats/stats_macros.h @@ -99,6 +99,38 @@ static inline std::string statPrefixJoin(absl::string_view prefix, absl::string_ #define POOL_HISTOGRAM(POOL) POOL_HISTOGRAM_PREFIX(POOL, "") #define POOL_TEXT_READOUT(POOL) POOL_TEXT_READOUT_PREFIX(POOL, "") +// Tagged variants of the POOL_*_PREFIX macros: create each stat directly on POOL with pre-encoded +// tags. BASE_PREFIX and PREFIX are Stats::StatNames (the tag-extracted and flat prefixes, +// pre-encoded once by the caller) and TAGS is a Stats::StatNameTagSpan. Callers must include +// "source/common/stats/utility.h". See Stats::Utility::counterFromTaggedPrefix. +#define POOL_COUNTER_TAGGED_PREFIX(POOL, BASE_PREFIX, TAGS, PREFIX) \ + Envoy::Stats::Utility::counterFromTaggedPrefix((POOL), (BASE_PREFIX), (TAGS), (PREFIX), \ + (FINISH_STAT_DECL_ +#define POOL_GAUGE_TAGGED_PREFIX(POOL, BASE_PREFIX, TAGS, PREFIX) \ + Envoy::Stats::Utility::gaugeFromTaggedPrefix((POOL), (BASE_PREFIX), (TAGS), (PREFIX), \ + (FINISH_STAT_DECL_MODE_ +#define POOL_HISTOGRAM_TAGGED_PREFIX(POOL, BASE_PREFIX, TAGS, PREFIX) \ + Envoy::Stats::Utility::histogramFromTaggedPrefix((POOL), (BASE_PREFIX), (TAGS), (PREFIX), \ + (FINISH_STAT_DECL_UNIT_ +#define POOL_TEXT_READOUT_TAGGED_PREFIX(POOL, BASE_PREFIX, TAGS, PREFIX) \ + Envoy::Stats::Utility::textReadoutFromTaggedPrefix((POOL), (BASE_PREFIX), (TAGS), (PREFIX), \ + (FINISH_STAT_DECL_ + +// Convenience wrappers taking a Stats::TaggedStatName (see utility.h), which pre-encodes the +// tag-extracted prefix, the flat prefix and the tags. +#define POOL_COUNTER_TAGGED(POOL, TAGGED_NAME) \ + POOL_COUNTER_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + (TAGGED_NAME).name()) +#define POOL_GAUGE_TAGGED(POOL, TAGGED_NAME) \ + POOL_GAUGE_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + (TAGGED_NAME).name()) +#define POOL_HISTOGRAM_TAGGED(POOL, TAGGED_NAME) \ + POOL_HISTOGRAM_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + (TAGGED_NAME).name()) +#define POOL_TEXT_READOUT_TAGGED(POOL, TAGGED_NAME) \ + POOL_TEXT_READOUT_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + (TAGGED_NAME).name()) + #define NULL_STAT_DECL_(X) std::string(#X)), #define NULL_STAT_DECL_IGNORE_MODE_(X, MODE) std::string(#X)), diff --git a/source/common/stats/BUILD b/source/common/stats/BUILD index 24e4c17017e11..938487f964cd4 100644 --- a/source/common/stats/BUILD +++ b/source/common/stats/BUILD @@ -294,3 +294,16 @@ envoy_cc_library( "//envoy/stats:stats_interface", ], ) + +envoy_cc_library( + name = "prefix_utility_lib", + srcs = ["prefix_utility.cc"], + hdrs = ["prefix_utility.h"], + deps = [ + ":utility_lib", + "//envoy/stats:stats_interface", + "//envoy/stats:tag_interface", + "//source/common/common:assert_lib", + "//source/common/config:well_known_names", + ], +) diff --git a/source/common/stats/prefix_utility.cc b/source/common/stats/prefix_utility.cc new file mode 100644 index 0000000000000..4c6cbbacc9026 --- /dev/null +++ b/source/common/stats/prefix_utility.cc @@ -0,0 +1,82 @@ +#include "source/common/stats/prefix_utility.h" + +#include +#include + +#include "source/common/common/assert.h" +#include "source/common/config/well_known_names.h" + +#include "absl/container/inlined_vector.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" + +namespace Envoy { +namespace Stats { + +namespace { + +// Extracts the parent prefix's single tag: "http.." -> {HTTP_CONN_MANAGER_PREFIX, x}, +// "cluster.." -> {CLUSTER_NAME, x}, anything else -> none. The trailing dot is stripped first. +// `prefix` must outlive the returned value. +std::optional extractParentTag(absl::string_view prefix) { + if (!absl::EndsWith(prefix, ".")) { + return std::nullopt; + } + prefix.remove_suffix(1); + + if (absl::StartsWith(prefix, "http.")) { + return TagStringView{Envoy::Config::TagNames::get().HTTP_CONN_MANAGER_PREFIX, prefix.substr(5)}; + } + if (absl::StartsWith(prefix, "cluster.")) { + return TagStringView{Envoy::Config::TagNames::get().CLUSTER_NAME, prefix.substr(8)}; + } + return std::nullopt; +} + +} // namespace + +TaggedStatName mergeStatPrefix(SymbolTable& symbol_table, absl::string_view prefix, + absl::string_view base_name, TagStringViewSpan tags, + absl::string_view name) { + // With no own tags the own prefix has no variable segment, so its tagged and tag-extracted forms + // are identical; callers need only supply base_name. + if (tags.empty()) { + name = base_name; + } else { + ASSERT(!name.empty(), "When tags are supplied, the caller must supply the tagged name with the " + "tag values interleaved."); + } + + absl::InlinedVector merged_tags; + + // The parent (HCM/cluster) prefix contributes at most one tag; the tag-extracted base then uses + // just its root ("http"/"cluster"). An unrecognized parent is kept verbatim and untagged. + absl::string_view base_prefix = prefix; + const std::optional prefix_tag = extractParentTag(prefix); + if (prefix_tag.has_value()) { + merged_tags.push_back(*prefix_tag); + ASSERT(prefix.find('.') != absl::string_view::npos); + // Keep the trailing dot of the base part of the parent prefix, so that the final base is + // "". + base_prefix = prefix.substr(0, prefix.find('.') + 1); + } + merged_tags.insert(merged_tags.end(), tags.begin(), tags.end()); + + // This helper function assumes the caller has already handled the dot correctly and then we can + // concatenate the two prefixes directly. The TaggedStatName then sanitizes the name, base, and + // tags to remove any leading or trailing dot. + const std::string tagged = absl::StrCat(prefix, name); + + // When no tag is contributed (neither the parent prefix nor the input tags), base_prefix is the + // full parent prefix and name equals base_name, so the base and tagged forms are identical; reuse + // the tagged name instead of building an identical base string. + if (merged_tags.empty()) { + ASSERT(tagged == absl::StrCat(base_prefix, base_name)); + return {symbol_table, tagged, merged_tags, tagged}; + } + const std::string base = absl::StrCat(base_prefix, base_name); + return {symbol_table, base, merged_tags, tagged}; +} + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/prefix_utility.h b/source/common/stats/prefix_utility.h new file mode 100644 index 0000000000000..f89af8e047201 --- /dev/null +++ b/source/common/stats/prefix_utility.h @@ -0,0 +1,44 @@ +#pragma once + +#include "envoy/stats/stats.h" +#include "envoy/stats/tag.h" + +#include "source/common/stats/utility.h" + +#include "absl/strings/string_view.h" + +namespace Envoy { +namespace Stats { + +/** + * Merges a parent prefix with an owner's own name and tags to produce a TaggedStatName. This helper + * extracts the well-known tag from the parent prefix and merges it with the input tags to produce + * the final TaggedStatName. + * + * The helper assumes the caller has already handled the dots correctly, so it can concatenate the + * parent prefix and the own name directly. + * + * The merged tagged name is `` and the merged base name is + * ``, where `` is `` with its well-known tag value + * removed (e.g. "http.ingress." -> "http."), or the full `` when it carries no well-known + * tag. When neither the parent prefix nor `tags` contributes a tag, the base and tagged names are + * identical. + * + * @param symbol_table the symbol table used to pre-encode the names and tags. + * @param prefix the parent prefix, whose well-known variable segment is extracted as a tag when + * the prefix ends with a trailing '.' (e.g. "http.." / "cluster.."); may be + * empty. + * @param base_name the tag-extracted own name (every own tag value removed), with no leading or + * trailing dot. + * @param tags one {tag_name, value} per variable segment of the own name. When empty, + * `name` is ignored and `base_name` is used for both forms. + * @param name the tagged own name (own tag values interleaved), with no leading or trailing + * dot. Combined with `prefix` this must equal the legacy flat prefix. Ignored when + * `tags` is empty. + */ +TaggedStatName mergeStatPrefix(SymbolTable& symbol_table, absl::string_view prefix, + absl::string_view base_name, TagStringViewSpan tags = {}, + absl::string_view name = {}); + +} // namespace Stats +} // namespace Envoy diff --git a/source/common/stats/symbol_table.h b/source/common/stats/symbol_table.h index 2c3a3ea748d19..acae0e4c745f1 100644 --- a/source/common/stats/symbol_table.h +++ b/source/common/stats/symbol_table.h @@ -803,6 +803,13 @@ class StatNamePool { */ void clear(); + /** + * Pre-allocates capacity in the underlying storage for at least `count` StatNames. This avoids + * incremental re-allocations when the number of names to be added is known up-front. + * @param count the number of StatNames the pool is expected to hold. + */ + void reserve(size_t count) { storage_vector_.reserve(count); } + /** * @param name the name to add the container. * @return the StatName held in the container for this name. diff --git a/source/common/stats/utility.cc b/source/common/stats/utility.cc index 585e74da9abce..432a7080f0d4b 100644 --- a/source/common/stats/utility.cc +++ b/source/common/stats/utility.cc @@ -4,6 +4,8 @@ #include #include +#include "source/common/stats/symbol_table.h" + #include "absl/strings/match.h" #include "absl/strings/str_replace.h" @@ -26,6 +28,38 @@ std::string Utility::sanitizeStatsName(absl::string_view name) { }); } +absl::string_view Utility::sanitizeStatsName(absl::string_view name, std::string& buffer) { + name = absl::StripPrefix(absl::StripSuffix(name, "."), "."); + + // Check if the name needs sanitization. + if (name.find(':') != absl::string_view::npos || name.find('\0') != absl::string_view::npos) { + buffer = absl::StrReplaceAll(name, { + {"://", "_"}, + {":/", "_"}, + {":", "_"}, + {absl::string_view("\0", 1), "_"}, + }); + return buffer; + } + return name; +} + +TaggedStatName::TaggedStatName(SymbolTable& symbol_table, absl::string_view base_name, + TagStringViewSpan name_tags, absl::string_view name) + : tag_pool_(symbol_table) { + tag_pool_.reserve(name_tags.size() * 2 + 2); + + std::string buffer; + name_ = tag_pool_.add(Utility::sanitizeStatsName(name, buffer)); + base_name_ = tag_pool_.add(Utility::sanitizeStatsName(base_name, buffer)); + + name_tags_.reserve(name_tags.size()); + for (const TagStringView& tag : name_tags) { + name_tags_.push_back({tag_pool_.add(Utility::sanitizeStatsName(tag.first, buffer)), + tag_pool_.add(Utility::sanitizeStatsName(tag.second, buffer))}); + } +} + std::optional Utility::findTag(const Metric& metric, StatName find_tag_name) { std::optional value; metric.iterateTagStatNames( @@ -125,6 +159,74 @@ TextReadout& textReadoutFromStatNames(Scope& scope, const StatNameVec& elements, return scope.textReadoutFromStatNameWithTags(StatName(joined.get()), tags); } +Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, absl::string_view name) { + StatNameManagedStorage name_storage(name, scope.symbolTable()); + return counterFromTaggedPrefix(scope, base_prefix, prefix_tags, prefix, name_storage.statName()); +} + +Gauge& gaugeFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, absl::string_view name, + Gauge::ImportMode import_mode) { + StatNameManagedStorage name_storage(name, scope.symbolTable()); + return gaugeFromTaggedPrefix(scope, base_prefix, prefix_tags, prefix, name_storage.statName(), + import_mode); +} + +Histogram& histogramFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, + absl::string_view name, Histogram::Unit unit) { + StatNameManagedStorage name_storage(name, scope.symbolTable()); + return histogramFromTaggedPrefix(scope, base_prefix, prefix_tags, prefix, name_storage.statName(), + unit); +} + +TextReadout& textReadoutFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, + absl::string_view name) { + StatNameManagedStorage name_storage(name, scope.symbolTable()); + return textReadoutFromTaggedPrefix(scope, base_prefix, prefix_tags, prefix, + name_storage.statName()); +} + +Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, StatName name) { + SymbolTable& symbol_table = scope.symbolTable(); + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); + SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); + return scope.counterFromTaggedName(StatName(full_name_base.get()), prefix_tags, + StatName(full_name.get())); +} + +Gauge& gaugeFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, StatName name, Gauge::ImportMode import_mode) { + SymbolTable& symbol_table = scope.symbolTable(); + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); + SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); + return scope.gaugeFromTaggedName(StatName(full_name_base.get()), prefix_tags, + StatName(full_name.get()), import_mode); +} + +Histogram& histogramFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, StatName name, + Histogram::Unit unit) { + SymbolTable& symbol_table = scope.symbolTable(); + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); + SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); + return scope.histogramFromTaggedName(StatName(full_name_base.get()), prefix_tags, + StatName(full_name.get()), unit); +} + +TextReadout& textReadoutFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, + StatName name) { + SymbolTable& symbol_table = scope.symbolTable(); + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); + SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); + return scope.textReadoutFromTaggedName(StatName(full_name_base.get()), prefix_tags, + StatName(full_name.get())); +} + } // namespace Utility } // namespace Stats } // namespace Envoy diff --git a/source/common/stats/utility.h b/source/common/stats/utility.h index 4b1aa994b9b19..4ce2a5b37ebcb 100644 --- a/source/common/stats/utility.h +++ b/source/common/stats/utility.h @@ -59,6 +59,40 @@ class DynamicSavedName : public std::string { using Element = absl::variant; using ElementVec = absl::InlinedVector; +/** + * Bundles the pre-encoded names and tags used with the POOL_*_TAGGED macros. Encoding the + * names and tags once (typically per config) lets an owner create many tagged stats directly on + * a shared scope, without allocating a per-owner sub-scope just to carry the tags. + * + * Neither name should have a trailing dot: the leaf stat name is appended with a '.' separator, + * matching statPrefixJoin() used by the untagged POOL_*_PREFIX macros. + */ +class TaggedStatName { +public: + /** + * @param symbol_table The symbol table used to encode the names and tags. + * @param base_name The tag-extracted name, which is used to create the tag-extracted stat + * name. + * @param name_tags The tags associated with the tagged name. + * @param name The tagged name, which is used to create the tagged stat name. + * + * Note: the names and tags are copied into a private pool, so the caller does not need to + * maintain their lifetime after this constructor returns. + */ + TaggedStatName(SymbolTable& symbol_table, absl::string_view base_name, + TagStringViewSpan name_tags, absl::string_view name); + + StatName name() const { return name_; } + StatName baseName() const { return base_name_; } + StatNameTagSpan nameTags() const { return name_tags_; } + +private: + StatNamePool tag_pool_; + StatName name_; + StatName base_name_; + StatNameTagVec name_tags_; +}; + /** * Common stats utility routines. */ @@ -72,6 +106,17 @@ namespace Utility { */ std::string sanitizeStatsName(absl::string_view name); +/** + * Sanitizes a stat name and writes it to the provided buffer. The buffer is + * used to hold the sanitized name, and the returned string_view points to the + * buffer. The buffer is not modified if the name does not need sanitization. + * @param name the stat name to sanitize. + * @param buffer the buffer to write the sanitized name to. + * @return a string_view pointing to the sanitized name in the buffer, or the + * original name if no sanitization was needed. + */ +absl::string_view sanitizeStatsName(absl::string_view name, std::string& buffer); + /** * Finds a metric tag with the specified name. * @@ -232,6 +277,39 @@ TextReadout& textReadoutFromElements(Scope& scope, const ElementVec& elements, TextReadout& textReadoutFromStatNames(Scope& scope, const StatNameVec& elements, StatNameTagVectorOptConstRef tags = std::nullopt); +/** + * The `*FromTaggedPrefix` helpers create stats from a pre-encoded prefix and tags, which are + * typically created once per config and then used to create many stats. The helpers are used by the + * `POOL_*_TAGGED` macros in stats_macros.h. + * + * @param scope The scope in which to create the stat (the scope's own prefix/tags still apply). + * @param base_prefix Pre-encoded prefix with tag values removed (the tag-extracted name). + * @param prefix_tags Pre-encoded tags to attach to the stat. + * @param prefix Pre-encoded prefix with tag values interleaved (the flat name). + * @param name The stat's leaf name. + */ +Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, absl::string_view name); +Gauge& gaugeFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, absl::string_view name, + Gauge::ImportMode import_mode); +Histogram& histogramFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, + absl::string_view name, Histogram::Unit unit); +TextReadout& textReadoutFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, + absl::string_view name); +Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, StatName name); +Gauge& gaugeFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, + StatName prefix, StatName name, Gauge::ImportMode import_mode); +Histogram& histogramFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, StatName name, + Histogram::Unit unit); +TextReadout& textReadoutFromTaggedPrefix(Scope& scope, StatName base_prefix, + StatNameTagSpan prefix_tags, StatName prefix, + StatName name); + } // namespace Utility /** diff --git a/test/common/stats/BUILD b/test/common/stats/BUILD index 7bd70f1a8ad41..cc3612e19f277 100644 --- a/test/common/stats/BUILD +++ b/test/common/stats/BUILD @@ -443,3 +443,15 @@ envoy_cc_test( "//source/common/stats:utility_lib", ], ) + +envoy_cc_test( + name = "prefix_utility_test", + srcs = ["prefix_utility_test.cc"], + rbe_pool = "6gig", + deps = [ + "//source/common/config:well_known_names", + "//source/common/stats:isolated_store_lib", + "//source/common/stats:prefix_utility_lib", + "//test/test_common:utility_lib", + ], +) diff --git a/test/common/stats/prefix_utility_test.cc b/test/common/stats/prefix_utility_test.cc new file mode 100644 index 0000000000000..770432e101e5b --- /dev/null +++ b/test/common/stats/prefix_utility_test.cc @@ -0,0 +1,158 @@ +#include +#include + +#include "envoy/stats/stats_macros.h" + +#include "source/common/config/well_known_names.h" +#include "source/common/stats/isolated_store_impl.h" +#include "source/common/stats/prefix_utility.h" + +#include "test/test_common/utility.h" + +#include "gtest/gtest.h" + +namespace Envoy { +namespace Stats { +namespace { + +class MergeStatPrefixTest : public testing::Test { +protected: + // Emits ".name" the way POOL_COUNTER_PREFIX would, for byte-identical comparison. + std::string legacyName(absl::string_view prefix) { return statPrefixJoin(prefix, "name"); } + + // The flat name the tagged prefix produces for a "name" leaf (must match legacyName()). + std::string taggedName(const TaggedStatName& p) { + return Utility::counterFromTaggedPrefix(scope_, p.baseName(), p.nameTags(), p.name(), "name") + .name(); + } + + std::string base(const TaggedStatName& p) { return symbol_table_.toString(p.baseName()); } + std::string prefix(const TaggedStatName& p) { return symbol_table_.toString(p.name()); } + + std::vector> tags(const TaggedStatName& p) { + std::vector> out; + for (const StatNameTag& t : p.nameTags()) { + out.emplace_back(symbol_table_.toString(t.first), symbol_table_.toString(t.second)); + } + return out; + } + + IsolatedStoreImpl store_; + Scope& scope_{*store_.rootScope()}; + SymbolTable& symbol_table_{store_.symbolTable()}; + const std::string& hcm_tag_{Envoy::Config::TagNames::get().HTTP_CONN_MANAGER_PREFIX}; + const std::string& cluster_tag_{Envoy::Config::TagNames::get().CLUSTER_NAME}; +}; + +// Parent "http.." + a literal own prefix; parent extracted as HCM tag, no own tag. +TEST_F(MergeStatPrefixTest, ParentPlusLiteralSelf) { + const auto p = mergeStatPrefix(symbol_table_, "http.ingress.", "fault", {}, "fault"); + EXPECT_EQ(taggedName(p), "http.ingress.fault.name"); + EXPECT_EQ(taggedName(p), legacyName("http.ingress.fault.")); + EXPECT_EQ(base(p), "http.fault"); + EXPECT_THAT(tags(p), testing::ElementsAre(std::make_pair(hcm_tag_, "ingress"))); +} + +// Parent + "." + an own-tag value (e.g. ext_authz's filter_stats_prefix). +TEST_F(MergeStatPrefixTest, ParentPlusSelfTag) { + const auto p = + mergeStatPrefix(symbol_table_, "http.ingress.", "ext_authz", + {{Envoy::Config::TagNames::get().EXT_AUTHZ_PREFIX, "waf"}}, "ext_authz.waf"); + EXPECT_EQ(taggedName(p), "http.ingress.ext_authz.waf.name"); + EXPECT_EQ(taggedName(p), legacyName("http.ingress.ext_authz.waf.")); + EXPECT_EQ(base(p), "http.ext_authz"); + EXPECT_THAT(tags(p), testing::ElementsAre( + std::make_pair(hcm_tag_, "ingress"), + std::make_pair(Envoy::Config::TagNames::get().EXT_AUTHZ_PREFIX, "waf"))); +} + +// Value-first own prefix, parent carried by the scope (empty parent -> no parent tag). +TEST_F(MergeStatPrefixTest, ValueFirstSelfNoParent) { + const auto p = + mergeStatPrefix(symbol_table_, "", "http_local_rate_limit", + {{Envoy::Config::TagNames::get().LOCAL_HTTP_RATELIMIT_PREFIX, "myprefix"}}, + "myprefix.http_local_rate_limit"); + EXPECT_EQ(taggedName(p), "myprefix.http_local_rate_limit.name"); + EXPECT_EQ(taggedName(p), legacyName("myprefix.http_local_rate_limit")); + EXPECT_EQ(base(p), "http_local_rate_limit"); + EXPECT_THAT(tags(p), + testing::ElementsAre(std::make_pair( + Envoy::Config::TagNames::get().LOCAL_HTTP_RATELIMIT_PREFIX, "myprefix"))); +} + +// Cluster-scoped parent "cluster.." extracted as CLUSTER_NAME tag. +TEST_F(MergeStatPrefixTest, ClusterParent) { + const auto p = mergeStatPrefix(symbol_table_, "cluster.foo.", "ext_authz", {}, "ext_authz"); + EXPECT_EQ(taggedName(p), "cluster.foo.ext_authz.name"); + EXPECT_EQ(taggedName(p), legacyName("cluster.foo.ext_authz.")); + EXPECT_EQ(base(p), "cluster.ext_authz"); + EXPECT_THAT(tags(p), testing::ElementsAre(std::make_pair(cluster_tag_, "foo"))); +} + +// Cluster parent combined with an own tag: both the parent tag and the own tag are emitted, parent +// first, and both prefixes are tag-extracted. +TEST_F(MergeStatPrefixTest, ClusterParentWithSelfTag) { + const auto p = + mergeStatPrefix(symbol_table_, "cluster.foo.", "ext_authz", + {{Envoy::Config::TagNames::get().EXT_AUTHZ_PREFIX, "waf"}}, "ext_authz.waf"); + EXPECT_EQ(taggedName(p), "cluster.foo.ext_authz.waf.name"); + EXPECT_EQ(taggedName(p), legacyName("cluster.foo.ext_authz.waf.")); + EXPECT_EQ(base(p), "cluster.ext_authz"); + EXPECT_THAT(tags(p), testing::ElementsAre( + std::make_pair(cluster_tag_, "foo"), + std::make_pair(Envoy::Config::TagNames::get().EXT_AUTHZ_PREFIX, "waf"))); +} + +// A parent passed without a trailing dot will not be recognized as a tag because we assume +// the caller should handle the dot correctly. +TEST_F(MergeStatPrefixTest, ParentWithoutTrailingDot) { + const auto p = mergeStatPrefix(symbol_table_, "http.ingress", "fault", {}, "fault"); + EXPECT_EQ(taggedName(p), "http.ingressfault.name"); + EXPECT_EQ(base(p), "http.ingressfault"); + EXPECT_EQ(prefix(p), "http.ingressfault"); + EXPECT_TRUE(tags(p).empty()); +} + +// The whole segment after the "http."/"cluster." root becomes the tag value (dots included). +TEST_F(MergeStatPrefixTest, MultiSegmentParentValue) { + const auto p = mergeStatPrefix(symbol_table_, "http.a.b.", "fault", {}, "fault"); + EXPECT_EQ(taggedName(p), "http.a.b.fault.name"); + EXPECT_EQ(base(p), "http.fault"); + EXPECT_THAT(tags(p), testing::ElementsAre(std::make_pair(hcm_tag_, "a.b"))); +} + +// An unrecognized parent root is passed through untagged. +TEST_F(MergeStatPrefixTest, UnrecognizedParentNotTagged) { + const auto p = mergeStatPrefix(symbol_table_, "listener.foo.", "fault", {}, "fault"); + EXPECT_EQ(taggedName(p), "listener.foo.fault.name"); + EXPECT_EQ(base(p), "listener.foo.fault"); + EXPECT_TRUE(tags(p).empty()); +} + +// With no own tags, the tagged own prefix is ignored and base_prefix drives both forms. +TEST_F(MergeStatPrefixTest, NoSelfTagsIgnoresSelfPrefix) { + const auto p = mergeStatPrefix(symbol_table_, "http.ingress.", "fault", {}, ""); + EXPECT_EQ(taggedName(p), "http.ingress.fault.name"); + EXPECT_EQ(base(p), "http.fault"); + EXPECT_THAT(tags(p), testing::ElementsAre(std::make_pair(hcm_tag_, "ingress"))); +} + +// Empty parent and empty own prefix: stats sit at the root with no prefix. +TEST_F(MergeStatPrefixTest, EmptyParentAndSelf) { + const auto p = mergeStatPrefix(symbol_table_, "", "", {}, ""); + EXPECT_EQ(taggedName(p), "name"); + EXPECT_EQ(base(p), ""); + EXPECT_TRUE(tags(p).empty()); +} + +// Empty own prefix with a tagged parent: stats sit directly under the parent. +TEST_F(MergeStatPrefixTest, EmptySelfWithParent) { + const auto p = mergeStatPrefix(symbol_table_, "http.ingress.", "", {}, ""); + EXPECT_EQ(taggedName(p), "http.ingress.name"); + EXPECT_EQ(base(p), "http"); + EXPECT_THAT(tags(p), testing::ElementsAre(std::make_pair(hcm_tag_, "ingress"))); +} + +} // namespace +} // namespace Stats +} // namespace Envoy diff --git a/test/common/stats/utility_test.cc b/test/common/stats/utility_test.cc index d51e7716a897c..0b1c4e3f06ee0 100644 --- a/test/common/stats/utility_test.cc +++ b/test/common/stats/utility_test.cc @@ -1,6 +1,8 @@ #include +#include #include "envoy/stats/stats_macros.h" +#include "envoy/stats/tag.h" #include "source/common/stats/isolated_store_impl.h" #include "source/common/stats/null_counter.h" @@ -188,6 +190,96 @@ TEST_P(StatsUtilityTest, Counters) { EXPECT_EQ("scope.x.token.y.tag1.value1.tag2.value2", ctags.name()); } +// Verifies the `*FromTaggedPrefix` helpers with a string_view leaf name. Unlike counterFromElements +// (which appends tags to the flat name), the tagged prefix keeps the tag value at its original +// position in the flat name (join of `tagged_prefix` and the leaf). The tag-extracted `base_prefix` +// only surfaces through a store configured with tag extractors, so it is exercised at the store +// level in thread_local_store_test / isolated_store_impl_test; here we pin the flat name the +// utility helper builds. +TEST_P(StatsUtilityTest, TaggedStatNames) { + ScopeSharedPtr scope = store_->createScope("scope."); + const StatName tag_extracted_prefix = pool_.add("prefix"); + const StatName tagged_prefix = pool_.add("prefix.value"); + + Counter& c = Utility::counterFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, tagged_prefix, + "requests"); + EXPECT_EQ("scope.prefix.value.requests", c.name()); + + Gauge& g = Utility::gaugeFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, tagged_prefix, + "active", Gauge::ImportMode::Accumulate); + EXPECT_EQ("scope.prefix.value.active", g.name()); + EXPECT_EQ(Gauge::ImportMode::Accumulate, g.importMode()); + + Histogram& h = Utility::histogramFromTaggedPrefix( + *scope, tag_extracted_prefix, tags_, tagged_prefix, "latency", Histogram::Unit::Unspecified); + EXPECT_EQ("scope.prefix.value.latency", h.name()); + EXPECT_EQ(Histogram::Unit::Unspecified, h.unit()); + + TextReadout& t = Utility::textReadoutFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, + tagged_prefix, "info"); + EXPECT_EQ("scope.prefix.value.info", t.name()); +} + +// Verifies the `*FromTaggedPrefix` overloads that take a symbolic StatName leaf (rather than a +// string_view), which are used when the leaf name is already interned. +TEST_P(StatsUtilityTest, TaggedStatNamesWithStatNameLeaf) { + ScopeSharedPtr scope = store_->createScope("scope."); + const StatName tag_extracted_prefix = pool_.add("prefix"); + const StatName tagged_prefix = pool_.add("prefix.value"); + const StatName requests = pool_.add("requests"); + const StatName active = pool_.add("active"); + const StatName latency = pool_.add("latency"); + const StatName info = pool_.add("info"); + + Counter& c = Utility::counterFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, tagged_prefix, + requests); + EXPECT_EQ("scope.prefix.value.requests", c.name()); + + Gauge& g = Utility::gaugeFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, tagged_prefix, + active, Gauge::ImportMode::NeverImport); + EXPECT_EQ("scope.prefix.value.active", g.name()); + EXPECT_EQ(Gauge::ImportMode::NeverImport, g.importMode()); + + Histogram& h = Utility::histogramFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, + tagged_prefix, latency, Histogram::Unit::Bytes); + EXPECT_EQ("scope.prefix.value.latency", h.name()); + EXPECT_EQ(Histogram::Unit::Bytes, h.unit()); + + TextReadout& t = Utility::textReadoutFromTaggedPrefix(*scope, tag_extracted_prefix, tags_, + tagged_prefix, info); + EXPECT_EQ("scope.prefix.value.info", t.name()); +} + +// A tagged prefix with no tags: base and tagged forms coincide, so the flat name is just the prefix +// joined with the leaf. +TEST_P(StatsUtilityTest, TaggedStatNamesNoTags) { + ScopeSharedPtr scope = store_->createScope("scope."); + const StatName prefix = pool_.add("prefix"); + Counter& c = Utility::counterFromTaggedPrefix(*scope, prefix, {}, prefix, "requests"); + EXPECT_EQ("scope.prefix.requests", c.name()); +} + +// Exercises TaggedStatName directly: it pre-encodes the base name, tagged name, and tags +// into its own pool, copying the string_view inputs so callers need not keep them alive. +TEST_P(StatsUtilityTest, TaggedStatNameAccessors) { + const std::vector tag_views{{"tagA", "valA"}, {"tagB", "valB"}}; + TaggedStatName tagged(*symbol_table_, "base.name", tag_views, "base.valA.name"); + EXPECT_EQ("base.name", symbol_table_->toString(tagged.baseName())); + EXPECT_EQ("base.valA.name", symbol_table_->toString(tagged.name())); + const StatNameTagSpan name_tags = tagged.nameTags(); + ASSERT_EQ(2, name_tags.size()); + EXPECT_EQ("tagA", symbol_table_->toString(name_tags[0].first)); + EXPECT_EQ("valA", symbol_table_->toString(name_tags[0].second)); + EXPECT_EQ("tagB", symbol_table_->toString(name_tags[1].first)); + EXPECT_EQ("valB", symbol_table_->toString(name_tags[1].second)); + + // Empty tags: base and tagged forms coincide and there are no tags. + TaggedStatName empty(*symbol_table_, "base", {}, "base"); + EXPECT_EQ("base", symbol_table_->toString(empty.baseName())); + EXPECT_EQ("base", symbol_table_->toString(empty.name())); + EXPECT_TRUE(empty.nameTags().empty()); +} + TEST_P(StatsUtilityTest, Gauges) { ScopeSharedPtr scope = store_->createScope("scope."); Gauge& g1 = Utility::gaugeFromElements(*scope, {DynamicName("a"), DynamicName("b")}, From 7266b6fe6b1420ede94e1a88b7e6305eaf9a79db Mon Sep 17 00:00:00 2001 From: wbpcode/wangbaiping Date: Sat, 4 Jul 2026 09:15:45 +0000 Subject: [PATCH 2/5] fix comment Signed-off-by: wbpcode/wangbaiping --- source/common/stats/prefix_utility.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/common/stats/prefix_utility.h b/source/common/stats/prefix_utility.h index f89af8e047201..5ffe41c631179 100644 --- a/source/common/stats/prefix_utility.h +++ b/source/common/stats/prefix_utility.h @@ -28,13 +28,11 @@ namespace Stats { * @param prefix the parent prefix, whose well-known variable segment is extracted as a tag when * the prefix ends with a trailing '.' (e.g. "http.." / "cluster.."); may be * empty. - * @param base_name the tag-extracted own name (every own tag value removed), with no leading or - * trailing dot. + * @param base_name the tag-extracted own name (every own tag value removed). * @param tags one {tag_name, value} per variable segment of the own name. When empty, * `name` is ignored and `base_name` is used for both forms. - * @param name the tagged own name (own tag values interleaved), with no leading or trailing - * dot. Combined with `prefix` this must equal the legacy flat prefix. Ignored when - * `tags` is empty. + * @param name the tagged own name (own tag values interleaved). Combined with `prefix` this must + * equal the legacy flat prefix. Ignored when `tags` is empty. */ TaggedStatName mergeStatPrefix(SymbolTable& symbol_table, absl::string_view prefix, absl::string_view base_name, TagStringViewSpan tags = {}, From 73e012fa39898d0829422973b5b43c1981ddd378 Mon Sep 17 00:00:00 2001 From: wbpcode/wangbaiping Date: Sun, 5 Jul 2026 12:57:50 +0000 Subject: [PATCH 3/5] add defensive check and also more tests Signed-off-by: wbpcode/wangbaiping --- envoy/stats/stats_macros.h | 8 ++-- source/common/stats/utility.cc | 52 ++++++++++++++++++++---- source/common/stats/utility.h | 16 ++++---- test/common/stats/prefix_utility_test.cc | 4 +- test/common/stats/utility_test.cc | 24 ++++++++++- 5 files changed, 82 insertions(+), 22 deletions(-) diff --git a/envoy/stats/stats_macros.h b/envoy/stats/stats_macros.h index 34cfcec35cf36..fb5f3a5b1b4c2 100644 --- a/envoy/stats/stats_macros.h +++ b/envoy/stats/stats_macros.h @@ -119,16 +119,16 @@ static inline std::string statPrefixJoin(absl::string_view prefix, absl::string_ // Convenience wrappers taking a Stats::TaggedStatName (see utility.h), which pre-encodes the // tag-extracted prefix, the flat prefix and the tags. #define POOL_COUNTER_TAGGED(POOL, TAGGED_NAME) \ - POOL_COUNTER_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + POOL_COUNTER_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).tags(), \ (TAGGED_NAME).name()) #define POOL_GAUGE_TAGGED(POOL, TAGGED_NAME) \ - POOL_GAUGE_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + POOL_GAUGE_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).tags(), \ (TAGGED_NAME).name()) #define POOL_HISTOGRAM_TAGGED(POOL, TAGGED_NAME) \ - POOL_HISTOGRAM_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + POOL_HISTOGRAM_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).tags(), \ (TAGGED_NAME).name()) #define POOL_TEXT_READOUT_TAGGED(POOL, TAGGED_NAME) \ - POOL_TEXT_READOUT_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).nameTags(), \ + POOL_TEXT_READOUT_TAGGED_PREFIX(POOL, (TAGGED_NAME).baseName(), (TAGGED_NAME).tags(), \ (TAGGED_NAME).name()) #define NULL_STAT_DECL_(X) std::string(#X)), diff --git a/source/common/stats/utility.cc b/source/common/stats/utility.cc index 432a7080f0d4b..5c63936237373 100644 --- a/source/common/stats/utility.cc +++ b/source/common/stats/utility.cc @@ -45,18 +45,24 @@ absl::string_view Utility::sanitizeStatsName(absl::string_view name, std::string } TaggedStatName::TaggedStatName(SymbolTable& symbol_table, absl::string_view base_name, - TagStringViewSpan name_tags, absl::string_view name) + TagStringViewSpan tags, absl::string_view name) : tag_pool_(symbol_table) { - tag_pool_.reserve(name_tags.size() * 2 + 2); + tag_pool_.reserve(tags.size() * 2 + 2); std::string buffer; - name_ = tag_pool_.add(Utility::sanitizeStatsName(name, buffer)); base_name_ = tag_pool_.add(Utility::sanitizeStatsName(base_name, buffer)); + if (tags.empty()) { + name_ = base_name_; + } else { + ASSERT(!name.empty(), "When tags are supplied, the caller must supply the tagged name with the " + "tag values interleaved."); + name_ = tag_pool_.add(Utility::sanitizeStatsName(name, buffer)); + } - name_tags_.reserve(name_tags.size()); - for (const TagStringView& tag : name_tags) { - name_tags_.push_back({tag_pool_.add(Utility::sanitizeStatsName(tag.first, buffer)), - tag_pool_.add(Utility::sanitizeStatsName(tag.second, buffer))}); + tags_.reserve(tags.size()); + for (const TagStringView& tag : tags) { + tags_.push_back({tag_pool_.add(Utility::sanitizeStatsName(tag.first, buffer)), + tag_pool_.add(Utility::sanitizeStatsName(tag.second, buffer))}); } } @@ -192,6 +198,14 @@ TextReadout& textReadoutFromTaggedPrefix(Scope& scope, StatName base_prefix, Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, StatName prefix, StatName name) { SymbolTable& symbol_table = scope.symbolTable(); + if (prefix_tags.empty()) { + prefix = base_prefix; + } else { + ASSERT(!prefix.empty(), + "When tags are supplied, the caller must supply the tagged prefix with the " + "tag values interleaved."); + } + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); return scope.counterFromTaggedName(StatName(full_name_base.get()), prefix_tags, @@ -201,6 +215,14 @@ Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTag Gauge& gaugeFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, StatName prefix, StatName name, Gauge::ImportMode import_mode) { SymbolTable& symbol_table = scope.symbolTable(); + if (prefix_tags.empty()) { + prefix = base_prefix; + } else { + ASSERT(!prefix.empty(), + "When tags are supplied, the caller must supply the tagged prefix with the " + "tag values interleaved."); + } + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); return scope.gaugeFromTaggedName(StatName(full_name_base.get()), prefix_tags, @@ -211,6 +233,14 @@ Histogram& histogramFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, StatName prefix, StatName name, Histogram::Unit unit) { SymbolTable& symbol_table = scope.symbolTable(); + if (prefix_tags.empty()) { + prefix = base_prefix; + } else { + ASSERT(!prefix.empty(), + "When tags are supplied, the caller must supply the tagged prefix with the " + "tag values interleaved."); + } + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); return scope.histogramFromTaggedName(StatName(full_name_base.get()), prefix_tags, @@ -221,6 +251,14 @@ TextReadout& textReadoutFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, StatName prefix, StatName name) { SymbolTable& symbol_table = scope.symbolTable(); + if (prefix_tags.empty()) { + prefix = base_prefix; + } else { + ASSERT(!prefix.empty(), + "When tags are supplied, the caller must supply the tagged prefix with the " + "tag values interleaved."); + } + SymbolTable::StoragePtr full_name = symbol_table.join({prefix, name}); SymbolTable::StoragePtr full_name_base = symbol_table.join({base_prefix, name}); return scope.textReadoutFromTaggedName(StatName(full_name_base.get()), prefix_tags, diff --git a/source/common/stats/utility.h b/source/common/stats/utility.h index 4ce2a5b37ebcb..8e0dd6d606909 100644 --- a/source/common/stats/utility.h +++ b/source/common/stats/utility.h @@ -73,24 +73,25 @@ class TaggedStatName { * @param symbol_table The symbol table used to encode the names and tags. * @param base_name The tag-extracted name, which is used to create the tag-extracted stat * name. - * @param name_tags The tags associated with the tagged name. - * @param name The tagged name, which is used to create the tagged stat name. + * @param tags The tags associated with the tagged name. + * @param name The tagged name, which is used to create the tagged stat name. If tags is empty, + * this is ignored and base_name is used for both forms. * * Note: the names and tags are copied into a private pool, so the caller does not need to * maintain their lifetime after this constructor returns. */ - TaggedStatName(SymbolTable& symbol_table, absl::string_view base_name, - TagStringViewSpan name_tags, absl::string_view name); + TaggedStatName(SymbolTable& symbol_table, absl::string_view base_name, TagStringViewSpan tags, + absl::string_view name); StatName name() const { return name_; } StatName baseName() const { return base_name_; } - StatNameTagSpan nameTags() const { return name_tags_; } + StatNameTagSpan tags() const { return tags_; } private: StatNamePool tag_pool_; StatName name_; StatName base_name_; - StatNameTagVec name_tags_; + StatNameTagVec tags_; }; /** @@ -285,7 +286,8 @@ TextReadout& textReadoutFromStatNames(Scope& scope, const StatNameVec& elements, * @param scope The scope in which to create the stat (the scope's own prefix/tags still apply). * @param base_prefix Pre-encoded prefix with tag values removed (the tag-extracted name). * @param prefix_tags Pre-encoded tags to attach to the stat. - * @param prefix Pre-encoded prefix with tag values interleaved (the flat name). + * @param prefix Pre-encoded prefix with tag values interleaved (the flat name). If prefix_tags is + * empty, this is ignored and base_prefix is used for both forms. * @param name The stat's leaf name. */ Counter& counterFromTaggedPrefix(Scope& scope, StatName base_prefix, StatNameTagSpan prefix_tags, diff --git a/test/common/stats/prefix_utility_test.cc b/test/common/stats/prefix_utility_test.cc index 770432e101e5b..b3183e7bee4d6 100644 --- a/test/common/stats/prefix_utility_test.cc +++ b/test/common/stats/prefix_utility_test.cc @@ -22,7 +22,7 @@ class MergeStatPrefixTest : public testing::Test { // The flat name the tagged prefix produces for a "name" leaf (must match legacyName()). std::string taggedName(const TaggedStatName& p) { - return Utility::counterFromTaggedPrefix(scope_, p.baseName(), p.nameTags(), p.name(), "name") + return Utility::counterFromTaggedPrefix(scope_, p.baseName(), p.tags(), p.name(), "name") .name(); } @@ -31,7 +31,7 @@ class MergeStatPrefixTest : public testing::Test { std::vector> tags(const TaggedStatName& p) { std::vector> out; - for (const StatNameTag& t : p.nameTags()) { + for (const StatNameTag& t : p.tags()) { out.emplace_back(symbol_table_.toString(t.first), symbol_table_.toString(t.second)); } return out; diff --git a/test/common/stats/utility_test.cc b/test/common/stats/utility_test.cc index 0b1c4e3f06ee0..fa86a2cb11e71 100644 --- a/test/common/stats/utility_test.cc +++ b/test/common/stats/utility_test.cc @@ -257,6 +257,20 @@ TEST_P(StatsUtilityTest, TaggedStatNamesNoTags) { const StatName prefix = pool_.add("prefix"); Counter& c = Utility::counterFromTaggedPrefix(*scope, prefix, {}, prefix, "requests"); EXPECT_EQ("scope.prefix.requests", c.name()); + + Counter& c2 = Utility::counterFromTaggedPrefix(*scope, prefix, {}, StatName(), "requests"); + EXPECT_EQ("scope.prefix.requests", c2.name()); + + Gauge& g = Utility::gaugeFromTaggedPrefix(*scope, prefix, {}, StatName(), "gauge", + Gauge::ImportMode::NeverImport); + EXPECT_EQ("scope.prefix.gauge", g.name()); + + Histogram& h = Utility::histogramFromTaggedPrefix(*scope, prefix, {}, StatName(), "histogram", + Histogram::Unit::Milliseconds); + EXPECT_EQ("scope.prefix.histogram", h.name()); + + TextReadout& t = Utility::textReadoutFromTaggedPrefix(*scope, prefix, {}, StatName(), "text"); + EXPECT_EQ("scope.prefix.text", t.name()); } // Exercises TaggedStatName directly: it pre-encodes the base name, tagged name, and tags @@ -266,7 +280,7 @@ TEST_P(StatsUtilityTest, TaggedStatNameAccessors) { TaggedStatName tagged(*symbol_table_, "base.name", tag_views, "base.valA.name"); EXPECT_EQ("base.name", symbol_table_->toString(tagged.baseName())); EXPECT_EQ("base.valA.name", symbol_table_->toString(tagged.name())); - const StatNameTagSpan name_tags = tagged.nameTags(); + const StatNameTagSpan name_tags = tagged.tags(); ASSERT_EQ(2, name_tags.size()); EXPECT_EQ("tagA", symbol_table_->toString(name_tags[0].first)); EXPECT_EQ("valA", symbol_table_->toString(name_tags[0].second)); @@ -277,7 +291,13 @@ TEST_P(StatsUtilityTest, TaggedStatNameAccessors) { TaggedStatName empty(*symbol_table_, "base", {}, "base"); EXPECT_EQ("base", symbol_table_->toString(empty.baseName())); EXPECT_EQ("base", symbol_table_->toString(empty.name())); - EXPECT_TRUE(empty.nameTags().empty()); + EXPECT_TRUE(empty.tags().empty()); + + // Empty tags with an empty tagged name: the name falls back to the base name. + TaggedStatName fallback(*symbol_table_, "base", {}, ""); + EXPECT_EQ("base", symbol_table_->toString(fallback.baseName())); + EXPECT_EQ("base", symbol_table_->toString(fallback.name())); + EXPECT_TRUE(fallback.tags().empty()); } TEST_P(StatsUtilityTest, Gauges) { From 8102df1403a1d9a1f02f19a71592798368b4e095 Mon Sep 17 00:00:00 2001 From: wbpcode/wangbaiping Date: Sun, 5 Jul 2026 13:25:08 +0000 Subject: [PATCH 4/5] stats: migrate almost all HTTP to new API Signed-off-by: wbpcode/wangbaiping --- source/common/http/BUILD | 2 + source/common/http/conn_manager_impl.cc | 16 ++++--- source/extensions/filters/http/a2a/BUILD | 1 + .../extensions/filters/http/a2a/a2a_filter.cc | 5 ++- .../http/adaptive_concurrency/config.cc | 3 +- .../adaptive_concurrency/controller/BUILD | 1 + .../controller/gradient_controller.cc | 19 ++++++--- .../controller/gradient_controller.h | 6 ++- .../filters/http/admission_control/BUILD | 1 + .../admission_control/admission_control.h | 7 +++- .../filters/http/admission_control/config.cc | 7 ++-- .../filters/http/api_key_auth/BUILD | 1 + .../filters/http/api_key_auth/api_key_auth.cc | 3 +- .../filters/http/api_key_auth/api_key_auth.h | 5 ++- .../extensions/filters/http/aws_lambda/BUILD | 1 + .../http/aws_lambda/aws_lambda_filter.cc | 8 ++-- .../filters/http/aws_request_signing/BUILD | 1 + .../aws_request_signing_filter.cc | 6 ++- .../extensions/filters/http/basic_auth/BUILD | 1 + .../http/basic_auth/basic_auth_filter.cc | 2 +- .../http/basic_auth/basic_auth_filter.h | 5 ++- .../extensions/filters/http/composite/BUILD | 1 + .../filters/http/composite/config.cc | 11 +++-- .../extensions/filters/http/compressor/BUILD | 1 + .../http/compressor/compressor_filter.cc | 31 +++++++------- .../http/compressor/compressor_filter.h | 32 ++++++++++---- source/extensions/filters/http/cors/BUILD | 1 + .../filters/http/cors/cors_filter.cc | 2 +- .../filters/http/cors/cors_filter.h | 5 ++- .../filters/http/credential_injector/BUILD | 1 + .../http/credential_injector/config.cc | 7 ++-- .../credential_injector_filter.h | 5 ++- source/extensions/filters/http/csrf/BUILD | 1 + .../filters/http/csrf/csrf_filter.cc | 5 ++- .../filters/http/decompressor/BUILD | 1 + .../http/decompressor/decompressor_filter.cc | 34 ++++++++------- .../http/decompressor/decompressor_filter.h | 24 ++++++++--- .../extensions/filters/http/ext_authz/BUILD | 2 + .../filters/http/ext_authz/ext_authz.h | 15 ++++++- source/extensions/filters/http/ext_proc/BUILD | 1 + .../filters/http/ext_proc/ext_proc.h | 11 ++++- source/extensions/filters/http/fault/BUILD | 1 + .../filters/http/fault/fault_filter.cc | 7 ++-- .../filters/http/filter_chain/BUILD | 1 + .../filters/http/filter_chain/filter.h | 6 ++- .../extensions/filters/http/gcp_authn/BUILD | 1 + .../filters/http/gcp_authn/gcp_authn_filter.h | 7 +++- source/extensions/filters/http/geoip/BUILD | 2 + .../filters/http/geoip/geoip_filter.cc | 9 ++-- .../filters/http/geoip/geoip_filter.h | 4 +- .../filters/http/grpc_json_transcoder/BUILD | 1 + .../filters/http/grpc_json_transcoder/stats.h | 9 +++- .../filters/http/health_check/BUILD | 1 + .../filters/http/health_check/health_check.h | 6 ++- .../extensions/filters/http/ip_tagging/BUILD | 2 + .../http/ip_tagging/ip_tagging_filter.cc | 8 ++-- .../http/ip_tagging/ip_tagging_filter.h | 3 +- .../extensions/filters/http/jwt_authn/BUILD | 1 + .../filters/http/jwt_authn/filter_config.h | 11 ++++- .../filters/http/local_ratelimit/BUILD | 2 + .../http/local_ratelimit/local_ratelimit.cc | 15 ++++++- source/extensions/filters/http/lua/BUILD | 1 + .../extensions/filters/http/lua/lua_filter.h | 11 ++++- source/extensions/filters/http/mcp/BUILD | 1 + .../extensions/filters/http/mcp/mcp_filter.cc | 5 ++- .../extensions/filters/http/mcp_router/BUILD | 1 + .../filters/http/mcp_router/filter_config.cc | 6 ++- source/extensions/filters/http/oauth2/BUILD | 1 + .../extensions/filters/http/oauth2/filter.cc | 8 +++- .../filters/http/set_metadata/BUILD | 1 + .../http/set_metadata/set_metadata_filter.cc | 6 ++- .../filters/http/stateful_session/BUILD | 1 + .../http/stateful_session/stateful_session.cc | 11 +++-- source/extensions/filters/http/tap/BUILD | 1 + .../extensions/filters/http/tap/tap_filter.cc | 8 ++-- .../extensions/filters/http/transform/BUILD | 1 + .../filters/http/transform/transform.h | 10 ++++- .../controller/gradient_controller_test.cc | 10 ++--- .../admission_control_filter_test.cc | 42 ++++++++++++------- 79 files changed, 365 insertions(+), 149 deletions(-) diff --git a/source/common/http/BUILD b/source/common/http/BUILD index 6857fa1b1c6bb..cd69066c6e8f0 100644 --- a/source/common/http/BUILD +++ b/source/common/http/BUILD @@ -419,6 +419,7 @@ envoy_cc_library( "//source/common/common:scope_tracker", "//source/common/common:utility_lib", "//source/common/config:utility_lib", + "//source/common/config:well_known_names", "//source/common/http/http1:codec_lib", "//source/common/http/http2:codec_lib", "//source/common/http/matching:data_impl_lib", @@ -428,6 +429,7 @@ envoy_cc_library( "//source/common/network:utility_lib", "//source/common/quic:quic_server_factory_stub_lib", "//source/common/router:config_lib", + "//source/common/stats:prefix_utility_lib", "//source/common/stats:timespan_lib", "//source/common/stream_info:stream_info_lib", "//source/common/tracing:http_tracer_lib", diff --git a/source/common/http/conn_manager_impl.cc b/source/common/http/conn_manager_impl.cc index 27db3598417de..85b5703979712 100644 --- a/source/common/http/conn_manager_impl.cc +++ b/source/common/http/conn_manager_impl.cc @@ -35,6 +35,7 @@ #include "source/common/common/perf_tracing.h" #include "source/common/common/scope_tracker.h" #include "source/common/common/utility.h" +#include "source/common/config/well_known_names.h" #include "source/common/http/codes.h" #include "source/common/http/conn_manager_utility.h" #include "source/common/http/exception.h" @@ -49,10 +50,10 @@ #include "source/common/network/utility.h" #include "source/common/router/config_impl.h" #include "source/common/runtime/runtime_features.h" +#include "source/common/stats/prefix_utility.h" #include "source/common/stats/timespan_impl.h" #include "source/common/stream_info/utility.h" -#include "absl/strings/escaping.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" @@ -99,20 +100,25 @@ upstreamOperationNameFormatter(const Http::TracingConnectionManagerConfig& hcm_c ConnectionManagerStats ConnectionManagerImpl::generateStats(const std::string& prefix, Stats::Scope& scope) { + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, ""); return ConnectionManagerStats( - {ALL_HTTP_CONN_MAN_STATS(POOL_COUNTER_PREFIX(scope, prefix), POOL_GAUGE_PREFIX(scope, prefix), - POOL_HISTOGRAM_PREFIX(scope, prefix))}, + {ALL_HTTP_CONN_MAN_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix), + POOL_GAUGE_TAGGED(scope, stat_prefix), + POOL_HISTOGRAM_TAGGED(scope, stat_prefix))}, prefix, scope); } ConnectionManagerTracingStats ConnectionManagerImpl::generateTracingStats(const std::string& prefix, Stats::Scope& scope) { - return {CONN_MAN_TRACING_STATS(POOL_COUNTER_PREFIX(scope, prefix + "tracing."))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "tracing."); + return {CONN_MAN_TRACING_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } ConnectionManagerListenerStats ConnectionManagerImpl::generateListenerStats(const std::string& prefix, Stats::Scope& scope) { - return {CONN_MAN_LISTENER_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, ""); + return {CONN_MAN_LISTENER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } ConnectionManagerImpl::ConnectionManagerImpl( diff --git a/source/extensions/filters/http/a2a/BUILD b/source/extensions/filters/http/a2a/BUILD index 5b6697ec541aa..0b5fa2950afea 100644 --- a/source/extensions/filters/http/a2a/BUILD +++ b/source/extensions/filters/http/a2a/BUILD @@ -37,6 +37,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/http:utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:string_view", diff --git a/source/extensions/filters/http/a2a/a2a_filter.cc b/source/extensions/filters/http/a2a/a2a_filter.cc index 2eda82b16dc7c..a298a72051bbd 100644 --- a/source/extensions/filters/http/a2a/a2a_filter.cc +++ b/source/extensions/filters/http/a2a/a2a_filter.cc @@ -2,6 +2,7 @@ #include "source/common/http/headers.h" #include "source/common/protobuf/utility.h" +#include "source/common/stats/prefix_utility.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" @@ -13,8 +14,8 @@ namespace A2a { namespace { A2aFilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "a2a."); - return A2aFilterStats{A2A_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, "a2a."); + return A2aFilterStats{A2A_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } } // namespace diff --git a/source/extensions/filters/http/adaptive_concurrency/config.cc b/source/extensions/filters/http/adaptive_concurrency/config.cc index c2fe46ff29e20..3dad0c3451ab1 100644 --- a/source/extensions/filters/http/adaptive_concurrency/config.cc +++ b/source/extensions/filters/http/adaptive_concurrency/config.cc @@ -27,7 +27,8 @@ Http::FilterFactoryCb AdaptiveConcurrencyFilterFactory::createFilterFactory( config.gradient_controller_config(), server_context.runtime()); controller = std::make_shared( std::move(gradient_controller_config), server_context.mainThreadDispatcher(), - server_context.runtime(), acc_stats_prefix + "gradient_controller.", scope, + server_context.runtime(), /*parent_stats_prefix=*/stats_prefix, + /*filter_stats_prefix=*/"adaptive_concurrency.gradient_controller.", scope, server_context.api().randomGenerator(), server_context.timeSource()); AdaptiveConcurrencyFilterConfigSharedPtr filter_config(new AdaptiveConcurrencyFilterConfig( diff --git a/source/extensions/filters/http/adaptive_concurrency/controller/BUILD b/source/extensions/filters/http/adaptive_concurrency/controller/BUILD index c7097772a5618..0011aca2b2930 100644 --- a/source/extensions/filters/http/adaptive_concurrency/controller/BUILD +++ b/source/extensions/filters/http/adaptive_concurrency/controller/BUILD @@ -26,6 +26,7 @@ envoy_cc_library( "//source/common/protobuf", "//source/common/runtime:runtime_lib", "//source/common/stats:isolated_store_lib", + "//source/common/stats:prefix_utility_lib", "//source/common/stats:stats_lib", "@envoy_api//envoy/extensions/filters/http/adaptive_concurrency/v3:pkg_cc_proto", "@libcircllhist", diff --git a/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.cc b/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.cc index 98714d613611b..281a85da837ff 100644 --- a/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.cc +++ b/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.cc @@ -12,6 +12,7 @@ #include "source/common/common/cleanup.h" #include "source/common/protobuf/protobuf.h" #include "source/common/protobuf/utility.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/adaptive_concurrency/controller/controller.h" #include "absl/synchronization/mutex.h" @@ -54,11 +55,12 @@ GradientControllerConfig::GradientControllerConfig( } GradientController::GradientController(GradientControllerConfig config, Event::Dispatcher& dispatcher, Runtime::Loader&, - const std::string& stats_prefix, Stats::Scope& scope, + const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix, Stats::Scope& scope, Random::RandomGenerator& random, TimeSource& time_source) : config_(std::move(config)), dispatcher_(dispatcher), scope_(scope), - stats_(generateStats(scope_, stats_prefix)), random_(random), time_source_(time_source), - deferred_limit_value_(0), num_rq_outstanding_(0), + stats_(generateStats(scope_, parent_stats_prefix, filter_stats_prefix)), random_(random), + time_source_(time_source), deferred_limit_value_(0), num_rq_outstanding_(0), concurrency_limit_(config_.minConcurrency()), latency_sample_hist_(hist_fast_alloc(), hist_free) { min_rtt_calc_timer_ = dispatcher_.createTimer([this]() -> void { enterMinRTTSamplingWindow(); }); @@ -92,9 +94,14 @@ GradientController::GradientController(GradientControllerConfig config, } GradientControllerStats GradientController::generateStats(Stats::Scope& scope, - const std::string& stats_prefix) { - return {ALL_GRADIENT_CONTROLLER_STATS(POOL_COUNTER_PREFIX(scope, stats_prefix), - POOL_GAUGE_PREFIX(scope, stats_prefix))}; + const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix) { + // The parent "http.." prefix is tagged; the filter's own + // "adaptive_concurrency.gradient_controller." segments are untagged literals. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), parent_stats_prefix, filter_stats_prefix); + return {ALL_GRADIENT_CONTROLLER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix), + POOL_GAUGE_TAGGED(scope, stat_prefix))}; } void GradientController::enterMinRTTSamplingWindow() { diff --git a/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.h b/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.h index 501b33319a159..360b8c5f01cbd 100644 --- a/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.h +++ b/source/extensions/filters/http/adaptive_concurrency/controller/gradient_controller.h @@ -217,7 +217,8 @@ using GradientControllerConfigSharedPtr = std::shared_ptr."/"cluster.." prefix is tagged; "admission_control" is the + // filter's own (untagged) segment. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "admission_control."); + return {ALL_ADMISSION_CONTROL_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } bool shouldRejectRequest() const; diff --git a/source/extensions/filters/http/admission_control/config.cc b/source/extensions/filters/http/admission_control/config.cc index 11f8907674487..1accc5c94e7f8 100644 --- a/source/extensions/filters/http/admission_control/config.cc +++ b/source/extensions/filters/http/admission_control/config.cc @@ -42,8 +42,6 @@ absl::StatusOr AdmissionControlFilterFactory::createFilte return absl::InvalidArgumentError("Success rate threshold cannot be less than 1.0%."); } - const std::string prefix = stats_prefix + "admission_control."; - // Create the thread-local controller. auto tls = ThreadLocal::TypedSlot::makeUnique(context.threadLocal()); auto sampling_window = std::chrono::seconds( @@ -71,8 +69,9 @@ absl::StatusOr AdmissionControlFilterFactory::createFilte context.api().randomGenerator(), scope, std::move(tls), std::move(response_evaluator)); - return [filter_config, prefix](Http::FilterChainFactoryCallbacks& callbacks) -> void { - callbacks.addStreamFilter(std::make_shared(filter_config, prefix)); + return [filter_config, stats_prefix](Http::FilterChainFactoryCallbacks& callbacks) -> void { + callbacks.addStreamFilter( + std::make_shared(filter_config, stats_prefix)); }; } diff --git a/source/extensions/filters/http/api_key_auth/BUILD b/source/extensions/filters/http/api_key_auth/BUILD index 40ba158b5e9f2..c7c57b9dd0699 100644 --- a/source/extensions/filters/http/api_key_auth/BUILD +++ b/source/extensions/filters/http/api_key_auth/BUILD @@ -20,6 +20,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:header_utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/api_key_auth/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/api_key_auth/api_key_auth.cc b/source/extensions/filters/http/api_key_auth/api_key_auth.cc index fea3b0bb8450a..981b2dbdcda62 100644 --- a/source/extensions/filters/http/api_key_auth/api_key_auth.cc +++ b/source/extensions/filters/http/api_key_auth/api_key_auth.cc @@ -108,8 +108,7 @@ Forwarding::Forwarding(const ForwardingProto& proto_config) { FilterConfig::FilterConfig(const ApiKeyAuthProto& proto_config, Stats::Scope& scope, const std::string& stats_prefix, absl::Status& creation_status) - : default_config_(proto_config, creation_status), - stats_(generateStats(scope, stats_prefix + "api_key_auth.")) {} + : default_config_(proto_config, creation_status), stats_(generateStats(scope, stats_prefix)) {} ApiKeyAuthFilter::ApiKeyAuthFilter(FilterConfigSharedPtr config) : config_(std::move(config)) {} diff --git a/source/extensions/filters/http/api_key_auth/api_key_auth.h b/source/extensions/filters/http/api_key_auth/api_key_auth.h index 264a251af952b..d665665648528 100644 --- a/source/extensions/filters/http/api_key_auth/api_key_auth.h +++ b/source/extensions/filters/http/api_key_auth/api_key_auth.h @@ -5,6 +5,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "absl/container/flat_hash_map.h" @@ -219,7 +220,9 @@ class FilterConfig { private: static ApiKeyAuthStats generateStats(Stats::Scope& scope, const std::string& prefix) { - return ApiKeyAuthStats{ALL_API_KEY_AUTH_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "api_key_auth."); + return ApiKeyAuthStats{ALL_API_KEY_AUTH_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } const ApiKeyAuthConfig default_config_; diff --git a/source/extensions/filters/http/aws_lambda/BUILD b/source/extensions/filters/http/aws_lambda/BUILD index ab1d64bf01e91..1b514f53ea844 100644 --- a/source/extensions/filters/http/aws_lambda/BUILD +++ b/source/extensions/filters/http/aws_lambda/BUILD @@ -27,6 +27,7 @@ envoy_cc_library( "//envoy/http:filter_interface", "//source/common/common:base64_lib", "//source/common/common:cancel_wrapper_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/common/aws/signers:sigv4_signer_impl_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", ], diff --git a/source/extensions/filters/http/aws_lambda/aws_lambda_filter.cc b/source/extensions/filters/http/aws_lambda/aws_lambda_filter.cc index 01364d5b27b4b..de121a96a932e 100644 --- a/source/extensions/filters/http/aws_lambda/aws_lambda_filter.cc +++ b/source/extensions/filters/http/aws_lambda/aws_lambda_filter.cc @@ -18,6 +18,7 @@ #include "source/common/protobuf/message_validator_impl.h" #include "source/common/protobuf/utility.h" #include "source/common/singleton/const_singleton.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/aws_lambda/request_response.pb.validate.h" #include "absl/strings/numbers.h" @@ -439,9 +440,10 @@ std::optional parseArn(absl::string_view arn) { } FilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = prefix + "aws_lambda."; - return {ALL_AWS_LAMBDA_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix), - POOL_HISTOGRAM_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "aws_lambda."); + return {ALL_AWS_LAMBDA_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix), + POOL_HISTOGRAM_TAGGED(scope, stat_prefix))}; } } // namespace AwsLambdaFilter diff --git a/source/extensions/filters/http/aws_request_signing/BUILD b/source/extensions/filters/http/aws_request_signing/BUILD index 7c2856d103271..79cd9f8b20937 100644 --- a/source/extensions/filters/http/aws_request_signing/BUILD +++ b/source/extensions/filters/http/aws_request_signing/BUILD @@ -19,6 +19,7 @@ envoy_cc_library( deps = [ "//envoy/http:filter_interface", "//source/common/common:cancel_wrapper_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/common/aws:credential_provider_chains_lib", "//source/extensions/common/aws:region_provider_impl_lib", "//source/extensions/common/aws/signers:sigv4_signer_impl_lib", diff --git a/source/extensions/filters/http/aws_request_signing/aws_request_signing_filter.cc b/source/extensions/filters/http/aws_request_signing/aws_request_signing_filter.cc index fb3b0350c1b50..0b598ecbdeda9 100644 --- a/source/extensions/filters/http/aws_request_signing/aws_request_signing_filter.cc +++ b/source/extensions/filters/http/aws_request_signing/aws_request_signing_filter.cc @@ -5,6 +5,7 @@ #include "source/common/common/hex.h" #include "source/common/crypto/utility.h" #include "source/common/http/utility.h" +#include "source/common/stats/prefix_utility.h" namespace Envoy { namespace Extensions { @@ -27,8 +28,9 @@ const std::string& FilterConfigImpl::hostRewrite() const { return host_rewrite_; bool FilterConfigImpl::useUnsignedPayload() const { return use_unsigned_payload_; } FilterStats Filter::generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = prefix + "aws_request_signing."; - return {ALL_AWS_REQUEST_SIGNING_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "aws_request_signing."); + return {ALL_AWS_REQUEST_SIGNING_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, bool end_stream) { diff --git a/source/extensions/filters/http/basic_auth/BUILD b/source/extensions/filters/http/basic_auth/BUILD index 6cc8fdeae0470..834dd8db7f2fe 100644 --- a/source/extensions/filters/http/basic_auth/BUILD +++ b/source/extensions/filters/http/basic_auth/BUILD @@ -21,6 +21,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:header_utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/basic_auth/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/basic_auth/basic_auth_filter.cc b/source/extensions/filters/http/basic_auth/basic_auth_filter.cc index 46555a1a7f795..edadb97c16069 100644 --- a/source/extensions/filters/http/basic_auth/basic_auth_filter.cc +++ b/source/extensions/filters/http/basic_auth/basic_auth_filter.cc @@ -41,7 +41,7 @@ FilterConfig::FilterConfig(UserMap&& users, const std::string& forward_username_ : users_(std::move(users)), forward_username_header_(forward_username_header), authentication_header_(Http::LowerCaseString(authentication_header)), allow_missing_(allow_missing), emit_dynamic_metadata_(emit_dynamic_metadata), - stats_(generateStats(stats_prefix + "basic_auth.", scope)) {} + stats_(generateStats(stats_prefix, scope)) {} BasicAuthFilter::BasicAuthFilter(FilterConfigConstSharedPtr config) : config_(std::move(config)) {} diff --git a/source/extensions/filters/http/basic_auth/basic_auth_filter.h b/source/extensions/filters/http/basic_auth/basic_auth_filter.h index 858fea354a25e..5f9b2df05cd94 100644 --- a/source/extensions/filters/http/basic_auth/basic_auth_filter.h +++ b/source/extensions/filters/http/basic_auth/basic_auth_filter.h @@ -4,6 +4,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "absl/container/flat_hash_map.h" @@ -56,7 +57,9 @@ class FilterConfig { private: static BasicAuthStats generateStats(const std::string& prefix, Stats::Scope& scope) { - return BasicAuthStats{ALL_BASIC_AUTH_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "basic_auth."); + return BasicAuthStats{ALL_BASIC_AUTH_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } const UserMap users_; diff --git a/source/extensions/filters/http/composite/BUILD b/source/extensions/filters/http/composite/BUILD index ccc790873b21d..34807e1828f5b 100644 --- a/source/extensions/filters/http/composite/BUILD +++ b/source/extensions/filters/http/composite/BUILD @@ -51,6 +51,7 @@ envoy_cc_extension( deps = [ "//envoy/registry", "//envoy/server:filter_config_interface", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:factory_base_lib", "//source/extensions/filters/http/composite:filter_lib", "@envoy_api//envoy/extensions/filters/http/composite/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/composite/config.cc b/source/extensions/filters/http/composite/config.cc index d18f6474b36a0..83b7b495eeca2 100644 --- a/source/extensions/filters/http/composite/config.cc +++ b/source/extensions/filters/http/composite/config.cc @@ -4,6 +4,7 @@ #include "envoy/registry/registry.h" #include "source/common/config/utility.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/composite/filter.h" namespace Envoy { @@ -103,9 +104,10 @@ absl::StatusOr CompositeFilterFactory::createFilterFactor match_tree = std::move(match_tree_or_error.value()); } - const auto& prefix = stats_prefix + "composite."; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(context.scope().symbolTable(), stats_prefix, "composite."); auto stats = std::make_shared( - FilterStats{ALL_COMPOSITE_FILTER_STATS(POOL_COUNTER_PREFIX(context.scope(), prefix))}); + FilterStats{ALL_COMPOSITE_FILTER_STATS(POOL_COUNTER_TAGGED(context.scope(), stat_prefix))}); return [stats, named_chains, match_tree](Http::FilterChainFactoryCallbacks& callbacks) -> void { auto filter = std::make_shared(*stats, callbacks.dispatcher(), false /* is_upstream */, @@ -138,9 +140,10 @@ absl::StatusOr CompositeFilterFactory::createFilte // This method is called for upstream filters. // Named filter chains are not supported for upstream filters. - const auto& prefix = stats_prefix + "composite."; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(context.scope().symbolTable(), stats_prefix, "composite."); auto stats = std::make_shared( - FilterStats{ALL_COMPOSITE_FILTER_STATS(POOL_COUNTER_PREFIX(context.scope(), prefix))}); + FilterStats{ALL_COMPOSITE_FILTER_STATS(POOL_COUNTER_TAGGED(context.scope(), stat_prefix))}); return [stats, match_tree](Http::FilterChainFactoryCallbacks& callbacks) -> void { auto filter = std::make_shared(*stats, callbacks.dispatcher(), true /* is_upstream */, diff --git a/source/extensions/filters/http/compressor/BUILD b/source/extensions/filters/http/compressor/BUILD index 218d64538f960..057c9fef98642 100644 --- a/source/extensions/filters/http/compressor/BUILD +++ b/source/extensions/filters/http/compressor/BUILD @@ -23,6 +23,7 @@ envoy_cc_library( "//envoy/stats:stats_macros", "//source/common/config:utility_lib", "//source/common/runtime:runtime_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/compressor/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/compressor/compressor_filter.cc b/source/extensions/filters/http/compressor/compressor_filter.cc index 3c49b5844b629..9524ec520f515 100644 --- a/source/extensions/filters/http/compressor/compressor_filter.cc +++ b/source/extensions/filters/http/compressor/compressor_filter.cc @@ -91,21 +91,21 @@ void compressAndUpdateStats(const Compression::Compressor::CompressorPtr& compre CompressorFilterConfig::DirectionConfig::DirectionConfig( const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime) : compression_enabled_(proto_config.enabled(), runtime), min_content_length_{contentLengthUint(proto_config.min_content_length().value())}, content_type_values_(contentTypeSet(proto_config.content_type())), - stats_{generateStats(stats_prefix, scope)} {} + stats_{generateStats(parent_stats_prefix, filter_stats_prefix, scope)} {} CompressorFilterConfig::CompressorFilterConfig( const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime, Compression::Compressor::CompressorFactoryPtr compressor_factory) - : common_stats_prefix_(fmt::format("{}compressor.{}.{}", stats_prefix, - proto_config.compressor_library().name(), + : filter_stats_prefix_(fmt::format("compressor.{}.{}", proto_config.compressor_library().name(), compressor_factory->statsPrefix())), - request_direction_config_(proto_config, common_stats_prefix_, scope, runtime), - response_direction_config_(proto_config, common_stats_prefix_, scope, runtime), + request_direction_config_(proto_config, stats_prefix, filter_stats_prefix_, scope, runtime), + response_direction_config_(proto_config, stats_prefix, filter_stats_prefix_, scope, runtime), content_encoding_(compressor_factory->contentEncoding()), compressor_factory_(std::move(compressor_factory)), choose_first_(proto_config.choose_first()) {} @@ -124,9 +124,10 @@ uint32_t CompressorFilterConfig::DirectionConfig::contentLengthUint(Protobuf::ui CompressorFilterConfig::RequestDirectionConfig::RequestDirectionConfig( const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : DirectionConfig(proto_config.request_direction_config().common_config(), - stats_prefix + "request.", scope, runtime), + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime) + : DirectionConfig(proto_config.request_direction_config().common_config(), parent_stats_prefix, + filter_stats_prefix + "request.", scope, runtime), is_set_{proto_config.has_request_direction_config()} {} absl::flat_hash_set @@ -136,10 +137,12 @@ uncompressibleResponseCodesSet(const Protobuf::RepeatedField& codes) { CompressorFilterConfig::ResponseDirectionConfig::ResponseDirectionConfig( const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : DirectionConfig(commonConfig(proto_config), - proto_config.has_response_direction_config() ? stats_prefix + "response." - : stats_prefix, + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime) + : DirectionConfig(commonConfig(proto_config), parent_stats_prefix, + proto_config.has_response_direction_config() + ? filter_stats_prefix + "response." + : filter_stats_prefix, scope, runtime), disable_on_etag_header_( proto_config.has_response_direction_config() @@ -153,7 +156,7 @@ CompressorFilterConfig::ResponseDirectionConfig::ResponseDirectionConfig( status_header_enabled_(proto_config.response_direction_config().status_header_enabled()), uncompressible_response_codes_(uncompressibleResponseCodesSet( proto_config.response_direction_config().uncompressible_response_codes())), - response_stats_{generateResponseStats(stats_prefix, scope)} {} + response_stats_{generateResponseStats(parent_stats_prefix, filter_stats_prefix, scope)} {} const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig CompressorFilterConfig::ResponseDirectionConfig::commonConfig( diff --git a/source/extensions/filters/http/compressor/compressor_filter.h b/source/extensions/filters/http/compressor/compressor_filter.h index b6bb86ef463fc..1ae786dc3352f 100644 --- a/source/extensions/filters/http/compressor/compressor_filter.h +++ b/source/extensions/filters/http/compressor/compressor_filter.h @@ -10,6 +10,7 @@ #include "source/common/common/logger.h" #include "source/common/protobuf/protobuf.h" #include "source/common/runtime/runtime_protos.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" namespace Envoy { @@ -68,7 +69,8 @@ class CompressorFilterConfig { DirectionConfig( const envoy::extensions::filters::http::compressor::v3::Compressor::CommonDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime); virtual ~DirectionConfig() = default; @@ -84,8 +86,14 @@ class CompressorFilterConfig { const Runtime::FeatureFlag compression_enabled_; private: - static CompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) { - return CompressorStats{COMMON_COMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + static CompressorStats generateStats(const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix, + Stats::Scope& scope) { + // The parent "http.."/"cluster.." prefix is tagged; the filter's own + // "compressor..[.request|.response]" segments are untagged literals. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), parent_stats_prefix, filter_stats_prefix); + return CompressorStats{COMMON_COMPRESSOR_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } static uint32_t contentLengthUint(Protobuf::uint32 length); @@ -102,7 +110,8 @@ class CompressorFilterConfig { public: RequestDirectionConfig( const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime); bool compressionEnabled() const override { return is_set_ && compression_enabled_.enabled(); } @@ -114,7 +123,8 @@ class CompressorFilterConfig { public: ResponseDirectionConfig( const envoy::extensions::filters::http::compressor::v3::Compressor& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime); bool compressionEnabled() const override { return compression_enabled_.enabled(); } const ResponseCompressorStats& responseStats() const { return response_stats_; } @@ -126,9 +136,13 @@ class CompressorFilterConfig { bool isResponseCodeCompressible(uint32_t response_code) const; private: - static ResponseCompressorStats generateResponseStats(const std::string& prefix, + static ResponseCompressorStats generateResponseStats(const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix, Stats::Scope& scope) { - return ResponseCompressorStats{RESPONSE_COMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), parent_stats_prefix, filter_stats_prefix); + return ResponseCompressorStats{ + RESPONSE_COMPRESSOR_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } // TODO(rojkov): delete this translation function once the deprecated fields @@ -161,7 +175,9 @@ class CompressorFilterConfig { } private: - const std::string common_stats_prefix_; + // The filter's own stat-prefix segments ("compressor.."), tag-free; the parent + // "http.."/"cluster.." prefix is kept separate and tagged by mergeStatPrefix. + const std::string filter_stats_prefix_; const RequestDirectionConfig request_direction_config_; const ResponseDirectionConfig response_direction_config_; diff --git a/source/extensions/filters/http/cors/BUILD b/source/extensions/filters/http/cors/BUILD index a2952ad422e15..0ba1861626512 100644 --- a/source/extensions/filters/http/cors/BUILD +++ b/source/extensions/filters/http/cors/BUILD @@ -25,6 +25,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@abseil-cpp//absl/container:inlined_vector", ], diff --git a/source/extensions/filters/http/cors/cors_filter.cc b/source/extensions/filters/http/cors/cors_filter.cc index 3335423f4bf40..8942edec6fe7d 100644 --- a/source/extensions/filters/http/cors/cors_filter.cc +++ b/source/extensions/filters/http/cors/cors_filter.cc @@ -49,7 +49,7 @@ Http::RegisterCustomInlineHeadercreateCredentialInjectorFromProto( *message, stats_prefix + "credential_injector.", context, init_manager); - FilterConfigSharedPtr config = - std::make_shared(std::move(credential_injector), proto_config.overwrite(), - proto_config.allow_request_without_credential(), - stats_prefix + "credential_injector.", scope); + FilterConfigSharedPtr config = std::make_shared( + std::move(credential_injector), proto_config.overwrite(), + proto_config.allow_request_without_credential(), stats_prefix, scope); return [config](Envoy::Http::FilterChainFactoryCallbacks& callbacks) -> void { callbacks.addStreamDecoderFilter(std::make_shared(config)); }; diff --git a/source/extensions/filters/http/credential_injector/credential_injector_filter.h b/source/extensions/filters/http/credential_injector/credential_injector_filter.h index f5e1a238dc1e5..321aac961c14d 100644 --- a/source/extensions/filters/http/credential_injector/credential_injector_filter.h +++ b/source/extensions/filters/http/credential_injector/credential_injector_filter.h @@ -3,6 +3,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/common/logger.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "source/extensions/http/injected_credentials/common/credential.h" @@ -46,8 +47,10 @@ class FilterConfig : public Logger::Loggable { private: static CredentialInjectorStats generateStats(const std::string& prefix, Stats::Scope& scope) { + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "credential_injector."); return CredentialInjectorStats{ - ALL_CREDENTIAL_INJECTOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + ALL_CREDENTIAL_INJECTOR_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } CredentialInjectorSharedPtr injector_; diff --git a/source/extensions/filters/http/csrf/BUILD b/source/extensions/filters/http/csrf/BUILD index 48889ab7d9dd1..9ead64b67bca5 100644 --- a/source/extensions/filters/http/csrf/BUILD +++ b/source/extensions/filters/http/csrf/BUILD @@ -23,6 +23,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", + "//source/common/stats:prefix_utility_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/http/csrf/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/csrf/csrf_filter.cc b/source/extensions/filters/http/csrf/csrf_filter.cc index b21ee029eb431..51309782d1636 100644 --- a/source/extensions/filters/http/csrf/csrf_filter.cc +++ b/source/extensions/filters/http/csrf/csrf_filter.cc @@ -7,6 +7,7 @@ #include "source/common/http/header_map_impl.h" #include "source/common/http/headers.h" #include "source/common/http/utility.h" +#include "source/common/stats/prefix_utility.h" namespace Envoy { namespace Extensions { @@ -76,8 +77,8 @@ std::string targetOriginValue(const Http::RequestHeaderMap& headers) { } static CsrfStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = prefix + "csrf."; - return CsrfStats{ALL_CSRF_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, "csrf."); + return CsrfStats{ALL_CSRF_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } static CsrfPolicyPtr diff --git a/source/extensions/filters/http/decompressor/BUILD b/source/extensions/filters/http/decompressor/BUILD index a30d31b3c1fba..f173897904ba8 100644 --- a/source/extensions/filters/http/decompressor/BUILD +++ b/source/extensions/filters/http/decompressor/BUILD @@ -24,6 +24,7 @@ envoy_cc_library( "//source/common/common:macros", "//source/common/http:headers_lib", "//source/common/runtime:runtime_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/decompressor/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/decompressor/decompressor_filter.cc b/source/extensions/filters/http/decompressor/decompressor_filter.cc index 5f4bb2ac61134..501f136d5c26e 100644 --- a/source/extensions/filters/http/decompressor/decompressor_filter.cc +++ b/source/extensions/filters/http/decompressor/decompressor_filter.cc @@ -24,40 +24,46 @@ DecompressorFilterConfig::DecompressorFilterConfig( const envoy::extensions::filters::http::decompressor::v3::Decompressor& proto_config, const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime, Compression::Decompressor::DecompressorFactoryPtr decompressor_factory) - : stats_prefix_(fmt::format("{}decompressor.{}.{}", stats_prefix, - proto_config.decompressor_library().name(), - decompressor_factory->statsPrefix())), + : filter_stats_prefix_(fmt::format("decompressor.{}.{}", + proto_config.decompressor_library().name(), + decompressor_factory->statsPrefix())), trailers_prefix_(fmt::format("{}-decompressor-{}", ThreadSafeSingleton::get().prefix(), proto_config.decompressor_library().name())), - decompressor_stats_prefix_(stats_prefix_ + "decompressor_library"), + decompressor_stats_prefix_( + fmt::format("{}{}decompressor_library", stats_prefix, filter_stats_prefix_)), decompressor_factory_(std::move(decompressor_factory)), - request_direction_config_(proto_config.request_direction_config(), stats_prefix_, scope, - runtime), - response_direction_config_(proto_config.response_direction_config(), stats_prefix_, scope, - runtime) {} + request_direction_config_(proto_config.request_direction_config(), stats_prefix, + filter_stats_prefix_, scope, runtime), + response_direction_config_(proto_config.response_direction_config(), stats_prefix, + filter_stats_prefix_, scope, runtime) {} DecompressorFilterConfig::DirectionConfig::DirectionConfig( const envoy::extensions::filters::http::decompressor::v3::Decompressor::CommonDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : stats_(generateStats(stats_prefix, scope)), + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime) + : stats_(generateStats(parent_stats_prefix, filter_stats_prefix, scope)), decompression_enabled_(proto_config.enabled(), runtime), ignore_no_transform_header_(proto_config.ignore_no_transform_header()) {} DecompressorFilterConfig::RequestDirectionConfig::RequestDirectionConfig( const envoy::extensions::filters::http::decompressor::v3::Decompressor::RequestDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : DirectionConfig(proto_config.common_config(), stats_prefix + "request.", scope, runtime), + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime) + : DirectionConfig(proto_config.common_config(), parent_stats_prefix, + filter_stats_prefix + "request.", scope, runtime), advertise_accept_encoding_( PROTOBUF_GET_WRAPPED_OR_DEFAULT(proto_config, advertise_accept_encoding, true)) {} DecompressorFilterConfig::ResponseDirectionConfig::ResponseDirectionConfig( const envoy::extensions::filters::http::decompressor::v3::Decompressor::ResponseDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime) - : DirectionConfig(proto_config.common_config(), stats_prefix + "response.", scope, runtime) {} + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime) + : DirectionConfig(proto_config.common_config(), parent_stats_prefix, + filter_stats_prefix + "response.", scope, runtime) {} DecompressorFilter::DecompressorFilter(DecompressorFilterConfigSharedPtr config) : config_(std::move(config)), request_byte_tracker_(config_->trailersCompressedBytesString(), diff --git a/source/extensions/filters/http/decompressor/decompressor_filter.h b/source/extensions/filters/http/decompressor/decompressor_filter.h index 75a84b6bc54fe..4bed341bb3272 100644 --- a/source/extensions/filters/http/decompressor/decompressor_filter.h +++ b/source/extensions/filters/http/decompressor/decompressor_filter.h @@ -9,6 +9,7 @@ #include "source/common/http/header_utility.h" #include "source/common/http/headers.h" #include "source/common/runtime/runtime_protos.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" namespace Envoy { @@ -41,7 +42,8 @@ class DecompressorFilterConfig { public: DirectionConfig(const envoy::extensions::filters::http::decompressor::v3::Decompressor:: CommonDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); + const std::string& parent_stats_prefix, const std::string& filter_stats_prefix, + Stats::Scope& scope, Runtime::Loader& runtime); virtual ~DirectionConfig() = default; @@ -51,8 +53,14 @@ class DecompressorFilterConfig { bool ignoreNoTransformHeader() const { return ignore_no_transform_header_; } private: - static DecompressorStats generateStats(const std::string& prefix, Stats::Scope& scope) { - return DecompressorStats{ALL_DECOMPRESSOR_STATS(POOL_COUNTER_PREFIX(scope, prefix))}; + static DecompressorStats generateStats(const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix, + Stats::Scope& scope) { + // The parent "http.."/"cluster.." prefix is tagged; the filter's own + // "decompressor..." segments are untagged literals. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), parent_stats_prefix, filter_stats_prefix); + return DecompressorStats{ALL_DECOMPRESSOR_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } const DecompressorStats stats_; @@ -64,7 +72,8 @@ class DecompressorFilterConfig { public: RequestDirectionConfig(const envoy::extensions::filters::http::decompressor::v3::Decompressor:: RequestDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, + const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); // DirectionConfig @@ -82,7 +91,8 @@ class DecompressorFilterConfig { public: ResponseDirectionConfig(const envoy::extensions::filters::http::decompressor::v3::Decompressor:: ResponseDirectionConfig& proto_config, - const std::string& stats_prefix, Stats::Scope& scope, + const std::string& parent_stats_prefix, + const std::string& filter_stats_prefix, Stats::Scope& scope, Runtime::Loader& runtime); // DirectionConfig @@ -112,7 +122,9 @@ class DecompressorFilterConfig { } private: - const std::string stats_prefix_; + // The filter's own stat-prefix segments ("decompressor.."), tag-free; the parent + // "http.."/"cluster.." prefix is kept separate and tagged by mergeStatPrefix. + const std::string filter_stats_prefix_; const std::string trailers_prefix_; const std::string decompressor_stats_prefix_; const Compression::Decompressor::DecompressorFactoryPtr decompressor_factory_; diff --git a/source/extensions/filters/http/ext_authz/BUILD b/source/extensions/filters/http/ext_authz/BUILD index 37f7d13dfc410..a57cbdc5595df 100644 --- a/source/extensions/filters/http/ext_authz/BUILD +++ b/source/extensions/filters/http/ext_authz/BUILD @@ -25,10 +25,12 @@ envoy_cc_library( "//source/common/common:enum_to_int", "//source/common/common:matchers_lib", "//source/common/common:minimal_logger_lib", + "//source/common/config:well_known_names", "//source/common/http:codes_lib", "//source/common/http:utility_lib", "//source/common/router:config_lib", "//source/common/runtime:runtime_protos_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/common/ext_authz:ext_authz_grpc_lib", "//source/extensions/filters/common/ext_authz:ext_authz_http_lib", "//source/extensions/filters/common/mutation_rules:mutation_rules_lib", diff --git a/source/extensions/filters/http/ext_authz/ext_authz.h b/source/extensions/filters/http/ext_authz/ext_authz.h index a7231feabd82b..24d0f73fe9a29 100644 --- a/source/extensions/filters/http/ext_authz/ext_authz.h +++ b/source/extensions/filters/http/ext_authz/ext_authz.h @@ -19,10 +19,12 @@ #include "source/common/common/logger.h" #include "source/common/common/matchers.h" #include "source/common/common/utility.h" +#include "source/common/config/well_known_names.h" #include "source/common/grpc/typed_async_client.h" #include "source/common/http/codes.h" #include "source/common/http/header_map_impl.h" #include "source/common/runtime/runtime_protos.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/common/ext_authz/check_request_utils.h" #include "source/extensions/filters/common/ext_authz/ext_authz.h" #include "source/extensions/filters/common/ext_authz/ext_authz_grpc_impl.h" @@ -298,8 +300,17 @@ class FilterConfig { ExtAuthzFilterStats generateStats(const std::string& prefix, const std::string& filter_stats_prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "ext_authz.", filter_stats_prefix); - return {ALL_EXT_AUTHZ_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // The parent "http.."/"cluster.." prefix is tagged by the helper; the optional + // filter_stats_prefix is this filter's own EXT_AUTHZ_PREFIX tag (well_known_names.cc: + // http.*.ext_authz.$.**). + Stats::TaggedStatName stat_prefix = + filter_stats_prefix.empty() + ? Stats::mergeStatPrefix(scope.symbolTable(), prefix, "ext_authz.") + : Stats::mergeStatPrefix( + scope.symbolTable(), prefix, "ext_authz.", + {{Envoy::Config::TagNames::get().EXT_AUTHZ_PREFIX, filter_stats_prefix}}, + absl::StrCat("ext_authz.", filter_stats_prefix, ".")); + return {ALL_EXT_AUTHZ_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } // This generates ext_authz..name, for example: ext_authz.waf.ok diff --git a/source/extensions/filters/http/ext_proc/BUILD b/source/extensions/filters/http/ext_proc/BUILD index ffd5d70e65b0d..fe769a7b7c73f 100644 --- a/source/extensions/filters/http/ext_proc/BUILD +++ b/source/extensions/filters/http/ext_proc/BUILD @@ -36,6 +36,7 @@ envoy_cc_library( "//source/common/protobuf", "//source/common/protobuf:utility_lib", "//source/common/runtime:runtime_features_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/common/mutation_rules:mutation_rules_lib", "//source/extensions/filters/common/processing_effect:processing_effect_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", diff --git a/source/extensions/filters/http/ext_proc/ext_proc.h b/source/extensions/filters/http/ext_proc/ext_proc.h index a2efc42b88486..182a908060ca2 100644 --- a/source/extensions/filters/http/ext_proc/ext_proc.h +++ b/source/extensions/filters/http/ext_proc/ext_proc.h @@ -19,6 +19,7 @@ #include "source/common/common/logger.h" #include "source/common/common/matchers.h" #include "source/common/protobuf/protobuf.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/common/ext_proc/client_base.h" #include "source/extensions/filters/common/mutation_rules/mutation_rules.h" #include "source/extensions/filters/common/processing_effect/processing_effect.h" @@ -355,8 +356,14 @@ class FilterConfig { ExtProcFilterStats generateStats(const std::string& prefix, const std::string& filter_stats_prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "ext_proc.", filter_stats_prefix); - return {ALL_EXT_PROC_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // No ext_proc tag exists in well_known_names.cc, so filter_stats_prefix stays an untagged + // literal; only the parent "http.." prefix is tagged (by the helper). + Stats::TaggedStatName stat_prefix = + filter_stats_prefix.empty() + ? Stats::mergeStatPrefix(scope.symbolTable(), prefix, "ext_proc.") + : Stats::mergeStatPrefix(scope.symbolTable(), prefix, + absl::StrCat("ext_proc.", filter_stats_prefix, ".")); + return {ALL_EXT_PROC_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } static std::function()> createOnProcessingResponseCb( const envoy::extensions::filters::http::ext_proc::v3::ExternalProcessor& config, diff --git a/source/extensions/filters/http/fault/BUILD b/source/extensions/filters/http/fault/BUILD index a62c905096564..2599f4ef79c1b 100644 --- a/source/extensions/filters/http/fault/BUILD +++ b/source/extensions/filters/http/fault/BUILD @@ -34,6 +34,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/common/stats:utility_lib", "//source/extensions/filters/common/fault:fault_config_lib", "//source/extensions/filters/http/common:stream_rate_limiter_lib", diff --git a/source/extensions/filters/http/fault/fault_filter.cc b/source/extensions/filters/http/fault/fault_filter.cc index c37b14f2baba1..162218ca450f6 100644 --- a/source/extensions/filters/http/fault/fault_filter.cc +++ b/source/extensions/filters/http/fault/fault_filter.cc @@ -20,6 +20,7 @@ #include "source/common/http/headers.h" #include "source/common/http/utility.h" #include "source/common/protobuf/utility.h" +#include "source/common/stats/prefix_utility.h" #include "source/common/stats/utility.h" namespace Envoy { @@ -402,9 +403,9 @@ Http::FilterTrailersStatus FaultFilter::decodeTrailers(Http::RequestTrailerMap&) } FaultFilterStats FaultFilterConfig::generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = prefix + "fault."; - return {ALL_FAULT_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix), - POOL_GAUGE_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, "fault."); + return {ALL_FAULT_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix), + POOL_GAUGE_TAGGED(scope, stat_prefix))}; } bool FaultFilter::tryIncActiveFaults() { diff --git a/source/extensions/filters/http/filter_chain/BUILD b/source/extensions/filters/http/filter_chain/BUILD index 6510e9dbe4995..794382b4a1555 100644 --- a/source/extensions/filters/http/filter_chain/BUILD +++ b/source/extensions/filters/http/filter_chain/BUILD @@ -22,6 +22,7 @@ envoy_cc_library( "//source/common/config:utility_lib", "//source/common/http:filter_chain_helper_lib", "//source/common/http:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/filter_chain/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/filter_chain/filter.h b/source/extensions/filters/http/filter_chain/filter.h index c8e1bfb239dac..af6335b8e571e 100644 --- a/source/extensions/filters/http/filter_chain/filter.h +++ b/source/extensions/filters/http/filter_chain/filter.h @@ -10,6 +10,7 @@ #include "envoy/stats/stats_macros.h" #include "source/common/http/filter_chain_helper.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" namespace Envoy { @@ -86,8 +87,9 @@ class FilterChainConfig { private: static FilterChainStats createStats(const std::string& stats_prefix, Stats::Scope& scope) { - const std::string final_prefix = fmt::format("{}filter_chain.", stats_prefix); - return {COMMON_FILTER_CHAIN_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), stats_prefix, "filter_chain."); + return {COMMON_FILTER_CHAIN_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } FilterChainConstSharedPtr default_filter_chain_; diff --git a/source/extensions/filters/http/gcp_authn/BUILD b/source/extensions/filters/http/gcp_authn/BUILD index e8e026e5a00cb..50d4ea1d2baea 100644 --- a/source/extensions/filters/http/gcp_authn/BUILD +++ b/source/extensions/filters/http/gcp_authn/BUILD @@ -21,6 +21,7 @@ envoy_cc_library( "//source/common/http:message_lib", "//source/common/http:utility_lib", "//source/common/runtime:runtime_features_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:factory_base_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/gcp_authn/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/gcp_authn/gcp_authn_filter.h b/source/extensions/filters/http/gcp_authn/gcp_authn_filter.h index 2508c8875791a..15250ac2abf12 100644 --- a/source/extensions/filters/http/gcp_authn/gcp_authn_filter.h +++ b/source/extensions/filters/http/gcp_authn/gcp_authn_filter.h @@ -5,6 +5,7 @@ #include "envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.pb.h" #include "envoy/extensions/filters/http/gcp_authn/v3/gcp_authn.pb.validate.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "source/extensions/filters/http/gcp_authn/crypto_utils.h" #include "source/extensions/filters/http/gcp_authn/gcp_authn_client.h" @@ -73,7 +74,11 @@ class GcpAuthnFilter : public Http::PassThroughFilter, std::optional getClientCertFingerprint(Upstream::ThreadLocalCluster* cluster); GcpAuthnFilterStats generateStats(const std::string& stats_prefix, Stats::Scope& scope) { - return {ALL_GCP_AUTHN_FILTER_STATS(POOL_COUNTER_PREFIX(scope, stats_prefix))}; + // gcp_authn passes the parent "http.." prefix straight through (its stat names carry no + // filter segment), so only the parent tag is extracted. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), stats_prefix, ""); + return {ALL_GCP_AUTHN_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } FilterConfigSharedPtr filter_config_; diff --git a/source/extensions/filters/http/geoip/BUILD b/source/extensions/filters/http/geoip/BUILD index 7d47565f71d1c..6518658dbd9d9 100644 --- a/source/extensions/filters/http/geoip/BUILD +++ b/source/extensions/filters/http/geoip/BUILD @@ -25,7 +25,9 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/http:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/common/stats:symbol_table_lib", + "//source/common/stats:utility_lib", "@envoy_api//envoy/extensions/filters/http/geoip/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/geoip/geoip_filter.cc b/source/extensions/filters/http/geoip/geoip_filter.cc index 17300c3eb5756..8f5adef8e1c37 100644 --- a/source/extensions/filters/http/geoip/geoip_filter.cc +++ b/source/extensions/filters/http/geoip/geoip_filter.cc @@ -4,6 +4,7 @@ #include "source/common/http/utility.h" #include "source/common/network/utility.h" +#include "source/common/stats/prefix_utility.h" #include "absl/memory/memory.h" @@ -16,7 +17,8 @@ GeoipFilterConfig::GeoipFilterConfig( const envoy::extensions::filters::http::geoip::v3::Geoip& config, const std::string& stat_prefix, Stats::Scope& scope) : scope_(scope), stat_name_set_(scope.symbolTable().makeSet("Geoip")), - stats_prefix_(stat_name_set_->add(stat_prefix + "geoip")), use_xff_(config.has_xff_config()), + stats_prefix_(Stats::mergeStatPrefix(scope.symbolTable(), stat_prefix, "geoip.")), + use_xff_(config.has_xff_config()), xff_num_trusted_hops_(config.has_xff_config() ? config.xff_config().xff_num_trusted_hops() : 0), ip_address_header_(config.has_custom_header_config() @@ -27,8 +29,9 @@ GeoipFilterConfig::GeoipFilterConfig( } void GeoipFilterConfig::incCounter(Stats::StatName name) { - Stats::SymbolTable::StoragePtr storage = scope_.symbolTable().join({stats_prefix_, name}); - scope_.counterFromStatName(Stats::StatName(storage.get())).inc(); + Stats::Utility::counterFromTaggedPrefix(scope_, stats_prefix_.baseName(), + stats_prefix_.nameTags(), stats_prefix_.name(), name) + .inc(); } GeoipFilter::GeoipFilter(GeoipFilterConfigSharedPtr config, Geolocation::DriverSharedPtr driver) diff --git a/source/extensions/filters/http/geoip/geoip_filter.h b/source/extensions/filters/http/geoip/geoip_filter.h index 927ff468aa60a..90a8e631dd0cc 100644 --- a/source/extensions/filters/http/geoip/geoip_filter.h +++ b/source/extensions/filters/http/geoip/geoip_filter.h @@ -8,6 +8,8 @@ #include "envoy/http/header_map.h" #include "envoy/stats/scope.h" +#include "source/common/stats/utility.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -34,7 +36,7 @@ class GeoipFilterConfig { Stats::Scope& scope_; Stats::StatNameSetPtr stat_name_set_; - const Stats::StatName stats_prefix_; + const Stats::TaggedStatName stats_prefix_; const Stats::StatName unknown_hit_; bool use_xff_; const uint32_t xff_num_trusted_hops_; diff --git a/source/extensions/filters/http/grpc_json_transcoder/BUILD b/source/extensions/filters/http/grpc_json_transcoder/BUILD index fa88fe00dbb1f..9fd6d72466160 100644 --- a/source/extensions/filters/http/grpc_json_transcoder/BUILD +++ b/source/extensions/filters/http/grpc_json_transcoder/BUILD @@ -34,6 +34,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/protobuf", "//source/common/runtime:runtime_features_lib", + "//source/common/stats:prefix_utility_lib", "@com_google_googleapis//google/api:http_cc_proto", "@envoy_api//envoy/extensions/filters/http/grpc_json_transcoder/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/grpc_json_transcoder/stats.h b/source/extensions/filters/http/grpc_json_transcoder/stats.h index d9e205ae9e1ea..8f178bb4bcfd1 100644 --- a/source/extensions/filters/http/grpc_json_transcoder/stats.h +++ b/source/extensions/filters/http/grpc_json_transcoder/stats.h @@ -5,6 +5,8 @@ #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" +#include "source/common/stats/prefix_utility.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -26,9 +28,12 @@ struct GrpcJsonTranscoderFilterStats { static GrpcJsonTranscoderFilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { + // The filter's stats sit directly under the "http.." parent prefix (no filter-name + // segment), so the whole prefix is the parent and the filter's own prefix is empty. + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, ""); return GrpcJsonTranscoderFilterStats{ALL_GRPC_JSON_TRANSCODER_FILTER_STATS( - POOL_COUNTER_PREFIX(scope, prefix), POOL_GAUGE_PREFIX(scope, prefix), - POOL_HISTOGRAM_PREFIX(scope, prefix))}; + POOL_COUNTER_TAGGED(scope, stat_prefix), POOL_GAUGE_TAGGED(scope, stat_prefix), + POOL_HISTOGRAM_TAGGED(scope, stat_prefix))}; } }; diff --git a/source/extensions/filters/http/health_check/BUILD b/source/extensions/filters/http/health_check/BUILD index 619cd53c03297..609e61d3d791d 100644 --- a/source/extensions/filters/http/health_check/BUILD +++ b/source/extensions/filters/http/health_check/BUILD @@ -30,6 +30,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/http:utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", ], ) diff --git a/source/extensions/filters/http/health_check/health_check.h b/source/extensions/filters/http/health_check/health_check.h index 0caca322e475f..474d240a5917b 100644 --- a/source/extensions/filters/http/health_check/health_check.h +++ b/source/extensions/filters/http/health_check/health_check.h @@ -13,6 +13,7 @@ #include "source/common/common/assert.h" #include "source/common/http/header_utility.h" +#include "source/common/stats/prefix_utility.h" namespace Envoy { namespace Extensions { @@ -39,8 +40,9 @@ struct HealthCheckFilterStats { ALL_HEALTH_CHECK_FILTER_STATS(GENERATE_COUNTER_STRUCT) static HealthCheckFilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "health_check."); - return {ALL_HEALTH_CHECK_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "health_check."); + return {ALL_HEALTH_CHECK_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } }; diff --git a/source/extensions/filters/http/ip_tagging/BUILD b/source/extensions/filters/http/ip_tagging/BUILD index a60ad1b337861..94b8e403fe41b 100644 --- a/source/extensions/filters/http/ip_tagging/BUILD +++ b/source/extensions/filters/http/ip_tagging/BUILD @@ -28,7 +28,9 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:headers_lib", "//source/common/network:lc_trie_lib", + "//source/common/stats:prefix_utility_lib", "//source/common/stats:symbol_table_lib", + "//source/common/stats:utility_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/http/ip_tagging/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc index bc3535f73230e..68af41e881c16 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc @@ -6,6 +6,7 @@ #include "source/common/config/datasource.h" #include "source/common/http/header_map_impl.h" #include "source/common/http/headers.h" +#include "source/common/stats/prefix_utility.h" #include "absl/strings/str_join.h" @@ -16,7 +17,7 @@ namespace IpTagging { IpTagsStats::IpTagsStats(const std::string& stat_prefix, Stats::ScopeSharedPtr scope) : scope_(std::move(scope)), stat_name_set_(scope_->symbolTable().makeSet("IpTagging")), - stats_prefix_(stat_name_set_->add(stat_prefix + "ip_tagging")), + stats_prefix_(Stats::mergeStatPrefix(scope_->symbolTable(), stat_prefix, "ip_tagging.")), unknown_tag_(stat_name_set_->add("unknown_tag.hit")), total_(stat_name_set_->add("total")), no_hit_(stat_name_set_->add("no_hit")), reload_success_(stat_name_set_->add("reload_success")) {} @@ -26,8 +27,9 @@ void IpTagsStats::incHit(absl::string_view tag) { } void IpTagsStats::incCounter(Stats::StatName name) { - Stats::SymbolTable::StoragePtr storage = scope_->symbolTable().join({stats_prefix_, name}); - scope_->counterFromStatName(Stats::StatName(storage.get())).inc(); + Stats::Utility::counterFromTaggedPrefix(*scope_, stats_prefix_.baseName(), + stats_prefix_.nameTags(), stats_prefix_.name(), name) + .inc(); } absl::StatusOr IpTagsStats::parseIpTagsAsProto( diff --git a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h index 2c1531da1bf75..9ed5755842751 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.h @@ -21,6 +21,7 @@ #include "source/common/network/cidr_range.h" #include "source/common/network/lc_trie.h" #include "source/common/stats/symbol_table.h" +#include "source/common/stats/utility.h" namespace Envoy { namespace Extensions { @@ -61,7 +62,7 @@ class IpTagsStats : public Logger::Loggable { // them goes away (relevant for providers shared across listeners). Stats::ScopeSharedPtr scope_; Stats::StatNameSetPtr stat_name_set_; - const Stats::StatName stats_prefix_; + const Stats::TaggedStatName stats_prefix_; const Stats::StatName unknown_tag_; const Stats::StatName total_; const Stats::StatName no_hit_; diff --git a/source/extensions/filters/http/jwt_authn/BUILD b/source/extensions/filters/http/jwt_authn/BUILD index 77fb16012ea76..e6fe94b178404 100644 --- a/source/extensions/filters/http/jwt_authn/BUILD +++ b/source/extensions/filters/http/jwt_authn/BUILD @@ -138,6 +138,7 @@ envoy_cc_library( "//envoy/server:filter_config_interface", "//envoy/stats:stats_macros", "//envoy/thread_local:thread_local_interface", + "//source/common/stats:prefix_utility_lib", "@envoy_api//envoy/extensions/filters/http/jwt_authn/v3:pkg_cc_proto", ], ) diff --git a/source/extensions/filters/http/jwt_authn/filter_config.h b/source/extensions/filters/http/jwt_authn/filter_config.h index d31ac5506828f..c6f1999ec5e8f 100644 --- a/source/extensions/filters/http/jwt_authn/filter_config.h +++ b/source/extensions/filters/http/jwt_authn/filter_config.h @@ -7,6 +7,7 @@ #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/jwt_authn/matcher.h" #include "source/extensions/filters/http/jwt_authn/stats.h" #include "source/extensions/filters/http/jwt_authn/verifier.h" @@ -118,8 +119,14 @@ class FilterConfigImpl : public Logger::Loggable, private: JwtAuthnFilterStats generateStats(const std::string& prefix, const std::string& filter_stats_prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "jwt_authn.", filter_stats_prefix); - return {ALL_JWT_AUTHN_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // No jwt_authn tag exists in well_known_names.cc, so filter_stats_prefix stays an untagged + // literal; only the parent "http.." prefix is tagged (by the helper). + Stats::TaggedStatName stat_prefix = + filter_stats_prefix.empty() + ? Stats::mergeStatPrefix(scope.symbolTable(), prefix, "jwt_authn.") + : Stats::mergeStatPrefix(scope.symbolTable(), prefix, + absl::StrCat("jwt_authn.", filter_stats_prefix, ".")); + return {ALL_JWT_AUTHN_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } struct MatcherVerifierPair { diff --git a/source/extensions/filters/http/local_ratelimit/BUILD b/source/extensions/filters/http/local_ratelimit/BUILD index 06488d24f8444..92cf64dd9c4a0 100644 --- a/source/extensions/filters/http/local_ratelimit/BUILD +++ b/source/extensions/filters/http/local_ratelimit/BUILD @@ -21,10 +21,12 @@ envoy_cc_library( "//envoy/server:filter_config_interface", "//envoy/stats:stats_macros", "//source/common/common:utility_lib", + "//source/common/config:well_known_names", "//source/common/http:header_utility_lib", "//source/common/http:headers_lib", "//source/common/router:header_parser_lib", "//source/common/runtime:runtime_lib", + "//source/common/stats:utility_lib", "//source/extensions/filters/common/local_ratelimit:local_ratelimit_lib", "//source/extensions/filters/common/ratelimit:ratelimit_lib", "//source/extensions/filters/common/ratelimit_config:ratelimit_config_lib", diff --git a/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc b/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc index 31b8135476af6..386f6f12d8895 100644 --- a/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc +++ b/source/extensions/filters/http/local_ratelimit/local_ratelimit.cc @@ -10,10 +10,14 @@ #include "envoy/extensions/filters/http/local_ratelimit/v3/local_rate_limit.pb.h" #include "envoy/http/codes.h" +#include "source/common/config/well_known_names.h" #include "source/common/http/utility.h" #include "source/common/router/config_impl.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/ratelimit_headers.h" +#include "absl/strings/str_cat.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -123,8 +127,15 @@ FilterConfig::requestAllowed(absl::Span request_des } LocalRateLimitStats FilterConfig::generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = prefix + ".http_local_rate_limit"; - return {ALL_LOCAL_RATE_LIMIT_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // Value-first prefix: the config stat_prefix leads and is this filter's own + // LOCAL_HTTP_RATELIMIT_PREFIX tag ("$.http_local_rate_limit.**"); there is no HCM/cluster parent + // in the prefix (it's carried by the scope), so this is built directly rather than via the + // parent-oriented mergeStatPrefix helper. + Stats::TaggedStatName stat_prefix( + scope.symbolTable(), "http_local_rate_limit.", + {{Envoy::Config::TagNames::get().LOCAL_HTTP_RATELIMIT_PREFIX, prefix}}, + absl::StrCat(prefix, ".http_local_rate_limit.")); + return {ALL_LOCAL_RATE_LIMIT_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } bool FilterConfig::enabled() const { diff --git a/source/extensions/filters/http/lua/BUILD b/source/extensions/filters/http/lua/BUILD index 827d4174276ae..7c24413b57501 100644 --- a/source/extensions/filters/http/lua/BUILD +++ b/source/extensions/filters/http/lua/BUILD @@ -27,6 +27,7 @@ envoy_cc_library( "//source/common/crypto:utility_lib", "//source/common/http:message_lib", "//source/common/runtime:runtime_features_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/common/lua:lua_lib", "//source/extensions/filters/common/lua:protobuf_converter_lib", "//source/extensions/filters/common/lua:wrappers_lib", diff --git a/source/extensions/filters/http/lua/lua_filter.h b/source/extensions/filters/http/lua/lua_filter.h index e7f93acf69df0..402adebf2c069 100644 --- a/source/extensions/filters/http/lua/lua_filter.h +++ b/source/extensions/filters/http/lua/lua_filter.h @@ -8,6 +8,7 @@ #include "source/common/crypto/utility.h" #include "source/common/http/utility.h" #include "source/common/runtime/runtime_features.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/common/lua/wrappers.h" #include "source/extensions/filters/http/common/factory_base.h" #include "source/extensions/filters/http/lua/wrappers.h" @@ -478,8 +479,14 @@ class FilterConfig : Logger::Loggable { private: LuaFilterStats generateStats(const std::string& prefix, const std::string& filter_stats_prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "lua.", filter_stats_prefix); - return {ALL_LUA_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // No lua tag exists in well_known_names.cc, so filter_stats_prefix stays an untagged literal; + // only the parent "http.." prefix is tagged (by the helper). + Stats::TaggedStatName stat_prefix = + filter_stats_prefix.empty() + ? Stats::mergeStatPrefix(scope.symbolTable(), prefix, "lua.") + : Stats::mergeStatPrefix(scope.symbolTable(), prefix, + absl::StrCat("lua.", filter_stats_prefix, ".")); + return {ALL_LUA_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } const bool clear_route_cache_{}; diff --git a/source/extensions/filters/http/mcp/BUILD b/source/extensions/filters/http/mcp/BUILD index 7600626fa32f5..49574f76df9d6 100644 --- a/source/extensions/filters/http/mcp/BUILD +++ b/source/extensions/filters/http/mcp/BUILD @@ -41,6 +41,7 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/http:utility_lib", "//source/common/protobuf", + "//source/common/stats:prefix_utility_lib", "//source/common/tracing:tracing_validation_lib", "//source/extensions/filters/common/mcp:constants_lib", "//source/extensions/filters/common/mcp:filter_state_lib", diff --git a/source/extensions/filters/http/mcp/mcp_filter.cc b/source/extensions/filters/http/mcp/mcp_filter.cc index d71568d7341a9..64a105927d06e 100644 --- a/source/extensions/filters/http/mcp/mcp_filter.cc +++ b/source/extensions/filters/http/mcp/mcp_filter.cc @@ -20,6 +20,7 @@ #include "source/common/http/headers.h" #include "source/common/http/utility.h" #include "source/common/protobuf/protobuf.h" +#include "source/common/stats/prefix_utility.h" #include "source/common/tracing/tracing_validation.h" #include "source/extensions/filters/common/mcp/constants.h" #include "source/extensions/filters/common/mcp/filter_state.h" @@ -42,8 +43,8 @@ const Http::LowerCaseString kMcpSessionId{ std::string(Filters::Common::Mcp::McpConstants::MCP_SESSION_ID_HEADER)}; McpFilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "mcp."); - return McpFilterStats{MCP_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, "mcp."); + return McpFilterStats{MCP_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } const Http::LowerCaseString& traceparentHeader() { diff --git a/source/extensions/filters/http/mcp_router/BUILD b/source/extensions/filters/http/mcp_router/BUILD index d3ee92fb326ce..2c05fd11bdbaa 100644 --- a/source/extensions/filters/http/mcp_router/BUILD +++ b/source/extensions/filters/http/mcp_router/BUILD @@ -30,6 +30,7 @@ envoy_cc_library( "//envoy/server:filter_config_interface", "//envoy/stats:stats_interface", "//envoy/stats:stats_macros", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/common/mcp:filter_state_lib", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/types:variant", diff --git a/source/extensions/filters/http/mcp_router/filter_config.cc b/source/extensions/filters/http/mcp_router/filter_config.cc index 3f87eaa1afb8e..274711add4f28 100644 --- a/source/extensions/filters/http/mcp_router/filter_config.cc +++ b/source/extensions/filters/http/mcp_router/filter_config.cc @@ -3,6 +3,7 @@ #include #include +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/common/mcp/filter_state.h" #include "absl/strings/str_cat.h" @@ -52,8 +53,9 @@ parseSessionIdentity(const envoy::extensions::filters::http::mcp_router::v3::Mcp } McpRouterStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, "mcp_router."); - return McpRouterStats{MCP_ROUTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "mcp_router."); + return McpRouterStats{MCP_ROUTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } template std::vector parseBackends(const Config& config) { diff --git a/source/extensions/filters/http/oauth2/BUILD b/source/extensions/filters/http/oauth2/BUILD index 680b12be728d1..cf2f7daba02fb 100644 --- a/source/extensions/filters/http/oauth2/BUILD +++ b/source/extensions/filters/http/oauth2/BUILD @@ -72,6 +72,7 @@ envoy_cc_library( "//source/common/protobuf:utility_lib", "//source/common/router:retry_policy_lib", "//source/common/secret:secret_provider_impl_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/config/core/v3:pkg_cc_proto", "@envoy_api//envoy/extensions/filters/http/oauth2/v3:pkg_cc_proto", diff --git a/source/extensions/filters/http/oauth2/filter.cc b/source/extensions/filters/http/oauth2/filter.cc index ef2fd6983392f..40c888db1cb78 100644 --- a/source/extensions/filters/http/oauth2/filter.cc +++ b/source/extensions/filters/http/oauth2/filter.cc @@ -23,6 +23,7 @@ #include "source/common/protobuf/utility.h" #include "source/common/router/retry_policy_impl.h" #include "source/common/runtime/runtime_features.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/oauth2/client_assertion.h" #include "absl/strings/escaping.h" @@ -770,8 +771,11 @@ FilterConfig::FilterConfig( FilterStats FilterConfig::generateStats(const std::string& prefix, const std::string& filter_stats_prefix, Stats::Scope& scope) { - const std::string final_prefix = absl::StrCat(prefix, filter_stats_prefix); - return {ALL_OAUTH_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // No oauth2 tag exists in well_known_names.cc, so filter_stats_prefix stays an untagged literal; + // only the parent "http.." prefix is tagged (by the helper). + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, filter_stats_prefix); + return {ALL_OAUTH_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } bool FilterConfig::shouldUseRefreshToken( diff --git a/source/extensions/filters/http/set_metadata/BUILD b/source/extensions/filters/http/set_metadata/BUILD index 202518f403752..7e20666c9f2b3 100644 --- a/source/extensions/filters/http/set_metadata/BUILD +++ b/source/extensions/filters/http/set_metadata/BUILD @@ -18,6 +18,7 @@ envoy_cc_library( "//source/common/config:well_known_names", "//source/common/http:utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/set_metadata/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/set_metadata/set_metadata_filter.cc b/source/extensions/filters/http/set_metadata/set_metadata_filter.cc index 65778dedd0646..60abb8c21a748 100644 --- a/source/extensions/filters/http/set_metadata/set_metadata_filter.cc +++ b/source/extensions/filters/http/set_metadata/set_metadata_filter.cc @@ -5,6 +5,7 @@ #include "source/common/config/well_known_names.h" #include "source/common/http/utility.h" #include "source/common/protobuf/protobuf.h" +#include "source/common/stats/prefix_utility.h" #include "absl/strings/str_format.h" @@ -87,8 +88,9 @@ Config::Config(const envoy::extensions::filters::http::set_metadata::v3::Config& } FilterStats Config::generateStats(const std::string& prefix, Stats::Scope& scope) { - std::string final_prefix = prefix + "set_metadata."; - return {ALL_SET_METADATA_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "set_metadata."); + return {ALL_SET_METADATA_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } SetMetadataFilter::SetMetadataFilter(const ConfigSharedPtr config) : config_(config) {} diff --git a/source/extensions/filters/http/stateful_session/BUILD b/source/extensions/filters/http/stateful_session/BUILD index 6fb123b92f23d..ee4c2ba67e9cd 100644 --- a/source/extensions/filters/http/stateful_session/BUILD +++ b/source/extensions/filters/http/stateful_session/BUILD @@ -20,6 +20,7 @@ envoy_cc_library( "//source/common/config:utility_lib", "//source/common/http:utility_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/common/upstream:load_balancer_context_base_lib", "//source/extensions/filters/http:well_known_names", "//source/extensions/filters/http/common:pass_through_filter_lib", diff --git a/source/extensions/filters/http/stateful_session/stateful_session.cc b/source/extensions/filters/http/stateful_session/stateful_session.cc index f9d0d83a9652b..0a86cd3b116c3 100644 --- a/source/extensions/filters/http/stateful_session/stateful_session.cc +++ b/source/extensions/filters/http/stateful_session/stateful_session.cc @@ -5,6 +5,7 @@ #include "source/common/config/utility.h" #include "source/common/http/utility.h" +#include "source/common/stats/prefix_utility.h" #include "source/common/upstream/load_balancer_context_base.h" namespace Envoy { @@ -33,10 +34,14 @@ StatefulSessionConfig::StatefulSessionConfig(const ProtoConfig& config, : Http::Code::ServiceUnavailable) { // Only construct stats if stat_prefix is explicitly set. if (!config.stat_prefix().empty()) { - const std::string final_prefix = - absl::StrCat(stats_prefix, "stateful_session.", config.stat_prefix(), "."); + // The parent (HCM/cluster) prefix in `stats_prefix` is tag-extracted automatically; the + // filter's own `stateful_session.` segment has no defined tag, so it stays a + // literal. The emitted flat name is byte-identical to the legacy POOL_COUNTER_PREFIX output. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), stats_prefix, + absl::StrCat("stateful_session.", config.stat_prefix(), ".")); stats_ = std::make_shared(StatefulSessionFilterStats{ - ALL_STATEFUL_SESSION_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}); + ALL_STATEFUL_SESSION_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}); } if (!config.has_session_state()) { diff --git a/source/extensions/filters/http/tap/BUILD b/source/extensions/filters/http/tap/BUILD index 6955cf61c279b..4504ffc35e161 100644 --- a/source/extensions/filters/http/tap/BUILD +++ b/source/extensions/filters/http/tap/BUILD @@ -46,6 +46,7 @@ envoy_cc_library( ":tap_config_interface", "//envoy/access_log:access_log_interface", "//envoy/http:filter_interface", + "//source/common/stats:prefix_utility_lib", "//source/extensions/common/tap:extension_config_base", "@envoy_api//envoy/extensions/filters/http/tap/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/tap/tap_filter.cc b/source/extensions/filters/http/tap/tap_filter.cc index 37dc206e27ffd..cadda5676511e 100644 --- a/source/extensions/filters/http/tap/tap_filter.cc +++ b/source/extensions/filters/http/tap/tap_filter.cc @@ -2,6 +2,8 @@ #include "envoy/extensions/filters/http/tap/v3/tap.pb.h" +#include "source/common/stats/prefix_utility.h" + namespace Envoy { namespace Extensions { namespace HttpFilters { @@ -9,7 +11,7 @@ namespace TapFilter { FilterConfigImpl::FilterConfigImpl( const envoy::extensions::filters::http::tap::v3::Tap& proto_config, - const std::string& stats_prefix, Common::Tap::TapConfigFactoryPtr&& config_factory, + const std::string& stats_prefix, Extensions::Common::Tap::TapConfigFactoryPtr&& config_factory, Stats::Scope& scope, OptRef admin, Singleton::Manager& singleton_manager, ThreadLocal::SlotAllocator& tls, Event::Dispatcher& main_thread_dispatcher) : ExtensionConfigBase(proto_config.common_config(), std::move(config_factory), admin, @@ -23,8 +25,8 @@ HttpTapConfigSharedPtr FilterConfigImpl::currentConfig() { FilterStats Filter::generateStats(const std::string& prefix, Stats::Scope& scope) { // TODO(mattklein123): Consider whether we want to additionally namespace the stats on the // filter's configured opaque ID. - std::string final_prefix = prefix + "tap."; - return {ALL_TAP_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + Stats::TaggedStatName stat_prefix = Stats::mergeStatPrefix(scope.symbolTable(), prefix, "tap."); + return {ALL_TAP_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } Http::FilterHeadersStatus Filter::decodeHeaders(Http::RequestHeaderMap& headers, bool) { diff --git a/source/extensions/filters/http/transform/BUILD b/source/extensions/filters/http/transform/BUILD index 4f8e9c8b130d1..3f51e9f59f276 100644 --- a/source/extensions/filters/http/transform/BUILD +++ b/source/extensions/filters/http/transform/BUILD @@ -20,6 +20,7 @@ envoy_cc_library( "//source/common/http:header_map_lib", "//source/common/http:header_mutation_lib", "//source/common/protobuf:utility_lib", + "//source/common/stats:prefix_utility_lib", "//source/extensions/filters/http/common:pass_through_filter_lib", "@envoy_api//envoy/extensions/filters/http/transform/v3:pkg_cc_proto", ], diff --git a/source/extensions/filters/http/transform/transform.h b/source/extensions/filters/http/transform/transform.h index 8096c6697208b..75806fd7eb9d6 100644 --- a/source/extensions/filters/http/transform/transform.h +++ b/source/extensions/filters/http/transform/transform.h @@ -12,6 +12,7 @@ #include "source/common/formatter/substitution_formatter.h" #include "source/common/http/header_mutation.h" #include "source/common/http/utility.h" +#include "source/common/stats/prefix_utility.h" #include "source/extensions/filters/http/common/pass_through_filter.h" #include "absl/strings/string_view.h" @@ -154,8 +155,13 @@ class FilterConfig : public TransformConfig { private: TransformFilterStats generateStats(const std::string& prefix, Stats::Scope& scope) { - const std::string final_prefix = prefix + ".http_transform"; - return {ALL_STATEFUL_SESSION_FILTER_STATS(POOL_COUNTER_PREFIX(scope, final_prefix))}; + // The parent (HCM/cluster) prefix is tag-extracted automatically; "http_transform" is the + // filter's own untagged literal. Using the helper also collapses the stray double dot the + // legacy `prefix + ".http_transform"` produced when `prefix` was the dot-terminated + // `http..`. + Stats::TaggedStatName stat_prefix = + Stats::mergeStatPrefix(scope.symbolTable(), prefix, "http_transform."); + return {ALL_STATEFUL_SESSION_FILTER_STATS(POOL_COUNTER_TAGGED(scope, stat_prefix))}; } TransformFilterStats stats_; }; diff --git a/test/extensions/filters/http/adaptive_concurrency/controller/gradient_controller_test.cc b/test/extensions/filters/http/adaptive_concurrency/controller/gradient_controller_test.cc index 27a099e56f220..2dc37d65a80ec 100644 --- a/test/extensions/filters/http/adaptive_concurrency/controller/gradient_controller_test.cc +++ b/test/extensions/filters/http/adaptive_concurrency/controller/gradient_controller_test.cc @@ -53,7 +53,7 @@ class GradientControllerTest : public testing::Test { GradientControllerSharedPtr makeController(const std::string& yaml_config) { const auto config = std::make_shared( - makeConfig(yaml_config, runtime_), *dispatcher_, runtime_, "test_prefix.", + makeConfig(yaml_config, runtime_), *dispatcher_, runtime_, "test_prefix.", "", *stats_.rootScope(), random_, time_system_); // Advance time so that the latency sample calculations don't underflow if monotonic time is 0. @@ -780,8 +780,8 @@ TEST_F(GradientControllerTest, TimerAccuracyTest) { .WillOnce(Return(sample_timer)); EXPECT_CALL(*sample_timer, enableTimer(std::chrono::milliseconds(123), _)); auto controller = std::make_shared( - makeConfig(yaml, runtime_), fake_dispatcher, runtime_, "test_prefix.", *stats_.rootScope(), - random_, time_system_); + makeConfig(yaml, runtime_), fake_dispatcher, runtime_, "test_prefix.", "", + *stats_.rootScope(), random_, time_system_); // Set the minRTT- this will trigger the timer for the next minRTT calculation. @@ -825,8 +825,8 @@ TEST_F(GradientControllerTest, TimerAccuracyTestNoJitter) { .WillOnce(Return(sample_timer)); EXPECT_CALL(*sample_timer, enableTimer(std::chrono::milliseconds(123), _)); auto controller = std::make_shared( - makeConfig(yaml, runtime_), fake_dispatcher, runtime_, "test_prefix.", *stats_.rootScope(), - random_, time_system_); + makeConfig(yaml, runtime_), fake_dispatcher, runtime_, "test_prefix.", "", + *stats_.rootScope(), random_, time_system_); // Set the minRTT- this will trigger the timer for the next minRTT calculation. EXPECT_CALL(*rtt_timer, enableTimer(std::chrono::milliseconds(45000), _)); diff --git a/test/extensions/filters/http/admission_control/admission_control_filter_test.cc b/test/extensions/filters/http/admission_control/admission_control_filter_test.cc index 1dc896b974b3c..57b97b58777ab 100644 --- a/test/extensions/filters/http/admission_control/admission_control_filter_test.cc +++ b/test/extensions/filters/http/admission_control/admission_control_filter_test.cc @@ -211,7 +211,8 @@ TEST_F(AdmissionControlTest, HttpFailureBehavior) { setupFilter(config); // We expect rejection counter to increment upon failure. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 0))); EXPECT_CALL(*evaluator_, isHttpSuccess(500)).WillRepeatedly(Return(false)); @@ -222,7 +223,8 @@ TEST_F(AdmissionControlTest, HttpFailureBehavior) { filter_->decodeHeaders(request_headers, true)); sampleHttpRequest("500"); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(1), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(1), time_system_)); } // Validate simple HTTP success case. @@ -231,7 +233,8 @@ TEST_F(AdmissionControlTest, HttpSuccessBehavior) { setupFilter(config); // We expect rejection counter to NOT increment upon success. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 100))); EXPECT_CALL(*evaluator_, isHttpSuccess(200)).WillRepeatedly(Return(true)); @@ -241,7 +244,8 @@ TEST_F(AdmissionControlTest, HttpSuccessBehavior) { EXPECT_EQ(Http::FilterHeadersStatus::Continue, filter_->decodeHeaders(request_headers, true)); sampleHttpRequest("200"); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); } // Validate simple gRPC failure case. @@ -249,7 +253,8 @@ TEST_F(AdmissionControlTest, GrpcFailureBehavior) { auto config = makeConfig(default_yaml_); setupFilter(config); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 0))); EXPECT_CALL(*evaluator_, isGrpcSuccess(7)).WillRepeatedly(Return(false)); @@ -261,7 +266,8 @@ TEST_F(AdmissionControlTest, GrpcFailureBehavior) { sampleGrpcRequest(Grpc::Status::WellKnownGrpcStatus::PermissionDenied); // We expect rejection counter to increment upon failure. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(1), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(1), time_system_)); } // Validate simple gRPC success case with status in the trailer. @@ -269,7 +275,8 @@ TEST_F(AdmissionControlTest, GrpcSuccessBehaviorTrailer) { auto config = makeConfig(default_yaml_); setupFilter(config); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 100))); EXPECT_CALL(*evaluator_, isGrpcSuccess(0)).WillRepeatedly(Return(true)); @@ -280,7 +287,8 @@ TEST_F(AdmissionControlTest, GrpcSuccessBehaviorTrailer) { sampleGrpcRequestTrailer(Grpc::Status::WellKnownGrpcStatus::Ok); // We expect rejection counter to NOT increment upon success. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); } // Validate simple gRPC failure case with status in the trailer. @@ -288,7 +296,8 @@ TEST_F(AdmissionControlTest, GrpcFailureBehaviorTrailer) { auto config = makeConfig(default_yaml_); setupFilter(config); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 0))); EXPECT_CALL(*evaluator_, isGrpcSuccess(7)).WillRepeatedly(Return(false)); @@ -300,7 +309,8 @@ TEST_F(AdmissionControlTest, GrpcFailureBehaviorTrailer) { sampleGrpcRequestTrailer(Grpc::Status::WellKnownGrpcStatus::PermissionDenied); // We expect rejection counter to increment upon failure. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(1), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(1), time_system_)); } // Validate simple gRPC success case. @@ -308,7 +318,8 @@ TEST_F(AdmissionControlTest, GrpcSuccessBehavior) { auto config = makeConfig(default_yaml_); setupFilter(config); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 100))); EXPECT_CALL(*evaluator_, isGrpcSuccess(0)).WillRepeatedly(Return(true)); @@ -319,7 +330,8 @@ TEST_F(AdmissionControlTest, GrpcSuccessBehavior) { sampleGrpcRequest(Grpc::Status::WellKnownGrpcStatus::Ok); // We expect rejection counter to NOT increment upon success. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); } // Validate rejection probabilities. @@ -423,7 +435,8 @@ sampling_window: 10s EXPECT_CALL(controller_, averageRps()).WillRepeatedly(Return(100)); // We expect rejection counter to increment upon failure. - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(0), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(0), time_system_)); EXPECT_CALL(controller_, requestCounts()).WillRepeatedly(Return(RequestData(100, 0))); EXPECT_CALL(*evaluator_, isHttpSuccess(500)).WillRepeatedly(Return(false)); @@ -433,7 +446,8 @@ sampling_window: 10s filter_->decodeHeaders(request_headers, true)); sampleHttpRequest("500"); - EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.rq_rejected", Eq(1), time_system_)); + EXPECT_TRUE(TestUtility::waitForCounter(store_, "test_prefix.admission_control.rq_rejected", + Eq(1), time_system_)); } // Validate max rejection probability. From 9eac50a6a80f1575a5ded26dad029e487f473d56 Mon Sep 17 00:00:00 2001 From: wbpcode/wangbaiping Date: Mon, 6 Jul 2026 02:36:35 +0000 Subject: [PATCH 5/5] fix name Signed-off-by: wbpcode/wangbaiping --- source/extensions/filters/http/geoip/geoip_filter.cc | 4 ++-- .../extensions/filters/http/ip_tagging/ip_tagging_filter.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/extensions/filters/http/geoip/geoip_filter.cc b/source/extensions/filters/http/geoip/geoip_filter.cc index 8f5adef8e1c37..7ab403a6d8893 100644 --- a/source/extensions/filters/http/geoip/geoip_filter.cc +++ b/source/extensions/filters/http/geoip/geoip_filter.cc @@ -29,8 +29,8 @@ GeoipFilterConfig::GeoipFilterConfig( } void GeoipFilterConfig::incCounter(Stats::StatName name) { - Stats::Utility::counterFromTaggedPrefix(scope_, stats_prefix_.baseName(), - stats_prefix_.nameTags(), stats_prefix_.name(), name) + Stats::Utility::counterFromTaggedPrefix(scope_, stats_prefix_.baseName(), stats_prefix_.tags(), + stats_prefix_.name(), name) .inc(); } diff --git a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc index 68af41e881c16..bf7ba5ca49782 100644 --- a/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc +++ b/source/extensions/filters/http/ip_tagging/ip_tagging_filter.cc @@ -27,8 +27,8 @@ void IpTagsStats::incHit(absl::string_view tag) { } void IpTagsStats::incCounter(Stats::StatName name) { - Stats::Utility::counterFromTaggedPrefix(*scope_, stats_prefix_.baseName(), - stats_prefix_.nameTags(), stats_prefix_.name(), name) + Stats::Utility::counterFromTaggedPrefix(*scope_, stats_prefix_.baseName(), stats_prefix_.tags(), + stats_prefix_.name(), name) .inc(); }