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