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