userver: userver/utils/statistics/metric_tag_impl.hpp Source File
Loading...
Searching...
No Matches
metric_tag_impl.hpp
1#pragma once
2
3#include <atomic>
4#include <cstddef>
5#include <functional>
6#include <memory>
7#include <string>
8#include <type_traits>
9#include <typeindex>
10#include <typeinfo>
11#include <unordered_map>
12
13#include <userver/formats/json/value.hpp>
14#include <userver/formats/json/value_builder.hpp>
15#include <userver/utils/meta_light.hpp>
16#include <userver/utils/statistics/writer.hpp>
17
18USERVER_NAMESPACE_BEGIN
19
20namespace utils::statistics::impl {
21
22template <typename Metric>
23formats::json::ValueBuilder DumpMetric(const std::atomic<Metric>& m) {
24 static_assert(std::atomic<Metric>::is_always_lock_free, "std::atomic misuse");
25 return m.load();
26}
27
28template <typename Metric>
29void ResetMetric(std::atomic<Metric>& m) {
30 static_assert(std::atomic<Metric>::is_always_lock_free, "std::atomic misuse");
31 m = Metric();
32}
33
34template <typename Metric>
35using HasDumpMetric = decltype(DumpMetric(std::declval<Metric&>()));
36
37template <typename Metric>
38using HasResetMetric = decltype(ResetMetric(std::declval<Metric&>()));
39
40// TODO remove after C++20 atomic value-initialization
41template <typename T>
42void InitializeAtomic(T& /*value*/) {}
43
44template <typename T>
45void InitializeAtomic(std::atomic<T>& value) {
46 value.store(T(), std::memory_order_relaxed);
47}
48
49class MetricWrapperBase {
50 public:
51 MetricWrapperBase& operator=(MetricWrapperBase&&) = delete;
52 virtual ~MetricWrapperBase();
53
54 virtual formats::json::ValueBuilder DeprecatedJsonDump() = 0;
55
56 virtual void DumpToWriter(utils::statistics::Writer& writer) = 0;
57
58 virtual bool HasWriterSupport() const noexcept = 0;
59
60 virtual void Reset() = 0;
61};
62
63template <typename Metric>
64class MetricWrapper final : public MetricWrapperBase {
65 static_assert(
66 meta::kIsDetected<HasDumpMetric, Metric> || kHasWriterSupport<Metric>,
67 "Provide a `void DumpMetric(utils::statistics::Writer&, const Metric&)`"
68 "function in the namespace of `Metric`.");
69
70 static_assert(!std::is_arithmetic_v<Metric>,
71 "Type is not atomic, use std::atomic<T> instead");
72
73 public:
74 template <typename... Args>
75 explicit MetricWrapper(std::in_place_t, const Args&... args)
76 : data_(args...) {
77 if constexpr (sizeof...(Args) == 0) {
78 InitializeAtomic(data_);
79 }
80 }
81
82 formats::json::ValueBuilder DeprecatedJsonDump() override {
83 if constexpr (!kHasWriterSupport<Metric>) {
84 return DumpMetric(data_);
85 }
86 return {};
87 }
88
89 void DumpToWriter(Writer& writer) override {
90 if constexpr (kHasWriterSupport<Metric>) {
91 writer = data_;
92 }
93 }
94
95 bool HasWriterSupport() const noexcept override {
96 return kHasWriterSupport<Metric>;
97 }
98
99 void Reset() override {
100 if constexpr (meta::kIsDetected<HasResetMetric, Metric>) {
101 ResetMetric(data_);
102 }
103 }
104
105 Metric& Get() { return data_; }
106
107 private:
108 Metric data_;
109};
110
111using MetricFactory = std::function<std::unique_ptr<MetricWrapperBase>()>;
112
113template <typename Metric, typename... Args>
114// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
115MetricFactory MakeMetricFactory(Args&&... args) {
116 if constexpr (sizeof...(Args) == 0) {
117 static_assert(std::is_default_constructible_v<Metric>,
118 "Metric type is not default-constructible. You can pass "
119 "additional args to MetricTag to forward them to Metric.");
120 } else {
121 static_assert(std::is_constructible_v<Metric, const Args&...>,
122 "Metric type is not constructible from the given args");
123 static_assert(
124 (true && ... && std::is_copy_constructible_v<std::decay_t<Args>>),
125 "Metric args must be copy-constructible");
126 }
127
128 return [args...] {
129 return std::make_unique<MetricWrapper<Metric>>(std::in_place, args...);
130 };
131}
132
133struct MetricKey final {
134 std::type_index idx;
135 std::string path;
136
137 bool operator==(const MetricKey& other) const noexcept {
138 return idx == other.idx && path == other.path;
139 }
140};
141
142struct MetricKeyHash final {
143 std::size_t operator()(const MetricKey& key) const noexcept;
144};
145
146using MetricMap =
147 std::unordered_map<MetricKey, std::unique_ptr<MetricWrapperBase>,
148 MetricKeyHash>;
149
150void RegisterMetricInfo(const MetricKey& key, MetricFactory factory);
151
152template <typename Metric>
153Metric& GetMetric(MetricMap& metrics, const MetricKey& key) {
154 return dynamic_cast<impl::MetricWrapper<Metric>&>(*metrics.at(key)).Get();
155}
156
157} // namespace utils::statistics::impl
158
159USERVER_NAMESPACE_END