userver: userver/utils/statistics/writer.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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