• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <memory>
18 #include <thread>
19 
20 #include "absl/log/log.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include "src/core/lib/event_engine/channel_args_endpoint_config.h"
24 #include "test/core/test_util/fake_stats_plugin.h"
25 #include "test/core/test_util/test_config.h"
26 
27 namespace grpc_core {
28 namespace {
29 
30 using experimental::StatsPluginChannelScope;
31 
32 class MetricsTest : public ::testing::Test {
33  protected:
MetricsTest()34   MetricsTest() : endpoint_config_(ChannelArgs()) {}
35 
TearDown()36   void TearDown() override {
37     GlobalInstrumentsRegistryTestPeer::ResetGlobalInstrumentsRegistry();
38     GlobalStatsPluginRegistryTestPeer::ResetGlobalStatsPluginRegistry();
39   }
40 
41   grpc_event_engine::experimental::ChannelArgsEndpointConfig endpoint_config_;
42 };
43 
TEST_F(MetricsTest,UInt64Counter)44 TEST_F(MetricsTest, UInt64Counter) {
45   auto uint64_counter_handle =
46       GlobalInstrumentsRegistry::RegisterUInt64Counter(
47           "uint64_counter", "A simple uint64 counter.", "unit", true)
48           .Labels("label_key_1", "label_key_2")
49           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
50           .Build();
51   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
52                                                    "label_value_2"};
53   std::array<absl::string_view, 2> kOptionalLabelValues = {
54       "optional_label_value_1", "optional_label_value_2"};
55   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
56   constexpr absl::string_view kDomain2To4 = "domain2.domain3.domain4";
57   constexpr absl::string_view kDomain3To4 = "domain3.domain4";
58   auto plugin1 = MakeStatsPluginForTarget(kDomain1To4);
59   auto plugin2 = MakeStatsPluginForTarget(kDomain2To4);
60   auto plugin3 = MakeStatsPluginForTarget(kDomain3To4);
61   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
62       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_))
63       .AddCounter(uint64_counter_handle, uint64_t(1), kLabelValues,
64                   kOptionalLabelValues);
65   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
66       StatsPluginChannelScope(kDomain2To4, "", endpoint_config_))
67       .AddCounter(uint64_counter_handle, uint64_t(2), kLabelValues,
68                   kOptionalLabelValues);
69   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
70       StatsPluginChannelScope(kDomain3To4, "", endpoint_config_))
71       .AddCounter(uint64_counter_handle, uint64_t(3), kLabelValues,
72                   kOptionalLabelValues);
73   EXPECT_THAT(plugin1->GetUInt64CounterValue(
74                   uint64_counter_handle, kLabelValues, kOptionalLabelValues),
75               ::testing::Optional(1));
76   EXPECT_THAT(plugin2->GetUInt64CounterValue(
77                   uint64_counter_handle, kLabelValues, kOptionalLabelValues),
78               ::testing::Optional(3));
79   EXPECT_THAT(plugin3->GetUInt64CounterValue(
80                   uint64_counter_handle, kLabelValues, kOptionalLabelValues),
81               ::testing::Optional(6));
82 }
83 
TEST_F(MetricsTest,DoubleCounter)84 TEST_F(MetricsTest, DoubleCounter) {
85   auto double_counter_handle =
86       GlobalInstrumentsRegistry::RegisterDoubleCounter(
87           "double_counter", "A simple double counter.", "unit", true)
88           .Labels("label_key_1", "label_key_2")
89           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
90           .Build();
91   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
92                                                    "label_value_2"};
93   std::array<absl::string_view, 2> kOptionalLabelValues = {
94       "optional_label_value_1", "optional_label_value_2"};
95   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
96   constexpr absl::string_view kDomain2To4 = "domain2.domain3.domain4";
97   constexpr absl::string_view kDomain3To4 = "domain3.domain4";
98   auto plugin1 = MakeStatsPluginForTarget(kDomain1To4);
99   auto plugin2 = MakeStatsPluginForTarget(kDomain2To4);
100   auto plugin3 = MakeStatsPluginForTarget(kDomain3To4);
101   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
102       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_))
103       .AddCounter(double_counter_handle, 1.23, kLabelValues,
104                   kOptionalLabelValues);
105   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
106       StatsPluginChannelScope(kDomain2To4, "", endpoint_config_))
107       .AddCounter(double_counter_handle, 2.34, kLabelValues,
108                   kOptionalLabelValues);
109   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
110       StatsPluginChannelScope(kDomain3To4, "", endpoint_config_))
111       .AddCounter(double_counter_handle, 3.45, kLabelValues,
112                   kOptionalLabelValues);
113   EXPECT_THAT(plugin1->GetDoubleCounterValue(
114                   double_counter_handle, kLabelValues, kOptionalLabelValues),
115               ::testing::Optional(1.23));
116   EXPECT_THAT(plugin2->GetDoubleCounterValue(
117                   double_counter_handle, kLabelValues, kOptionalLabelValues),
118               ::testing::Optional(3.57));
119   EXPECT_THAT(plugin3->GetDoubleCounterValue(
120                   double_counter_handle, kLabelValues, kOptionalLabelValues),
121               ::testing::Optional(7.02));
122 }
123 
TEST_F(MetricsTest,UInt64Histogram)124 TEST_F(MetricsTest, UInt64Histogram) {
125   auto uint64_histogram_handle =
126       GlobalInstrumentsRegistry::RegisterUInt64Histogram(
127           "uint64_histogram", "A simple uint64 histogram.", "unit", true)
128           .Labels("label_key_1", "label_key_2")
129           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
130           .Build();
131   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
132                                                    "label_value_2"};
133   std::array<absl::string_view, 2> kOptionalLabelValues = {
134       "optional_label_value_1", "optional_label_value_2"};
135   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
136   constexpr absl::string_view kDomain2To4 = "domain2.domain3.domain4";
137   constexpr absl::string_view kDomain3To4 = "domain3.domain4";
138   auto plugin1 = MakeStatsPluginForTarget(kDomain1To4);
139   auto plugin2 = MakeStatsPluginForTarget(kDomain2To4);
140   auto plugin3 = MakeStatsPluginForTarget(kDomain3To4);
141   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
142       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_))
143       .RecordHistogram(uint64_histogram_handle, uint64_t(1), kLabelValues,
144                        kOptionalLabelValues);
145   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
146       StatsPluginChannelScope(kDomain2To4, "", endpoint_config_))
147       .RecordHistogram(uint64_histogram_handle, uint64_t(2), kLabelValues,
148                        kOptionalLabelValues);
149   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
150       StatsPluginChannelScope(kDomain3To4, "", endpoint_config_))
151       .RecordHistogram(uint64_histogram_handle, uint64_t(3), kLabelValues,
152                        kOptionalLabelValues);
153   EXPECT_THAT(plugin1->GetUInt64HistogramValue(
154                   uint64_histogram_handle, kLabelValues, kOptionalLabelValues),
155               ::testing::Optional(::testing::UnorderedElementsAre(1)));
156   EXPECT_THAT(plugin2->GetUInt64HistogramValue(
157                   uint64_histogram_handle, kLabelValues, kOptionalLabelValues),
158               ::testing::Optional(::testing::UnorderedElementsAre(1, 2)));
159   EXPECT_THAT(plugin3->GetUInt64HistogramValue(
160                   uint64_histogram_handle, kLabelValues, kOptionalLabelValues),
161               ::testing::Optional(::testing::UnorderedElementsAre(1, 2, 3)));
162 }
163 
TEST_F(MetricsTest,DoubleHistogram)164 TEST_F(MetricsTest, DoubleHistogram) {
165   auto double_histogram_handle =
166       GlobalInstrumentsRegistry::RegisterDoubleHistogram(
167           "double_histogram", "A simple double histogram.", "unit", true)
168           .Labels("label_key_1", "label_key_2")
169           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
170           .Build();
171   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
172                                                    "label_value_2"};
173   std::array<absl::string_view, 2> kOptionalLabelValues = {
174       "optional_label_value_1", "optional_label_value_2"};
175   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
176   constexpr absl::string_view kDomain2To4 = "domain2.domain3.domain4";
177   constexpr absl::string_view kDomain3To4 = "domain3.domain4";
178   auto plugin1 = MakeStatsPluginForTarget(kDomain1To4);
179   auto plugin2 = MakeStatsPluginForTarget(kDomain2To4);
180   auto plugin3 = MakeStatsPluginForTarget(kDomain3To4);
181   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
182       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_))
183       .RecordHistogram(double_histogram_handle, 1.23, kLabelValues,
184                        kOptionalLabelValues);
185   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
186       StatsPluginChannelScope(kDomain2To4, "", endpoint_config_))
187       .RecordHistogram(double_histogram_handle, 2.34, kLabelValues,
188                        kOptionalLabelValues);
189   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
190       StatsPluginChannelScope(kDomain3To4, "", endpoint_config_))
191       .RecordHistogram(double_histogram_handle, 3.45, kLabelValues,
192                        kOptionalLabelValues);
193   EXPECT_THAT(plugin1->GetDoubleHistogramValue(
194                   double_histogram_handle, kLabelValues, kOptionalLabelValues),
195               ::testing::Optional(::testing::UnorderedElementsAre(1.23)));
196   EXPECT_THAT(plugin2->GetDoubleHistogramValue(
197                   double_histogram_handle, kLabelValues, kOptionalLabelValues),
198               ::testing::Optional(::testing::UnorderedElementsAre(1.23, 2.34)));
199   EXPECT_THAT(
200       plugin3->GetDoubleHistogramValue(double_histogram_handle, kLabelValues,
201                                        kOptionalLabelValues),
202       ::testing::Optional(::testing::UnorderedElementsAre(1.23, 2.34, 3.45)));
203 }
204 
TEST_F(MetricsTest,Int64CallbackGauge)205 TEST_F(MetricsTest, Int64CallbackGauge) {
206   auto int64_gauge_handle =
207       GlobalInstrumentsRegistry::RegisterCallbackInt64Gauge(
208           "int64_gauge", "A simple int64 gauge.", "unit", true)
209           .Labels("label_key_1", "label_key_2")
210           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
211           .Build();
212   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
213                                                    "label_value_2"};
214   std::array<absl::string_view, 2> kLabelValues2 = {"label_value_3",
215                                                     "label_value_2"};
216   std::array<absl::string_view, 2> kOptionalLabelValues = {
217       "optional_label_value_1", "optional_label_value_2"};
218   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
219   constexpr absl::string_view kDomain2To4 = "domain2.domain3.domain4";
220   constexpr absl::string_view kDomain3To4 = "domain3.domain4";
221   auto plugin1 = MakeStatsPluginForTarget(kDomain3To4);
222   auto plugin2 = MakeStatsPluginForTarget(kDomain2To4);
223   auto plugin3 = MakeStatsPluginForTarget(kDomain1To4);
224   // Register two callbacks that set the same metric but with different
225   // label values.  The callbacks get used only by plugin1.
226   LOG(INFO) << "testing callbacks for: plugin1";
227   auto group1 = GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
228       StatsPluginChannelScope(kDomain3To4, "", endpoint_config_));
229   auto callback1 = group1.RegisterCallback(
230       [&](CallbackMetricReporter& reporter) {
231         reporter.Report(int64_gauge_handle, int64_t(1), kLabelValues,
232                         kOptionalLabelValues);
233       },
234       Duration::Seconds(5), int64_gauge_handle);
235   auto callback2 = group1.RegisterCallback(
236       [&](CallbackMetricReporter& reporter) {
237         reporter.Report(int64_gauge_handle, int64_t(2), kLabelValues2,
238                         kOptionalLabelValues);
239       },
240       Duration::Seconds(5), int64_gauge_handle);
241   // No plugins have data yet.
242   EXPECT_EQ(plugin1->GetInt64CallbackGaugeValue(
243                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
244             absl::nullopt);
245   EXPECT_EQ(plugin1->GetInt64CallbackGaugeValue(
246                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
247             absl::nullopt);
248   EXPECT_EQ(plugin2->GetInt64CallbackGaugeValue(
249                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
250             absl::nullopt);
251   EXPECT_EQ(plugin2->GetInt64CallbackGaugeValue(
252                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
253             absl::nullopt);
254   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
255                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
256             absl::nullopt);
257   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
258                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
259             absl::nullopt);
260   // Now invoke the callbacks.
261   plugin1->TriggerCallbacks();
262   plugin2->TriggerCallbacks();
263   plugin3->TriggerCallbacks();
264   // Now plugin1 should have data, but the others should not.
265   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
266                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
267               ::testing::Optional(1));
268   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
269                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
270               ::testing::Optional(2));
271   EXPECT_EQ(plugin2->GetInt64CallbackGaugeValue(
272                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
273             absl::nullopt);
274   EXPECT_EQ(plugin2->GetInt64CallbackGaugeValue(
275                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
276             absl::nullopt);
277   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
278                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
279             absl::nullopt);
280   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
281                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
282             absl::nullopt);
283   // De-register the callbacks.
284   callback1.reset();
285   callback2.reset();
286   // Now register callbacks that hit both plugin1 and plugin2.
287   LOG(INFO) << "testing callbacks for: plugin1, plugin2";
288   auto group2 = GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
289       StatsPluginChannelScope(kDomain2To4, "", endpoint_config_));
290   callback1 = group2.RegisterCallback(
291       [&](CallbackMetricReporter& reporter) {
292         reporter.Report(int64_gauge_handle, int64_t(3), kLabelValues,
293                         kOptionalLabelValues);
294       },
295       Duration::Seconds(5), int64_gauge_handle);
296   callback2 = group2.RegisterCallback(
297       [&](CallbackMetricReporter& reporter) {
298         reporter.Report(int64_gauge_handle, int64_t(4), kLabelValues2,
299                         kOptionalLabelValues);
300       },
301       Duration::Seconds(5), int64_gauge_handle);
302   // Plugin1 still has data from before, but the others have none.
303   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
304                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
305               ::testing::Optional(1));
306   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
307                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
308               ::testing::Optional(2));
309   EXPECT_EQ(plugin2->GetInt64CallbackGaugeValue(
310                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
311             absl::nullopt);
312   EXPECT_EQ(plugin2->GetInt64CallbackGaugeValue(
313                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
314             absl::nullopt);
315   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
316                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
317             absl::nullopt);
318   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
319                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
320             absl::nullopt);
321   // Now invoke the callbacks.
322   plugin1->TriggerCallbacks();
323   plugin2->TriggerCallbacks();
324   plugin3->TriggerCallbacks();
325   // Now plugin1 and plugin2 should have data, but plugin3 should not.
326   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
327                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
328               ::testing::Optional(3));
329   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
330                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
331               ::testing::Optional(4));
332   EXPECT_THAT(plugin2->GetInt64CallbackGaugeValue(
333                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
334               ::testing::Optional(3));
335   EXPECT_THAT(plugin2->GetInt64CallbackGaugeValue(
336                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
337               ::testing::Optional(4));
338   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
339                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
340             absl::nullopt);
341   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
342                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
343             absl::nullopt);
344   // De-register the callbacks.
345   callback1.reset();
346   callback2.reset();
347   // Now register callbacks that hit all three plugins.
348   LOG(INFO) << "testing callbacks for: plugin1, plugin2, plugin3";
349   auto group3 = GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
350       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_));
351   callback1 = group3.RegisterCallback(
352       [&](CallbackMetricReporter& reporter) {
353         reporter.Report(int64_gauge_handle, int64_t(5), kLabelValues,
354                         kOptionalLabelValues);
355       },
356       Duration::Seconds(5), int64_gauge_handle);
357   callback2 = group3.RegisterCallback(
358       [&](CallbackMetricReporter& reporter) {
359         reporter.Report(int64_gauge_handle, int64_t(6), kLabelValues2,
360                         kOptionalLabelValues);
361       },
362       Duration::Seconds(5), int64_gauge_handle);
363   // Plugin1 and plugin2 still has data from before, but plugin3 has none.
364   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
365                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
366               ::testing::Optional(3));
367   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
368                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
369               ::testing::Optional(4));
370   EXPECT_THAT(plugin2->GetInt64CallbackGaugeValue(
371                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
372               ::testing::Optional(3));
373   EXPECT_THAT(plugin2->GetInt64CallbackGaugeValue(
374                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
375               ::testing::Optional(4));
376   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
377                 int64_gauge_handle, kLabelValues, kOptionalLabelValues),
378             absl::nullopt);
379   EXPECT_EQ(plugin3->GetInt64CallbackGaugeValue(
380                 int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
381             absl::nullopt);
382   // Now invoke the callbacks.
383   plugin1->TriggerCallbacks();
384   plugin2->TriggerCallbacks();
385   plugin3->TriggerCallbacks();
386   // Now plugin1 and plugin2 should have data, but plugin3 should not.
387   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
388                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
389               ::testing::Optional(5));
390   EXPECT_THAT(plugin1->GetInt64CallbackGaugeValue(
391                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
392               ::testing::Optional(6));
393   EXPECT_THAT(plugin2->GetInt64CallbackGaugeValue(
394                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
395               ::testing::Optional(5));
396   EXPECT_THAT(plugin2->GetInt64CallbackGaugeValue(
397                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
398               ::testing::Optional(6));
399   EXPECT_THAT(plugin3->GetInt64CallbackGaugeValue(
400                   int64_gauge_handle, kLabelValues, kOptionalLabelValues),
401               ::testing::Optional(5));
402   EXPECT_THAT(plugin3->GetInt64CallbackGaugeValue(
403                   int64_gauge_handle, kLabelValues2, kOptionalLabelValues),
404               ::testing::Optional(6));
405   // Need to destroy callbacks before the plugin group that created them.
406   callback1.reset();
407   callback2.reset();
408 }
409 
TEST_F(MetricsTest,DoubleCallbackGauge)410 TEST_F(MetricsTest, DoubleCallbackGauge) {
411   auto double_gauge_handle =
412       GlobalInstrumentsRegistry::RegisterCallbackDoubleGauge(
413           "double_gauge", "A simple double gauge.", "unit", true)
414           .Labels("label_key_1", "label_key_2")
415           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
416           .Build();
417   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
418                                                    "label_value_2"};
419   std::array<absl::string_view, 2> kLabelValues2 = {"label_value_3",
420                                                     "label_value_2"};
421   std::array<absl::string_view, 2> kOptionalLabelValues = {
422       "optional_label_value_1", "optional_label_value_2"};
423   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
424   constexpr absl::string_view kDomain2To4 = "domain2.domain3.domain4";
425   constexpr absl::string_view kDomain3To4 = "domain3.domain4";
426   auto plugin1 = MakeStatsPluginForTarget(kDomain3To4);
427   auto plugin2 = MakeStatsPluginForTarget(kDomain2To4);
428   auto plugin3 = MakeStatsPluginForTarget(kDomain1To4);
429   // Register two callbacks that set the same metric but with different
430   // label values.  The callbacks get used only by plugin1.
431   LOG(INFO) << "testing callbacks for: plugin1";
432   auto group1 = GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
433       StatsPluginChannelScope(kDomain3To4, "", endpoint_config_));
434   auto callback1 = group1.RegisterCallback(
435       [&](CallbackMetricReporter& reporter) {
436         reporter.Report(double_gauge_handle, 1.23, kLabelValues,
437                         kOptionalLabelValues);
438       },
439       Duration::Seconds(5), double_gauge_handle);
440   auto callback2 = group1.RegisterCallback(
441       [&](CallbackMetricReporter& reporter) {
442         reporter.Report(double_gauge_handle, 2.34, kLabelValues2,
443                         kOptionalLabelValues);
444       },
445       Duration::Seconds(5), double_gauge_handle);
446   // No plugins have data yet.
447   EXPECT_EQ(plugin1->GetDoubleCallbackGaugeValue(
448                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
449             absl::nullopt);
450   EXPECT_EQ(plugin1->GetDoubleCallbackGaugeValue(
451                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
452             absl::nullopt);
453   EXPECT_EQ(plugin2->GetDoubleCallbackGaugeValue(
454                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
455             absl::nullopt);
456   EXPECT_EQ(plugin2->GetDoubleCallbackGaugeValue(
457                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
458             absl::nullopt);
459   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
460                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
461             absl::nullopt);
462   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
463                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
464             absl::nullopt);
465   // Now invoke the callbacks.
466   plugin1->TriggerCallbacks();
467   plugin2->TriggerCallbacks();
468   plugin3->TriggerCallbacks();
469   // Now plugin1 should have data, but the others should not.
470   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
471                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
472               ::testing::Optional(1.23));
473   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
474                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
475               ::testing::Optional(2.34));
476   EXPECT_EQ(plugin2->GetDoubleCallbackGaugeValue(
477                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
478             absl::nullopt);
479   EXPECT_EQ(plugin2->GetDoubleCallbackGaugeValue(
480                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
481             absl::nullopt);
482   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
483                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
484             absl::nullopt);
485   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
486                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
487             absl::nullopt);
488   // De-register the callbacks.
489   callback1.reset();
490   callback2.reset();
491   // Now register callbacks that hit both plugin1 and plugin2.
492   LOG(INFO) << "testing callbacks for: plugin1, plugin2";
493   auto group2 = GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
494       StatsPluginChannelScope(kDomain2To4, "", endpoint_config_));
495   callback1 = group2.RegisterCallback(
496       [&](CallbackMetricReporter& reporter) {
497         reporter.Report(double_gauge_handle, 3.45, kLabelValues,
498                         kOptionalLabelValues);
499       },
500       Duration::Seconds(5), double_gauge_handle);
501   callback2 = group2.RegisterCallback(
502       [&](CallbackMetricReporter& reporter) {
503         reporter.Report(double_gauge_handle, 4.56, kLabelValues2,
504                         kOptionalLabelValues);
505       },
506       Duration::Seconds(5), double_gauge_handle);
507   // Plugin1 still has data from before, but the others have none.
508   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
509                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
510               ::testing::Optional(1.23));
511   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
512                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
513               ::testing::Optional(2.34));
514   EXPECT_EQ(plugin2->GetDoubleCallbackGaugeValue(
515                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
516             absl::nullopt);
517   EXPECT_EQ(plugin2->GetDoubleCallbackGaugeValue(
518                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
519             absl::nullopt);
520   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
521                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
522             absl::nullopt);
523   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
524                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
525             absl::nullopt);
526   // Now invoke the callbacks.
527   plugin1->TriggerCallbacks();
528   plugin2->TriggerCallbacks();
529   plugin3->TriggerCallbacks();
530   // Now plugin1 and plugin2 should have data, but plugin3 should not.
531   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
532                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
533               ::testing::Optional(3.45));
534   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
535                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
536               ::testing::Optional(4.56));
537   EXPECT_THAT(plugin2->GetDoubleCallbackGaugeValue(
538                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
539               ::testing::Optional(3.45));
540   EXPECT_THAT(plugin2->GetDoubleCallbackGaugeValue(
541                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
542               ::testing::Optional(4.56));
543   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
544                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
545             absl::nullopt);
546   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
547                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
548             absl::nullopt);
549   // De-register the callbacks.
550   callback1.reset();
551   callback2.reset();
552   // Now register callbacks that hit all three plugins.
553   LOG(INFO) << "testing callbacks for: plugin1, plugin2, plugin3";
554   auto group3 = GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
555       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_));
556   callback1 = group3.RegisterCallback(
557       [&](CallbackMetricReporter& reporter) {
558         reporter.Report(double_gauge_handle, 5.67, kLabelValues,
559                         kOptionalLabelValues);
560       },
561       Duration::Seconds(5), double_gauge_handle);
562   callback2 = group3.RegisterCallback(
563       [&](CallbackMetricReporter& reporter) {
564         reporter.Report(double_gauge_handle, 6.78, kLabelValues2,
565                         kOptionalLabelValues);
566       },
567       Duration::Seconds(5), double_gauge_handle);
568   // Plugin1 and plugin2 still has data from before, but plugin3 has none.
569   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
570                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
571               ::testing::Optional(3.45));
572   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
573                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
574               ::testing::Optional(4.56));
575   EXPECT_THAT(plugin2->GetDoubleCallbackGaugeValue(
576                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
577               ::testing::Optional(3.45));
578   EXPECT_THAT(plugin2->GetDoubleCallbackGaugeValue(
579                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
580               ::testing::Optional(4.56));
581   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
582                 double_gauge_handle, kLabelValues, kOptionalLabelValues),
583             absl::nullopt);
584   EXPECT_EQ(plugin3->GetDoubleCallbackGaugeValue(
585                 double_gauge_handle, kLabelValues2, kOptionalLabelValues),
586             absl::nullopt);
587   // Now invoke the callbacks.
588   plugin1->TriggerCallbacks();
589   plugin2->TriggerCallbacks();
590   plugin3->TriggerCallbacks();
591   // Now plugin1 and plugin2 should have data, but plugin3 should not.
592   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
593                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
594               ::testing::Optional(5.67));
595   EXPECT_THAT(plugin1->GetDoubleCallbackGaugeValue(
596                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
597               ::testing::Optional(6.78));
598   EXPECT_THAT(plugin2->GetDoubleCallbackGaugeValue(
599                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
600               ::testing::Optional(5.67));
601   EXPECT_THAT(plugin2->GetDoubleCallbackGaugeValue(
602                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
603               ::testing::Optional(6.78));
604   EXPECT_THAT(plugin3->GetDoubleCallbackGaugeValue(
605                   double_gauge_handle, kLabelValues, kOptionalLabelValues),
606               ::testing::Optional(5.67));
607   EXPECT_THAT(plugin3->GetDoubleCallbackGaugeValue(
608                   double_gauge_handle, kLabelValues2, kOptionalLabelValues),
609               ::testing::Optional(6.78));
610   // Need to destroy callbacks before the plugin group that created them.
611   callback1.reset();
612   callback2.reset();
613 }
614 
TEST_F(MetricsTest,DisableByDefaultMetricIsNotRecordedByFakeStatsPlugin)615 TEST_F(MetricsTest, DisableByDefaultMetricIsNotRecordedByFakeStatsPlugin) {
616   auto double_histogram_handle =
617       GlobalInstrumentsRegistry::RegisterDoubleHistogram(
618           "double_histogram", "A simple double histogram.", "unit", false)
619           .Labels("label_key_1", "label_key_2")
620           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
621           .Build();
622   std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
623                                                    "label_value_2"};
624   std::array<absl::string_view, 2> kOptionalLabelValues = {
625       "optional_label_value_1", "optional_label_value_2"};
626   constexpr absl::string_view kDomain1To4 = "domain1.domain2.domain3.domain4";
627   auto plugin = MakeStatsPluginForTarget(kDomain1To4);
628   GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
629       StatsPluginChannelScope(kDomain1To4, "", endpoint_config_))
630       .RecordHistogram(double_histogram_handle, 1.23, kLabelValues,
631                        kOptionalLabelValues);
632   EXPECT_EQ(plugin->GetDoubleHistogramValue(double_histogram_handle,
633                                             kLabelValues, kOptionalLabelValues),
634             absl::nullopt);
635 }
636 
TEST_F(MetricsTest,FindInstrumentByName)637 TEST_F(MetricsTest, FindInstrumentByName) {
638   auto uint64_counter_handle =
639       GlobalInstrumentsRegistry::RegisterUInt64Counter(
640           "uint64_counter", "A simple uint64 counter.", "unit", true)
641           .Labels("label_key_1", "label_key_2")
642           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
643           .Build();
644   auto instrument =
645       GlobalInstrumentsRegistry::FindInstrumentByName("uint64_counter");
646   EXPECT_THAT(instrument,
647               ::testing::Optional(::testing::Field(
648                   &GlobalInstrumentsRegistry::GlobalInstrumentHandle::index,
649                   ::testing::Eq(uint64_counter_handle.index))));
650 }
651 
TEST_F(MetricsTest,ParallelStatsPluginRegistrationAndLookup)652 TEST_F(MetricsTest, ParallelStatsPluginRegistrationAndLookup) {
653   std::vector<std::thread> register_threads;
654   std::vector<std::thread> lookup_threads;
655   register_threads.reserve(100);
656   lookup_threads.reserve(100);
657   // 100 threads that register 100 stats plugins each
658   for (int i = 0; i < 100; ++i) {
659     register_threads.emplace_back([] {
660       for (int j = 0; j < 100; ++j) {
661         FakeStatsPluginBuilder().BuildAndRegister();
662       }
663     });
664   }
665   // 100 threads that keep looking up stats plugins till they see 10000 stats
666   // plugins
667   for (int i = 0; i < 100; ++i) {
668     lookup_threads.emplace_back([this] {
669       while (GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
670                  StatsPluginChannelScope("", "", endpoint_config_))
671                  .size() < 10000) {
672         // Yield to avoid starving the register threads.
673         std::this_thread::yield();
674       };
675     });
676   }
677   for (int i = 0; i < 100; ++i) {
678     register_threads[i].join();
679     lookup_threads[i].join();
680   }
681   EXPECT_THAT(GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
682                   StatsPluginChannelScope("", "", endpoint_config_)),
683               ::testing::SizeIs(10000));
684 }
685 
686 using MetricsDeathTest = MetricsTest;
687 
TEST_F(MetricsDeathTest,RegisterTheSameMetricNameWouldCrash)688 TEST_F(MetricsDeathTest, RegisterTheSameMetricNameWouldCrash) {
689   (void)GlobalInstrumentsRegistry::RegisterDoubleHistogram(
690       "double_histogram", "A simple double histogram.", "unit", true)
691       .Labels("label_key_1", "label_key_2")
692       .OptionalLabels("optional_label_key_1", "optional_label_key_2")
693       .Build();
694   EXPECT_DEATH(
695       GlobalInstrumentsRegistry::RegisterDoubleHistogram(
696           "double_histogram", "A simple double histogram.", "unit", true)
697           .Labels("label_key_1", "label_key_2")
698           .OptionalLabels("optional_label_key_1", "optional_label_key_2")
699           .Build(),
700       "Metric name double_histogram has already been registered.");
701 }
702 
703 }  // namespace
704 }  // namespace grpc_core
705 
main(int argc,char ** argv)706 int main(int argc, char** argv) {
707   grpc::testing::TestEnvironment env(&argc, argv);
708   ::testing::InitGoogleTest(&argc, argv);
709   int ret = RUN_ALL_TESTS();
710   return ret;
711 }
712