zeek/src/telemetry/Manager.h
2022-01-24 14:26:20 -08:00

423 lines
18 KiB
C++

// See the file "COPYING" in the main distribution directory for copyright.
#pragma once
#include <cstdint>
#include <initializer_list>
#include <string_view>
#include <vector>
#include "zeek/Span.h"
#include "zeek/telemetry/Counter.h"
#include "zeek/telemetry/Gauge.h"
#include "zeek/telemetry/Histogram.h"
namespace zeek::telemetry
{
/**
* Manages a collection of metric families.
*/
class Manager
{
public:
class Impl;
explicit Manager(Impl* ptr) : pimpl(ptr) { }
Manager(const Manager&) = delete;
Manager& operator=(const Manager&) = delete;
virtual ~Manager();
/**
* Initialization of the manager. This is called late during Zeek's
* initialization after any scripts are processed.
*/
virtual void InitPostScript();
/**
* @return A counter metric family. Creates the family lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to.
* @param name The human-readable name of the metric, e.g., `requests`.
* @param labels Names for all label dimensions of the metric.
* @param helptext Short explanation of the metric.
* @param unit Unit of measurement.
* @param is_sum Indicates whether this metric accumulates something, where
* only the total value is of interest.
*/
template <class ValueType = int64_t>
auto CounterFamily(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
if constexpr ( std::is_same<ValueType, int64_t>::value )
{
return IntCounterFam(prefix, name, labels, helptext, unit, is_sum);
}
else
{
static_assert(std::is_same<ValueType, double>::value,
"metrics only support int64_t and double values");
return DblCounterFam(prefix, name, labels, helptext, unit, is_sum);
}
}
/// @copydoc CounterFamily
template <class ValueType = int64_t>
auto CounterFamily(std::string_view prefix, std::string_view name,
std::initializer_list<std::string_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
auto lbl_span = Span{labels.begin(), labels.size()};
return CounterFamily<ValueType>(prefix, name, lbl_span, helptext, unit, is_sum);
}
/**
* Accesses a counter instance. Creates the hosting metric family as well
* as the counter lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to.
* @param name The human-readable name of the metric, e.g., `requests`.
* @param labels Values for all label dimensions of the metric.
* @param helptext Short explanation of the metric.
* @param unit Unit of measurement.
* @param is_sum Indicates whether this metric accumulates something, where
* only the total value is of interest.
*/
template <class ValueType = int64_t>
Counter<ValueType> CounterInstance(std::string_view prefix, std::string_view name,
Span<const LabelView> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
return WithLabelNames(labels,
[&, this](auto labelNames)
{
auto family = CounterFamily<ValueType>(prefix, name, labelNames,
helptext, unit, is_sum);
return family.getOrAdd(labels);
});
}
/// @copydoc counterInstance
template <class ValueType = int64_t>
Counter<ValueType> CounterInstance(std::string_view prefix, std::string_view name,
std::initializer_list<LabelView> labels,
std::string_view helptext, std::string_view unit = "1",
bool is_sum = false)
{
auto lbl_span = Span{labels.begin(), labels.size()};
return CounterInstance(prefix, name, lbl_span, helptext, unit, is_sum);
}
/**
* Accesses a counter singleton, i.e., a counter that belongs to a family
* without label dimensions (which thus only has a single member). Creates
* the hosting metric family as well as the counter lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to.
* @param name The human-readable name of the metric, e.g., `requests`.
* @param helptext Short explanation of the metric.
* @param unit Unit of measurement.
* @param is_sum Indicates whether this metric accumulates something, where
* only the total value is of interest.
*/
template <class ValueType = int64_t>
Counter<ValueType> CounterSingleton(std::string_view prefix, std::string_view name,
std::string_view helptext, std::string_view unit = "1",
bool is_sum = false)
{
auto labels = Span<const std::string_view>{};
auto fam = CounterFamily<ValueType>(prefix, name, labels, helptext, unit, is_sum);
return fam.GetOrAdd({});
}
/**
* @return A gauge metric family. Creates the family lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to.
* @param name The human-readable name of the metric, e.g., `requests`.
* @param labels Names for all label dimensions of the metric.
* @param helptext Short explanation of the metric.
* @param unit Unit of measurement.
* @param is_sum Indicates whether this metric accumulates something, where
* only the total value is of interest.
*/
template <class ValueType = int64_t>
auto GaugeFamily(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
if constexpr ( std::is_same<ValueType, int64_t>::value )
{
return IntGaugeFam(prefix, name, labels, helptext, unit, is_sum);
}
else
{
static_assert(std::is_same<ValueType, double>::value,
"metrics only support int64_t and double values");
return DblGaugeFam(prefix, name, labels, helptext, unit, is_sum);
}
}
/// @copydoc GaugeFamily
template <class ValueType = int64_t>
auto GaugeFamily(std::string_view prefix, std::string_view name,
std::initializer_list<std::string_view> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
auto lbl_span = Span{labels.begin(), labels.size()};
return GaugeFamily<ValueType>(prefix, name, lbl_span, helptext, unit, is_sum);
}
/**
* Accesses a gauge instance. Creates the hosting metric family as well
* as the gauge lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to.
* @param name The human-readable name of the metric, e.g., `requests`.
* @param labels Values for all label dimensions of the metric.
* @param helptext Short explanation of the metric.
* @param unit Unit of measurement.
* @param is_sum Indicates whether this metric accumulates something, where
* only the total value is of interest.
*/
template <class ValueType = int64_t>
Gauge<ValueType> GaugeInstance(std::string_view prefix, std::string_view name,
Span<const LabelView> labels, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
return WithLabelNames(labels,
[&, this](auto labelNames)
{
auto family = GaugeFamily<ValueType>(prefix, name, labelNames,
helptext, unit, is_sum);
return family.getOrAdd(labels);
});
}
/// @copydoc GaugeInstance
template <class ValueType = int64_t>
Gauge<ValueType> GaugeInstance(std::string_view prefix, std::string_view name,
std::initializer_list<LabelView> labels,
std::string_view helptext, std::string_view unit = "1",
bool is_sum = false)
{
auto lbl_span = Span{labels.begin(), labels.size()};
return GaugeInstance(prefix, name, lbl_span, helptext, unit, is_sum);
}
/**
* Accesses a gauge singleton, i.e., a gauge that belongs to a family
* without label dimensions (which thus only has a single member). Creates
* the hosting metric family as well as the gauge lazily if necessary.
* @param prefix The prefix (namespace) this family belongs to.
* @param name The human-readable name of the metric, e.g., `requests`.
* @param helptext Short explanation of the metric.
* @param unit Unit of measurement.
* @param is_sum Indicates whether this metric accumulates something, where
* only the total value is of interest.
*/
template <class ValueType = int64_t>
Gauge<ValueType> GaugeSingleton(std::string_view prefix, std::string_view name,
std::string_view helptext, std::string_view unit = "1",
bool is_sum = false)
{
auto labels = Span<const std::string_view>{};
auto fam = GaugeFamily<ValueType>(prefix, name, labels, helptext, unit, is_sum);
return fam.GetOrAdd({});
}
// Forces the compiler to use the type `Span<const T>` instead of trying to
// match paremeters to a `span`.
template <class T> struct ConstSpanOracle
{
using Type = Span<const T>;
};
// Convenience alias to safe some typing.
template <class T> using ConstSpan = typename ConstSpanOracle<T>::Type;
/**
* Returns a histogram metric family. Creates the family lazily if
* necessary.
* @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 labels Names for all label dimensions of the metric.
* @param default_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.
* @note The first call wins when calling this function multiple times with
* different bucket settings. Users may also override
* @p default_upper_bounds via run-time configuration.
*/
template <class ValueType = int64_t>
auto HistogramFamily(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels,
ConstSpan<ValueType> default_upper_bounds, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
if constexpr ( std::is_same<ValueType, int64_t>::value )
{
return IntHistoFam(prefix, name, labels, default_upper_bounds, helptext, unit, is_sum);
}
else
{
static_assert(std::is_same<ValueType, double>::value,
"metrics only support int64_t and double values");
return DblHistoFam(prefix, name, labels, default_upper_bounds, helptext, unit, is_sum);
}
}
/// @copydoc HistogramFamily
template <class ValueType = int64_t>
auto HistogramFamily(std::string_view prefix, std::string_view name,
std::initializer_list<std::string_view> labels,
ConstSpan<ValueType> default_upper_bounds, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
auto lbl_span = Span{labels.begin(), labels.size()};
return HistogramFamily<ValueType>(prefix, name, lbl_span, default_upper_bounds, helptext,
unit, is_sum);
}
/**
* Returns a histogram. Creates the family lazily if necessary.
* @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 labels Names for all label dimensions of the metric.
* @param default_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.
* @note The first call wins when calling this function multiple times with
* different bucket settings. Users may also override
* @p default_upper_bounds via run-time configuration.
*/
template <class ValueType = int64_t>
Histogram<ValueType>
HistogramInstance(std::string_view prefix, std::string_view name, Span<const LabelView> labels,
ConstSpan<ValueType> default_upper_bounds, std::string_view helptext,
std::string_view unit = "1", bool is_sum = false)
{
return WithLabelNames(labels,
[&, this](auto labelNames)
{
auto family = HistogramFamily<ValueType>(prefix, name, labelNames,
default_upper_bounds,
helptext, unit, is_sum);
return family.getOrAdd(labels);
});
}
/// @copdoc HistogramInstance
template <class ValueType = int64_t>
Histogram<ValueType> HistogramInstance(std::string_view prefix, std::string_view name,
std::initializer_list<LabelView> labels,
ConstSpan<ValueType> default_upper_bounds,
std::string_view helptext, std::string_view unit = "1",
bool is_sum = false)
{
auto lbls = Span{labels.begin(), labels.size()};
return HistogramInstance(prefix, name, lbls, default_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 default_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.
* @note The first call wins when calling this function multiple times with
* different bucket settings. Users may also override
* @p default_upper_bounds via run-time configuration.
*/
template <class ValueType = int64_t>
Histogram<ValueType> HistogramSingleton(std::string_view prefix, std::string_view name,
ConstSpan<ValueType> default_upper_bounds,
std::string_view helptext, std::string_view unit = "1",
bool is_sum = false)
{
auto lbls = Span<const std::string_view>{};
auto fam = HistogramFamily<ValueType>(prefix, name, lbls, default_upper_bounds, helptext,
unit, is_sum);
return fam.GetOrAdd({});
}
protected:
IntCounterFamily IntCounterFam(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, std::string_view helptext,
std::string_view unit, bool is_sum);
DblCounterFamily DblCounterFam(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, std::string_view helptext,
std::string_view unit, bool is_sum);
IntGaugeFamily IntGaugeFam(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, std::string_view helptext,
std::string_view unit, bool is_sum);
DblGaugeFamily DblGaugeFam(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, std::string_view helptext,
std::string_view unit, bool is_sum);
IntHistogramFamily IntHistoFam(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, Span<const int64_t> ubounds,
std::string_view helptext, std::string_view unit, bool is_sum);
DblHistogramFamily DblHistoFam(std::string_view prefix, std::string_view name,
Span<const std::string_view> labels, Span<const double> ubounds,
std::string_view helptext, std::string_view unit, bool is_sum);
template <class F> static void WithLabelNames(Span<const LabelView> xs, F continuation)
{
if ( xs.size() <= 10 )
{
std::string_view buf[10];
for ( size_t index = 0; index < xs.size(); ++index )
buf[index] = xs[index].first;
return continuation(Span{buf, xs.size()});
}
else
{
std::vector<std::string_view> buf;
for ( auto x : xs )
buf.emplace_back(x.first, x.second);
return continuation(Span{buf});
}
}
Impl* pimpl;
};
} // namespace zeek::telemetry
namespace zeek
{
extern telemetry::Manager* telemetry_mgr;
} // namespace zeek