Skip to content

Commit 763f11d

Browse files
authored
Prometheus exporter sanitizes invalid characters (#1934)
1 parent fc8853e commit 763f11d

File tree

3 files changed

+67
-7
lines changed

3 files changed

+67
-7
lines changed

exporters/prometheus/include/opentelemetry/exporters/prometheus/exporter_utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ class PrometheusExporterUtils
107107
const std::vector<double> &boundaries,
108108
const std::vector<uint64_t> &counts,
109109
::prometheus::ClientMetric *metric);
110+
111+
// For testing
112+
friend class SanitizeNameTester;
110113
};
111114
} // namespace metrics
112115
} // namespace exporter

exporters/prometheus/src/exporter_utils.cc

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,10 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
3939
for (const auto &metric_data : instrumentation_info.metric_data_)
4040
{
4141
auto origin_name = metric_data.instrument_descriptor.name_;
42+
auto unit = metric_data.instrument_descriptor.unit_;
4243
auto sanitized = SanitizeNames(origin_name);
4344
prometheus_client::MetricFamily metric_family;
44-
metric_family.name = sanitized;
45+
metric_family.name = sanitized + "_" + unit;
4546
metric_family.help = metric_data.instrument_descriptor.description_;
4647
auto time = metric_data.start_ts.time_since_epoch();
4748
for (const auto &point_data_attr : metric_data.point_data_attr_)
@@ -129,10 +130,40 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
129130
*/
130131
std::string PrometheusExporterUtils::SanitizeNames(std::string name)
131132
{
132-
// replace all '.' and '-' with '_'
133-
std::replace(name.begin(), name.end(), '.', '_');
134-
std::replace(name.begin(), name.end(), '-', '_');
133+
constexpr const auto replacement = '_';
134+
constexpr const auto replacement_dup = '=';
135135

136+
auto valid = [](int i, char c) {
137+
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == ':' ||
138+
(c >= '0' && c <= '9' && i > 0))
139+
{
140+
return true;
141+
}
142+
return false;
143+
};
144+
145+
bool has_dup = false;
146+
for (int i = 0; i < (int)name.size(); ++i)
147+
{
148+
if (valid(i, name[i]))
149+
{
150+
continue;
151+
}
152+
if (i > 0 && (name[i - 1] == replacement || name[i - 1] == replacement_dup))
153+
{
154+
has_dup = true;
155+
name[i] = replacement_dup;
156+
}
157+
else
158+
{
159+
name[i] = replacement;
160+
}
161+
}
162+
if (has_dup)
163+
{
164+
auto end = std::remove(name.begin(), name.end(), replacement_dup);
165+
return std::string{name.begin(), end};
166+
}
136167
return name;
137168
}
138169

exporters/prometheus/test/exporter_utils_test.cc

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ namespace metric_api = opentelemetry::metrics;
1414
namespace prometheus_client = ::prometheus;
1515

1616
OPENTELEMETRY_BEGIN_NAMESPACE
17+
18+
namespace exporter
19+
{
20+
namespace metrics
21+
{
22+
class SanitizeNameTester
23+
{
24+
public:
25+
static std::string sanitize(std::string name)
26+
{
27+
return PrometheusExporterUtils::SanitizeNames(name);
28+
}
29+
};
30+
} // namespace metrics
31+
} // namespace exporter
32+
1733
template <typename T>
1834
void assert_basic(prometheus_client::MetricFamily &metric,
1935
const std::string &sanitized_name,
@@ -22,9 +38,9 @@ void assert_basic(prometheus_client::MetricFamily &metric,
2238
int label_num,
2339
std::vector<T> vals)
2440
{
25-
ASSERT_EQ(metric.name, sanitized_name); // name sanitized
26-
ASSERT_EQ(metric.help, description); // description not changed
27-
ASSERT_EQ(metric.type, type); // type translated
41+
ASSERT_EQ(metric.name, sanitized_name + "_unit"); // name sanitized
42+
ASSERT_EQ(metric.help, description); // description not changed
43+
ASSERT_EQ(metric.type, type); // type translated
2844

2945
auto metric_data = metric.metric[0];
3046
ASSERT_EQ(metric_data.label.size(), label_num);
@@ -129,4 +145,14 @@ TEST(PrometheusExporterUtils, TranslateToPrometheusHistogramNormal)
129145
assert_histogram(metric, std::list<double>{10.1, 20.2, 30.2}, {200, 300, 400, 500});
130146
}
131147

148+
TEST(PrometheusExporterUtils, SanitizeName)
149+
{
150+
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name"), "name");
151+
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?"), "name_");
152+
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name???"), "name_");
153+
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?__"), "name_");
154+
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?__name"), "name_name");
155+
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?__name:"), "name_name:");
156+
}
157+
132158
OPENTELEMETRY_END_NAMESPACE

0 commit comments

Comments
 (0)