123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154 |
- #pragma once
- #include <vector>
- #include <cassert>
- #include <algorithm>
- #include "prometheus/metric.h"
- #include "prometheus/family.h"
- #include "prometheus/counter.h"
- namespace prometheus {
- /// \brief A histogram metric to represent aggregatable distributions of events.
- ///
- /// This class represents the metric type histogram:
- /// https://prometheus.io/docs/concepts/metric_types/#histogram
- ///
- /// A histogram tracks the number of observations and the sum of the observed
- /// values, allowing to calculate the average of the observed values.
- ///
- /// At its core a histogram has a counter per bucket. The sum of observations
- /// also behaves like a counter as long as there are no negative observations.
- ///
- /// See https://prometheus.io/docs/practices/histograms/ for detailed
- /// explanations of histogram usage and differences to summaries.
- ///
- /// The class is thread-safe. No concurrent call to any API of this type causes
- /// a data race.
- template <typename Value_ = uint64_t>
- class Histogram : public Metric {
- public:
- using Value = Value_;
- using BucketBoundaries = std::vector<Value_>;
- using Family = CustomFamily<Histogram<Value>>;
- static const Metric::Type static_type = Metric::Type::Histogram;
- /// \brief Create a histogram with manually chosen buckets.
- ///
- /// The BucketBoundaries are a list of monotonically increasing values
- /// representing the bucket boundaries. Each consecutive pair of values is
- /// interpreted as a half-open interval [b_n, b_n+1) which defines one bucket.
- ///
- /// There is no limitation on how the buckets are divided, i.e, equal size,
- /// exponential etc..
- ///
- /// The bucket boundaries cannot be changed once the histogram is created.
- Histogram (const BucketBoundaries& buckets)
- : Metric(static_type), bucket_boundaries_{ buckets }, bucket_counts_{ buckets.size() + 1 }, sum_{} {
- assert(std::is_sorted(std::begin(bucket_boundaries_),
- std::end(bucket_boundaries_)));
- }
- /// \brief Observe the given amount.
- ///
- /// The given amount selects the 'observed' bucket. The observed bucket is
- /// chosen for which the given amount falls into the half-open interval [b_n,
- /// b_n+1). The counter of the observed bucket is incremented. Also the total
- /// sum of all observations is incremented.
- void Observe(const Value value) {
- // TODO: determine bucket list size at which binary search would be faster
- const auto bucket_index = static_cast<std::size_t>(std::distance(
- bucket_boundaries_.begin(),
- std::find_if(
- std::begin(bucket_boundaries_), std::end(bucket_boundaries_),
- [value](const Value boundary) { return boundary >= value; })));
- sum_.Increment(value);
- bucket_counts_[bucket_index].Increment();
- }
- /// \brief Observe multiple data points.
- ///
- /// Increments counters given a count for each bucket. (i.e. the caller of
- /// this function must have already sorted the values into buckets).
- /// Also increments the total sum of all observations by the given value.
- void ObserveMultiple(const std::vector<Value>& bucket_increments,
- const Value sum_of_values) {
- if (bucket_increments.size() != bucket_counts_.size()) {
- throw std::length_error(
- "The size of bucket_increments was not equal to"
- "the number of buckets in the histogram.");
- }
- sum_.Increment(sum_of_values);
- for (std::size_t i{ 0 }; i < bucket_counts_.size(); ++i) {
- bucket_counts_[i].Increment(bucket_increments[i]);
- }
- }
- /// \brief Get the current value of the counter.
- ///
- /// Collect is called by the Registry when collecting metrics.
- virtual ClientMetric Collect() const {
- auto metric = ClientMetric{};
- auto cumulative_count = 0ULL;
- metric.histogram.bucket.reserve(bucket_counts_.size());
- for (std::size_t i{0}; i < bucket_counts_.size(); ++i) {
- cumulative_count += static_cast<std::size_t>(bucket_counts_[i].Get());
- auto bucket = ClientMetric::Bucket{};
- bucket.cumulative_count = cumulative_count;
- bucket.upper_bound = (i == bucket_boundaries_.size()
- ? std::numeric_limits<double>::infinity()
- : static_cast<double>(bucket_boundaries_[i]));
- metric.histogram.bucket.push_back(std::move(bucket));
- }
- metric.histogram.sample_count = cumulative_count;
- metric.histogram.sample_sum = sum_.Get();
- return metric;
- }
- private:
- const BucketBoundaries bucket_boundaries_;
- std::vector<Counter<Value_>> bucket_counts_;
- Gauge<Value_> sum_;
- };
- /// \brief Return a builder to configure and register a Histogram metric.
- ///
- /// @copydetails Family<>::Family()
- ///
- /// Example usage:
- ///
- /// \code
- /// auto registry = std::make_shared<Registry>();
- /// auto& histogram_family = prometheus::BuildHistogram()
- /// .Name("some_name")
- /// .Help("Additional description.")
- /// .Labels({{"key", "value"}})
- /// .Register(*registry);
- ///
- /// ...
- /// \endcode
- ///
- /// \return An object of unspecified type T, i.e., an implementation detail
- /// except that it has the following members:
- ///
- /// - Name(const std::string&) to set the metric name,
- /// - Help(const std::string&) to set an additional description.
- /// - Label(const std::map<std::string, std::string>&) to assign a set of
- /// key-value pairs (= labels) to the metric.
- ///
- /// To finish the configuration of the Histogram metric register it with
- /// Register(Registry&).
- using BuildHistogram = Builder<Histogram<double>>;
- } // namespace prometheus
|