Skip to content

replace invalid characters in ids #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 16, 2024
Merged
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -2,4 +2,5 @@
.idea/
cmake-build-debug
cmake-build/
spectator/valid_chars.inc
venv/
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -39,6 +39,17 @@ add_library(spectator
"spectator/registry.h"
"spectator/stateful_meters.h"
"spectator/stateless_meters.h"
"spectator/valid_chars.inc"
)
target_link_libraries(spectator ${CONAN_LIBS})
target_link_options(spectator PRIVATE "$<$<CONFIG:Release>:-static-libstdc++>")

#-- generator tools
add_executable(gen_valid_chars "tools/gen_valid_chars.cc")

#-- file generators, must exist where the outputs are referenced
add_custom_command(
OUTPUT "spectator/valid_chars.inc"
COMMAND "${CMAKE_BINARY_DIR}/bin/gen_valid_chars" > "${CMAKE_SOURCE_DIR}/spectator/valid_chars.inc"
DEPENDS gen_valid_chars
)
7 changes: 5 additions & 2 deletions build.sh
Original file line number Diff line number Diff line change
@@ -10,8 +10,11 @@ NC="\033[0m"
if [[ "$1" == "clean" ]]; then
echo -e "${BLUE}==== clean ====${NC}"
rm -rf $BUILD_DIR
# remove all packages and binaries from the local cache, to allow swapping between Debug/Release builds
conan remove '*' --force
rm -f spectator/*.inc
if [[ "$2" == "--force" ]]; then
# remove all packages and binaries from the local cache, to allow swapping between Debug/Release builds
conan remove '*' --force
fi
fi

if [[ "$OSTYPE" == "linux-gnu"* ]]; then
8 changes: 8 additions & 0 deletions spectator/age_gauge_test.cc
Original file line number Diff line number Diff line change
@@ -25,4 +25,12 @@ TEST(AgeGauge, Set) {
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(AgeGauge, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
AgeGauge g{id, &publisher};
EXPECT_EQ("A:test______^____-_~______________.___foo,tag1___=value1___:", g.GetPrefix());
}
} // namespace
14 changes: 11 additions & 3 deletions spectator/counter_test.cc
Original file line number Diff line number Diff line change
@@ -13,8 +13,8 @@ TEST(Counter, Activity) {
TestPublisher publisher;
auto id = std::make_shared<Id>("ctr.name", Tags{});
auto id2 = std::make_shared<Id>("c2", Tags{{"key", "val"}});
Counter<TestPublisher> c{id, &publisher};
Counter<TestPublisher> c2{id2, &publisher};
Counter c{id, &publisher};
Counter c2{id2, &publisher};
c.Increment();
c2.Add(1.2);
c.Add(0.1);
@@ -25,10 +25,18 @@ TEST(Counter, Activity) {

TEST(Counter, Id) {
TestPublisher publisher;
Counter<TestPublisher> c{std::make_shared<Id>("foo", Tags{{"key", "val"}}),
Counter c{std::make_shared<Id>("foo", Tags{{"key", "val"}}),
&publisher};
auto id = std::make_shared<Id>("foo", Tags{{"key", "val"}});
EXPECT_EQ(*(c.MeterId()), *id);
}

TEST(Counter, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
Counter c{id, &publisher};
EXPECT_EQ("c:test______^____-_~______________.___foo,tag1___=value1___:", c.GetPrefix());
}
} // namespace
8 changes: 8 additions & 0 deletions spectator/dist_summary_test.cc
Original file line number Diff line number Diff line change
@@ -22,4 +22,12 @@ TEST(DistributionSummary, Record) {
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(DistributionSummary, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
DistributionSummary d{id, &publisher};
EXPECT_EQ("d:test______^____-_~______________.___foo,tag1___=value1___:", d.GetPrefix());
}
} // namespace
8 changes: 8 additions & 0 deletions spectator/gauge_test.cc
Original file line number Diff line number Diff line change
@@ -24,4 +24,12 @@ TEST(Gauge, Set) {
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(Gauge, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
Gauge g{id, &publisher};
EXPECT_EQ("g:test______^____-_~______________.___foo,tag1___=value1___:", g.GetPrefix());
}
} // namespace
1 change: 1 addition & 0 deletions spectator/id_test.cc
Original file line number Diff line number Diff line change
@@ -17,6 +17,7 @@ TEST(Id, Create) {
std::shared_ptr<Id> id_of{Id::of("name", Tags{{"k", "v"}, {"k1", "v1"}})};
EXPECT_EQ(id_of->Name(), "name");
EXPECT_EQ(id_of->GetTags().size(), 2);
fmt::format("{}", id);
}

TEST(Id, Tags) {
8 changes: 8 additions & 0 deletions spectator/max_gauge_test.cc
Original file line number Diff line number Diff line change
@@ -24,4 +24,12 @@ TEST(MaxGauge, Set) {
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(MaxGauge, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
MaxGauge g{id, &publisher};
EXPECT_EQ("m:test______^____-_~______________.___foo,tag1___=value1___:", g.GetPrefix());
}
} // namespace
9 changes: 9 additions & 0 deletions spectator/monotonic_counter_test.cc
Original file line number Diff line number Diff line change
@@ -22,4 +22,13 @@ TEST(MonotonicCounter, Set) {
std::vector<std::string> expected = {"C:ctr:42.1", "C:ctr2,key=val:2", "C:ctr:43"};
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(MonotonicCounter, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
MonotonicCounter c{id, &publisher};
EXPECT_EQ("C:test______^____-_~______________.___foo,tag1___=value1___:", c.GetPrefix());
}
} // namespace
9 changes: 9 additions & 0 deletions spectator/monotonic_counter_uint_test.cc
Original file line number Diff line number Diff line change
@@ -22,4 +22,13 @@ TEST(MonotonicCounterUint, Set) {
std::vector<std::string> expected = {"U:ctr:42", "U:ctr2,key=val:2", "U:ctr:18446744073709551615"};
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(MonotonicCounterUint, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
MonotonicCounterUint c{id, &publisher};
EXPECT_EQ("U:test______^____-_~______________.___foo,tag1___=value1___:", c.GetPrefix());
}
} // namespace
16 changes: 12 additions & 4 deletions spectator/perc_dist_summary_test.cc
Original file line number Diff line number Diff line change
@@ -11,13 +11,21 @@ using spectator::TestPublisher;
TEST(PercDistSum, Record) {
TestPublisher publisher;
auto id = std::make_shared<Id>("pds", Tags{});
PercentileDistributionSummary<TestPublisher> c{id, &publisher, 0, 1000};
c.Record(50);
c.Record(5000);
c.Record(-5000);
PercentileDistributionSummary d{id, &publisher, 0, 1000};
d.Record(50);
d.Record(5000);
d.Record(-5000);
std::vector<std::string> expected = {"D:pds:50", "D:pds:1000",
"D:pds:0"};
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(PercDistSum, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
PercentileDistributionSummary d{id, &publisher, 0, 1000};
EXPECT_EQ("D:test______^____-_~______________.___foo,tag1___=value1___:", d.GetPrefix());
}
} // namespace
14 changes: 10 additions & 4 deletions spectator/perc_timer_test.cc
Original file line number Diff line number Diff line change
@@ -11,14 +11,20 @@ using spectator::TestPublisher;
TEST(PercentileTimer, Record) {
TestPublisher publisher;
auto id = std::make_shared<Id>("pt", Tags{});
PercentileTimer<TestPublisher> c{id, &publisher, absl::ZeroDuration(),
absl::Seconds(5)};
PercentileTimer c{id, &publisher, absl::ZeroDuration(), absl::Seconds(5)};
c.Record(absl::Milliseconds(42));
c.Record(std::chrono::microseconds(500));
c.Record(absl::Seconds(10));
std::vector<std::string> expected = {"T:pt:0.042", "T:pt:0.0005",
"T:pt:5"};
std::vector<std::string> expected = {"T:pt:0.042", "T:pt:0.0005", "T:pt:5"};
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(PercentileTimer, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
PercentileTimer t{id, &publisher, absl::ZeroDuration(), absl::Seconds(5)};
EXPECT_EQ("T:test______^____-_~______________.___foo,tag1___=value1___:", t.GetPrefix());
}
} // namespace
35 changes: 33 additions & 2 deletions spectator/stateless_meters.h
Original file line number Diff line number Diff line change
@@ -6,14 +6,39 @@
namespace spectator {

namespace detail {

#include "valid_chars.inc"

inline std::string as_string(std::string_view v) {
return {v.data(), v.size()};
}

inline bool contains_non_atlas_char(const std::string& input) {
return std::any_of(input.begin(), input.end(), [](char c) { return !kAtlasChars[c]; });
}

inline std::string replace_invalid_characters(const std::string& input) {
if (contains_non_atlas_char(input)) {
std::string result{input};
for (char &c : result) {
if (!kAtlasChars[c]) {
c = '_';
}
}
return result;
} else {
return input;
}
}

inline std::string create_prefix(const Id& id, std::string_view type_name) {
std::string res = as_string(type_name) + ":" + id.Name();
std::string res = as_string(type_name) + ":" + replace_invalid_characters(id.Name());
for (const auto& tags : id.GetTags()) {
absl::StrAppend(&res, ",", tags.first, "=", tags.second);
auto first = replace_invalid_characters(tags.first);
auto second = replace_invalid_characters(tags.second);
absl::StrAppend(&res, ",", first, "=", second);
}

absl::StrAppend(&res, ":");
return res;
}
@@ -38,6 +63,12 @@ class StatelessMeter {
assert(publisher_ != nullptr);
}
virtual ~StatelessMeter() = default;
std::string GetPrefix() {
if (value_prefix_.empty()) {
value_prefix_ = detail::create_prefix(*id_, Type());
}
return value_prefix_;
}
[[nodiscard]] IdPtr MeterId() const noexcept { return id_; }
[[nodiscard]] virtual std::string_view Type() = 0;

15 changes: 11 additions & 4 deletions spectator/timer_test.cc
Original file line number Diff line number Diff line change
@@ -12,14 +12,21 @@ TEST(Timer, Record) {
TestPublisher publisher;
auto id = std::make_shared<Id>("t.name", Tags{});
auto id2 = std::make_shared<Id>("t2", Tags{{"key", "val"}});
Timer<TestPublisher> t{id, &publisher};
Timer<TestPublisher> t2{id2, &publisher};
Timer t{id, &publisher};
Timer t2{id2, &publisher};
t.Record(std::chrono::milliseconds(1));
t2.Record(absl::Seconds(0.1));
t2.Record(absl::Microseconds(500));
std::vector<std::string> expected = {
"t:t.name:0.001", "t:t2,key=val:0.1", "t:t2,key=val:0.0005"};
std::vector<std::string> expected = {"t:t.name:0.001", "t:t2,key=val:0.1", "t:t2,key=val:0.0005"};
EXPECT_EQ(publisher.SentMessages(), expected);
}

TEST(Timer, InvalidTags) {
TestPublisher publisher;
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
auto id = std::make_shared<Id>("timer`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
Tags{{"tag1,:=", "value1,:="}});
Timer t{id, &publisher};
EXPECT_EQ("t:timer______^____-_~______________.___foo,tag1___=value1___:", t.GetPrefix());
}
} // namespace
48 changes: 48 additions & 0 deletions tools/gen_valid_chars.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// generate the atlas valid charsets

#include <array>
#include <fstream>

void dump_array(std::ostream& os, const std::string& name, const std::array<bool, 256>& chars) {
os << "static constexpr std::array<bool, 256> " << name << " = {{";

os << chars[0];
for (auto i = 1u; i < chars.size(); ++i) {
os << ", " << chars[i];
}

os << "}};\n";
}

int main(int argc, char* argv[]) {
std::ofstream of;
if (argc > 1) {
of.open(argv[1]);
} else {
of.open("/dev/stdout");
}

// default false
std::array<bool, 256> charsAllowed{};
for (int i = 0; i < 256; ++i) {
charsAllowed[i] = false;
}

// configure allowed characters
charsAllowed['.'] = true;
charsAllowed['-'] = true;

for (auto ch = '0'; ch <= '9'; ++ch) {
charsAllowed[ch] = true;
}
for (auto ch = 'a'; ch <= 'z'; ++ch) {
charsAllowed[ch] = true;
}
for (auto ch = 'A'; ch <= 'Z'; ++ch) {
charsAllowed[ch] = true;
}
charsAllowed['~'] = true;
charsAllowed['^'] = true;

dump_array(of, "kAtlasChars", charsAllowed);
}