Files
clio/src/util/prometheus/Histogram.hpp
2026-03-24 15:25:32 +00:00

138 lines
3.6 KiB
C++

#pragma once
#include "util/Assert.hpp"
#include "util/prometheus/MetricBase.hpp"
#include "util/prometheus/OStream.hpp"
#include "util/prometheus/impl/HistogramImpl.hpp"
#include <cstdint>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
namespace util::prometheus {
/**
* @brief A Prometheus histogram metric with a generic value type
*
* @tparam NumberType The number type of the histogram
*/
template <SomeNumberType NumberType>
class AnyHistogram : public MetricBase {
public:
using ValueType = NumberType;
using Buckets = std::vector<NumberType>;
/**
* @brief Construct a new Histogram object
*
* @tparam ImplType The type of the implementation of the histogram
* @param name The name of the metric
* @param labelsString The labels of the metric in serialized format, e.g.
* {name="value",name2="value2"}
* @param buckets The buckets of the histogram
* @param impl The implementation of the histogram (has default value and need to be specified
* only for testing)
*/
template <impl::SomeHistogramImpl ImplType = impl::HistogramImpl<ValueType>>
requires std::same_as<ValueType, typename std::remove_cvref_t<ImplType>::ValueType>
AnyHistogram(
std::string name,
std::string labelsString,
Buckets const& buckets,
ImplType&& impl = ImplType{}
)
: MetricBase(std::move(name), std::move(labelsString))
, pimpl_(std::make_unique<Model<ImplType>>(std::forward<ImplType>(impl)))
{
ASSERT(!buckets.empty(), "Histogram must have at least one bucket.");
ASSERT(
std::is_sorted(buckets.begin(), buckets.end()), "Buckets for histogra must be sorted."
);
pimpl_->setBuckets(buckets);
}
/**
* @brief Add a value to the histogram
*
* @param value The value to add
*/
void
observe(ValueType const value)
{
pimpl_->observe(value);
}
/**
* @brief Serialize the metric to a string in Prometheus format
*
* @param stream The stream to serialize into
*/
void
serializeValue(OStream& stream) const override
{
pimpl_->serializeValue(name(), labelsString(), stream);
}
private:
struct Concept {
virtual ~Concept() = default;
virtual void observe(NumberType) = 0;
virtual void
setBuckets(Buckets const& buckets) = 0;
virtual void
serializeValue(
std::string const& name,
std::string const& labelsString,
OStream&
) const = 0;
};
template <impl::SomeHistogramImpl ImplType>
requires std::same_as<NumberType, typename std::remove_cvref_t<ImplType>::ValueType>
struct Model : Concept {
template <typename SomeImplType>
requires std::same_as<SomeImplType, ImplType>
Model(SomeImplType&& impl) : impl_(std::forward<SomeImplType>(impl))
{
}
void
observe(NumberType value) override
{
impl_.observe(value);
}
void
setBuckets(Buckets const& buckets) override
{
impl_.setBuckets(buckets);
}
void
serializeValue(
std::string const& name,
std::string const& labelsString,
OStream& stream
) const override
{
impl_.serializeValue(name, labelsString, stream);
}
private:
ImplType impl_;
};
std::unique_ptr<Concept> pimpl_;
};
using HistogramInt = AnyHistogram<std::int64_t>;
using HistogramDouble = AnyHistogram<double>;
} // namespace util::prometheus