userver: userver/utils/statistics/writer.hpp Source File
Loading...
Searching...
No Matches
writer.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/statistics/writer.hpp
4/// @brief @copybrief utils::statistics::Writer
5
6#include <atomic>
7#include <string_view>
8#include <type_traits>
9
10#include <userver/utils/impl/internal_tag.hpp>
11#include <userver/utils/statistics/histogram_view.hpp>
12#include <userver/utils/statistics/labels.hpp>
13#include <userver/utils/statistics/rate.hpp>
14
15USERVER_NAMESPACE_BEGIN
16
17namespace utils::statistics {
18
19class Writer;
20class MetricValue;
21
22namespace impl {
23
24struct WriterState;
25
26template <class Metric>
27constexpr auto HasDumpMetricWriter() noexcept
28 -> decltype(DumpMetric(std::declval<Writer&>(), std::declval<const Metric&>()), std::true_type{}) {
29 return {};
30}
31
32template <class Metric, class... Args>
33constexpr auto HasDumpMetricWriter(Args...) noexcept {
34 return std::is_arithmetic_v<Metric>;
35}
36
37} // namespace impl
38
39/// @brief Returns true, if the `Metric` could be written by
40/// utils::statistics::Writer.
41///
42/// In other words, checks that the DumpMetric for the `Metric` is provided or
43/// that the metric could be written without providing one.
44template <class Metric>
46
47// clang-format off
48
49/// @brief Class for writing metrics
50///
51/// The Writer could be customized by providing a
52/// `void DumpMetric(utils::statistics::Writer& writer, const Metric& value)`
53/// function:
54///
55/// @snippet core/src/utils/statistics/writer_test.cpp DumpMetric basic
56///
57/// DumpMetric functions nest well, labels are propagated to the nested
58/// DumpMetric:
59///
60/// @snippet core/src/utils/statistics/writer_test.cpp DumpMetric nested
61///
62/// To use the above writers register the metric writer in
63/// utils::statistics::Storage component:
64///
65/// @snippet core/src/utils/statistics/writer_test.cpp DumpMetric RegisterWriter
66///
67/// The above metrics in Graphite format would look like:
68/// @snippet core/src/utils/statistics/writer_test.cpp metrics graphite
69///
70/// The Writer is usable by the utils::statistics::MetricTag. For example, for
71/// the following structure:
72/// @snippet samples/tcp_full_duplex_service/tcp_full_duplex_service.cpp TCP sample - Stats definition
73///
74/// The DumpMetric function may look like:
75/// @snippet samples/tcp_full_duplex_service/tcp_full_duplex_service.cpp TCP sample - Stats tag
76///
77/// For information on metrics testing in testsuite refer to
78/// @ref TESTSUITE_METRICS_TESTING "Testsuite - Metrics".
79///
80/// For metrics testing in unit-tests see utils::statistics::Snapshot.
81
82// clang-format on
83
84class Writer final {
85public:
86 /// Path parts delimiter. In other words, writer["a"]["b"] becomes "a.b"
87 static inline constexpr char kDelimiter = '.';
88
89 Writer() = delete;
90 Writer(Writer&& other) = delete;
91 Writer(const Writer&) = delete;
92 Writer& operator=(Writer&&) = delete;
93 Writer& operator=(const Writer&) = delete;
94
95 ~Writer();
96
97 /// Returns a Writer with a ('.' + path) appended
98 [[nodiscard]] Writer operator[](std::string_view path) &;
99
100 /// Returns a Writer with a ('.' + path) appended
101 [[nodiscard]] Writer operator[](std::string_view path) &&;
102
103 /// Write metric value to metrics builder via using DumpMetric
104 /// function.
105 template <class T>
106 void operator=(const T& value) {
107 if constexpr (std::is_arithmetic_v<T> || std::is_same_v<std::decay_t<T>, Rate> || std::is_same_v<std::decay_t<T>, HistogramView> || std::is_same_v<std::decay_t<T>, MetricValue>) {
108 Write(value);
109 } else {
110 if (state_) {
111 static_assert(
112 kHasWriterSupport<T>,
113 "Cast the metric to an arithmetic type or provide a "
114 "`void DumpMetric(utils::statistics::Writer& writer, "
115 "const Metric& value)` function for the `Metric` type"
116 );
117 DumpMetric(*this, value);
118 }
119 }
120 }
121
122 /// Write metric value with labels to metrics builder
123 template <class T>
124 void ValueWithLabels(const T& value, LabelsSpan labels) {
125 auto new_writer = MakeChild();
126 new_writer.AppendLabelsSpan(labels);
127 new_writer = value;
128 }
129
130 /// Write metric value with labels to metrics builder
131 template <class T>
132 void ValueWithLabels(const T& value, std::initializer_list<LabelView> il) {
133 ValueWithLabels(value, LabelsSpan{il});
134 }
135
136 /// Write metric value with label to metrics builder
137 template <class T>
138 void ValueWithLabels(const T& value, const LabelView& label) {
139 ValueWithLabels(value, LabelsSpan{&label, &label + 1});
140 }
141
142 /// @cond
143 /// func must be called even for filtered out Writers, e.g. to always collect
144 /// totals
145 template <class Func>
146 void WithLabels(utils::impl::InternalTag, LabelsSpan labels, Func func) {
147 auto new_writer = MakeChild();
148 new_writer.AppendLabelsSpan(labels);
149 func(new_writer);
150 }
151
152 template <class Func>
153 void WithLabels(utils::impl::InternalTag, std::initializer_list<LabelView> il, Func func) {
154 WithLabels(utils::impl::InternalTag{}, LabelsSpan{il}, func);
155 }
156
157 template <class Func>
158 void WithLabels(utils::impl::InternalTag, const LabelView& label, Func func) {
159 WithLabels(utils::impl::InternalTag{}, LabelsSpan{&label, &label + 1}, func);
160 }
161 /// @endcond
162
163 /// Returns true if this writer would actually write data. Returns false if
164 /// the data is not required by request and metrics construction could be
165 /// skipped.
166 explicit operator bool() const noexcept { return !!state_; }
167
168 /// @cond
169 explicit Writer(impl::WriterState* state) noexcept;
170 explicit Writer(impl::WriterState& state, LabelsSpan labels);
171 /// @endcond
172
173private:
174 using ULongLong = unsigned long long;
175
176 using PathSizeType = std::uint16_t;
177 using LabelsSizeType = std::uint8_t;
178
179 void Write(unsigned long long value);
180 void Write(long long value);
181 void Write(double value);
182 void Write(Rate value);
183 void Write(HistogramView value);
184 void Write(MetricValue value);
185
186 void Write(float value) { Write(static_cast<double>(value)); }
187
188 void Write(unsigned long value) { Write(static_cast<ULongLong>(value)); }
189 void Write(long value) { Write(static_cast<long long>(value)); }
190 void Write(unsigned int value) { Write(static_cast<long long>(value)); }
191 void Write(int value) { Write(static_cast<long long>(value)); }
192 void Write(unsigned short value) { Write(static_cast<long long>(value)); }
193 void Write(short value) { Write(static_cast<long long>(value)); }
194
195 Writer MakeChild();
196
197 struct MoveTag {};
198 Writer(Writer& other, MoveTag) noexcept;
199
200 Writer MoveOut() noexcept { return Writer{*this, MoveTag{}}; }
201
202 void ResetState() noexcept;
203 void ValidateUsage();
204
205 void AppendPath(std::string_view path);
206 void AppendLabelsSpan(LabelsSpan labels);
207
208 impl::WriterState* state_;
209 const PathSizeType initial_path_size_;
210 PathSizeType current_path_size_;
211 const LabelsSizeType initial_labels_size_;
212 LabelsSizeType current_labels_size_;
213};
214
215template <class Metric>
216void DumpMetric(Writer& writer, const std::atomic<Metric>& m) {
217 static_assert(std::atomic<Metric>::is_always_lock_free, "std::atomic misuse");
218 writer = m.load();
219}
220
221} // namespace utils::statistics
222
223USERVER_NAMESPACE_END