Commit 4dc849fa authored by Dominik Charousset's avatar Dominik Charousset

Streamline metric registry API and metric naming

parent 0db5680e
......@@ -75,12 +75,22 @@ public:
void operator()(const metric_family* family, const metric* instance,
const int_gauge* gauge);
void operator()(const metric_family* family, const metric* instance,
const dbl_histogram* val);
void operator()(const metric_family* family, const metric* instance,
const int_histogram* val);
private:
/// Sets `current_family_` if not pointing to `family` already. When setting
/// the member variable, also writes meta information to `buf_`.
void set_current_family(const metric_family* family,
string_view prometheus_type);
template <class ValueType>
void append_histogram(const metric_family* family, const metric* instance,
const histogram<ValueType>* val);
/// Stores the generated text output.
char_buffer buf_;
......@@ -90,6 +100,9 @@ private:
/// Caches type information and help text for a metric.
std::unordered_map<const metric_family*, char_buffer> meta_info_;
/// Caches type information and help text for a metric.
std::unordered_map<const metric*, std::vector<char_buffer>> virtual_metrics_;
/// Caches which metric family is currently collected.
const metric_family* current_family_ = nullptr;
......
......@@ -35,6 +35,8 @@ public:
using value_type = double;
using family_setting = unit_t;
// -- constants --------------------------------------------------------------
static constexpr metric_type runtime_type = metric_type::dbl_gauge;
......
......@@ -39,6 +39,8 @@ public:
using gauge_type = gauge<value_type>;
using family_setting = std::vector<value_type>;
struct bucket_type {
value_type upper_bound;
gauge_type gauge;
......@@ -52,7 +54,7 @@ public:
// -- constructors, destructors, and assignment operators --------------------
histogram(span<const value_type> upper_bounds) {
explicit histogram(span<const value_type> upper_bounds) {
using limits = std::numeric_limits<value_type>;
CAF_ASSERT(std::is_sorted(upper_bounds.begin(), upper_bounds.end()));
num_buckets_ = upper_bounds.size() + 1;
......@@ -66,11 +68,15 @@ public:
buckets_[index].upper_bound = limits::max();
}
histogram(std::initializer_list<value_type> upper_bounds)
explicit histogram(std::initializer_list<value_type> upper_bounds)
: histogram(make_span(upper_bounds.begin(), upper_bounds.size())) {
// nop
}
histogram(const histogram&) = delete;
histogram& operator=(const histogram&) = delete;
~histogram() {
delete[] buckets_;
}
......@@ -106,9 +112,10 @@ private:
gauge_type sum_;
};
///
/// Convenience alias for a histogram with value type `double`.
using dbl_histogram = histogram<double>;
/// Convenience alias for a histogram with value type `int64_t`.
using int_histogram = histogram<int64_t>;
} // namespace caf::telemetry
......@@ -35,6 +35,8 @@ public:
using value_type = int64_t;
using family_setting = unit_t;
// -- constants --------------------------------------------------------------
static constexpr metric_type runtime_type = metric_type::int_gauge;
......
......@@ -59,6 +59,8 @@ public:
str_.size() - name_length_ - 1};
}
void value(string_view new_value);
/// Returns the label in `name=value` notation.
const std::string& str() const noexcept {
return str_;
......
......@@ -36,9 +36,21 @@ namespace caf::telemetry {
template <class Type>
class metric_family_impl : public metric_family {
public:
using super = metric_family;
using impl_type = metric_impl<Type>;
using metric_family::metric_family;
using extra_setting_type = typename impl_type::family_setting;
template <class... Ts>
metric_family_impl(std::string prefix, std::string name,
std::vector<std::string> label_names, std::string helptext,
std::string unit, bool is_sum, Ts&&... xs)
: super(std::move(prefix), std::move(name), std::move(label_names),
std::move(helptext), std::move(unit), is_sum),
extra_setting_(std::forward<Ts>(xs)...) {
// nop
}
metric_type type() const noexcept override {
return Type::runtime_type;
......@@ -55,7 +67,12 @@ public:
if (m == metrics_.end()) {
std::vector<label> cpy{labels.begin(), labels.end()};
std::sort(cpy.begin(), cpy.end());
m = metrics_.emplace(m, std::make_unique<impl_type>(std::move(cpy)));
std::unique_ptr<impl_type> ptr;
if constexpr (std::is_same<extra_setting_type, unit_t>::value)
ptr.reset(new impl_type(std::move(cpy)));
else
ptr.reset(new impl_type(std::move(cpy), extra_setting_));
m = metrics_.emplace(m, std::move(ptr));
}
return std::addressof(m->get()->impl());
}
......@@ -64,6 +81,12 @@ public:
return get_or_add(span<const label_view>{labels.begin(), labels.size()});
}
// -- properties --
const auto& extra_setting() const noexcept {
return extra_setting_;
}
template <class Collector>
void collect(Collector& collector) const {
std::unique_lock<std::mutex> guard{mx_};
......@@ -72,6 +95,7 @@ public:
}
private:
extra_setting_type extra_setting_;
mutable std::mutex mx_;
std::vector<std::unique_ptr<impl_type>> metrics_;
};
......
......@@ -31,6 +31,8 @@ namespace caf::telemetry {
template <class Type>
class metric_impl : public metric {
public:
using family_setting = typename Type::family_setting;
template <class... Ts>
metric_impl(std::vector<label> labels, Ts&&... xs)
: metric(std::move(labels)), impl_(std::forward<Ts>(xs)...) {
......
......@@ -25,7 +25,10 @@
#include "caf/detail/core_export.hpp"
#include "caf/fwd.hpp"
#include "caf/raise_error.hpp"
#include "caf/string_view.hpp"
#include "caf/telemetry/gauge.hpp"
#include "caf/telemetry/histogram.hpp"
#include "caf/telemetry/metric_family_impl.hpp"
namespace caf::telemetry {
......@@ -49,8 +52,8 @@ public:
// -- factories --------------------------------------------------------------
/// Returns a metric family. Creates the family lazily if necessary, but fails
/// if the full name already belongs to a different family.
/// Returns a gauge metric family. Creates the family lazily if necessary, but
/// fails if the full name already belongs to a different family.
/// @param prefix The prefix (namespace) this family belongs to. Usually the
/// application or protocol name, e.g., `http`. The prefix `caf`
/// as well as prefixes starting with an underscore are
......@@ -64,41 +67,43 @@ public:
/// @param is_sum Setting this to `true` indicates that this metric adds
/// something up to a total, where only the total value is of
/// interest. For example, the total number of HTTP requests.
template <class Type>
metric_family_impl<Type>* family(string_view prefix, string_view name,
span<const string_view> label_names,
string_view helptext, string_view unit = "1",
bool is_sum = false) {
using family_type = metric_family_impl<Type>;
template <class ValueType = int64_t>
metric_family_impl<gauge<ValueType>>*
gauge_family(string_view prefix, string_view name,
span<const string_view> label_names, string_view helptext,
string_view unit = "1", bool is_sum = false) {
using gauge_type = gauge<ValueType>;
using family_type = metric_family_impl<gauge_type>;
std::unique_lock<std::mutex> guard{families_mx_};
if (auto ptr = fetch(prefix, name)) {
assert_properties(ptr, Type::runtime_type, label_names, unit, is_sum);
assert_properties(ptr, gauge_type::runtime_type, label_names, unit,
is_sum);
return static_cast<family_type*>(ptr);
}
auto ptr = std::make_unique<family_type>(to_string(prefix), to_string(name),
to_sorted_vec(label_names),
to_string(helptext),
to_string(unit), is_sum);
auto& families = container_by_type<Type>();
auto& families = container_by_type<gauge_type>();
families.emplace_back(std::move(ptr));
return families.back().get();
}
/// @copydoc family
template <class Type>
metric_family_impl<Type>*
family(string_view prefix, string_view name,
std::initializer_list<string_view> label_names, string_view helptext,
string_view unit = "1", bool is_sum = false) {
return family<Type>(prefix, name,
span<const string_view>{label_names.begin(),
label_names.size()},
helptext, unit, is_sum);
/// @copydoc gauge_family
template <class ValueType = int64_t>
metric_family_impl<gauge<ValueType>>*
gauge_family(string_view prefix, string_view name,
std::initializer_list<string_view> label_names,
string_view helptext, string_view unit = "1",
bool is_sum = false) {
auto lbl_span = make_span(label_names.begin(), label_names.size());
return gauge_family<ValueType>(prefix, name, lbl_span, helptext, unit,
is_sum);
}
/// Returns a metric singleton, i.e., the single instance of a family without
/// label dimensions. Creates all objects lazily if necessary, but fails if
/// the full name already belongs to a different family.
/// Returns a gauge metric singleton, i.e., the single instance of a family
/// without label dimensions. Creates all objects lazily if necessary, but
/// fails if the full name already belongs to a different family.
/// @param prefix The prefix (namespace) this family belongs to. Usually the
/// application or protocol name, e.g., `http`. The prefix `caf`
/// as well as prefixes starting with an underscore are
......@@ -111,10 +116,91 @@ public:
/// @param is_sum Setting this to `true` indicates that this metric adds
/// something up to a total, where only the total value is of
/// interest. For example, the total number of HTTP requests.
template <class Type>
Type* singleton(string_view prefix, string_view name, string_view helptext,
template <class ValueType = int64_t>
gauge<ValueType>*
gauge_singleton(string_view prefix, string_view name, string_view helptext,
string_view unit = "1", bool is_sum = false) {
auto fptr = family<Type>(prefix, name, {}, helptext, unit, is_sum);
span<const string_view> lbls;
auto fptr = gauge_family<ValueType>(prefix, name, lbls, helptext, unit,
is_sum);
return fptr->get_or_add({});
}
/// Returns a histogram metric family. Creates the family lazily if necessary,
/// but fails if the full name already belongs to a different family.
/// @param prefix The prefix (namespace) this family belongs to. Usually the
/// application or protocol name, e.g., `http`. The prefix `caf`
/// as well as prefixes starting with an underscore are
/// reserved.
/// @param name The human-readable name of the metric, e.g., `requests`.
/// @param label_names Names for all label dimensions of the metric.
/// @param upper_bounds Upper bounds for the metric buckets.
/// @param helptext Short explanation of the metric.
/// @param unit Unit of measurement. Please use base units such as `bytes` or
/// `seconds` (prefer lowercase). The pseudo-unit `1` identifies
/// dimensionless counts.
/// @param is_sum Setting this to `true` indicates that this metric adds
/// something up to a total, where only the total value is of
/// interest. For example, the total number of HTTP requests.
template <class ValueType = int64_t>
metric_family_impl<histogram<ValueType>>* histogram_family(
string_view prefix, string_view name, span<const string_view> label_names,
span<const typename histogram<ValueType>::value_type> upper_bounds,
string_view helptext, string_view unit = "1", bool is_sum = false) {
if (upper_bounds.empty())
CAF_RAISE_ERROR("at least one bucket must exist");
using histogram_type = histogram<ValueType>;
using family_type = metric_family_impl<histogram_type>;
std::unique_lock<std::mutex> guard{families_mx_};
if (auto ptr = fetch(prefix, name)) {
assert_histogram_properties(ptr, histogram_type::runtime_type,
label_names, upper_bounds, unit, is_sum);
return static_cast<family_type*>(ptr);
}
auto ptr = std::make_unique<family_type>(
to_string(prefix), to_string(name), to_sorted_vec(label_names),
to_string(helptext), to_string(unit), is_sum, upper_bounds.begin(),
upper_bounds.end());
auto& families = container_by_type<histogram_type>();
families.emplace_back(std::move(ptr));
return families.back().get();
}
/// @copydoc gauge_family
template <class ValueType = int64_t>
metric_family_impl<histogram<ValueType>>* histogram_family(
string_view prefix, string_view name,
std::initializer_list<string_view> label_names,
span<const typename histogram<ValueType>::value_type> upper_bounds,
string_view helptext, string_view unit = "1", bool is_sum = false) {
auto lbl_span = make_span(label_names.begin(), label_names.size());
return histogram_family<ValueType>(prefix, name, lbl_span, upper_bounds,
helptext, unit, is_sum);
}
/// Returns a histogram metric singleton, i.e., the single instance of a
/// family without label dimensions. Creates all objects lazily if necessary,
/// but fails if the full name already belongs to a different family.
/// @param prefix The prefix (namespace) this family belongs to. Usually the
/// application or protocol name, e.g., `http`. The prefix `caf`
/// as well as prefixes starting with an underscore are
/// reserved.
/// @param name The human-readable name of the metric, e.g., `requests`.
/// @param helptext Short explanation of the metric.
/// @param unit Unit of measurement. Please use base units such as `bytes` or
/// `seconds` (prefer lowercase). The pseudo-unit `1` identifies
/// dimensionless counts.
/// @param is_sum Setting this to `true` indicates that this metric adds
/// something up to a total, where only the total value is of
/// interest. For example, the total number of HTTP requests.
template <class ValueType = int64_t>
histogram<ValueType>*
histogram_singleton(string_view prefix, string_view name,
string_view helptext, span<const ValueType> upper_bounds,
string_view unit = "1", bool is_sum = false) {
span<const string_view> lbls;
auto fptr = histogram_family<ValueType>(prefix, name, lbls, upper_bounds,
helptext, unit, is_sum);
return fptr->get_or_add({});
}
......@@ -127,6 +213,10 @@ public:
ptr->collect(collector);
for (auto& ptr : int_gauges_)
ptr->collect(collector);
for (auto& ptr : dbl_histograms_)
ptr->collect(collector);
for (auto& ptr : int_histograms_)
ptr->collect(collector);
}
private:
......@@ -144,11 +234,23 @@ private:
return result;
}
void assert_properties(metric_family* ptr, metric_type type,
void assert_properties(const metric_family* ptr, metric_type type,
span<const string_view> label_names, string_view unit,
bool is_sum);
void assert_equal(metric_family* old_ptr, metric_family* new_ptr);
template <class ValueType>
void assert_histogram_properties(const metric_family* ptr, metric_type type,
span<const string_view> label_names,
span<const ValueType> upper_bounds,
string_view unit, bool is_sum) {
assert_properties(ptr, type, label_names, unit, is_sum);
using family_type = metric_family_impl<histogram<ValueType>>;
auto dptr = static_cast<const family_type*>(ptr);
auto& xs = dptr->extra_setting();
if (!std::equal(xs.begin(), xs.end(), upper_bounds.begin(),
upper_bounds.end()))
CAF_RAISE_ERROR("full name with different bucket settings found");
}
template <class Type>
metric_family_container<Type>& container_by_type();
......@@ -157,6 +259,8 @@ private:
metric_family_container<telemetry::dbl_gauge> dbl_gauges_;
metric_family_container<telemetry::int_gauge> int_gauges_;
metric_family_container<telemetry::dbl_histogram> dbl_histograms_;
metric_family_container<telemetry::int_histogram> int_histograms_;
};
template <>
......@@ -171,4 +275,16 @@ metric_registry::container_by_type<int_gauge>() {
return int_gauges_;
}
template <>
inline metric_registry::metric_family_container<int_histogram>&
metric_registry::container_by_type<int_histogram>() {
return int_histograms_;
}
template <>
inline metric_registry::metric_family_container<dbl_histogram>&
metric_registry::container_by_type<dbl_histogram>() {
return dbl_histograms_;
}
} // namespace caf::telemetry
......@@ -49,7 +49,7 @@ actor_registry::~actor_registry() {
}
actor_registry::actor_registry(actor_system& sys) : system_(sys) {
running_ = sys.telemetry().singleton<telemetry::int_gauge>(
running_ = sys.telemetry().gauge_singleton(
"caf", "running_actors", "Number of currently running actors.");
}
......
......@@ -48,6 +48,10 @@ struct ms_timestamp {
ms_timestamp& operator=(const ms_timestamp&) = default;
};
struct underline_to_hyphen {
string_view str;
};
void append(prometheus::char_buffer&) {
// End of recursion.
}
......@@ -55,6 +59,9 @@ void append(prometheus::char_buffer&) {
template <class... Ts>
void append(prometheus::char_buffer&, string_view, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer&, underline_to_hyphen, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer&, char, Ts&&...);
......@@ -68,18 +75,31 @@ append(prometheus::char_buffer& buf, T val, Ts&&... xs);
template <class... Ts>
void append(prometheus::char_buffer&, const metric_family*, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer&, const std::vector<label>&, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer&, const metric*, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer&, ms_timestamp, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer&, const prometheus::char_buffer&, Ts&&...);
template <class... Ts>
void append(prometheus::char_buffer& buf, string_view str, Ts&&... xs) {
buf.insert(buf.end(), str.begin(), str.end());
append(buf, std::forward<Ts>(xs)...);
}
template <class... Ts>
void append(prometheus::char_buffer& buf, underline_to_hyphen x, Ts&&... xs) {
for (auto c : x.str)
buf.emplace_back(c != '-' ? c : '_');
append(buf, std::forward<Ts>(xs)...);
}
template <class... Ts>
void append(prometheus::char_buffer& buf, char ch, Ts&&... xs) {
buf.emplace_back(ch);
......@@ -111,7 +131,8 @@ append(prometheus::char_buffer& buf, T val, Ts&&... xs) {
template <class... Ts>
void append(prometheus::char_buffer& buf, const metric_family* family,
Ts&&... xs) {
append(buf, family->prefix(), '_', family->name());
append(buf, underline_to_hyphen{family->prefix()}, '_',
underline_to_hyphen{family->name()});
if (family->unit() != "1"_sv)
append(buf, '_', family->unit());
if (family->is_sum())
......@@ -120,8 +141,8 @@ void append(prometheus::char_buffer& buf, const metric_family* family,
}
template <class... Ts>
void append(prometheus::char_buffer& buf, const metric* instance, Ts&&... xs) {
const auto& labels = instance->labels();
void append(prometheus::char_buffer& buf, const std::vector<label>& labels,
Ts&&... xs) {
if (!labels.empty()) {
append(buf, '{');
auto i = labels.begin();
......@@ -133,12 +154,24 @@ void append(prometheus::char_buffer& buf, const metric* instance, Ts&&... xs) {
append(buf, std::forward<Ts>(xs)...);
}
template <class... Ts>
void append(prometheus::char_buffer& buf, const metric* instance, Ts&&... xs) {
append(buf, instance->labels(), std::forward<Ts>(xs)...);
}
template <class... Ts>
void append(prometheus::char_buffer& buf, ms_timestamp ts, Ts&&... xs) {
append(buf, ts.value);
append(buf, std::forward<Ts>(xs)...);
}
template <class... Ts>
void append(prometheus::char_buffer& buf, const prometheus::char_buffer& x,
Ts&&... xs) {
buf.insert(buf.end(), x.begin(), x.end());
append(buf, std::forward<Ts>(xs)...);
}
} // namespace
string_view prometheus::collect_from(const metric_registry& registry,
......@@ -170,6 +203,16 @@ void prometheus::operator()(const metric_family* family, const metric* instance,
'\n');
}
void prometheus::operator()(const metric_family* family, const metric* instance,
const dbl_histogram* val) {
append_histogram(family, instance, val);
}
void prometheus::operator()(const metric_family* family, const metric* instance,
const int_histogram* val) {
append_histogram(family, instance, val);
}
void prometheus::set_current_family(const metric_family* family,
string_view prometheus_type) {
if (current_family_ == family)
......@@ -185,4 +228,61 @@ void prometheus::set_current_family(const metric_family* family,
buf_.insert(buf_.end(), i->second.begin(), i->second.end());
}
namespace {
template <class ValueType>
auto make_virtual_metrics(const metric_family* family, const metric* instance,
const histogram<ValueType>* val) {
std::vector<prometheus::char_buffer> result;
auto add_result = [&](auto&&... xs) {
result.emplace_back();
append(result.back(), std::forward<decltype(xs)>(xs)...);
};
auto buckets = val->buckets();
auto num_buckets = buckets.size();
CAF_ASSERT(num_buckets > 1);
auto labels = instance->labels();
labels.emplace_back("le", "");
result.reserve(num_buckets + 2);
size_t index = 0;
// Create bucket variable names for 1..N-1.
for (; index < num_buckets - 1; ++index) {
auto upper_bound = std::to_string(buckets[index].upper_bound);
labels.back().value(upper_bound);
add_result(family, "_bucket", labels, ' ');
}
// The last bucket always sets le="+Inf"
labels.back().value("+Inf");
add_result(family, "_bucket", labels, ' ');
labels.pop_back();
add_result(family, "_sum", labels, ' ');
add_result(family, "_count", labels, ' ');
return result;
}
} // namespace
template <class ValueType>
void prometheus::append_histogram(const metric_family* family,
const metric* instance,
const histogram<ValueType>* val) {
auto i = virtual_metrics_.find(instance);
if (i == virtual_metrics_.end()) {
auto metrics = make_virtual_metrics(family, instance, val);
i = virtual_metrics_.emplace(instance, std::move(metrics)).first;
}
set_current_family(family, "histogram");
auto& vm = i->second;
auto buckets = val->buckets();
size_t index = 0;
for (; index < buckets.size() - 1; ++index) {
append(buf_, vm[index], buckets[index].gauge.value(), ' ',
ms_timestamp{now_}, '\n');
}
auto count = buckets[index].gauge.value();
append(buf_, vm[index], count, ' ', ms_timestamp{now_}, '\n');
append(buf_, vm[++index], val->sum(), ' ', ms_timestamp{now_}, '\n');
append(buf_, vm[++index], count, ' ', ms_timestamp{now_}, '\n');
}
} // namespace caf::telemetry::collector
......@@ -33,6 +33,11 @@ label::label(const label_view& view) : label(view.name(), view.value()) {
// nop
}
void label::value(string_view new_value) {
str_.erase(name_length_ + 1);
str_.insert(str_.end(), new_value.begin(), new_value.end());
}
int label::compare(const label& x) const noexcept {
return str_.compare(x.str());
}
......
......@@ -19,13 +19,21 @@
#include "caf/telemetry/metric_registry.hpp"
#include "caf/config.hpp"
#include "caf/raise_error.hpp"
#include "caf/telemetry/dbl_gauge.hpp"
#include "caf/telemetry/int_gauge.hpp"
#include "caf/telemetry/metric_family_impl.hpp"
#include "caf/telemetry/metric_impl.hpp"
#include "caf/telemetry/metric_type.hpp"
namespace {
template <class Range1, class Range2>
bool equals(Range1&& xs, Range2&& ys) {
return std::equal(xs.begin(), xs.end(), ys.begin(), ys.end());
}
} // namespace
namespace caf::telemetry {
metric_registry::metric_registry() {
......@@ -38,19 +46,26 @@ metric_registry::~metric_registry() {
metric_family* metric_registry::fetch(const string_view& prefix,
const string_view& name) {
auto matches = [&](const auto& ptr) {
auto eq = [&](const auto& ptr) {
return ptr->prefix() == prefix && ptr->name() == name;
};
if (auto i = std::find_if(dbl_gauges_.begin(), dbl_gauges_.end(), matches);
if (auto i = std::find_if(dbl_gauges_.begin(), dbl_gauges_.end(), eq);
i != dbl_gauges_.end())
return i->get();
if (auto i = std::find_if(int_gauges_.begin(), int_gauges_.end(), matches);
if (auto i = std::find_if(int_gauges_.begin(), int_gauges_.end(), eq);
i != int_gauges_.end())
return i->get();
if (auto i = std::find_if(dbl_histograms_.begin(), dbl_histograms_.end(), eq);
i != dbl_histograms_.end())
return i->get();
if (auto i = std::find_if(int_histograms_.begin(), int_histograms_.end(), eq);
i != int_histograms_.end())
return i->get();
return nullptr;
}
void metric_registry::assert_properties(metric_family* ptr, metric_type type,
void metric_registry::assert_properties(const metric_family* ptr,
metric_type type,
span<const string_view> label_names,
string_view unit, bool is_sum) {
auto labels_match = [&] {
......
......@@ -32,7 +32,6 @@ using namespace caf::telemetry;
namespace {
struct fixture {
using ig = int_gauge;
collector::prometheus exporter;
metric_registry registry;
};
......@@ -42,16 +41,23 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(prometheus_tests, fixture)
CAF_TEST(the Prometheus collector generates text output) {
auto fb = registry.family<ig>("foo", "bar", {}, "Some value without labels.",
"seconds");
auto sv = registry.family<ig>("some", "value", {"a", "b"},
"Some (total) value with two labels.", "1",
true);
auto ov = registry.family<ig>("other", "value", {"x"}, "", "seconds", true);
auto fb = registry.gauge_family("foo", "bar", {},
"Some value without labels.", "seconds");
auto sv = registry.gauge_family("some", "value", {"a", "b"},
"Some (total) value with two labels.", "1",
true);
auto ov = registry.gauge_family("other", "value", {"x"}, "", "seconds", true);
std::vector<int64_t> upper_bounds{1, 2, 4};
auto sr = registry.histogram_family("some", "request-duration", {"x"},
upper_bounds, "Some help.", "seconds");
fb->get_or_add({})->value(123);
sv->get_or_add({{"a", "1"}, {"b", "2"}})->value(12);
sv->get_or_add({{"b", "1"}, {"a", "2"}})->value(21);
ov->get_or_add({{"x", "true"}})->value(31337);
auto h = sr->get_or_add({{"x", "get"}});
h->observe(3);
h->observe(4);
h->observe(7);
CAF_CHECK_EQUAL(exporter.collect_from(registry, 42),
R"(# HELP foo_bar_seconds Some value without labels.
# TYPE foo_bar_seconds gauge
......@@ -62,6 +68,14 @@ some_value_total{a="1",b="2"} 12 42000
some_value_total{a="2",b="1"} 21 42000
# TYPE other_value_seconds_total gauge
other_value_seconds_total{x="true"} 31337 42000
# HELP some_request_duration_seconds Some help.
# TYPE some_request_duration_seconds histogram
some_request_duration_seconds_bucket{x="get",le="1"} 0 42000
some_request_duration_seconds_bucket{x="get",le="2"} 0 42000
some_request_duration_seconds_bucket{x="get",le="4"} 2 42000
some_request_duration_seconds_bucket{x="get",le="+Inf"} 1 42000
some_request_duration_seconds_sum{x="get"} 14 42000
some_request_duration_seconds_count{x="get"} 1 42000
)"_sv);
CAF_MESSAGE("multiple runs generate the same output");
std::string res1;
......
......@@ -48,17 +48,24 @@ struct test_collector {
result += std::to_string(wrapped->value());
}
template <class T>
void operator()(const metric_family* family, const metric* instance,
const histogram<T>* wrapped) {
concat(family, instance);
result += std::to_string(wrapped->sum());
}
void concat(const metric_family* family, const metric* instance) {
result += '\n';
result += family->prefix();
result += '_';
result += '.';
result += family->name();
if (family->unit() != "1") {
result += '_';
result += '.';
result += family->unit();
}
if (family->is_sum())
result += "_total";
result += ".total";
if (!instance->labels().empty()) {
result += '{';
auto i = instance->labels().begin();
......@@ -81,7 +88,6 @@ struct test_collector {
};
struct fixture {
using ig = int_gauge;
metric_registry registry;
test_collector collector;
};
......@@ -91,8 +97,11 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE(metric_registry_tests, fixture)
CAF_TEST(registries lazily create metrics) {
auto f = registry.family<ig>("caf", "running_actors", {"var1", "var2"},
"How many actors are currently running?");
std::vector<int64_t> upper_bounds{1, 2, 4, 8};
auto f = registry.gauge_family("caf", "running-actors", {"var1", "var2"},
"How many actors are currently running?");
auto g = registry.histogram_family("caf", "response-time", {"var1", "var2"},
upper_bounds, "How long take requests?");
std::vector<label_view> v1{{"var1", "foo"}, {"var2", "bar"}};
std::vector<label_view> v1_reversed{{"var2", "bar"}, {"var1", "foo"}};
std::vector<label_view> v2{{"var1", "bar"}, {"var2", "foo"}};
......@@ -103,27 +112,33 @@ CAF_TEST(registries lazily create metrics) {
CAF_CHECK_EQUAL(f->get_or_add(v1_reversed)->value(), 42);
CAF_CHECK_EQUAL(f->get_or_add(v2)->value(), 23);
CAF_CHECK_EQUAL(f->get_or_add(v2_reversed)->value(), 23);
g->get_or_add(v1)->observe(3);
g->get_or_add(v2)->observe(7);
CAF_CHECK_EQUAL(g->get_or_add(v1)->sum(), 3);
CAF_CHECK_EQUAL(g->get_or_add(v1_reversed)->sum(), 3);
CAF_CHECK_EQUAL(g->get_or_add(v2)->sum(), 7);
CAF_CHECK_EQUAL(g->get_or_add(v2_reversed)->sum(), 7);
}
CAF_TEST(registries allow users to collect all registered metrics) {
auto fb = registry.family<ig>("foo", "bar", {}, "Some value without labels.",
"seconds");
auto sv = registry.family<ig>("some", "value", {"a", "b"},
"Some (total) value with two labels.", "1",
true);
auto ov = registry.family<ig>("other", "value", {"x"},
"Some (total) seconds with one label.",
"seconds", true);
auto ra = registry.family<ig>("caf", "running_actors", {"node"},
"How many actors are running?");
auto ms = registry.family<ig>("caf", "mailbox_size", {"name"},
"How full is the mailbox?");
auto fb = registry.gauge_family("foo", "bar", {},
"Some value without labels.", "seconds");
auto sv = registry.gauge_family("some", "value", {"a", "b"},
"Some (total) value with two labels.", "1",
true);
auto ov = registry.gauge_family("other", "value", {"x"},
"Some (total) seconds with one label.",
"seconds", true);
auto ra = registry.gauge_family("caf", "running-actors", {"node"},
"How many actors are running?");
auto ms = registry.gauge_family("caf", "mailbox-size", {"name"},
"How full is the mailbox?");
CAF_MESSAGE("the registry always returns the same family object");
CAF_CHECK_EQUAL(fb, registry.family<ig>("foo", "bar", {}, "", "seconds"));
CAF_CHECK_EQUAL(sv, registry.family<ig>("some", "value", {"a", "b"}, "", "1",
true));
CAF_CHECK_EQUAL(sv, registry.family<ig>("some", "value", {"b", "a"}, "", "1",
true));
CAF_CHECK_EQUAL(fb, registry.gauge_family("foo", "bar", {}, "", "seconds"));
CAF_CHECK_EQUAL(sv, registry.gauge_family("some", "value", {"a", "b"}, "",
"1", true));
CAF_CHECK_EQUAL(sv, registry.gauge_family("some", "value", {"b", "a"}, "",
"1", true));
CAF_MESSAGE("families always return the same metric object for given labels");
CAF_CHECK_EQUAL(fb->get_or_add({}), fb->get_or_add({}));
CAF_CHECK_EQUAL(sv->get_or_add({{"a", "1"}, {"b", "2"}}),
......@@ -138,13 +153,13 @@ CAF_TEST(registries allow users to collect all registered metrics) {
ms->get_or_add({{"name", "parser"}})->value(12);
registry.collect(collector);
CAF_CHECK_EQUAL(collector.result, R"(
foo_bar_seconds 123
some_value_total{a="1",b="2"} 12
some_value_total{a="2",b="1"} 21
other_value_seconds_total{x="true"} 31337
caf_running_actors{node="localhost"} 42
caf_mailbox_size{name="printer"} 3
caf_mailbox_size{name="parser"} 12)");
foo.bar.seconds 123
some.value.total{a="1",b="2"} 12
some.value.total{a="2",b="1"} 21
other.value.seconds.total{x="true"} 31337
caf.running-actors{node="localhost"} 42
caf.mailbox-size{name="printer"} 3
caf.mailbox-size{name="parser"} 12)");
}
CAF_TEST_FIXTURE_SCOPE_END()
......@@ -192,12 +192,12 @@ prometheus_broker::prometheus_broker(actor_config& cfg) : io::broker(cfg) {
using telemetry::dbl_gauge;
using telemetry::int_gauge;
auto& reg = system().telemetry();
cpu_time_ = reg.singleton<dbl_gauge>(
cpu_time_ = reg.gauge_singleton<double>(
"process", "cpu", "Total user and system CPU time spent.", "seconds", true);
mem_size_ = reg.singleton<int_gauge>("process", "resident_memory",
"Resident memory size.", "bytes");
virt_mem_size_ = reg.singleton<int_gauge>("process", "virtual_memory",
"Virtual memory size.", "bytes");
mem_size_ = reg.gauge_singleton("process", "resident_memory",
"Resident memory size.", "bytes");
virt_mem_size_ = reg.gauge_singleton("process", "virtual_memory",
"Virtual memory size.", "bytes");
#endif // HAS_PROCESS_METRICS
}
......
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