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 #ifndef GRPC_SRC_CORE_LIB_CHANNEL_METRICS_H 16 #define GRPC_SRC_CORE_LIB_CHANNEL_METRICS_H 17 18 #include <grpc/support/port_platform.h> 19 20 #include <cstdint> 21 #include <memory> 22 #include <vector> 23 24 #include "absl/container/flat_hash_map.h" 25 #include "absl/functional/any_invocable.h" 26 #include "absl/functional/function_ref.h" 27 #include "absl/strings/string_view.h" 28 #include "absl/types/span.h" 29 30 #include <grpc/support/log.h> 31 #include <grpc/support/metrics.h> 32 33 #include "src/core/lib/channel/call_tracer.h" 34 #include "src/core/lib/channel/channel_args.h" 35 #include "src/core/lib/channel/context.h" 36 #include "src/core/lib/gprpp/no_destruct.h" 37 #include "src/core/lib/gprpp/sync.h" 38 #include "src/core/lib/gprpp/time.h" 39 #include "src/core/lib/slice/slice.h" 40 41 namespace grpc_core { 42 43 constexpr absl::string_view kMetricLabelTarget = "grpc.target"; 44 45 // A global registry of instruments(metrics). This API is designed to be used 46 // to register instruments (Counter, Histogram, and Gauge) as part of program 47 // startup, before the execution of the main function (during dynamic 48 // initialization time). Using this API after the main function begins may 49 // result into missing instruments. This API is thread-unsafe. 50 class GlobalInstrumentsRegistry { 51 public: 52 enum class ValueType { 53 kUndefined, 54 kInt64, 55 kUInt64, 56 kDouble, 57 }; 58 enum class InstrumentType { 59 kUndefined, 60 kCounter, 61 kHistogram, 62 kGauge, 63 kCallbackGauge, 64 }; 65 using InstrumentID = uint32_t; 66 struct GlobalInstrumentDescriptor { 67 ValueType value_type; 68 InstrumentType instrument_type; 69 InstrumentID index; 70 bool enable_by_default; 71 absl::string_view name; 72 absl::string_view description; 73 absl::string_view unit; 74 std::vector<absl::string_view> label_keys; 75 std::vector<absl::string_view> optional_label_keys; 76 }; 77 struct GlobalInstrumentHandle { 78 // This is the index for the corresponding registered instrument that 79 // StatsPlugins can use to uniquely identify an instrument in the current 80 // process. Though this is not guaranteed to be stable between different 81 // runs or between different versions. 82 InstrumentID index; 83 }; 84 struct GlobalUInt64CounterHandle : public GlobalInstrumentHandle {}; 85 struct GlobalDoubleCounterHandle : public GlobalInstrumentHandle {}; 86 struct GlobalUInt64HistogramHandle : public GlobalInstrumentHandle {}; 87 struct GlobalDoubleHistogramHandle : public GlobalInstrumentHandle {}; 88 struct GlobalInt64GaugeHandle : public GlobalInstrumentHandle {}; 89 struct GlobalDoubleGaugeHandle : public GlobalInstrumentHandle {}; 90 struct GlobalCallbackInt64GaugeHandle : public GlobalInstrumentHandle {}; 91 struct GlobalCallbackDoubleGaugeHandle : public GlobalInstrumentHandle {}; 92 using GlobalCallbackHandle = absl::variant<GlobalCallbackInt64GaugeHandle, 93 GlobalCallbackDoubleGaugeHandle>; 94 95 // Creates instrument in the GlobalInstrumentsRegistry. 96 static GlobalUInt64CounterHandle RegisterUInt64Counter( 97 absl::string_view name, absl::string_view description, 98 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 99 absl::Span<const absl::string_view> optional_label_keys, 100 bool enable_by_default); 101 static GlobalDoubleCounterHandle RegisterDoubleCounter( 102 absl::string_view name, absl::string_view description, 103 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 104 absl::Span<const absl::string_view> optional_label_keys, 105 bool enable_by_default); 106 static GlobalUInt64HistogramHandle RegisterUInt64Histogram( 107 absl::string_view name, absl::string_view description, 108 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 109 absl::Span<const absl::string_view> optional_label_keys, 110 bool enable_by_default); 111 static GlobalDoubleHistogramHandle RegisterDoubleHistogram( 112 absl::string_view name, absl::string_view description, 113 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 114 absl::Span<const absl::string_view> optional_label_keys, 115 bool enable_by_default); 116 static GlobalInt64GaugeHandle RegisterInt64Gauge( 117 absl::string_view name, absl::string_view description, 118 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 119 absl::Span<const absl::string_view> optional_label_keys, 120 bool enable_by_default); 121 static GlobalDoubleGaugeHandle RegisterDoubleGauge( 122 absl::string_view name, absl::string_view description, 123 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 124 absl::Span<const absl::string_view> optional_label_keys, 125 bool enable_by_default); 126 static GlobalCallbackInt64GaugeHandle RegisterCallbackInt64Gauge( 127 absl::string_view name, absl::string_view description, 128 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 129 absl::Span<const absl::string_view> optional_label_keys, 130 bool enable_by_default); 131 static GlobalCallbackDoubleGaugeHandle RegisterCallbackDoubleGauge( 132 absl::string_view name, absl::string_view description, 133 absl::string_view unit, absl::Span<const absl::string_view> label_keys, 134 absl::Span<const absl::string_view> optional_label_keys, 135 bool enable_by_default); 136 137 static void ForEach( 138 absl::FunctionRef<void(const GlobalInstrumentDescriptor&)> f); 139 static const GlobalInstrumentDescriptor& GetInstrumentDescriptor( 140 GlobalInstrumentHandle handle); 141 142 private: 143 friend class GlobalInstrumentsRegistryTestPeer; 144 145 GlobalInstrumentsRegistry() = delete; 146 147 static std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>& 148 GetInstrumentList(); 149 }; 150 151 // An interface for implementing callback-style metrics. 152 // To be implemented by stats plugins. 153 class CallbackMetricReporter { 154 public: 155 virtual ~CallbackMetricReporter() = default; 156 157 virtual void Report( 158 GlobalInstrumentsRegistry::GlobalCallbackInt64GaugeHandle handle, 159 int64_t value, absl::Span<const absl::string_view> label_values, 160 absl::Span<const absl::string_view> optional_values) = 0; 161 virtual void Report( 162 GlobalInstrumentsRegistry::GlobalCallbackDoubleGaugeHandle handle, 163 double value, absl::Span<const absl::string_view> label_values, 164 absl::Span<const absl::string_view> optional_values) = 0; 165 }; 166 167 class RegisteredMetricCallback; 168 169 // The StatsPlugin interface. 170 class StatsPlugin { 171 public: 172 // A general-purpose way for stats plugin to store per-channel or per-server 173 // state. 174 class ScopeConfig { 175 public: 176 virtual ~ScopeConfig() = default; 177 }; 178 179 virtual ~StatsPlugin() = default; 180 181 // Whether this stats plugin is enabled for the channel specified by \a scope. 182 // Returns true and a channel-specific ScopeConfig which may then be used to 183 // configure the ClientCallTracer in GetClientCallTracer(). 184 virtual std::pair<bool, std::shared_ptr<ScopeConfig>> IsEnabledForChannel( 185 const experimental::StatsPluginChannelScope& scope) const = 0; 186 // Whether this stats plugin is enabled for the server specified by \a args. 187 // Returns true and a server-specific ScopeConfig which may then be used to 188 // configure the ServerCallTracer in GetServerCallTracer(). 189 virtual std::pair<bool, std::shared_ptr<ScopeConfig>> IsEnabledForServer( 190 const ChannelArgs& args) const = 0; 191 192 // Adds \a value to the uint64 counter specified by \a handle. \a label_values 193 // and \a optional_label_values specify attributes that are associated with 194 // this measurement and must match with their corresponding keys in 195 // GlobalInstrumentsRegistry::RegisterUInt64Counter(). 196 virtual void AddCounter( 197 GlobalInstrumentsRegistry::GlobalUInt64CounterHandle handle, 198 uint64_t value, absl::Span<const absl::string_view> label_values, 199 absl::Span<const absl::string_view> optional_label_values) = 0; 200 // Adds \a value to the double counter specified by \a handle. \a label_values 201 // and \a optional_label_values specify attributes that are associated with 202 // this measurement and must match with their corresponding keys in 203 // GlobalInstrumentsRegistry::RegisterDoubleCounter(). 204 virtual void AddCounter( 205 GlobalInstrumentsRegistry::GlobalDoubleCounterHandle handle, double value, 206 absl::Span<const absl::string_view> label_values, 207 absl::Span<const absl::string_view> optional_label_values) = 0; 208 // Records a uint64 \a value to the histogram specified by \a handle. \a 209 // label_values and \a optional_label_values specify attributes that are 210 // associated with this measurement and must match with their corresponding 211 // keys in GlobalInstrumentsRegistry::RegisterUInt64Histogram(). 212 virtual void RecordHistogram( 213 GlobalInstrumentsRegistry::GlobalUInt64HistogramHandle handle, 214 uint64_t value, absl::Span<const absl::string_view> label_values, 215 absl::Span<const absl::string_view> optional_label_values) = 0; 216 // Records a double \a value to the histogram specified by \a handle. \a 217 // label_values and \a optional_label_values specify attributes that are 218 // associated with this measurement and must match with their corresponding 219 // keys in GlobalInstrumentsRegistry::RegisterDoubleHistogram(). 220 virtual void RecordHistogram( 221 GlobalInstrumentsRegistry::GlobalDoubleHistogramHandle handle, 222 double value, absl::Span<const absl::string_view> label_values, 223 absl::Span<const absl::string_view> optional_label_values) = 0; 224 // Sets an int64 \a value to the gauge specifed by \a handle. \a 225 // label_values and \a optional_label_values specify attributes that are 226 // associated with this measurement and must match with their corresponding 227 // keys in GlobalInstrumentsRegistry::RegisterInt64Gauge(). 228 virtual void SetGauge( 229 GlobalInstrumentsRegistry::GlobalInt64GaugeHandle handle, int64_t value, 230 absl::Span<const absl::string_view> label_values, 231 absl::Span<const absl::string_view> optional_label_values) = 0; 232 // Sets a double \a value to the gauge specifed by \a handle. \a 233 // label_values and \a optional_label_values specify attributes that are 234 // associated with this measurement and must match with their corresponding 235 // keys in GlobalInstrumentsRegistry::RegisterDoubleGauge(). 236 virtual void SetGauge( 237 GlobalInstrumentsRegistry::GlobalDoubleGaugeHandle handle, double value, 238 absl::Span<const absl::string_view> label_values, 239 absl::Span<const absl::string_view> optional_label_values) = 0; 240 // Adds a callback to be invoked when the stats plugin wants to 241 // populate the corresponding metrics (see callback->metrics() for list). 242 virtual void AddCallback(RegisteredMetricCallback* callback) = 0; 243 // Removes a callback previously added via AddCallback(). The stats 244 // plugin may not use the callback after this method returns. 245 virtual void RemoveCallback(RegisteredMetricCallback* callback) = 0; 246 247 // Gets a ClientCallTracer associated with this stats plugin which can be used 248 // in a call. 249 virtual ClientCallTracer* GetClientCallTracer( 250 const Slice& path, bool registered_method, 251 std::shared_ptr<ScopeConfig> scope_config) = 0; 252 // Gets a ServerCallTracer associated with this stats plugin which can be used 253 // in a call. 254 virtual ServerCallTracer* GetServerCallTracer( 255 std::shared_ptr<ScopeConfig> scope_config) = 0; 256 257 // TODO(yijiem): This is an optimization for the StatsPlugin to create its own 258 // representation of the label_values and use it multiple times. We would 259 // change AddCounter and RecordHistogram to take RefCountedPtr<LabelValueSet> 260 // and also change the StatsPluginsGroup to support this. 261 // Use the StatsPlugin to get a representation of label values that can be 262 // saved for multiple uses later. 263 // virtual RefCountedPtr<LabelValueSet> MakeLabelValueSet( 264 // absl::Span<absl::string_view> label_values) = 0; 265 }; 266 267 // A global registry of stats plugins. It has shared ownership to the registered 268 // stats plugins. This API is supposed to be used during runtime after the main 269 // function begins. This API is thread-safe. 270 class GlobalStatsPluginRegistry { 271 public: 272 // A stats plugin group object is how the code in gRPC normally interacts with 273 // stats plugins. They got a stats plugin group which contains all the stats 274 // plugins for a specific scope and all operations on the stats plugin group 275 // will be applied to all the stats plugins within the group. 276 class StatsPluginGroup { 277 public: 278 // Adds a stats plugin and a scope config (per-channel or per-server) to the 279 // group. AddStatsPlugin(std::shared_ptr<StatsPlugin> plugin,std::shared_ptr<StatsPlugin::ScopeConfig> config)280 void AddStatsPlugin(std::shared_ptr<StatsPlugin> plugin, 281 std::shared_ptr<StatsPlugin::ScopeConfig> config) { 282 PluginState plugin_state; 283 plugin_state.plugin = std::move(plugin); 284 plugin_state.scope_config = std::move(config); 285 plugins_state_.push_back(std::move(plugin_state)); 286 } 287 // Adds a counter in all stats plugins within the group. See the StatsPlugin 288 // interface for more documentation and valid types. 289 template <class HandleType, class ValueType> AddCounter(HandleType handle,ValueType value,absl::Span<const absl::string_view> label_values,absl::Span<const absl::string_view> optional_values)290 void AddCounter(HandleType handle, ValueType value, 291 absl::Span<const absl::string_view> label_values, 292 absl::Span<const absl::string_view> optional_values) { 293 for (auto& state : plugins_state_) { 294 state.plugin->AddCounter(handle, value, label_values, optional_values); 295 } 296 } 297 // Records a value to a histogram in all stats plugins within the group. See 298 // the StatsPlugin interface for more documentation and valid types. 299 template <class HandleType, class ValueType> RecordHistogram(HandleType handle,ValueType value,absl::Span<const absl::string_view> label_values,absl::Span<const absl::string_view> optional_values)300 void RecordHistogram(HandleType handle, ValueType value, 301 absl::Span<const absl::string_view> label_values, 302 absl::Span<const absl::string_view> optional_values) { 303 for (auto& state : plugins_state_) { 304 state.plugin->RecordHistogram(handle, value, label_values, 305 optional_values); 306 } 307 } 308 // Sets a value to a gauge in all stats plugins within the group. See the 309 // StatsPlugin interface for more documentation and valid types. 310 template <class HandleType, class ValueType> SetGauge(HandleType handle,ValueType value,absl::Span<const absl::string_view> label_values,absl::Span<const absl::string_view> optional_values)311 void SetGauge(HandleType handle, ValueType value, 312 absl::Span<const absl::string_view> label_values, 313 absl::Span<const absl::string_view> optional_values) { 314 for (auto& state : plugins_state_) { 315 state.plugin->SetGauge(handle, value, label_values, optional_values); 316 } 317 } 318 319 // Registers a callback to be used to populate callback metrics. 320 // The callback will update the specified metrics. The callback 321 // will be invoked no more often than min_interval. Multiple callbacks may 322 // be registered for the same metrics, as long as no two callbacks report 323 // data for the same set of labels in which case the behavior is undefined. 324 // 325 // The returned object is a handle that allows the caller to control 326 // the lifetime of the callback; when the returned object is 327 // destroyed, the callback is de-registered. The returned object 328 // must not outlive the StatsPluginGroup object that created it. 329 GRPC_MUST_USE_RESULT std::unique_ptr<RegisteredMetricCallback> 330 RegisterCallback( 331 absl::AnyInvocable<void(CallbackMetricReporter&)> callback, 332 std::vector<GlobalInstrumentsRegistry::GlobalCallbackHandle> metrics, 333 Duration min_interval = Duration::Seconds(5)); 334 335 // Adds all available client call tracers associated with the stats plugins 336 // within the group to \a call_context. 337 void AddClientCallTracers(const Slice& path, bool registered_method, 338 grpc_call_context_element* call_context); 339 // Adds all available server call tracers associated with the stats plugins 340 // within the group to \a call_context. 341 void AddServerCallTracers(grpc_call_context_element* call_context); 342 343 private: 344 friend class RegisteredMetricCallback; 345 346 struct PluginState { 347 std::shared_ptr<StatsPlugin::ScopeConfig> scope_config; 348 std::shared_ptr<StatsPlugin> plugin; 349 }; 350 351 std::vector<PluginState> plugins_state_; 352 }; 353 354 // Registers a stats plugin with the global stats plugin registry. 355 static void RegisterStatsPlugin(std::shared_ptr<StatsPlugin> plugin); 356 357 // The following functions can be invoked to get a StatsPluginGroup for 358 // a specified scope. 359 static StatsPluginGroup GetStatsPluginsForChannel( 360 const experimental::StatsPluginChannelScope& scope); 361 static StatsPluginGroup GetStatsPluginsForServer(const ChannelArgs& args); 362 363 private: 364 friend class GlobalStatsPluginRegistryTestPeer; 365 366 GlobalStatsPluginRegistry() = default; 367 368 static NoDestruct<Mutex> mutex_; 369 static NoDestruct<std::vector<std::shared_ptr<StatsPlugin>>> plugins_ 370 ABSL_GUARDED_BY(mutex_); 371 }; 372 373 // A metric callback that is registered with a stats plugin group. 374 class RegisteredMetricCallback { 375 public: 376 RegisteredMetricCallback( 377 GlobalStatsPluginRegistry::StatsPluginGroup& stats_plugin_group, 378 absl::AnyInvocable<void(CallbackMetricReporter&)> callback, 379 std::vector<GlobalInstrumentsRegistry::GlobalCallbackHandle> metrics, 380 Duration min_interval); 381 382 ~RegisteredMetricCallback(); 383 384 // Invokes the callback. The callback will report metric data via reporter. Run(CallbackMetricReporter & reporter)385 void Run(CallbackMetricReporter& reporter) { callback_(reporter); } 386 387 // Returns the set of metrics that this callback will modify. metrics()388 const std::vector<GlobalInstrumentsRegistry::GlobalCallbackHandle>& metrics() 389 const { 390 return metrics_; 391 } 392 393 // Returns the minimum interval at which a stats plugin may invoke the 394 // callback. min_interval()395 Duration min_interval() const { return min_interval_; } 396 397 private: 398 GlobalStatsPluginRegistry::StatsPluginGroup& stats_plugin_group_; 399 absl::AnyInvocable<void(CallbackMetricReporter&)> callback_; 400 std::vector<GlobalInstrumentsRegistry::GlobalCallbackHandle> metrics_; 401 Duration min_interval_; 402 }; 403 404 } // namespace grpc_core 405 406 #endif // GRPC_SRC_CORE_LIB_CHANNEL_METRICS_H 407