Skip to content

Commit

Permalink
Prometheus exporter sanitizes invalid characters (#1934)
Browse files Browse the repository at this point in the history
  • Loading branch information
esigo authored Jun 16, 2023
1 parent fc8853e commit 763f11d
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ class PrometheusExporterUtils
const std::vector<double> &boundaries,
const std::vector<uint64_t> &counts,
::prometheus::ClientMetric *metric);

// For testing
friend class SanitizeNameTester;
};
} // namespace metrics
} // namespace exporter
Expand Down
39 changes: 35 additions & 4 deletions exporters/prometheus/src/exporter_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,10 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
for (const auto &metric_data : instrumentation_info.metric_data_)
{
auto origin_name = metric_data.instrument_descriptor.name_;
auto unit = metric_data.instrument_descriptor.unit_;
auto sanitized = SanitizeNames(origin_name);
prometheus_client::MetricFamily metric_family;
metric_family.name = sanitized;
metric_family.name = sanitized + "_" + unit;
metric_family.help = metric_data.instrument_descriptor.description_;
auto time = metric_data.start_ts.time_since_epoch();
for (const auto &point_data_attr : metric_data.point_data_attr_)
Expand Down Expand Up @@ -129,10 +130,40 @@ std::vector<prometheus_client::MetricFamily> PrometheusExporterUtils::TranslateT
*/
std::string PrometheusExporterUtils::SanitizeNames(std::string name)
{
// replace all '.' and '-' with '_'
std::replace(name.begin(), name.end(), '.', '_');
std::replace(name.begin(), name.end(), '-', '_');
constexpr const auto replacement = '_';
constexpr const auto replacement_dup = '=';

auto valid = [](int i, char c) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == ':' ||
(c >= '0' && c <= '9' && i > 0))
{
return true;
}
return false;
};

bool has_dup = false;
for (int i = 0; i < (int)name.size(); ++i)
{
if (valid(i, name[i]))
{
continue;
}
if (i > 0 && (name[i - 1] == replacement || name[i - 1] == replacement_dup))
{
has_dup = true;
name[i] = replacement_dup;
}
else
{
name[i] = replacement;
}
}
if (has_dup)
{
auto end = std::remove(name.begin(), name.end(), replacement_dup);
return std::string{name.begin(), end};
}
return name;
}

Expand Down
32 changes: 29 additions & 3 deletions exporters/prometheus/test/exporter_utils_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ namespace metric_api = opentelemetry::metrics;
namespace prometheus_client = ::prometheus;

OPENTELEMETRY_BEGIN_NAMESPACE

namespace exporter
{
namespace metrics
{
class SanitizeNameTester
{
public:
static std::string sanitize(std::string name)
{
return PrometheusExporterUtils::SanitizeNames(name);
}
};
} // namespace metrics
} // namespace exporter

template <typename T>
void assert_basic(prometheus_client::MetricFamily &metric,
const std::string &sanitized_name,
Expand All @@ -22,9 +38,9 @@ void assert_basic(prometheus_client::MetricFamily &metric,
int label_num,
std::vector<T> vals)
{
ASSERT_EQ(metric.name, sanitized_name); // name sanitized
ASSERT_EQ(metric.help, description); // description not changed
ASSERT_EQ(metric.type, type); // type translated
ASSERT_EQ(metric.name, sanitized_name + "_unit"); // name sanitized
ASSERT_EQ(metric.help, description); // description not changed
ASSERT_EQ(metric.type, type); // type translated

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

TEST(PrometheusExporterUtils, SanitizeName)
{
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name"), "name");
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?"), "name_");
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name???"), "name_");
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?__"), "name_");
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?__name"), "name_name");
ASSERT_EQ(exporter::metrics::SanitizeNameTester::sanitize("name?__name:"), "name_name:");
}

OPENTELEMETRY_END_NAMESPACE

0 comments on commit 763f11d

Please sign in to comment.