Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
HistogramImpl.hpp
1#pragma once
2
3#include "util/Assert.hpp"
4#include "util/Concepts.hpp"
5#include "util/Mutex.hpp"
6#include "util/prometheus/OStream.hpp"
7
8#include <cstdint>
9#include <limits>
10#include <memory>
11#include <mutex>
12#include <string>
13#include <type_traits>
14#include <utility>
15#include <vector>
16
17namespace util::prometheus::impl {
18
19template <typename T>
20concept SomeHistogramImpl = requires(T t) {
21 typename std::remove_cvref_t<T>::ValueType;
23 { t.observe(typename std::remove_cvref_t<T>::ValueType{1}) } -> std::same_as<void>;
24 {
25 t.setBuckets(std::vector<typename std::remove_cvref_t<T>::ValueType>{})
26 } -> std::same_as<void>;
27 {
28 t.serializeValue(std::string{}, std::string{}, std::declval<OStream&>())
29 } -> std::same_as<void>;
30};
31
32template <SomeNumberType NumberType>
33class HistogramImpl {
34public:
35 using ValueType = NumberType;
36
37 HistogramImpl() = default;
38
39 HistogramImpl(HistogramImpl const&) = delete;
40 HistogramImpl(HistogramImpl&&) = default;
41
42 HistogramImpl&
43 operator=(HistogramImpl const&) = delete;
44 HistogramImpl&
45 operator=(HistogramImpl&&) = default;
46
47 void
48 setBuckets(std::vector<ValueType> const& bounds)
49 {
50 auto data = data_->template lock<std::scoped_lock>();
51 ASSERT(data->buckets.empty(), "Buckets can be set only once.");
52 data->buckets.reserve(bounds.size());
53 for (auto const& bound : bounds) {
54 data->buckets.emplace_back(bound);
55 }
56 }
57
58 void
59 observe(ValueType const value)
60 {
61 auto data = data_->template lock<std::scoped_lock>();
62 auto const bucket = std::lower_bound(
63 data->buckets.begin(),
64 data->buckets.end(),
65 value,
66 [](Bucket const& bucket, ValueType const& value) { return bucket.upperBound < value; }
67 );
68 if (bucket != data->buckets.end()) {
69 ++bucket->count;
70 } else {
71 ++data->lastBucket.count;
72 }
73 data->sum += value;
74 }
75
76 void
77 serializeValue(std::string const& name, std::string labelsString, OStream& stream) const
78 {
79 if (labelsString.empty()) {
80 labelsString = "{";
81 } else {
82 ASSERT(
83 labelsString.front() == '{' && labelsString.back() == '}',
84 "Labels must be in Prometheus serialized format."
85 );
86 labelsString.back() = ',';
87 }
88
89 auto data = data_->template lock<std::scoped_lock>();
90 std::uint64_t cumulativeCount = 0;
91
92 for (auto const& bucket : data->buckets) {
93 cumulativeCount += bucket.count;
94 stream << name << "_bucket" << labelsString << "le=\"" << bucket.upperBound << "\"} "
95 << cumulativeCount << '\n';
96 }
97 cumulativeCount += data->lastBucket.count;
98 stream << name << "_bucket" << labelsString << "le=\"+Inf\"} " << cumulativeCount << '\n';
99
100 if (labelsString.size() == 1) {
101 labelsString = "";
102 } else {
103 labelsString.back() = '}';
104 }
105 stream << name << "_sum" << labelsString << " " << data->sum << '\n';
106 stream << name << "_count" << labelsString << " " << cumulativeCount << '\n';
107 }
108
109private:
110 struct Bucket {
111 Bucket(ValueType upperBound) : upperBound(upperBound)
112 {
113 }
114
115 ValueType upperBound;
116 std::uint64_t count = 0;
117 };
118
119 struct Data {
120 std::vector<Bucket> buckets;
121 Bucket lastBucket{std::numeric_limits<ValueType>::max()};
122 ValueType sum = 0;
123 };
124 std::unique_ptr<util::Mutex<Data>> data_ = std::make_unique<util::Mutex<Data>>();
125};
126
127} // namespace util::prometheus::impl
A stream that can optionally compress its data.
Definition OStream.hpp:12
Specifies a number type.
Definition Concepts.hpp:15
Definition HistogramImpl.hpp:20
This namespace implements the data access layer and related components.
Definition AmendmentCenter.cpp:56