1 // Copyright 2024 The gRPC Authors.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "src/core/telemetry/metrics.h"
16
17 #include <grpc/support/port_platform.h>
18
19 #include <memory>
20
21 #include "absl/log/check.h"
22 #include "absl/types/optional.h"
23 #include "src/core/util/crash.h"
24
25 namespace grpc_core {
26
27 // Uses the Construct-on-First-Use idiom to avoid the static initialization
28 // order fiasco.
29 std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>&
GetInstrumentList()30 GlobalInstrumentsRegistry::GetInstrumentList() {
31 static NoDestruct<
32 std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>>
33 instruments;
34 return *instruments;
35 }
36
37 GlobalInstrumentsRegistry::InstrumentID
RegisterInstrument(GlobalInstrumentsRegistry::ValueType value_type,GlobalInstrumentsRegistry::InstrumentType instrument_type,absl::string_view name,absl::string_view description,absl::string_view unit,bool enable_by_default,absl::Span<const absl::string_view> label_keys,absl::Span<const absl::string_view> optional_label_keys)38 GlobalInstrumentsRegistry::RegisterInstrument(
39 GlobalInstrumentsRegistry::ValueType value_type,
40 GlobalInstrumentsRegistry::InstrumentType instrument_type,
41 absl::string_view name, absl::string_view description,
42 absl::string_view unit, bool enable_by_default,
43 absl::Span<const absl::string_view> label_keys,
44 absl::Span<const absl::string_view> optional_label_keys) {
45 auto& instruments = GetInstrumentList();
46 for (const auto& descriptor : instruments) {
47 if (descriptor.name == name) {
48 Crash(
49 absl::StrFormat("Metric name %s has already been registered.", name));
50 }
51 }
52 InstrumentID index = instruments.size();
53 CHECK_LT(index, std::numeric_limits<uint32_t>::max());
54 GlobalInstrumentDescriptor descriptor;
55 descriptor.value_type = value_type;
56 descriptor.instrument_type = instrument_type;
57 descriptor.index = index;
58 descriptor.enable_by_default = enable_by_default;
59 descriptor.name = name;
60 descriptor.description = description;
61 descriptor.unit = unit;
62 descriptor.label_keys = {label_keys.begin(), label_keys.end()};
63 descriptor.optional_label_keys = {optional_label_keys.begin(),
64 optional_label_keys.end()};
65 instruments.push_back(std::move(descriptor));
66 return index;
67 }
68
ForEach(absl::FunctionRef<void (const GlobalInstrumentDescriptor &)> f)69 void GlobalInstrumentsRegistry::ForEach(
70 absl::FunctionRef<void(const GlobalInstrumentDescriptor&)> f) {
71 for (const auto& instrument : GetInstrumentList()) {
72 f(instrument);
73 }
74 }
75
76 const GlobalInstrumentsRegistry::GlobalInstrumentDescriptor&
GetInstrumentDescriptor(GlobalInstrumentHandle handle)77 GlobalInstrumentsRegistry::GetInstrumentDescriptor(
78 GlobalInstrumentHandle handle) {
79 return GetInstrumentList().at(handle.index);
80 }
81
RegisteredMetricCallback(GlobalStatsPluginRegistry::StatsPluginGroup & stats_plugin_group,absl::AnyInvocable<void (CallbackMetricReporter &)> callback,std::vector<GlobalInstrumentsRegistry::GlobalInstrumentHandle> metrics,Duration min_interval)82 RegisteredMetricCallback::RegisteredMetricCallback(
83 GlobalStatsPluginRegistry::StatsPluginGroup& stats_plugin_group,
84 absl::AnyInvocable<void(CallbackMetricReporter&)> callback,
85 std::vector<GlobalInstrumentsRegistry::GlobalInstrumentHandle> metrics,
86 Duration min_interval)
87 : stats_plugin_group_(stats_plugin_group),
88 callback_(std::move(callback)),
89 metrics_(std::move(metrics)),
90 min_interval_(min_interval) {
91 for (auto& state : stats_plugin_group_.plugins_state_) {
92 state.plugin->AddCallback(this);
93 }
94 }
95
~RegisteredMetricCallback()96 RegisteredMetricCallback::~RegisteredMetricCallback() {
97 for (auto& state : stats_plugin_group_.plugins_state_) {
98 state.plugin->RemoveCallback(this);
99 }
100 }
101
AddClientCallTracers(const Slice & path,bool registered_method,Arena * arena)102 void GlobalStatsPluginRegistry::StatsPluginGroup::AddClientCallTracers(
103 const Slice& path, bool registered_method, Arena* arena) {
104 for (auto& state : plugins_state_) {
105 auto* call_tracer = state.plugin->GetClientCallTracer(
106 path, registered_method, state.scope_config);
107 if (call_tracer != nullptr) {
108 AddClientCallTracerToContext(arena, call_tracer);
109 }
110 }
111 }
112
AddServerCallTracers(Arena * arena)113 void GlobalStatsPluginRegistry::StatsPluginGroup::AddServerCallTracers(
114 Arena* arena) {
115 for (auto& state : plugins_state_) {
116 auto* call_tracer = state.plugin->GetServerCallTracer(state.scope_config);
117 if (call_tracer != nullptr) {
118 AddServerCallTracerToContext(arena, call_tracer);
119 }
120 }
121 }
122
123 std::atomic<GlobalStatsPluginRegistry::GlobalStatsPluginNode*>
124 GlobalStatsPluginRegistry::plugins_;
125
RegisterStatsPlugin(std::shared_ptr<StatsPlugin> plugin)126 void GlobalStatsPluginRegistry::RegisterStatsPlugin(
127 std::shared_ptr<StatsPlugin> plugin) {
128 GlobalStatsPluginNode* node = new GlobalStatsPluginNode();
129 node->plugin = std::move(plugin);
130 node->next = plugins_.load(std::memory_order_relaxed);
131 while (!plugins_.compare_exchange_weak(
132 node->next, node, std::memory_order_acq_rel, std::memory_order_relaxed)) {
133 }
134 }
135
136 GlobalStatsPluginRegistry::StatsPluginGroup
GetStatsPluginsForChannel(const experimental::StatsPluginChannelScope & scope)137 GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
138 const experimental::StatsPluginChannelScope& scope) {
139 StatsPluginGroup group;
140 for (GlobalStatsPluginNode* node = plugins_.load(std::memory_order_acquire);
141 node != nullptr; node = node->next) {
142 bool is_enabled = false;
143 std::shared_ptr<StatsPlugin::ScopeConfig> config;
144 std::tie(is_enabled, config) = node->plugin->IsEnabledForChannel(scope);
145 if (is_enabled) {
146 group.AddStatsPlugin(node->plugin, std::move(config));
147 }
148 }
149 return group;
150 }
151
152 GlobalStatsPluginRegistry::StatsPluginGroup
GetStatsPluginsForServer(const ChannelArgs & args)153 GlobalStatsPluginRegistry::GetStatsPluginsForServer(const ChannelArgs& args) {
154 StatsPluginGroup group;
155 for (GlobalStatsPluginNode* node = plugins_.load(std::memory_order_acquire);
156 node != nullptr; node = node->next) {
157 bool is_enabled = false;
158 std::shared_ptr<StatsPlugin::ScopeConfig> config;
159 std::tie(is_enabled, config) = node->plugin->IsEnabledForServer(args);
160 if (is_enabled) {
161 group.AddStatsPlugin(node->plugin, std::move(config));
162 }
163 }
164 return group;
165 }
166
167 absl::optional<GlobalInstrumentsRegistry::GlobalInstrumentHandle>
FindInstrumentByName(absl::string_view name)168 GlobalInstrumentsRegistry::FindInstrumentByName(absl::string_view name) {
169 const auto& instruments = GetInstrumentList();
170 for (const auto& descriptor : instruments) {
171 if (descriptor.name == name) {
172 GlobalInstrumentsRegistry::GlobalInstrumentHandle handle;
173 handle.index = descriptor.index;
174 return handle;
175 }
176 }
177 return absl::nullopt;
178 }
179
180 } // namespace grpc_core
181