Skip to content

Commit

Permalink
core: Add option to merge families
Browse files Browse the repository at this point in the history
  • Loading branch information
gjasny committed Jul 13, 2019
1 parent d83dd68 commit 42b93d1
Show file tree
Hide file tree
Showing 8 changed files with 222 additions and 20 deletions.
20 changes: 20 additions & 0 deletions core/include/prometheus/family.h
Expand Up @@ -115,6 +115,16 @@ class Family : public Collectable {
/// if the given metric was not returned by Add().
void Remove(T* metric);

/// \brief Returns the name for this family.
///
/// \return The family name.
const std::string& GetName() const;

/// \brief Returns the constant labels for this family.
///
/// \return All constant labels as key-value pairs .
const std::map<std::string, std::string> GetConstantLabels() const;

/// \brief Returns the current value of each dimensional data.
///
/// Collect is called by the Registry when collecting metrics.
Expand Down Expand Up @@ -188,6 +198,16 @@ void Family<T>::Remove(T* metric) {
labels_reverse_lookup_.erase(metric);
}

template <typename T>
const std::string& Family<T>::GetName() const {
return name_;
}

template <typename T>
const std::map<std::string, std::string> Family<T>::GetConstantLabels() const {
return constant_labels_;
}

template <typename T>
std::vector<MetricFamily> Family<T>::Collect() {
std::lock_guard<std::mutex> lock{mutex_};
Expand Down
46 changes: 34 additions & 12 deletions core/include/prometheus/registry.h
Expand Up @@ -36,6 +36,20 @@ namespace prometheus {
/// a data race.
class Registry : public Collectable {
public:
/// \brief How to deal with repeatedly added family names for a type
enum class InsertBehavior {
/// \brief Create new family object and append
Append,
/// \brief Merge with existing ones if possible
Merge,
};

/// \brief name Create a new registry.
///
/// \param insert_behavior How to handle families with the same name.
explicit Registry(InsertBehavior insert_behavior = InsertBehavior::Append)
: insert_behavior_{insert_behavior} {}

/// \brief Returns a list of metrics and their samples.
///
/// Every time the Registry is scraped it calls each of the metrics Collect
Expand All @@ -52,20 +66,28 @@ class Registry : public Collectable {

template <typename T>
Family<T>& Add(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);
const std::map<std::string, std::string>& labels,
std::vector<std::unique_ptr<Family<T>>>& families);

Family<Counter>& AddCounter(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);

std::vector<std::unique_ptr<Collectable>> collectables_;
Family<Gauge>& AddGauge(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);

Family<Histogram>& AddHistogram(
const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);

Family<Summary>& AddSummary(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels);

const InsertBehavior insert_behavior_;
std::vector<std::unique_ptr<Family<Counter>>> counters_;
std::vector<std::unique_ptr<Family<Gauge>>> gauges_;
std::vector<std::unique_ptr<Family<Histogram>>> histograms_;
std::vector<std::unique_ptr<Family<Summary>>> summaries_;
std::mutex mutex_;
};

template <typename T>
Family<T>& Registry::Add(const std::string& name, const std::string& help,
const std::map<std::string, std::string>& labels) {
std::lock_guard<std::mutex> lock{mutex_};
auto family = detail::make_unique<Family<T>>(name, help, labels);
auto& ref = *family;
collectables_.push_back(std::move(family));
return ref;
}

} // namespace prometheus
2 changes: 1 addition & 1 deletion core/src/detail/counter_builder.cc
Expand Up @@ -22,7 +22,7 @@ CounterBuilder& CounterBuilder::Help(const std::string& help) {
}

Family<Counter>& CounterBuilder::Register(Registry& registry) {
return registry.Add<Counter>(name_, help_, labels_);
return registry.AddCounter(name_, help_, labels_);
}

} // namespace detail
Expand Down
2 changes: 1 addition & 1 deletion core/src/detail/gauge_builder.cc
Expand Up @@ -22,7 +22,7 @@ GaugeBuilder& GaugeBuilder::Help(const std::string& help) {
}

Family<Gauge>& GaugeBuilder::Register(Registry& registry) {
return registry.Add<Gauge>(name_, help_, labels_);
return registry.AddGauge(name_, help_, labels_);
}

} // namespace detail
Expand Down
2 changes: 1 addition & 1 deletion core/src/detail/histogram_builder.cc
Expand Up @@ -22,7 +22,7 @@ HistogramBuilder& HistogramBuilder::Help(const std::string& help) {
}

Family<Histogram>& HistogramBuilder::Register(Registry& registry) {
return registry.Add<Histogram>(name_, help_, labels_);
return registry.AddHistogram(name_, help_, labels_);
}

} // namespace detail
Expand Down
2 changes: 1 addition & 1 deletion core/src/detail/summary_builder.cc
Expand Up @@ -22,7 +22,7 @@ SummaryBuilder& SummaryBuilder::Help(const std::string& help) {
}

Family<Summary>& SummaryBuilder::Register(Registry& registry) {
return registry.Add<Summary>(name_, help_, labels_);
return registry.AddSummary(name_, help_, labels_);
}

} // namespace detail
Expand Down
94 changes: 90 additions & 4 deletions core/src/registry.cc
@@ -1,17 +1,103 @@
#include "prometheus/registry.h"

