diff --git a/core/benchmarks/registry_bench.cc b/core/benchmarks/registry_bench.cc index b33ddc4d..46645900 100644 --- a/core/benchmarks/registry_bench.cc +++ b/core/benchmarks/registry_bench.cc @@ -42,3 +42,37 @@ static void BM_Registry_CreateCounter(benchmark::State& state) { } } BENCHMARK(BM_Registry_CreateCounter)->Range(0, 4096); + +static void BM_Registry_CreateCounter_WithLabelValues(benchmark::State& state) { + using prometheus::BuildCounter; + using prometheus::Counter; + using prometheus::Registry; + Registry registry; + + const auto& labels = GenerateRandomLabels(state.range(0)); + std::vector label_names; + std::vector label_values; + std::for_each(labels.begin(), labels.end(), + [&](const std::pair& p){ + label_names.push_back(p.first); + label_values.push_back(p.second); + }); + + auto& counter_family = BuildCounter() + .Labels(GenerateRandomLabels(10)) + .Name("benchmark_counter") + .Help("") + .LabelNamesVec(label_names) + .Register(registry); + + while (state.KeepRunning()) { + auto start = std::chrono::high_resolution_clock::now(); + counter_family.WithLabelValues(label_values); + auto end = std::chrono::high_resolution_clock::now(); + + auto elapsed_seconds = + std::chrono::duration_cast>(end - start); + state.SetIterationTime(elapsed_seconds.count()); + } +} +BENCHMARK(BM_Registry_CreateCounter_WithLabelValues)->Range(0, 4096); \ No newline at end of file diff --git a/core/include/prometheus/counter.h b/core/include/prometheus/counter.h index c67ddde2..e7718b6d 100644 --- a/core/include/prometheus/counter.h +++ b/core/include/prometheus/counter.h @@ -5,6 +5,7 @@ #include "prometheus/detail/core_export.h" #include "prometheus/gauge.h" #include "prometheus/metric_type.h" +#include "prometheus/family.h" namespace prometheus { @@ -64,6 +65,22 @@ class PROMETHEUS_CPP_CORE_EXPORT Counter { /// .Labels({{"key", "value"}}) /// .Register(*registry); /// +/// counter_family.Add({{"key2","value2"}}).Increment(); +/// ... +/// \endcode +/// +/// Example usage2: +/// +/// \code +/// auto registry = std::make_shared(); +/// auto& counter_family = prometheus::BuildCounter() +/// .Name("some_name") +/// .Help("Additional description.") +/// .Labels({{"key", "value"}}) +/// .LabelNamesVec({"key2","key3"}) +/// .Register(*registry); +/// +/// counter_family.WithLabelValues({"value2","value3"}).Increment(); /// ... /// \endcode /// @@ -74,9 +91,16 @@ class PROMETHEUS_CPP_CORE_EXPORT Counter { /// - Help(const std::string&) to set an additional description. /// - Label(const std::map&) to assign a set of /// key-value pairs (= labels) to the metric. +/// - LabelNamesVec(const std::vector.size() == vector.size() /// /// To finish the configuration of the Counter metric, register it with /// Register(Registry&). PROMETHEUS_CPP_CORE_EXPORT detail::Builder BuildCounter(); +/// \brief Specialization of WithLabelValues. +PROMETHEUS_CPP_CORE_EXPORT template <> +Counter& Family::WithLabelValues(const std::vector& values); + } // namespace prometheus diff --git a/core/include/prometheus/detail/builder.h b/core/include/prometheus/detail/builder.h index f811498f..0e854695 100644 --- a/core/include/prometheus/detail/builder.h +++ b/core/include/prometheus/detail/builder.h @@ -1,13 +1,18 @@ #pragma once #include +#include #include +#include "prometheus/detail/ckms_quantiles.h" // IWYU pragma: private // IWYU pragma: no_include "prometheus/family.h" namespace prometheus { +class Counter; +class Summary; +class Histogram; template class Family; // IWYU pragma: keep class Registry; // IWYU pragma: keep @@ -18,15 +23,39 @@ template class Builder { public: Builder& Labels(const std::map& labels); + Builder& LabelNamesVec(const std::vector& labels); Builder& Name(const std::string&); Builder& Help(const std::string&); Family& Register(Registry&); - private: + template ::value,Summary>::type> + Builder& Quantiles(const std::vector&); + + template ::value,Histogram>::type> + Builder& BucketBoundaries(const std::vector&); + +private: std::map labels_; + std::vector variable_labels_; + std::vector quantiles_; + std::vector bucket_boundaries_; std::string name_; std::string help_; }; +template +template +Builder& Builder::Quantiles(const std::vector& quantiles) { + quantiles_ = quantiles; + return *this; +} + +template +template +Builder& Builder::BucketBoundaries(const std::vector& bucket_boundaries) { + bucket_boundaries_ = bucket_boundaries; + return *this; +} + } // namespace detail } // namespace prometheus diff --git a/core/include/prometheus/detail/ckms_quantiles.h b/core/include/prometheus/detail/ckms_quantiles.h index dad7815c..27c6aec1 100644 --- a/core/include/prometheus/detail/ckms_quantiles.h +++ b/core/include/prometheus/detail/ckms_quantiles.h @@ -15,10 +15,10 @@ namespace detail { class PROMETHEUS_CPP_CORE_EXPORT CKMSQuantiles { public: struct PROMETHEUS_CPP_CORE_EXPORT Quantile { - const double quantile; - const double error; - const double u; - const double v; + double quantile; + double error; + double u; + double v; Quantile(double quantile, double error); }; diff --git a/core/include/prometheus/family.h b/core/include/prometheus/family.h index 1c333a7a..b966a4b1 100644 --- a/core/include/prometheus/family.h +++ b/core/include/prometheus/family.h @@ -12,6 +12,8 @@ #include "prometheus/collectable.h" #include "prometheus/detail/core_export.h" #include "prometheus/detail/future_std.h" +#include "prometheus/detail/utils.h" +#include "prometheus/detail/ckms_quantiles.h" #include "prometheus/metric_family.h" // IWYU pragma: no_include "prometheus/counter.h" @@ -21,6 +23,9 @@ namespace prometheus { +class Summary; +class Histogram; + /// \brief A metric of type T with a set of labeled dimensions. /// /// One of Prometheus main feature is a multi-dimensional data model with time @@ -91,6 +96,10 @@ class PROMETHEUS_CPP_CORE_EXPORT Family : public Collectable { Family(const std::string& name, const std::string& help, const std::map& constant_labels); + Family(const std::string& name, const std::string& help, + const std::vector& variable_labels, + const std::map& constant_labels); + /// \brief Add a new dimensional data. /// /// Each new set of labels adds a new dimensional data and is exposed in @@ -112,6 +121,23 @@ class PROMETHEUS_CPP_CORE_EXPORT Family : public Collectable { T& Add(const std::map& labels, Args&&... args) { return Add(labels, detail::make_unique(args...)); } + /// \brief Add a new dimensional data. + /// different with Add, this method call only when build.LavelVec(...) + /// is call before. + /// then this method call Add + T& WithLabelValues(const std::vector& values); + + /// \brief when T is Summary, set the Quantiles. + /// should be call before call WithLabelValues + template + typename std::enable_if<(std::is_same::value), Family&>::type + SetQuantiles(std::vector& quantiles); + + /// \brief when T is Summary, set the BucketBoundaries. + /// should be call before call WithLabelValues + template + typename std::enable_if<(std::is_same::value), Family&>::type + SetBucketBoundaries(std::vector& bucket_boundaries); /// \brief Remove the given dimensional data. /// @@ -143,12 +169,34 @@ class PROMETHEUS_CPP_CORE_EXPORT Family : public Collectable { const std::string name_; const std::string help_; + std::vector variable_labels_; const std::map constant_labels_; + std::vector quantiles_; + std::vector bucket_boundaries_; mutable std::mutex mutex_; ClientMetric CollectMetric(std::size_t hash, T* metric) const; T& Add(const std::map& labels, std::unique_ptr object); + std::map VariableLabels( + const std::vector& values); }; +template +template +typename std::enable_if<(std::is_same::value), Family&>::type +Family::SetQuantiles(std::vector& quantiles){ + quantiles_ = quantiles; + return *this; +} + +template +template +typename std::enable_if<(std::is_same::value), Family&>::type +Family::SetBucketBoundaries(std::vector& bucket_boundaries){ + bucket_boundaries_ = bucket_boundaries; + return *this; +} + + } // namespace prometheus diff --git a/core/include/prometheus/gauge.h b/core/include/prometheus/gauge.h index 62498376..23e2aef1 100644 --- a/core/include/prometheus/gauge.h +++ b/core/include/prometheus/gauge.h @@ -6,6 +6,7 @@ #include "prometheus/detail/builder.h" // IWYU pragma: export #include "prometheus/detail/core_export.h" #include "prometheus/metric_type.h" +#include "prometheus/family.h" namespace prometheus { @@ -76,6 +77,22 @@ class PROMETHEUS_CPP_CORE_EXPORT Gauge { /// .Labels({{"key", "value"}}) /// .Register(*registry); /// +/// gauge_family.Add({{"key2","value2"}}).Increment(); +/// ... +/// \endcode +/// +/// Example usage: +/// +/// \code +/// auto registry = std::make_shared(); +/// auto& gauge_family = prometheus::BuildGauge() +/// .Name("some_name") +/// .Help("Additional description.") +/// .Labels({{"key", "value"}}) +/// .LabelNamesVec({"key2","key3"}) +/// .Register(*registry); +/// +/// gauge_family.WithLabelValues({"value2","value3"}).Increment(); /// ... /// \endcode /// @@ -86,9 +103,16 @@ class PROMETHEUS_CPP_CORE_EXPORT Gauge { /// - Help(const std::string&) to set an additional description. /// - Label(const std::map&) to assign a set of /// key-value pairs (= labels) to the metric. +/// - LabelNamesVec(const std::vector.size() == vector.size() /// /// To finish the configuration of the Gauge metric register it with /// Register(Registry&). PROMETHEUS_CPP_CORE_EXPORT detail::Builder BuildGauge(); +/// \brief Specialization of WithLabelValues. +PROMETHEUS_CPP_CORE_EXPORT template <> +Gauge& Family::WithLabelValues(const std::vector& values); + } // namespace prometheus diff --git a/core/include/prometheus/histogram.h b/core/include/prometheus/histogram.h index f8be2435..9f794b73 100644 --- a/core/include/prometheus/histogram.h +++ b/core/include/prometheus/histogram.h @@ -8,6 +8,7 @@ #include "prometheus/detail/core_export.h" #include "prometheus/gauge.h" #include "prometheus/metric_type.h" +#include "prometheus/family.h" namespace prometheus { @@ -28,6 +29,18 @@ namespace prometheus { /// The class is thread-safe. No concurrent call to any API of this type causes /// a data race. class PROMETHEUS_CPP_CORE_EXPORT Histogram { + public: + + /// \brief ExponentialBuckets creates 'count' buckets, where the lowest bucket has an + /// upper bound of 'start' and each following bucket's upper bound is 'factor' + /// times the previous bucket's upper bound. The final +Inf bucket is not counted + /// and not included in the returned vector. The returned vector is meant to be + /// used for the Buckets field of Histogram. + /// + /// The function assert if 'count' is 0 or negative, if 'start' is 0 or negative, + /// or if 'factor' is less than or equal 1. + static std::vector ExponentialBuckets(double start, + double factor, int count); public: using BucketBoundaries = std::vector; @@ -86,6 +99,23 @@ class PROMETHEUS_CPP_CORE_EXPORT Histogram { /// .Labels({{"key", "value"}}) /// .Register(*registry); /// +/// histogram_family.Add({{"key1","value1"}}, Histogram::BucketBoundaries{1, 2}).Observe(1.0); +/// ... +/// \endcode +/// +/// Example usage2: +/// +/// \code +/// auto registry = std::make_shared(); +/// auto& histogram_family = prometheus::BuildHistogram() +/// .Name("some_name") +/// .Help("Additional description.") +/// .Labels({{"key", "value"}}) +/// .LabelNamesVec({"key2","key3"}) +/// .BucketBoundaries({Histogram::BucketBoundaries{1, 2}}) +/// .Register(*registry); +/// +/// histogram_family.WithLabelValues({"value2","value3"}).Observe(1.0); /// ... /// \endcode /// @@ -96,9 +126,24 @@ class PROMETHEUS_CPP_CORE_EXPORT Histogram { /// - Help(const std::string&) to set an additional description. /// - Label(const std::map&) to assign a set of /// key-value pairs (= labels) to the metric. +/// - LabelNamesVec(const std::vector.size() == vector.size() +/// - BucketBoundaries(const std::vector&) to pre-affirmation bucketBoundaries +/// when use WithLabelValues() /// /// To finish the configuration of the Histogram metric register it with /// Register(Registry&). PROMETHEUS_CPP_CORE_EXPORT detail::Builder BuildHistogram(); +/// \brief Specialization of WithLabelValues. +PROMETHEUS_CPP_CORE_EXPORT template <> +Histogram& Family::WithLabelValues(const std::vector& values); + +namespace detail { +/// \brief Specialization of Register. +PROMETHEUS_CPP_CORE_EXPORT template<> +Family &Builder::Register(Registry ®istry); +} // namespace detail + } // namespace prometheus diff --git a/core/include/prometheus/registry.h b/core/include/prometheus/registry.h index ef83438c..164165fd 100644 --- a/core/include/prometheus/registry.h +++ b/core/include/prometheus/registry.h @@ -84,6 +84,7 @@ class PROMETHEUS_CPP_CORE_EXPORT Registry : public Collectable { template Family& Add(const std::string& name, const std::string& help, + const std::vector& variable_labels, const std::map& labels); const InsertBehavior insert_behavior_; diff --git a/core/include/prometheus/summary.h b/core/include/prometheus/summary.h index 87381344..466ad410 100644 --- a/core/include/prometheus/summary.h +++ b/core/include/prometheus/summary.h @@ -11,6 +11,7 @@ #include "prometheus/detail/core_export.h" #include "prometheus/detail/time_window_quantiles.h" #include "prometheus/metric_type.h" +#include "prometheus/family.h" namespace prometheus { @@ -105,6 +106,23 @@ class PROMETHEUS_CPP_CORE_EXPORT Summary { /// .Labels({{"key", "value"}}) /// .Register(*registry); /// +/// summary_family.Add({{"key1","value1"}}, Summary::Quantiles{}).Observe(1.0); +/// ... +/// \endcode +/// +/// Example usage2: +/// +/// \code +/// auto registry = std::make_shared(); +/// auto& summary_family = prometheus::BuildSummary() +/// .Name("some_name") +/// .Help("Additional description.") +/// .Labels({{"key", "value"}}) +///// .LabelNamesVec({"key2","key3"}) +/// .Quantiles(Summary::Quantiles{}) +/// .Register(*registry); +/// +/// summary_family.WithLabelValues({"value2","value3"}).Observe(1.0); /// ... /// \endcode /// @@ -115,9 +133,24 @@ class PROMETHEUS_CPP_CORE_EXPORT Summary { /// - Help(const std::string&) to set an additional description. /// - Label(const std::map&) to assign a set of /// key-value pairs (= labels) to the metric. +/// - LabelNamesVec(const std::vector.size() == vector.size() +/// - Quantiles(const std::vector&) to pre-affirmation Quantiles +/// when use WithLabelValues() /// /// To finish the configuration of the Summary metric register it with /// Register(Registry&). PROMETHEUS_CPP_CORE_EXPORT detail::Builder BuildSummary(); +/// \brief Specialization of WithLabelValues. +PROMETHEUS_CPP_CORE_EXPORT template <> +Summary& Family::WithLabelValues(const std::vector& values); + +namespace detail { +/// \brief Specialization of Register. +PROMETHEUS_CPP_CORE_EXPORT template<> +Family& detail::Builder::Register(Registry& registry); +} // namespace detail + } // namespace prometheus diff --git a/core/src/detail/builder.cc b/core/src/detail/builder.cc index 72253834..ea7e12bb 100644 --- a/core/src/detail/builder.cc +++ b/core/src/detail/builder.cc @@ -18,6 +18,13 @@ Builder& Builder::Labels( return *this; } +template +Builder& Builder::LabelNamesVec( + const std::vector& labels) { + variable_labels_ = labels; + return *this; +} + template Builder& Builder::Name(const std::string& name) { name_ = name; @@ -32,7 +39,7 @@ Builder& Builder::Help(const std::string& help) { template Family& Builder::Register(Registry& registry) { - return registry.Add(name_, help_, labels_); + return registry.Add(name_, help_, variable_labels_, labels_); } template class PROMETHEUS_CPP_CORE_EXPORT Builder; @@ -40,6 +47,20 @@ template class PROMETHEUS_CPP_CORE_EXPORT Builder; template class PROMETHEUS_CPP_CORE_EXPORT Builder; template class PROMETHEUS_CPP_CORE_EXPORT Builder; + +template<> +Family& detail::Builder::Register(Registry& registry){ + Family& family = registry.Add(name_, help_, variable_labels_, labels_); + return family.SetBucketBoundaries(bucket_boundaries_); +} + +template<> +Family &detail::Builder::Register(Registry ®istry) { + Family &family = registry.Add(name_, help_, variable_labels_, labels_); + return family.SetQuantiles(quantiles_); +} + + } // namespace detail detail::Builder BuildCounter() { return {}; } diff --git a/core/src/family.cc b/core/src/family.cc index b78f3619..a95fa837 100644 --- a/core/src/family.cc +++ b/core/src/family.cc @@ -29,6 +29,36 @@ Family::Family(const std::string& name, const std::string& help, } } +template +Family::Family(const std::string& name, const std::string& help, + const std::vector& variable_labels, + const std::map& constant_labels) + : name_(name), help_(help), variable_labels_(variable_labels), + constant_labels_(constant_labels) { + assert(CheckMetricName(name_)); +} + +template +std::map Family::VariableLabels(const std::vector& values) +{ + if (variable_labels_.size() != values.size()) { + throw std::length_error("The size of variable_labels was not equal to" + "the number of values when call WithLabelValues."); + } + std::map labels_map; + + int i = 0; + for (auto str : variable_labels_) { + labels_map.emplace(str, values[i++]); + } + return labels_map; +} + +template +T& Family::WithLabelValues(const std::vector& values) { + return Add(VariableLabels(values)); +} + template T& Family::Add(const std::map& labels, std::unique_ptr object) { @@ -119,10 +149,30 @@ ClientMetric Family::CollectMetric(std::size_t hash, T* metric) const { std::for_each(metric_labels.cbegin(), metric_labels.cend(), add_label); return collected; } - template class PROMETHEUS_CPP_CORE_EXPORT Family; template class PROMETHEUS_CPP_CORE_EXPORT Family; template class PROMETHEUS_CPP_CORE_EXPORT Family; template class PROMETHEUS_CPP_CORE_EXPORT Family; + +template <> +Gauge& Family::WithLabelValues(const std::vector& values) {\ + return Add(VariableLabels(values)); +} + +template <> +Counter& Family::WithLabelValues(const std::vector& values) {\ + return Add(VariableLabels(values)); +} + +template <> +Summary& Family::WithLabelValues(const std::vector& values) { + return Add(VariableLabels(values), quantiles_); +} + +template <> +Histogram& Family::WithLabelValues(const std::vector& values) {\ + return Add(VariableLabels(values), bucket_boundaries_); +} + } // namespace prometheus diff --git a/core/src/histogram.cc b/core/src/histogram.cc index 9c05d066..cc1fd859 100644 --- a/core/src/histogram.cc +++ b/core/src/histogram.cc @@ -1,4 +1,5 @@ #include "prometheus/histogram.h" +#include "prometheus/registry.h" #include #include @@ -11,6 +12,20 @@ namespace prometheus { +std::vector Histogram::ExponentialBuckets(double start, + double factor, int count) { + assert(count >= 1); + assert(start > 0); + assert(factor > 1); + + std::vector buckets; + for (int i=0; i < count; i++) { + buckets.push_back(start); + start *= factor; + } + return buckets; +} + Histogram::Histogram(const BucketBoundaries& buckets) : bucket_boundaries_{buckets}, bucket_counts_{buckets.size() + 1}, sum_{} { assert(std::is_sorted(std::begin(bucket_boundaries_), diff --git a/core/src/registry.cc b/core/src/registry.cc index ad565d35..ed9367d2 100644 --- a/core/src/registry.cc +++ b/core/src/registry.cc @@ -96,6 +96,7 @@ bool Registry::NameExistsInOtherType(const std::string& name) const { template Family& Registry::Add(const std::string& name, const std::string& help, + const std::vector& variable_labels, const std::map& labels) { std::lock_guard lock{mutex_}; @@ -131,7 +132,7 @@ Family& Registry::Add(const std::string& name, const std::string& help, } } - auto family = detail::make_unique>(name, help, labels); + auto family = detail::make_unique>(name, help, variable_labels, labels); auto& ref = *family; families.push_back(std::move(family)); return ref; @@ -139,18 +140,22 @@ Family& Registry::Add(const std::string& name, const std::string& help, template Family& Registry::Add( const std::string& name, const std::string& help, + const std::vector& variable_labels, const std::map& labels); template Family& Registry::Add( const std::string& name, const std::string& help, + const std::vector& variable_labels, const std::map& labels); template Family& Registry::Add( const std::string& name, const std::string& help, + const std::vector& variable_labels, const std::map& labels); template Family& Registry::Add( const std::string& name, const std::string& help, + const std::vector& variable_labels, const std::map& labels); } // namespace prometheus diff --git a/core/src/summary.cc b/core/src/summary.cc index 5e309795..34e64589 100644 --- a/core/src/summary.cc +++ b/core/src/summary.cc @@ -1,4 +1,5 @@ #include "prometheus/summary.h" +#include "prometheus/registry.h" #include diff --git a/core/tests/builder_test.cc b/core/tests/builder_test.cc index ce460d73..cac13d1b 100644 --- a/core/tests/builder_test.cc +++ b/core/tests/builder_test.cc @@ -22,21 +22,36 @@ namespace { class BuilderTest : public testing::Test { protected: + enum class Variable { no, yes }; + + template std::vector getExpectedLabels() { std::vector labels; - auto gen = [](std::pair p) { + int i = 0; + auto gen_pair = [](std::pair p) { return ClientMetric::Label{p.first, p.second}; }; + auto gen_vec = [&](const std::string k) { + return ClientMetric::Label{k, variable_values[i++]}; + }; + std::transform(std::begin(const_labels), std::end(const_labels), - std::back_inserter(labels), gen); - std::transform(std::begin(more_labels), std::end(more_labels), - std::back_inserter(labels), gen); + std::back_inserter(labels), gen_pair); + if ( v == Variable::no ) { + std::transform(std::begin(more_labels), std::end(more_labels), + std::back_inserter(labels), gen_pair); + }else { + std::transform(std::begin(variable_labels), std::end(variable_labels), + std::back_inserter(labels), gen_vec); + + } return labels; } + template void verifyCollectedLabels() { const auto collected = registry.Collect(); @@ -45,17 +60,45 @@ class BuilderTest : public testing::Test { EXPECT_EQ(help, collected.at(0).help); ASSERT_EQ(1U, collected.at(0).metric.size()); - EXPECT_THAT(collected.at(0).metric.at(0).label, - testing::UnorderedElementsAreArray(expected_labels)); + // std::cout<<"===========got==========="< variable_labels = {"vkey","vname"}; + const std::vector variable_values = {"vvalue","vtest"}; const std::map const_labels = {{"key", "value"}}; const std::map more_labels = {{"name", "test"}}; - const std::vector expected_labels = getExpectedLabels(); + const std::vector expected_labels = getExpectedLabels(); + const std::vector expected_variable_labels = getExpectedLabels(); }; TEST_F(BuilderTest, build_counter) { @@ -66,7 +109,7 @@ TEST_F(BuilderTest, build_counter) { .Register(registry); family.Add(more_labels); - verifyCollectedLabels(); + verifyCollectedLabels(); } TEST_F(BuilderTest, build_gauge) { @@ -77,7 +120,7 @@ TEST_F(BuilderTest, build_gauge) { .Register(registry); family.Add(more_labels); - verifyCollectedLabels(); + verifyCollectedLabels(); } TEST_F(BuilderTest, build_histogram) { @@ -88,7 +131,20 @@ TEST_F(BuilderTest, build_histogram) { .Register(registry); family.Add(more_labels, Histogram::BucketBoundaries{1, 2}); - verifyCollectedLabels(); + verifyCollectedLabels(); +} + +TEST_F(BuilderTest, build_histogram_vec) { + auto& family = BuildHistogram() + .Name(name) + .Help(help) + .Labels(const_labels) + .LabelNamesVec(variable_labels) + .BucketBoundaries({Histogram::BucketBoundaries{1, 2}}) + .Register(registry); + family.WithLabelValues(variable_values); + + verifyCollectedLabels(); } TEST_F(BuilderTest, build_summary) { @@ -99,7 +155,20 @@ TEST_F(BuilderTest, build_summary) { .Register(registry); family.Add(more_labels, Summary::Quantiles{}); - verifyCollectedLabels(); + verifyCollectedLabels(); +} + +TEST_F(BuilderTest, build_summary_vec) { + auto& family = BuildSummary() + .Name(name) + .Help(help) + .Labels(const_labels) + .LabelNamesVec(variable_labels) + .Quantiles(Summary::Quantiles{}) + .Register(registry); + family.WithLabelValues(variable_values); + + verifyCollectedLabels(); } } // namespace diff --git a/core/tests/family_test.cc b/core/tests/family_test.cc index ddbe9839..fcd0440b 100644 --- a/core/tests/family_test.cc +++ b/core/tests/family_test.cc @@ -28,6 +28,22 @@ TEST(FamilyTest, labels) { ::testing::ElementsAre(const_label, dynamic_label)); } +TEST(FamilyTest, variable_labels) { + auto const_label = ClientMetric::Label{"component", "test"}; + auto dynamic_label = ClientMetric::Label{"status", "200"}; + + Family family{"total_requests", + "Counts all requests", + {dynamic_label.name}, + {{const_label.name, const_label.value}}}; + family.WithLabelValues({{dynamic_label.value}}); + auto collected = family.Collect(); + ASSERT_GE(collected.size(), 1U); + ASSERT_GE(collected.at(0).metric.size(), 1U); + EXPECT_THAT(collected.at(0).metric.at(0).label, + ::testing::ElementsAre(const_label, dynamic_label)); +} + TEST(FamilyTest, reject_same_label_keys) { auto labels = std::map{{"component", "test"}}; @@ -45,6 +61,24 @@ TEST(FamilyTest, counter_value) { EXPECT_EQ(1, collected[0].metric.at(0).counter.value); } +TEST(FamilyTest, variable_counter_value) { + Family family{"total_requests", "Counts all requests", {}}; + auto& counter = family.WithLabelValues({}); + counter.Increment(); + auto collected = family.Collect(); + ASSERT_GE(collected.size(), 1U); + ASSERT_GE(collected[0].metric.size(), 1U); + EXPECT_EQ(1, collected[0].metric.at(0).counter.value); +} + +TEST(FamilyTest, should_assert_error_variable_value_size) { + Family family{"total_requests", "Counts all requests", {}}; + auto create_family_with_invalid_name = [&]() { + family.WithLabelValues({"haha"}); + }; + EXPECT_ANY_THROW(create_family_with_invalid_name()); +} + TEST(FamilyTest, remove) { Family family{"total_requests", "Counts all requests", {}}; auto& counter1 = family.Add({{"name", "counter1"}}); diff --git a/pull/tests/integration/sample_server.cc b/pull/tests/integration/sample_server.cc index 742a365c..b80a1fca 100644 --- a/pull/tests/integration/sample_server.cc +++ b/pull/tests/integration/sample_server.cc @@ -29,6 +29,8 @@ int main() { auto& packet_counter = BuildCounter() .Name("observed_packets_total") .Help("Number of observed packets") + .Labels({{"label", "value"}}) + .LabelNamesVec({"label", "value"}) .Register(*registry); // add and remember dimensional data, incrementing those is very cheap @@ -66,6 +68,7 @@ int main() { // dynamically calling Family.Add() works but is slow and should be // avoided http_requests_counter.Add({{"method", method}}).Increment(); + packet_counter.WithLabelValues({"v1", "v2"}).Increment(); } return 0; }