Skip to content

Commit

Permalink
Merge 1d91f0c into 1d844b6
Browse files Browse the repository at this point in the history
  • Loading branch information
gjasny committed Oct 18, 2019
2 parents 1d844b6 + 1d91f0c commit 2053db3
Show file tree
Hide file tree
Showing 5 changed files with 255 additions and 11 deletions.
10 changes: 10 additions & 0 deletions core/include/prometheus/family.h
Expand Up @@ -118,6 +118,16 @@ class PROMETHEUS_CPP_CORE_EXPORT 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
33 changes: 31 additions & 2 deletions core/include/prometheus/registry.h
Expand Up @@ -40,8 +40,27 @@ class Builder;
/// a data race.
class PROMETHEUS_CPP_CORE_EXPORT Registry : public Collectable {
public:
/// \brief How to deal with repeatedly added family names for a type.
///
/// Adding a family with the same name but different types is always an error
/// and will lead to an exception.
enum class InsertBehavior {
/// \brief If a family with the same name and labels already exists return
/// the existing one. If no family with that name exists create it.
/// Otherwise throw.
Merge,
/// \brief Never merge and always create a new family. This violates the
/// prometheus specification.
NonStandardAppend,
/// \brief Throws if a family with the same name already exists.
Throw,
};

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

/// \brief name Destroys a registry.
~Registry();
Expand All @@ -58,11 +77,21 @@ class PROMETHEUS_CPP_CORE_EXPORT Registry : public Collectable {
template <typename T>
friend class detail::Builder;

template <typename T>
std::vector<std::unique_ptr<Family<T>>>& GetFamilies();

template <typename T>
bool NameExistsInOtherType(const std::string& name) const;

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

std::vector<std::unique_ptr<Collectable>> collectables_;
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_;
};

Expand Down
10 changes: 10 additions & 0 deletions core/src/family.cc
Expand Up @@ -58,6 +58,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
112 changes: 105 additions & 7 deletions core/src/registry.cc
Expand Up @@ -9,29 +9,127 @@

namespace prometheus {

Registry::Registry() = default;
namespace {
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()));
}
}

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...);
}
} // namespace

Registry::Registry(InsertBehavior insert_behavior)
: insert_behavior_{insert_behavior} {}

Registry::~Registry() = default;

std::vector<MetricFamily> Registry::Collect() {
std::lock_guard<std::mutex> lock{mutex_};
auto results = std::vector<MetricFamily>{};
for (auto&& collectable : collectables_) {
auto metrics = collectable->Collect();
results.insert(results.end(), std::make_move_iterator(metrics.begin()),
std::make_move_iterator(metrics.end()));
}

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

return results;
}

template <>
std::vector<std::unique_ptr<Family<Counter>>>& Registry::GetFamilies() {
return counters_;
}

template <>
std::vector<std::unique_ptr<Family<Gauge>>>& Registry::GetFamilies() {
return gauges_;
}

template <>
std::vector<std::unique_ptr<Family<Histogram>>>& Registry::GetFamilies() {
return histograms_;
}

template <>
std::vector<std::unique_ptr<Family<Summary>>>& Registry::GetFamilies() {
return summaries_;
}

template <>
bool Registry::NameExistsInOtherType<Counter>(const std::string& name) const {
return FamilyNameExists(name, gauges_, histograms_, summaries_);
}

template <>
bool Registry::NameExistsInOtherType<Gauge>(const std::string& name) const {
return FamilyNameExists(name, counters_, histograms_, summaries_);
}

template <>
bool Registry::NameExistsInOtherType<Histogram>(const std::string& name) const {
return FamilyNameExists(name, counters_, gauges_, summaries_);
}

template <>
bool Registry::NameExistsInOtherType<Summary>(const std::string& name) const {
return FamilyNameExists(name, counters_, gauges_, histograms_);
}

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_};

if (NameExistsInOtherType<T>(name)) {
throw std::invalid_argument(
"Family name already exists with different type");
}

auto& families = GetFamilies<T>();

if (insert_behavior_ == InsertBehavior::Merge) {
auto same_name_and_labels =
[&name, &labels](const std::unique_ptr<Family<T>>& family) {
return std::tie(name, labels) ==
std::tie(family->GetName(), family->GetConstantLabels());
};

auto it =
std::find_if(families.begin(), families.end(), same_name_and_labels);
if (it != families.end()) {
return **it;
}
}

if (insert_behavior_ != InsertBehavior::NonStandardAppend) {
auto same_name = [&name](const std::unique_ptr<Family<T>>& family) {
return name == family->GetName();
};

auto it = std::find_if(families.begin(), families.end(), same_name);
if (it != families.end()) {
throw std::invalid_argument("Family name already exists");
}
}

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

Expand Down
101 changes: 99 additions & 2 deletions core/tests/registry_test.cc
@@ -1,13 +1,12 @@
#include "prometheus/registry.h"
#include "prometheus/counter.h"
#include "prometheus/histogram.h"
#include "prometheus/summary.h"

#include <vector>

#include <gmock/gmock.h>

#include "prometheus/collectable.h"

namespace prometheus {
namespace {

Expand Down Expand Up @@ -39,5 +38,103 @@ TEST(RegistryTest, build_histogram_family) {
ASSERT_EQ(collected.size(), 1U);
}

TEST(RegistryTest, reject_different_type_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_different_type_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_different_type_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_different_type_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::NonStandardAppend};

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, throw_for_same_family_name) {
const auto same_name = std::string{"same_name"};
Registry registry{Registry::InsertBehavior::Throw};

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

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());
}

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

EXPECT_NO_THROW(BuildCounter()
.Name("counter")
.Help("Test Counter")
.Labels({{"a", "A"}})
.Register(registry));

EXPECT_ANY_THROW(BuildCounter()
.Name("counter")
.Help("Test Counter")
.Labels({{"b", "B"}})
.Register(registry));
}

} // namespace
} // namespace prometheus

0 comments on commit 2053db3

Please sign in to comment.