Skip to content

Commit 8b9d409

Browse files
authored
replace invalid characters in ids (#106)
It is easy enough to break the spectatord line protocol, if any of the control characters (`:,=`) are inserted in unexpected places. Since metric names and tags are often programmatically generated, we want to only construct id strings which are valid.
1 parent 3a8023c commit 8b9d409

16 files changed

Lines changed: 193 additions & 19 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
.idea/
33
cmake-build-debug
44
cmake-build/
5+
spectator/valid_chars.inc
56
venv/

CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,17 @@ add_library(spectator
3939
"spectator/registry.h"
4040
"spectator/stateful_meters.h"
4141
"spectator/stateless_meters.h"
42+
"spectator/valid_chars.inc"
4243
)
4344
target_link_libraries(spectator ${CONAN_LIBS})
4445
target_link_options(spectator PRIVATE "$<$<CONFIG:Release>:-static-libstdc++>")
46+
47+
#-- generator tools
48+
add_executable(gen_valid_chars "tools/gen_valid_chars.cc")
49+
50+
#-- file generators, must exist where the outputs are referenced
51+
add_custom_command(
52+
OUTPUT "spectator/valid_chars.inc"
53+
COMMAND "${CMAKE_BINARY_DIR}/bin/gen_valid_chars" > "${CMAKE_SOURCE_DIR}/spectator/valid_chars.inc"
54+
DEPENDS gen_valid_chars
55+
)

build.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ NC="\033[0m"
1010
if [[ "$1" == "clean" ]]; then
1111
echo -e "${BLUE}==== clean ====${NC}"
1212
rm -rf $BUILD_DIR
13-
# remove all packages and binaries from the local cache, to allow swapping between Debug/Release builds
14-
conan remove '*' --force
13+
rm -f spectator/*.inc
14+
if [[ "$2" == "--force" ]]; then
15+
# remove all packages and binaries from the local cache, to allow swapping between Debug/Release builds
16+
conan remove '*' --force
17+
fi
1518
fi
1619

1720
if [[ "$OSTYPE" == "linux-gnu"* ]]; then

spectator/age_gauge_test.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,12 @@ TEST(AgeGauge, Set) {
2525
EXPECT_EQ(publisher.SentMessages(), expected);
2626
}
2727

28+
TEST(AgeGauge, InvalidTags) {
29+
TestPublisher publisher;
30+
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
31+
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
32+
Tags{{"tag1,:=", "value1,:="}});
33+
AgeGauge g{id, &publisher};
34+
EXPECT_EQ("A:test______^____-_~______________.___foo,tag1___=value1___:", g.GetPrefix());
35+
}
2836
} // namespace

spectator/counter_test.cc

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ TEST(Counter, Activity) {
1313
TestPublisher publisher;
1414
auto id = std::make_shared<Id>("ctr.name", Tags{});
1515
auto id2 = std::make_shared<Id>("c2", Tags{{"key", "val"}});
16-
Counter<TestPublisher> c{id, &publisher};
17-
Counter<TestPublisher> c2{id2, &publisher};
16+
Counter c{id, &publisher};
17+
Counter c2{id2, &publisher};
1818
c.Increment();
1919
c2.Add(1.2);
2020
c.Add(0.1);
@@ -25,10 +25,18 @@ TEST(Counter, Activity) {
2525

2626
TEST(Counter, Id) {
2727
TestPublisher publisher;
28-
Counter<TestPublisher> c{std::make_shared<Id>("foo", Tags{{"key", "val"}}),
28+
Counter c{std::make_shared<Id>("foo", Tags{{"key", "val"}}),
2929
&publisher};
3030
auto id = std::make_shared<Id>("foo", Tags{{"key", "val"}});
3131
EXPECT_EQ(*(c.MeterId()), *id);
3232
}
3333

34+
TEST(Counter, InvalidTags) {
35+
TestPublisher publisher;
36+
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
37+
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
38+
Tags{{"tag1,:=", "value1,:="}});
39+
Counter c{id, &publisher};
40+
EXPECT_EQ("c:test______^____-_~______________.___foo,tag1___=value1___:", c.GetPrefix());
41+
}
3442
} // namespace

spectator/dist_summary_test.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,12 @@ TEST(DistributionSummary, Record) {
2222
EXPECT_EQ(publisher.SentMessages(), expected);
2323
}
2424

25+
TEST(DistributionSummary, InvalidTags) {
26+
TestPublisher publisher;
27+
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
28+
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
29+
Tags{{"tag1,:=", "value1,:="}});
30+
DistributionSummary d{id, &publisher};
31+
EXPECT_EQ("d:test______^____-_~______________.___foo,tag1___=value1___:", d.GetPrefix());
32+
}
2533
} // namespace

spectator/gauge_test.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@ TEST(Gauge, Set) {
2424
EXPECT_EQ(publisher.SentMessages(), expected);
2525
}
2626

27+
TEST(Gauge, InvalidTags) {
28+
TestPublisher publisher;
29+
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
30+
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
31+
Tags{{"tag1,:=", "value1,:="}});
32+
Gauge g{id, &publisher};
33+
EXPECT_EQ("g:test______^____-_~______________.___foo,tag1___=value1___:", g.GetPrefix());
34+
}
2735
} // namespace

spectator/id_test.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ TEST(Id, Create) {
1717
std::shared_ptr<Id> id_of{Id::of("name", Tags{{"k", "v"}, {"k1", "v1"}})};
1818
EXPECT_EQ(id_of->Name(), "name");
1919
EXPECT_EQ(id_of->GetTags().size(), 2);
20+
fmt::format("{}", id);
2021
}
2122

2223
TEST(Id, Tags) {

spectator/max_gauge_test.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,12 @@ TEST(MaxGauge, Set) {
2424
EXPECT_EQ(publisher.SentMessages(), expected);
2525
}
2626

27+
TEST(MaxGauge, InvalidTags) {
28+
TestPublisher publisher;
29+
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
30+
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
31+
Tags{{"tag1,:=", "value1,:="}});
32+
MaxGauge g{id, &publisher};
33+
EXPECT_EQ("m:test______^____-_~______________.___foo,tag1___=value1___:", g.GetPrefix());
34+
}
2735
} // namespace

spectator/monotonic_counter_test.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,13 @@ TEST(MonotonicCounter, Set) {
2222
std::vector<std::string> expected = {"C:ctr:42.1", "C:ctr2,key=val:2", "C:ctr:43"};
2323
EXPECT_EQ(publisher.SentMessages(), expected);
2424
}
25+
26+
TEST(MonotonicCounter, InvalidTags) {
27+
TestPublisher publisher;
28+
// test with a single tag, because tags order is not guaranteed in a flat_hash_map
29+
auto id = std::make_shared<Id>("test`!@#$%^&*()-=~_+[]{}\\|;:'\",<.>/?foo",
30+
Tags{{"tag1,:=", "value1,:="}});
31+
MonotonicCounter c{id, &publisher};
32+
EXPECT_EQ("C:test______^____-_~______________.___foo,tag1___=value1___:", c.GetPrefix());
33+
}
2534
} // namespace

0 commit comments

Comments
 (0)