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