Commit 32c4f24d authored by Dominik Charousset's avatar Dominik Charousset

Add new counter metrics

parent 3a29305f
......@@ -309,6 +309,7 @@ caf_add_test_suites(caf-core-test
string_view
sum_type
telemetry.collector.prometheus
telemetry.counter
telemetry.gauge
telemetry.histogram
telemetry.label
......
......@@ -245,16 +245,21 @@ class timer;
enum class metric_type : uint8_t;
template <class ValueType>
class counter;
template <class ValueType>
class histogram;
template <class Type>
class metric_family_impl;
template <class Type>
class metric_impl;
template <class ValueType>
class histogram;
using dbl_counter = counter<double>;
using dbl_histogram = histogram<double>;
using int_counter = counter<int64_t>;
using int_histogram = histogram<int64_t>;
} // namespace telemetry
......
......@@ -69,6 +69,12 @@ public:
// -- call operators for the metric registry ---------------------------------
void operator()(const metric_family* family, const metric* instance,
const dbl_counter* counter);
void operator()(const metric_family* family, const metric* instance,
const int_counter* counter);
void operator()(const metric_family* family, const metric* instance,
const dbl_gauge* gauge);
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/config.hpp"
#include "caf/fwd.hpp"
#include "caf/telemetry/gauge.hpp"
namespace caf::telemetry {
/// A metric that represents a single value that can only go up.
template <class ValueType>
class counter {
public:
// -- member types -----------------------------------------------------------
using value_type = ValueType;
using family_setting = unit_t;
// -- constants --------------------------------------------------------------
static constexpr metric_type runtime_type
= std::is_same<value_type, double>::value ? metric_type::dbl_counter
: metric_type::int_counter;
// -- constructors, destructors, and assignment operators --------------------
counter() noexcept = default;
explicit counter(value_type initial_value) noexcept : gauge_(initial_value) {
// nop
}
// -- modifiers --------------------------------------------------------------
/// Increments the counter by 1.
void inc() noexcept {
gauge_.inc();
}
/// Increments the counter by `amount`.
/// @pre `amount > 0`
void inc(value_type amount) noexcept {
CAF_ASSERT(amount > 0);
gauge_.inc(amount);
}
// -- observers --------------------------------------------------------------
/// Returns the current value of the counter.
value_type value() const noexcept {
return gauge_.value();
}
private:
gauge<value_type> gauge_;
};
/// Convenience alias for a counter with value type `double`.
using dbl_counter = counter<double>;
/// Convenience alias for a counter with value type `int64_t`.
using int_counter = counter<int64_t>;
} // namespace caf::telemetry
......@@ -33,10 +33,11 @@ class CAF_CORE_EXPORT metric_family {
public:
// -- constructors, destructors, and assignment operators --------------------
metric_family(std::string prefix, std::string name,
metric_family(metric_type type, std::string prefix, std::string name,
std::vector<std::string> label_names, std::string helptext,
std::string unit, bool is_sum)
: prefix_(std::move(prefix)),
: type_(type),
prefix_(std::move(prefix)),
name_(std::move(name)),
label_names_(std::move(label_names)),
helptext_(std::move(helptext)),
......@@ -53,6 +54,10 @@ public:
// -- properties -------------------------------------------------------------
auto type() const noexcept {
return type_;
}
const auto& prefix() const noexcept {
return prefix_;
}
......@@ -77,9 +82,8 @@ public:
return is_sum_;
}
virtual metric_type type() const noexcept = 0;
private:
metric_type type_;
std::string prefix_;
std::string name_;
std::vector<std::string> label_names_;
......
......@@ -46,16 +46,13 @@ public:
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),
: super(Type::runtime_type, 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;
}
Type* get_or_add(span<const label_view> labels) {
auto has_label_values = [labels](const auto& metric_ptr) {
const auto& metric_labels = metric_ptr->labels();
......
......@@ -28,6 +28,7 @@
#include "caf/raise_error.hpp"
#include "caf/settings.hpp"
#include "caf/string_view.hpp"
#include "caf/telemetry/counter.hpp"
#include "caf/telemetry/gauge.hpp"
#include "caf/telemetry/histogram.hpp"
#include "caf/telemetry/metric_family_impl.hpp"
......@@ -37,14 +38,6 @@ namespace caf::telemetry {
/// Manages a collection of metric families.
class CAF_CORE_EXPORT metric_registry {
public:
// -- member types -----------------------------------------------------------
template <class Type>
using metric_family_ptr = std::unique_ptr<metric_family_impl<Type>>;
template <class Type>
using metric_family_container = std::vector<metric_family_ptr<Type>>;
// -- constructors, destructors, and assignment operators --------------------
metric_registry();
......@@ -85,9 +78,9 @@ public:
to_sorted_vec(label_names),
to_string(helptext),
to_string(unit), is_sum);
auto& families = container_by_type<gauge_type>();
families.emplace_back(std::move(ptr));
return families.back().get();
auto result = ptr.get();
families_.emplace_back(std::move(ptr));
return result;
}
/// @copydoc gauge_family
......@@ -102,6 +95,55 @@ public:
is_sum);
}
/// Returns a counter 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 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<counter<ValueType>>*
counter_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 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, 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 result = ptr.get();
families_.emplace_back(std::move(ptr));
return result;
}
/// @copydoc counter_family
template <class ValueType = int64_t>
metric_family_impl<counter<ValueType>>*
counter_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 counter_family<ValueType>(prefix, name, lbl_span, helptext, unit,
is_sum);
}
/// 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.
......@@ -177,9 +219,9 @@ public:
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, std::move(upper_bounds));
auto& families = container_by_type<histogram_type>();
families.emplace_back(std::move(ptr));
return families.back().get();
auto result = ptr.get();
families_.emplace_back(std::move(ptr));
return result;
}
/// @copydoc gauge_family
......@@ -226,15 +268,10 @@ public:
template <class Collector>
void collect(Collector& collector) const {
auto f = [&](auto* ptr) { ptr->collect(collector); };
std::unique_lock<std::mutex> guard{families_mx_};
for (auto& ptr : dbl_gauges_)
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);
for (auto& ptr : families_)
visit_family(f, ptr.get());
}
private:
......@@ -252,43 +289,31 @@ private:
return result;
}
template <class F>
static auto visit_family(F& f, const metric_family* ptr) {
switch (ptr->type()) {
case metric_type::dbl_counter:
return f(static_cast<const metric_family_impl<dbl_counter>*>(ptr));
case metric_type::int_counter:
return f(static_cast<const metric_family_impl<int_counter>*>(ptr));
case metric_type::dbl_gauge:
return f(static_cast<const metric_family_impl<dbl_gauge>*>(ptr));
case metric_type::int_gauge:
return f(static_cast<const metric_family_impl<int_gauge>*>(ptr));
case metric_type::dbl_histogram:
return f(static_cast<const metric_family_impl<dbl_histogram>*>(ptr));
default:
CAF_ASSERT(ptr->type() == metric_type::int_histogram);
return f(static_cast<const metric_family_impl<int_histogram>*>(ptr));
}
}
void assert_properties(const metric_family* ptr, metric_type type,
span<const string_view> label_names, string_view unit,
bool is_sum);
template <class Type>
metric_family_container<Type>& container_by_type();
mutable std::mutex families_mx_;
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_;
std::vector<std::unique_ptr<metric_family>> families_;
};
template <>
inline metric_registry::metric_family_container<dbl_gauge>&
metric_registry::container_by_type<dbl_gauge>() {
return dbl_gauges_;
}
template <>
inline metric_registry::metric_family_container<int_gauge>&
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
......@@ -21,6 +21,8 @@
namespace caf::telemetry {
enum class metric_type : uint8_t {
dbl_counter,
int_counter,
dbl_gauge,
int_gauge,
dbl_histogram,
......
......@@ -189,6 +189,20 @@ string_view prometheus::collect_from(const metric_registry& registry) {
return collect_from(registry, time(NULL));
}
void prometheus::operator()(const metric_family* family, const metric* instance,
const dbl_counter* counter) {
set_current_family(family, "counter");
append(buf_, family, instance, ' ', counter->value(), ' ', ms_timestamp{now_},
'\n');
}
void prometheus::operator()(const metric_family* family, const metric* instance,
const int_counter* counter) {
set_current_family(family, "counter");
append(buf_, family, instance, ' ', counter->value(), ' ', ms_timestamp{now_},
'\n');
}
void prometheus::operator()(const metric_family* family, const metric* instance,
const dbl_gauge* gauge) {
set_current_family(family, "gauge");
......
......@@ -49,17 +49,8 @@ metric_family* metric_registry::fetch(const string_view& prefix,
auto eq = [&](const auto& ptr) {
return ptr->prefix() == prefix && ptr->name() == name;
};
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(), 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())
if (auto i = std::find_if(families_.begin(), families_.end(), eq);
i != families_.end())
return i->get();
return nullptr;
}
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#define CAF_SUITE telemetry.counter
#include "caf/telemetry/counter.hpp"
#include "caf/test/dsl.hpp"
using namespace caf;
CAF_TEST(double counters can only increment) {
telemetry::dbl_counter g;
CAF_MESSAGE("counters start at 0");
CAF_CHECK_EQUAL(g.value(), 0.0);
CAF_MESSAGE("counters are incrementable");
g.inc();
g.inc(2.0);
CAF_CHECK_EQUAL(g.value(), 3.0);
CAF_MESSAGE("users can create counters with custom start values");
CAF_CHECK_EQUAL(telemetry::dbl_counter{42.0}.value(), 42.0);
}
CAF_TEST(integer counters can only increment) {
telemetry::int_counter g;
CAF_MESSAGE("counters start at 0");
CAF_CHECK_EQUAL(g.value(), 0);
CAF_MESSAGE("counters are incrementable");
g.inc();
g.inc(2);
CAF_CHECK_EQUAL(g.value(), 3);
CAF_MESSAGE("users can create counters with custom start values");
CAF_CHECK_EQUAL(telemetry::int_counter{42}.value(), 42);
}
......@@ -23,8 +23,8 @@
#include "caf/test/dsl.hpp"
#include "caf/string_view.hpp"
#include "caf/telemetry/dbl_gauge.hpp"
#include "caf/telemetry/int_gauge.hpp"
#include "caf/telemetry/counter.hpp"
#include "caf/telemetry/gauge.hpp"
#include "caf/telemetry/label_view.hpp"
#include "caf/telemetry/metric_type.hpp"
......@@ -36,6 +36,13 @@ namespace {
struct test_collector {
std::string result;
template <class T>
void operator()(const metric_family* family, const metric* instance,
const counter<T>* wrapped) {
concat(family, instance);
result += std::to_string(wrapped->value());
}
void operator()(const metric_family* family, const metric* instance,
const dbl_gauge* wrapped) {
concat(family, instance);
......
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