Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions include/envoy/stats/stats.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,30 @@ class Metric {
*/
virtual StatName tagExtractedStatName() const PURE;

// Function to be called from iterateTagStatNames passing name and value as StatNames.
using TagStatNameIterFn = std::function<bool(StatName, StatName)>;

/**
* Iterates over all tags, calling a functor for each name/value pair. The
* functor can return 'true' to continue or 'false' to stop the
* iteration.
*
* @param fn The functor to call for StatName pair.
*/
virtual void iterateTagStatNames(const TagStatNameIterFn& fn) const PURE;

// Function to be called from iterateTags passing name and value as const Tag&.
using TagIterFn = std::function<bool(const Tag&)>;

/**
* Iterates over all tags, calling a functor for each one. The
* functor can return 'true' to continue or 'false' to stop the
* iteration.
*
* @param fn The functor to call for each Tag.
*/
virtual void iterateTags(const TagIterFn& fn) const PURE;

/**
* Indicates whether this metric has been updated since the server was started.
*/
Expand Down
31 changes: 23 additions & 8 deletions source/common/stats/metric_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -46,33 +46,48 @@ StatName MetricImpl::tagExtractedStatName() const {
return stat_name;
}

std::vector<Tag> MetricImpl::tags() const {
std::vector<Tag> tags;
void MetricImpl::iterateTagStatNames(const TagStatNameIterFn& fn) const {
enum { TagExtractedName, TagName, TagValue } state = TagExtractedName;
Tag tag;
const SymbolTable& symbol_table = symbolTable();
StatName tag_name;

// StatNameList maintains a linear ordered collection of StatNames, and we
// are mapping that into a tag-extracted name (the first element), followed
// by alternating TagName and TagValue. So we use a little state machine
// as we iterate through the stat_names_.
stat_names_.iterate([&tags, &state, &tag, &symbol_table](StatName stat_name) -> bool {
stat_names_.iterate([&state, &tag_name, &fn](StatName stat_name) -> bool {
switch (state) {
case TagExtractedName:
state = TagName;
break;
case TagName:
tag.name_ = symbol_table.toString(stat_name);
tag_name = stat_name;
state = TagValue;
break;
case TagValue:
tag.value_ = symbol_table.toString(stat_name);
tags.emplace_back(tag);
state = TagName;
if (!fn(tag_name, stat_name)) {
return false; // early exit.
}
break;
}
return true;
});
ASSERT(state != TagValue);
}

void MetricImpl::iterateTags(const TagIterFn& fn) const {
const SymbolTable& symbol_table = symbolTable();
iterateTagStatNames([&fn, &symbol_table](StatName name, StatName value) -> bool {
return fn(Tag{symbol_table.toString(name), symbol_table.toString(value)});
});
}

std::vector<Tag> MetricImpl::tags() const {
std::vector<Tag> tags;
iterateTags([&tags](const Tag& tag) -> bool {
tags.emplace_back(tag);
return true;
});
return tags;
}

Expand Down
2 changes: 2 additions & 0 deletions source/common/stats/metric_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ class MetricImpl : public virtual Metric {
std::string tagExtractedName() const override;
std::vector<Tag> tags() const override;
StatName tagExtractedStatName() const override;
void iterateTagStatNames(const TagStatNameIterFn& fn) const override;
void iterateTags(const TagIterFn& fn) const override;

protected:
void clear();
Expand Down
10 changes: 10 additions & 0 deletions test/common/stats/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ envoy_cc_test(
],
)

envoy_cc_test(
name = "metric_impl_test",
srcs = ["metric_impl_test.cc"],
deps = [
"//source/common/stats:fake_symbol_table_lib",
"//source/common/stats:heap_stat_data_lib",
"//test/test_common:logging_lib",
],
)

envoy_cc_test(
name = "source_impl_test",
srcs = ["source_impl_test.cc"],
Expand Down
65 changes: 65 additions & 0 deletions test/common/stats/metric_impl_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include <string>

#include "common/stats/fake_symbol_table_impl.h"
#include "common/stats/heap_stat_data.h"

#include "test/test_common/logging.h"

#include "gtest/gtest.h"

namespace Envoy {
namespace Stats {
namespace {

class MetricImplTest : public testing::Test {
protected:
MetricImplTest() : alloc_(symbol_table_), pool_(symbol_table_) {}
~MetricImplTest() { clearStorage(); }

StatName makeStat(absl::string_view name) { return pool_.add(name); }

void clearStorage() {
pool_.clear();
EXPECT_EQ(0, symbol_table_.numSymbols());
}

FakeSymbolTableImpl symbol_table_;
HeapStatDataAllocator alloc_;
StatNamePool pool_;
};

// No truncation occurs in the implementation of HeapStatData.
TEST_F(MetricImplTest, NoTags) {
CounterSharedPtr counter = alloc_.makeCounter(makeStat("counter"), "", {});
EXPECT_EQ(0, counter->tags().size());
}

TEST_F(MetricImplTest, OneTag) {
CounterSharedPtr counter =
alloc_.makeCounter(makeStat("counter.name.value"), "counter", {{"name", "value"}});
std::vector<Tag> tags = counter->tags();
ASSERT_EQ(1, tags.size());
EXPECT_EQ("name", tags[0].name_);
EXPECT_EQ("value", tags[0].value_);
EXPECT_EQ("counter", counter->tagExtractedName());
EXPECT_EQ(makeStat("counter"), counter->tagExtractedStatName());
}

TEST_F(MetricImplTest, TwoTagsIterOnce) {
CounterSharedPtr counter = alloc_.makeCounter(makeStat("counter.name.value"), "counter",
{{"name1", "value1"}, {"name2", "value2"}});
StatName name1 = makeStat("name1");
StatName value1 = makeStat("value1");
int count = 0;
counter->iterateTagStatNames([&name1, &value1, &count](StatName name, StatName value) -> bool {
EXPECT_EQ(name1, name);
EXPECT_EQ(value1, value);
++count;
return false; // Abort the iteration at first tag.
});
EXPECT_EQ(1, count);
}

} // namespace
} // namespace Stats
} // namespace Envoy
19 changes: 19 additions & 0 deletions test/mocks/stats/mocks.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,25 @@ void MockMetric::setTagExtractedName(absl::string_view name) {
std::make_unique<StatNameManagedStorage>(tagExtractedName(), *symbol_table_);
}

void MockMetric::iterateTags(const TagIterFn& fn) const {
for (const Tag& tag : tags_) {
if (!fn(tag)) {
return;
}
}
}

void MockMetric::iterateTagStatNames(const TagStatNameIterFn& fn) const {
SymbolTable& symbol_table = const_cast<SymbolTable&>(symbolTable());
for (const Tag& tag : tags_) {
StatNameManagedStorage name(tag.name_, symbol_table);
StatNameManagedStorage value(tag.value_, symbol_table);
if (!fn(name.statName(), value.statName())) {
return;
}
}
}

void MockMetric::MetricName::MetricName::operator=(absl::string_view name) {
name_ = std::string(name);
stat_name_storage_ = std::make_unique<StatNameStorage>(name, mock_metric_.symbolTable());
Expand Down
2 changes: 2 additions & 0 deletions test/mocks/stats/mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class MockMetric : public virtual Metric {
return tag_extracted_name_.empty() ? name() : tag_extracted_name_;
}
StatName tagExtractedStatName() const override { return tag_extracted_stat_name_->statName(); }
void iterateTagStatNames(const TagStatNameIterFn& fn) const override;
void iterateTags(const TagIterFn& fn) const override;

Test::Global<FakeSymbolTableImpl> symbol_table_; // Must outlive name_.
MetricName name_;
Expand Down