#include <iterator>
#include <stdexcept>
#include <tuple>

namespace prometheus {

std::vector<MetricFamily> Registry::Collect() {
std::lock_guard<std::mutex> lock{mutex_};
auto results = std::vector<MetricFamily>{};
for (auto&& collectable : collectables_) {
namespace {

bool FamilyNameExists(const std::string & /* name */) { return false; }

template <typename T, typename... Args>
bool FamilyNameExists(const std::string &name, const T &families,
Args &&... args) {
auto sameName = [&name](const typename T::value_type &entry) {
return name == entry->GetName();
};
auto exists = std::find_if(std::begin(families), std::end(families),
sameName) != std::end(families);
return exists || FamilyNameExists(name, args...);
}

template <typename... Args>
void EnsureUniqueType(const std::string &name, Args &&... args) {
if (FamilyNameExists(name, args...)) {
throw std::invalid_argument("Family already exists with different type");
}
}

template <typename T>
void CollectAll(std::vector<MetricFamily> &results, const T &families) {
for (auto &&collectable : families) {
auto metrics = collectable->Collect();
results.insert(results.end(), std::make_move_iterator(metrics.begin()),
std::make_move_iterator(metrics.end()));
}
}

} // namespace

template <typename T>
Family<T> &Registry::Add(const std::string &name, const std::string &help,
const std::map<std::string, std::string> &labels,
std::vector<std::unique_ptr<Family<T>>> &families) {
std::lock_guard<std::mutex> lock{mutex_};

if (insert_behavior_ == InsertBehavior::Merge) {
for (auto &family : families) {
auto same = std::tie(name, labels) ==
std::tie(family->GetName(), family->GetConstantLabels());
if (same) {
return *family;
}
}
}

auto family = detail::make_unique<Family<T>>(name, help, labels);
auto &ref = *family;
families.push_back(std::move(family));
return ref;
}

Family<Counter> &Registry::AddCounter(
const std::string &name, const std::string &help,
const std::map<std::string, std::string> &labels) {
EnsureUniqueType(name, gauges_, histograms_, summaries_);
return Add(name, help, labels, counters_);
}

Family<Gauge> &Registry::AddGauge(
const std::string &name, const std::string &help,
const std::map<std::string, std::string> &labels) {
EnsureUniqueType(name, counters_, histograms_, summaries_);
return Add(name, help, labels, gauges_);
}

Family<Histogram> &Registry::AddHistogram(
const std::string &name, const std::string &help,
const std::map<std::string, std::string> &labels) {
EnsureUniqueType(name, counters_, gauges_, summaries_);
return Add(name, help, labels, histograms_);
}

Family<Summary> &Registry::AddSummary(
const std::string &name, const std::string &help,
const std::map<std::string, std::string> &labels) {
EnsureUniqueType(name, counters_, gauges_, histograms_);
return Add(name, help, labels, summaries_);
}

std::vector<MetricFamily> Registry::Collect() {
std::lock_guard<std::mutex> lock{mutex_};

auto results = std::vector<MetricFamily>{};

CollectAll(results, counters_);
CollectAll(results, gauges_);
CollectAll(results, histograms_);
CollectAll(results, summaries_);

return results;
}
Expand Down
74 changes: 74 additions & 0 deletions core/tests/registry_test.cc
Expand Up @@ -37,5 +37,79 @@ TEST(RegistryTest, build_histogram_family) {
ASSERT_EQ(collected.size(), 1U);
}

TEST(RegistryTest, reject_type_different_than_counter) {
const auto same_name = std::string{"same_name"};
Registry registry{};

EXPECT_NO_THROW(BuildCounter().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
}

TEST(RegistryTest, reject_type_different_than_gauge) {
const auto same_name = std::string{"same_name"};
Registry registry{};

EXPECT_NO_THROW(BuildGauge().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
}

TEST(RegistryTest, reject_type_different_than_histogram) {
const auto same_name = std::string{"same_name"};
Registry registry{};

EXPECT_NO_THROW(BuildHistogram().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildSummary().Name(same_name).Register(registry));
}

TEST(RegistryTest, reject_type_different_than_summary) {
const auto same_name = std::string{"same_name"};
Registry registry{};

EXPECT_NO_THROW(BuildSummary().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildCounter().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildGauge().Name(same_name).Register(registry));
EXPECT_ANY_THROW(BuildHistogram().Name(same_name).Register(registry));
}

TEST(RegistryTest, append_same_families) {
Registry registry{Registry::InsertBehavior::Append};

std::size_t loops = 4;

while (loops-- > 0) {
BuildCounter()
.Name("counter")
.Help("Test Counter")
.Register(registry)
.Add({{"name", "test_counter"}});
}

auto collected = registry.Collect();
EXPECT_EQ(4U, collected.size());
}

TEST(RegistryTest, merge_same_families) {
Registry registry{Registry::InsertBehavior::Merge};

std::size_t loops = 4;

while (loops-- > 0) {
BuildCounter()
.Name("counter")
.Help("Test Counter")
.Register(registry)
.Add({{"name", "test_counter"}});
}

auto collected = registry.Collect();
EXPECT_EQ(1U, collected.size());
}

} // namespace
} // namespace prometheus

0 comments on commit 42b93d1

Please sign in to comment.