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 {
50public:
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
71 static_assert(!std::is_arithmetic_v<Metric>, "Type is not atomic, use std::atomic<T> instead");
72
73public:
74 template <typename... Args>
75 explicit MetricWrapper(std::in_place_t, const Args&... args) : data_(args...) {
76 if constexpr (sizeof...(Args) == 0) {
77 InitializeAtomic(data_);
78 }
79 }
80
81 formats::json::ValueBuilder DeprecatedJsonDump() override {
82 if constexpr (!kHasWriterSupport<Metric>) {
83 return DumpMetric(data_);
84 }
85 return {};
86 }
87
88 void DumpToWriter(Writer& writer) override {
89 if constexpr (kHasWriterSupport<Metric>) {
90 writer = data_;
91 }
92 }
93
94 bool HasWriterSupport() const noexcept override { return kHasWriterSupport<Metric>; }
95
96 void Reset() override {
97 if constexpr (meta::kIsDetected<HasResetMetric, Metric>) {
98 ResetMetric(data_);
99 }
100 }
101
102 Metric& Get() { return data_; }
103
104private:
105 Metric data_;
106};
107
108using MetricFactory = std::function<std::unique_ptr<MetricWrapperBase>()>;
109
110template <typename Metric, typename... Args>
111// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward)
112MetricFactory MakeMetricFactory(Args&&... args) {
113 if constexpr (sizeof...(Args) == 0) {
114 static_assert(
115 std::is_default_constructible_v<Metric>,
116 "Metric type is not default-constructible. You can pass "
117 "additional args to MetricTag to forward them to Metric."
118 );
119 } else {
120 static_assert(
121 std::is_constructible_v<Metric, const Args&...>, "Metric type is not constructible from the given args"
122 );
123 static_assert(
124 (true && ... && std::is_copy_constructible_v<std::decay_t<Args>>), "Metric args must be copy-constructible"
125 );
126 }
127
128 return [args...] { return std::make_unique<MetricWrapper<Metric>>(std::in_place, args...); };
129}
130
131struct MetricKey final {
132 std::type_index idx;
133 std::string path;
134
135 bool operator==(const MetricKey& other) const noexcept { return idx == other.idx && path == other.path; }
136};
137
138struct MetricKeyHash final {
139 std::size_t operator()(const MetricKey& key) const noexcept;
140};
141
142using MetricMap = std::unordered_map<MetricKey, std::unique_ptr<MetricWrapperBase>, MetricKeyHash>;
143
144void RegisterMetricInfo(const MetricKey& key, MetricFactory factory);
145
146template <typename Metric>
147Metric& GetMetric(MetricMap& metrics, const MetricKey& key) {
148 return dynamic_cast<impl::MetricWrapper<Metric>&>(*metrics.at(key)).Get();
149}
150
151} // namespace utils::statistics::impl
152
153USERVER_NAMESPACE_END