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/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