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/function_ref.hpp>
11#include <userver/utils/impl/internal_tag.hpp>
12#include <userver/utils/statistics/histogram_view.hpp>
13#include <userver/utils/statistics/labels.hpp>
14#include <userver/utils/statistics/rate.hpp>
15#include <userver/utils/statistics/request.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace utils::statistics {
20
21class Writer;
22class MetricValue;
23
24namespace impl {
25struct WriterState;
26} // namespace impl
27
28/// @brief Returns true, if the `Metric` could be written by @ref utils::statistics::Writer.
29///
30/// In other words, checks that the `DumpMetric` for the `Metric` is provided or
31/// that the metric could be written without providing one.
32template <class Metric>
33concept HasWriterSupport = std::is_arithmetic_v<Metric> || requires(Writer& writer, const Metric& metric) {
34 DumpMetric(writer, metric);
35};
36
37/// @ingroup userver_universal
38///
39/// @brief Class for writing metrics that is provided by utils::statistics::Storage.
40///
41/// Usage is quite straightforward:
42///
43/// @snippet core/src/utils/statistics/pretty_format_test.cpp Writer basic sample
44///
45/// The above sample would produce the following metrics:
46///
47/// @snippet core/src/utils/statistics/pretty_format_test.cpp metrics pretty
48///
49/// The Writer can be customized for writing custom metric types by providing a
50/// `void DumpMetric(utils::statistics::Writer& writer, const CustomMetric& value)`
51/// function:
52///
53/// @snippet core/src/utils/statistics/writer_test.cpp DumpMetric basic
54///
55/// DumpMetric functions nest well, labels are propagated to the nested
56/// DumpMetric:
57///
58/// @snippet core/src/utils/statistics/writer_test.cpp DumpMetric nested
59///
60/// To use the above writers register the metric writer in
61/// @ref utils::statistics::Storage component:
62///
63/// @snippet core/src/utils/statistics/writer_test.cpp DumpMetric RegisterWriter
64///
65/// The above metrics in Graphite format would look like:
66/// @snippet core/src/utils/statistics/writer_test.cpp metrics graphite
67///
68/// The Writer is usable by the utils::statistics::MetricTag. For example, for
69/// the following structure:
70/// @snippet samples/tcp_full_duplex_service/main.cpp TCP sample - Stats definition
71///
72/// The DumpMetric function may look like:
73/// @snippet samples/tcp_full_duplex_service/main.cpp TCP sample - Stats tag
74///
75/// For information on metrics testing in testsuite refer to
76/// @ref TESTSUITE_METRICS_TESTING "Testsuite - Metrics".
77///
78/// For metrics testing in unit-tests see utils::statistics::Snapshot.
79///
80/// For introduction to metrics see @ref scripts/docs/en/userver/metrics.md
81class Writer final {
82public:
83 /// Path parts delimiter. In other words, writer["a"]["b"] becomes "a.b"
84 static constexpr char kDelimiter = '.';
85
86 Writer() = delete;
87 Writer(Writer&& other) = delete;
88 Writer(const Writer&) = delete;
89 Writer& operator=(Writer&&) = delete;
90 Writer& operator=(const Writer&) = delete;
91
92 ~Writer();
93
94 /// Returns a Writer with a ('.' + path) appended
95 [[nodiscard]] Writer operator[](std::string_view path) &;
96
97 /// Returns a Writer with a ('.' + path) appended
98 [[nodiscard]] Writer operator[](std::string_view path) &&;
99
100 /// Write metric value to metrics builder via using DumpMetric
101 /// function.
102 template <class T>
103 void operator=(const T& value) {
104 if constexpr (std::is_arithmetic_v<T> || std::is_same_v<std::decay_t<T>, Rate> ||
105 std::is_same_v<std::decay_t<T>, HistogramView> || std::is_same_v<std::decay_t<T>, MetricValue>)
106 {
107 Write(value);
108 } else {
109 if (state_) {
110 static_assert(
112 "Cast the metric to an arithmetic type or provide a "
113 "`void DumpMetric(utils::statistics::Writer& writer, "
114 "const Metric& value)` function for the `Metric` type"
115 );
116 DumpMetric(*this, value);
117 }
118 }
119 }
120
121 /// Write metric value with labels to metrics builder
122 template <class T>
123 void ValueWithLabels(const T& value, LabelsSpan labels) {
124 auto new_writer = MakeChild();
125 new_writer.AppendLabelsSpan(labels);
126 new_writer = value;
127 }
128
129 /// Write metric value with labels to metrics builder
130 template <class T>
131 void ValueWithLabels(const T& value, std::initializer_list<LabelView> il) {
132 ValueWithLabels(value, LabelsSpan{il});
133 }
134
135 /// Write metric value with label to metrics builder
136 template <class T>
137 void ValueWithLabels(const T& value, const LabelView& label) {
138 ValueWithLabels(value, LabelsSpan{&label, &label + 1});
139 }
140
141 /// @cond
142 /// func must be called even for filtered out Writers, e.g. to always collect
143 /// totals
144 template <class Func>
145 void WithLabels(utils::impl::InternalTag, LabelsSpan labels, Func func) {
146 auto new_writer = MakeChild();
147 new_writer.AppendLabelsSpan(labels);
148 func(new_writer);
149 }
150
151 template <class Func>
152 void WithLabels(utils::impl::InternalTag, std::initializer_list<LabelView> il, Func func) {
153 WithLabels(utils::impl::InternalTag{}, LabelsSpan{il}, func);
154 }
155
156 template <class Func>
157 void WithLabels(utils::impl::InternalTag, const LabelView& label, Func func) {
158 WithLabels(utils::impl::InternalTag{}, LabelsSpan{&label, &label + 1}, func);
159 }
160 /// @endcond
161
162 /// Returns true if this writer would actually write data. Returns false if
163 /// the data is not required by request and metrics construction could be
164 /// skipped.
165 explicit operator bool() const noexcept { return !!state_; }
166
167 /// @cond
168 explicit Writer(impl::WriterState* state) noexcept;
169 explicit Writer(impl::WriterState& state, LabelsSpan labels);
170 /// @endcond
171
172private:
173 using ULongLong = unsigned long long;
174
175 using PathSizeType = std::uint16_t;
176 using LabelsSizeType = std::uint8_t;
177
178 void Write(unsigned long long value);
179 void Write(long long value);
180 void Write(double value);
181 void Write(Rate value);
182 void Write(HistogramView value);
183 void Write(MetricValue value);
184
185 void Write(float value) { Write(static_cast<double>(value)); }
186
187 void Write(unsigned long value) { Write(static_cast<ULongLong>(value)); }
188 void Write(long value) { Write(static_cast<long long>(value)); }
189 void Write(unsigned int value) { Write(static_cast<long long>(value)); }
190 void Write(int value) { Write(static_cast<long long>(value)); }
191 void Write(unsigned short value) { Write(static_cast<long long>(value)); }
192 void Write(short value) { Write(static_cast<long long>(value)); }
193
194 Writer MakeChild();
195
196 struct MoveTag {};
197 Writer(Writer& other, MoveTag) noexcept;
198
199 Writer MoveOut() noexcept { return Writer{*this, MoveTag{}}; }
200
201 void ResetState() noexcept;
202 void ValidateUsage();
203
204 void AppendPath(std::string_view path);
205 void AppendLabelsSpan(LabelsSpan labels);
206
207 impl::WriterState* state_;
208 const PathSizeType initial_path_size_;
209 PathSizeType current_path_size_;
210 const LabelsSizeType initial_labels_size_;
211 LabelsSizeType current_labels_size_;
212};
213
214/// Non-owning reference to a function that writes metrics via @ref Writer.
215using WriterFuncRef = utils::function_ref<void(Writer&) const>;
216
217template <class Metric>
218void DumpMetric(Writer& writer, const std::atomic<Metric>& m) {
219 static_assert(std::atomic<Metric>::is_always_lock_free, "std::atomic misuse");
220 writer = m.load();
221}
222
223/// @brief Low-level connecting function that dumps metrics from @a func into @a out.
224///
225/// Calls @a func with a @ref Writer. Each metric written through that `Writer`
226/// is forwarded to `out.HandleMetric`. @a request filters the metrics and may
227/// attach extra labels; `request.prefix` is a match filter, not a path to write
228/// under.
229///
230/// @ref utils::statistics::Storage::VisitMetrics uses this function to dump
231/// Writer-based metrics. It is used to implement formats such as:
232/// - @ref utils::statistics::ToPrometheusFormat "Prometheus"
233/// - @ref utils::statistics::ToGraphiteFormat "Graphite"
234/// - @ref utils::statistics::ToJsonFormat "JSON"
235/// - @ref utils::statistics::ToPrettyFormat "pretty format"
236/// - @ref utils::statistics::ToSolomonFormat "Solomon"
237/// - @ref utils::statistics::GetPortabilityWarnings "portability info"
238///
239/// @param func writes metrics to the provided @ref Writer
240/// @param out receives each written metric via @ref BaseFormatBuilder::HandleMetric
241/// @param request metric filter and extra labels
242void VisitMetrics(WriterFuncRef func, BaseFormatBuilder& out, const Request& request = {});
243
244/// @overload
245///
246/// Dumps @a metric via `writer = metric` (`DumpMetric` / built-in Writer support).
250
251} // namespace utils::statistics
252
253USERVER_NAMESPACE_END