Unverified Commit 2cfdc177 authored by Dominik Charousset's avatar Dominik Charousset Committed by GitHub

Merge pull request #1348

Fix instance getters on the metric registry
parents 1ea3128e 052e3b60
...@@ -16,6 +16,8 @@ is based on [Keep a Changelog](https://keepachangelog.com). ...@@ -16,6 +16,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
- The JSON reader now automatically widens integers to doubles as necessary. - The JSON reader now automatically widens integers to doubles as necessary.
- Module options (e.g. for the `middleman`) now show up in `--long-help` output. - Module options (e.g. for the `middleman`) now show up in `--long-help` output.
- Fix undefined behavior in the Qt group chat example (#1336). - Fix undefined behavior in the Qt group chat example (#1336).
- The `..._instance` convenience functions on the registry metric now properly
support `double` metrics and histograms.
### Changed ### Changed
......
...@@ -136,8 +136,9 @@ public: ...@@ -136,8 +136,9 @@ public:
gauge_instance(std::string_view prefix, std::string_view name, gauge_instance(std::string_view prefix, std::string_view name,
span_t<label_view> labels, std::string_view helptext, span_t<label_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false) { std::string_view unit = "1", bool is_sum = false) {
auto fptr = gauge_family<ValueType>(prefix, name, labels, helptext, unit, auto label_names = get_label_names(labels);
is_sum); auto fptr = gauge_family<ValueType>(prefix, name, label_names, helptext,
unit, is_sum);
return fptr->get_or_add(labels); return fptr->get_or_add(labels);
} }
...@@ -228,27 +229,6 @@ public: ...@@ -228,27 +229,6 @@ public:
is_sum); is_sum);
} }
/// @copydoc counter_family
template <class ValueType = int64_t>
metric_family_impl<counter<ValueType>>*
counter_family(std::string_view prefix, std::string_view name,
span_t<label_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false) {
using counter_type = counter<ValueType>;
using family_type = metric_family_impl<counter_type>;
std::unique_lock<std::mutex> guard{families_mx_};
if (auto ptr = fetch(prefix, name)) {
assert_properties(ptr, counter_type::runtime_type, labels, unit, is_sum);
return static_cast<family_type*>(ptr);
}
auto ptr = std::make_unique<family_type>(
std::string{prefix}, std::string{name}, to_sorted_vec(labels),
std::string{helptext}, std::string{unit}, is_sum);
auto result = ptr.get();
families_.emplace_back(std::move(ptr));
return result;
}
/// Returns a counter. Creates all objects lazily if necessary, but fails /// Returns a counter. Creates all objects lazily if necessary, but fails
/// if the full name already belongs to a different family. /// if the full name already belongs to a different family.
/// @param prefix The prefix (namespace) this family belongs to. Usually the /// @param prefix The prefix (namespace) this family belongs to. Usually the
...@@ -269,8 +249,9 @@ public: ...@@ -269,8 +249,9 @@ public:
counter_instance(std::string_view prefix, std::string_view name, counter_instance(std::string_view prefix, std::string_view name,
span_t<label_view> labels, std::string_view helptext, span_t<label_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false) { std::string_view unit = "1", bool is_sum = false) {
auto fptr = counter_family<ValueType>(prefix, name, labels, helptext, unit, auto label_names = get_label_names(labels);
is_sum); auto fptr = counter_family<ValueType>(prefix, name, label_names, helptext,
unit, is_sum);
return fptr->get_or_add(labels); return fptr->get_or_add(labels);
} }
...@@ -419,8 +400,10 @@ public: ...@@ -419,8 +400,10 @@ public:
span_t<label_view> labels, span_t<ValueType> upper_bounds, span_t<label_view> labels, span_t<ValueType> upper_bounds,
std::string_view helptext, std::string_view unit = "1", std::string_view helptext, std::string_view unit = "1",
bool is_sum = false) { bool is_sum = false) {
auto fptr = histogram_family<ValueType>(prefix, name, labels, upper_bounds, auto label_names = get_label_names(labels);
helptext, unit, is_sum); auto fptr = histogram_family<ValueType>(prefix, name, label_names,
upper_bounds, helptext, unit,
is_sum);
return fptr->get_or_add(labels); return fptr->get_or_add(labels);
} }
...@@ -432,8 +415,8 @@ public: ...@@ -432,8 +415,8 @@ public:
span_t<ValueType> upper_bounds, std::string_view helptext, span_t<ValueType> upper_bounds, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false) { std::string_view unit = "1", bool is_sum = false) {
span_t<label_view> lbls{labels.begin(), labels.size()}; span_t<label_view> lbls{labels.begin(), labels.size()};
return histogram_instance(prefix, name, lbls, upper_bounds, helptext, unit, return histogram_instance<ValueType>(prefix, name, lbls, upper_bounds,
is_sum); helptext, unit, is_sum);
} }
/// Returns a histogram metric singleton, i.e., the single instance of a /// Returns a histogram metric singleton, i.e., the single instance of a
...@@ -489,6 +472,8 @@ private: ...@@ -489,6 +472,8 @@ private:
metric_family* fetch(const std::string_view& prefix, metric_family* fetch(const std::string_view& prefix,
const std::string_view& name); const std::string_view& name);
static std::vector<std::string_view> get_label_names(span_t<label_view> xs);
static std::vector<std::string> to_sorted_vec(span_t<std::string_view> xs); static std::vector<std::string> to_sorted_vec(span_t<std::string_view> xs);
static std::vector<std::string> to_sorted_vec(span_t<label_view> xs); static std::vector<std::string> to_sorted_vec(span_t<label_view> xs);
......
...@@ -63,6 +63,15 @@ metric_family* metric_registry::fetch(const std::string_view& prefix, ...@@ -63,6 +63,15 @@ metric_family* metric_registry::fetch(const std::string_view& prefix,
return nullptr; return nullptr;
} }
std::vector<std::string_view>
metric_registry::get_label_names(span_t<label_view> xs) {
std::vector<std::string_view> result;
result.reserve(xs.size());
for (auto& x : xs)
result.push_back(x.name());
return result;
}
std::vector<std::string> std::vector<std::string>
metric_registry::to_sorted_vec(span<const std::string_view> xs) { metric_registry::to_sorted_vec(span<const std::string_view> xs) {
std::vector<std::string> result; std::vector<std::string> result;
......
...@@ -80,7 +80,7 @@ struct test_collector { ...@@ -80,7 +80,7 @@ struct test_collector {
}; };
struct fixture { struct fixture {
metric_registry registry; metric_registry reg;
test_collector collector; test_collector collector;
}; };
...@@ -90,9 +90,9 @@ BEGIN_FIXTURE_SCOPE(fixture) ...@@ -90,9 +90,9 @@ BEGIN_FIXTURE_SCOPE(fixture)
CAF_TEST(registries lazily create metrics) { CAF_TEST(registries lazily create metrics) {
std::vector<int64_t> upper_bounds{1, 2, 4, 8}; std::vector<int64_t> upper_bounds{1, 2, 4, 8};
auto f = registry.gauge_family("caf", "running-actors", {"var1", "var2"}, auto f = reg.gauge_family("caf", "running-actors", {"var1", "var2"},
"How many actors are currently running?"); "How many actors are currently running?");
auto g = registry.histogram_family("caf", "response-time", {"var1", "var2"}, auto g = reg.histogram_family("caf", "response-time", {"var1", "var2"},
upper_bounds, "How long take requests?"); upper_bounds, "How long take requests?");
std::vector<label_view> v1{{"var1", "foo"}, {"var2", "bar"}}; std::vector<label_view> v1{{"var1", "foo"}, {"var2", "bar"}};
std::vector<label_view> v1_reversed{{"var2", "bar"}, {"var1", "foo"}}; std::vector<label_view> v1_reversed{{"var2", "bar"}, {"var1", "foo"}};
...@@ -113,24 +113,21 @@ CAF_TEST(registries lazily create metrics) { ...@@ -113,24 +113,21 @@ CAF_TEST(registries lazily create metrics) {
} }
CAF_TEST(registries allow users to collect all registered metrics) { CAF_TEST(registries allow users to collect all registered metrics) {
auto fb = registry.gauge_family("foo", "bar", {}, auto fb = reg.gauge_family("foo", "bar", {}, "Some value without labels.",
"Some value without labels.", "seconds"); "seconds");
auto sv = registry.gauge_family("some", "value", {"a", "b"}, auto sv = reg.gauge_family("some", "value", {"a", "b"},
"Some (total) value with two labels.", "1", "Some (total) value with two labels.", "1", true);
auto ov = reg.gauge_family("other", "value", {"x"},
"Some (total) seconds with one label.", "seconds",
true); true);
auto ov = registry.gauge_family("other", "value", {"x"}, auto ra = reg.gauge_family("caf", "running-actors", {"node"},
"Some (total) seconds with one label.",
"seconds", true);
auto ra = registry.gauge_family("caf", "running-actors", {"node"},
"How many actors are running?"); "How many actors are running?");
auto ms = registry.gauge_family("caf", "mailbox-size", {"name"}, auto ms = reg.gauge_family("caf", "mailbox-size", {"name"},
"How full is the mailbox?"); "How full is the mailbox?");
MESSAGE("the registry always returns the same family object"); MESSAGE("the registry always returns the same family object");
CHECK_EQ(fb, registry.gauge_family("foo", "bar", {}, "", "seconds")); CHECK_EQ(fb, reg.gauge_family("foo", "bar", {}, "", "seconds"));
CHECK_EQ(sv, CHECK_EQ(sv, reg.gauge_family("some", "value", {"a", "b"}, "", "1", true));
registry.gauge_family("some", "value", {"a", "b"}, "", "1", true)); CHECK_EQ(sv, reg.gauge_family("some", "value", {"b", "a"}, "", "1", true));
CHECK_EQ(sv,
registry.gauge_family("some", "value", {"b", "a"}, "", "1", true));
MESSAGE("families always return the same metric object for given labels"); MESSAGE("families always return the same metric object for given labels");
CHECK_EQ(fb->get_or_add({}), fb->get_or_add({})); CHECK_EQ(fb->get_or_add({}), fb->get_or_add({}));
CHECK_EQ(sv->get_or_add({{"a", "1"}, {"b", "2"}}), CHECK_EQ(sv->get_or_add({{"a", "1"}, {"b", "2"}}),
...@@ -143,7 +140,7 @@ CAF_TEST(registries allow users to collect all registered metrics) { ...@@ -143,7 +140,7 @@ CAF_TEST(registries allow users to collect all registered metrics) {
ra->get_or_add({{"node", "localhost"}})->value(42); ra->get_or_add({{"node", "localhost"}})->value(42);
ms->get_or_add({{"name", "printer"}})->value(3); ms->get_or_add({{"name", "printer"}})->value(3);
ms->get_or_add({{"name", "parser"}})->value(12); ms->get_or_add({{"name", "parser"}})->value(12);
registry.collect(collector); reg.collect(collector);
CHECK_EQ(collector.result, R"( CHECK_EQ(collector.result, R"(
foo.bar.seconds 123 foo.bar.seconds 123
some.value.total{a="1",b="2"} 12 some.value.total{a="1",b="2"} 12
...@@ -168,8 +165,8 @@ CAF_TEST(buckets for histograms are configurable via runtime settings) { ...@@ -168,8 +165,8 @@ CAF_TEST(buckets for histograms are configurable via runtime settings) {
std::vector<int64_t> alternative_upper_bounds{10, 20, 30}; std::vector<int64_t> alternative_upper_bounds{10, 20, 30};
put(cfg, "caf.response-time.buckets", upper_bounds); put(cfg, "caf.response-time.buckets", upper_bounds);
put(cfg, "caf.response-time.var1=foo.buckets", alternative_upper_bounds); put(cfg, "caf.response-time.var1=foo.buckets", alternative_upper_bounds);
registry.config(&cfg); reg.config(&cfg);
auto hf = registry.histogram_family("caf", "response-time", {"var1", "var2"}, auto hf = reg.histogram_family("caf", "response-time", {"var1", "var2"},
default_upper_bounds, default_upper_bounds,
"How long take requests?"); "How long take requests?");
CHECK_EQ(hf->config(), get_if<settings>(&cfg, "caf.response-time")); CHECK_EQ(hf->config(), get_if<settings>(&cfg, "caf.response-time"));
...@@ -181,15 +178,92 @@ CAF_TEST(buckets for histograms are configurable via runtime settings) { ...@@ -181,15 +178,92 @@ CAF_TEST(buckets for histograms are configurable via runtime settings) {
CHECK_EQ(bounds(h2->buckets()), alternative_upper_bounds); CHECK_EQ(bounds(h2->buckets()), alternative_upper_bounds);
} }
CAF_TEST(counter_instance is a shortcut for using the family manually) { SCENARIO("instance methods provide a shortcut for using the family manually") {
auto fptr = registry.counter_family("http", "requests", {"method"}, GIVEN("an int counter family with at least one label dimension") {
WHEN("calling counter_instance on the registry") {
THEN("calling get_or_add on the family object returns the same pointer") {
auto fp = reg.counter_family("http", "requests", {"method"},
"Number of HTTP requests.", "seconds",
true);
auto p1 = fp->get_or_add({{"method", "put"}});
auto p2 = reg.counter_instance("http", "requests", {{"method", "put"}},
"Number of HTTP requests.", "seconds", "Number of HTTP requests.", "seconds",
true); true);
auto count = fptr->get_or_add({{"method", "put"}}); CHECK_EQ(p1, p2);
auto count2 }
= registry.counter_instance("http", "requests", {{"method", "put"}}, }
"Number of HTTP requests.", "seconds", true); }
CHECK_EQ(count, count2); GIVEN("an int gauge family with at least one label dimension") {
WHEN("calling gauge_instance on the registry") {
THEN("calling get_or_add on the family object returns the same pointer") {
auto fp = reg.gauge_family("db", "pending", {"operation"},
"Pending DB operations.");
auto p1 = fp->get_or_add({{"operation", "update"}});
auto p2 = reg.gauge_instance("db", "pending", {{"operation", "update"}},
"Pending DB operations.");
CHECK_EQ(p1, p2);
}
}
}
GIVEN("an int histogram family with at least one label dimension") {
WHEN("calling histogram_instance on the registry") {
THEN("calling get_or_add on the family object returns the same pointer") {
std::vector<int64_t> upper_bounds{1, 2, 3, 5, 7};
auto fp = reg.histogram_family("db", "query-results", {"operation"},
upper_bounds, "Results per query.");
auto p1 = fp->get_or_add({{"operation", "update"}});
auto p2 = reg.histogram_instance("db", "query-results",
{{"operation", "update"}},
upper_bounds, "Results per query.");
CHECK_EQ(p1, p2);
}
}
}
GIVEN("a double counter family with at least one label dimension") {
WHEN("calling counter_instance on the registry") {
THEN("calling get_or_add on the family object returns the same pointer") {
auto fp = reg.counter_family<double>("db", "cpu-usage", {"operation"},
"Total CPU time by query type.",
"seconds", true);
auto p1 = fp->get_or_add({{"operation", "update"}});
auto p2 = reg.counter_instance<double>("db", "cpu-usage",
{{"operation", "update"}},
"Total CPU time by query type.",
"seconds", true);
CHECK_EQ(p1, p2);
}
}
}
GIVEN("a double gauge family with at least one label dimension") {
WHEN("calling gauge_instance on the registry") {
THEN("calling get_or_add on the family object returns the same pointer") {
auto fp = reg.gauge_family<double>("sensor", "water-level",
{"location"},
"Water level by location.");
auto p1 = fp->get_or_add({{"location", "tank-1"}});
auto p2 = reg.gauge_instance<double>("sensor", "water-level",
{{"location", "tank-1"}},
"Water level by location.");
CHECK_EQ(p1, p2);
}
}
}
GIVEN("a double histogram family with at least one label dimension") {
WHEN("calling histogram_instance on the registry") {
THEN("calling get_or_add on the family object returns the same pointer") {
std::vector<double> upper_bounds{1, 2, 3, 5, 7};
auto fp = reg.histogram_family<double>("db", "query-duration",
{"operation"}, upper_bounds,
"Query processing time.");
auto p1 = fp->get_or_add({{"operation", "update"}});
auto p2 = reg.histogram_instance<double>("db", "query-duration",
{{"operation", "update"}},
upper_bounds,
"Query processing time.");
CHECK_EQ(p1, p2);
}
}
}
} }
SCENARIO("metric registries can merge families from other registries") { SCENARIO("metric registries can merge families from other registries") {
...@@ -198,12 +272,10 @@ SCENARIO("metric registries can merge families from other registries") { ...@@ -198,12 +272,10 @@ SCENARIO("metric registries can merge families from other registries") {
auto foo_bar = tmp.counter_singleton("foo", "bar", "test metric"); auto foo_bar = tmp.counter_singleton("foo", "bar", "test metric");
auto bar_foo = tmp.counter_singleton("bar", "foo", "test metric"); auto bar_foo = tmp.counter_singleton("bar", "foo", "test metric");
WHEN("merging the registry into another one") { WHEN("merging the registry into another one") {
registry.merge(tmp); reg.merge(tmp);
THEN("all metrics move into the new location") { THEN("all metrics move into the new location") {
CHECK_EQ(foo_bar, CHECK_EQ(foo_bar, reg.counter_singleton("foo", "bar", "test metric"));
registry.counter_singleton("foo", "bar", "test metric")); CHECK_EQ(bar_foo, reg.counter_singleton("bar", "foo", "test metric"));
CHECK_EQ(bar_foo,
registry.counter_singleton("bar", "foo", "test metric"));
tmp.collect(collector); tmp.collect(collector);
CHECK(collector.result.empty()); CHECK(collector.result.empty());
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment