Skip to content

Commit 8786dc3

Browse files
authored
Merge pull request #60 from jupp0r/feature/check-labels
Check metric and label names
2 parents f3d91b9 + 46af289 commit 8786dc3

File tree

10 files changed

+93
-6
lines changed

10 files changed

+93
-6
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ env:
1313
install:
1414
- sudo apt-get update
1515
- sudo apt-get install -y software-properties-common cmake curl python-pip git lcov
16+
- sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
17+
- sudo apt-get update
18+
- sudo apt-get install -y gcc-5 g++-5
19+
- sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-5 1 --slave /usr/bin/g++ g++ /usr/bin/g++-5
1620
- wget https://github.com/google/protobuf/archive/v3.1.0.tar.gz
1721
- tar xzf v3.1.0.tar.gz
1822
- cd protobuf-3.1.0

BUILD

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cc_library(
22
name = "prometheus-cpp",
3-
srcs = ["lib/counter.cc",
3+
srcs = ["lib/check_names.cc",
4+
"lib/counter.cc",
45
"lib/gauge.cc",
56
"lib/exposer.cc",
67
"lib/handler.cc",
@@ -27,6 +28,6 @@ cc_library(
2728
"@prometheus_client_model//:prometheus_client_model",
2829
"@civetweb//:civetweb",
2930
],
30-
linkstatic = 1,
31-
copts = ["-I."],
31+
linkstatic = 1,
32+
copts = ["-I."],
3233
)

include/prometheus/check_names.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#pragma once
2+
3+
#include <string>
4+
5+
namespace prometheus {
6+
7+
bool CheckMetricName(const std::string& name);
8+
bool CheckLabelName(const std::string& name);
9+
}

include/prometheus/family.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@
88
#include <string>
99
#include <unordered_map>
1010

11+
#include "check_names.h"
1112
#include "collectable.h"
12-
#include "metric.h"
1313
#include "counter_builder.h"
1414
#include "gauge_builder.h"
1515
#include "histogram_builder.h"
16+
#include "metric.h"
1617

1718
namespace prometheus {
1819

@@ -51,12 +52,21 @@ class Family : public Collectable {
5152
template <typename T>
5253
Family<T>::Family(const std::string& name, const std::string& help,
5354
const std::map<std::string, std::string>& constant_labels)
54-
: name_(name), help_(help), constant_labels_(constant_labels) {}
55+
: name_(name), help_(help), constant_labels_(constant_labels) {
56+
assert(CheckMetricName(name_));
57+
}
5558

5659
template <typename T>
5760
template <typename... Args>
5861
T& Family<T>::Add(const std::map<std::string, std::string>& labels,
5962
Args&&... args) {
63+
#ifndef NDEBUG
64+
for (auto& label_pair : labels) {
65+
auto& label_name = label_pair.first;
66+
assert(CheckLabelName(label_name));
67+
}
68+
#endif
69+
6070
std::lock_guard<std::mutex> lock{mutex_};
6171
auto hash = hash_labels(labels);
6272
auto metric = new T(std::forward<Args>(args)...);

lib/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ add_custom_command(
1515
VERBATIM)
1616

1717
add_library(prometheus-cpp
18+
check_names.cc
1819
counter.cc
1920
counter_builder.cc
2021
exposer.cc

lib/check_names.cc

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <regex>
2+
3+
#include <prometheus/check_names.h>
4+
5+
namespace prometheus {
6+
bool CheckMetricName(const std::string& name) {
7+
// see https://prometheus.io/docs/concepts/data_model/
8+
auto reserved_for_internal_purposes = name.compare(0, 2, "__") == 0;
9+
static const std::regex metric_name_regex("[a-zA-Z_:][a-zA-Z0-9_:]*");
10+
return std::regex_match(name, metric_name_regex) &&
11+
!reserved_for_internal_purposes;
12+
}
13+
14+
bool CheckLabelName(const std::string& name) {
15+
auto reserved_for_internal_purposes = name.compare(0, 2, "__") == 0;
16+
// see https://prometheus.io/docs/concepts/data_model/
17+
static const std::regex label_name_regex("[a-zA-Z_][a-zA-Z0-9_]*");
18+
return std::regex_match(name, label_name_regex) &&
19+
!reserved_for_internal_purposes;
20+
;
21+
}
22+
}

tests/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
cc_test(
22
name = "prometheus-test",
33
srcs = [
4+
"check_names_test.cc",
45
"counter_test.cc",
56
"family_test.cc",
67
"gauge_test.cc",

tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ endif()
55
add_subdirectory(integration)
66

77
add_executable(prometheus_test
8+
check_names_test.cc
89
counter_test.cc
910
family_test.cc
1011
gauge_test.cc

tests/check_names_test.cc

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <gmock/gmock.h>
2+
#include <prometheus/check_names.h>
3+
4+
using namespace testing;
5+
using namespace prometheus;
6+
7+
class CheckNamesTest : public Test {};
8+
9+
TEST_F(CheckNamesTest, empty_metric_name) { EXPECT_FALSE(CheckMetricName("")); }
10+
TEST_F(CheckNamesTest, good_metric_name) {
11+
EXPECT_TRUE(CheckMetricName("prometheus_notifications_total"));
12+
}
13+
TEST_F(CheckNamesTest, reserved_metric_name) {
14+
EXPECT_FALSE(CheckMetricName("__some_reserved_metric"));
15+
}
16+
17+
TEST_F(CheckNamesTest, empty_label_name) { EXPECT_FALSE(CheckLabelName("")); }
18+
TEST_F(CheckNamesTest, good_label_name) { EXPECT_TRUE(CheckLabelName("type")); }
19+
TEST_F(CheckNamesTest, reserved_label_name) {
20+
EXPECT_FALSE(CheckMetricName("__some_reserved_label"));
21+
}

tests/family_test.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,28 @@ TEST_F(FamilyTest, remove) {
6666
TEST_F(FamilyTest, Histogram) {
6767
Family<Histogram> family{"request_latency", "Latency Histogram", {}};
6868
auto& histogram1 = family.Add({{"name", "histogram1"}},
69-
Histogram::BucketBoundaries{0, 1, 2});
69+
Histogram::BucketBoundaries{0, 1, 2});
7070
histogram1.Observe(0);
7171
auto collected = family.Collect();
7272
ASSERT_EQ(collected.size(), 1);
7373
ASSERT_GE(collected[0].metric_size(), 1);
7474
ASSERT_TRUE(collected[0].metric(0).has_histogram());
7575
EXPECT_THAT(collected[0].metric(0).histogram().sample_count(), Eq(1));
7676
}
77+
78+
#ifndef NDEBUG
79+
TEST_F(FamilyTest, should_assert_on_invalid_metric_name) {
80+
auto create_family_with_invalid_name = []() {
81+
new Family<Counter>("", "empty name", {});
82+
};
83+
EXPECT_DEATH(create_family_with_invalid_name(), ".*");
84+
}
85+
86+
TEST_F(FamilyTest, should_assert_on_invalid_labels) {
87+
Family<Counter> family{"total_requests", "Counts all requests", {}};
88+
auto add_metric_with_invalid_label_name = [&family]() {
89+
family.Add({{"__invalid", "counter1"}});
90+
};
91+
EXPECT_DEATH(add_metric_with_invalid_label_name(), ".*");
92+
}
93+
#endif

0 commit comments

Comments
 (0)