Merge branch 'master' into server-callback-on-ee

pull/36126/head
AJ Heller 8 months ago
commit 6242a78a3f
  1. 2
      .bazelversion
  2. 50
      BUILD
  3. 102
      CMakeLists.txt
  4. 3
      Package.swift
  5. 2
      bazel/supported_versions.txt
  6. 230
      build_autogenerated.yaml
  7. 2
      doc/bazel_support.md
  8. 9
      doc/compression.md
  9. 6
      gRPC-C++.podspec
  10. 6
      gRPC-Core.podspec
  11. 3
      grpc.gemspec
  12. 3
      include/grpcpp/ext/otel_plugin.h
  13. 3
      package.xml
  14. 31
      src/core/BUILD
  15. 10
      src/core/client_channel/client_channel_filter.cc
  16. 1
      src/core/ext/filters/server_config_selector/server_config_selector_filter.cc
  17. 2
      src/core/ext/transport/binder/java/io/grpc/binder/cpp/BUILD
  18. 2
      src/core/ext/transport/chaotic_good/client/chaotic_good_connector.cc
  19. 2
      src/core/ext/transport/chaotic_good/server/chaotic_good_server.cc
  20. 1
      src/core/ext/transport/inproc/inproc_transport.cc
  21. 28
      src/core/ext/xds/xds_bootstrap_grpc.cc
  22. 2
      src/core/ext/xds/xds_bootstrap_grpc.h
  23. 92
      src/core/ext/xds/xds_client.cc
  24. 27
      src/core/ext/xds/xds_client.h
  25. 210
      src/core/ext/xds/xds_client_grpc.cc
  26. 16
      src/core/ext/xds/xds_client_grpc.h
  27. 39
      src/core/ext/xds/xds_metrics.h
  28. 5
      src/core/ext/xds/xds_server_config_fetcher.cc
  29. 3
      src/core/lib/channel/call_tracer.h
  30. 141
      src/core/lib/channel/metrics.cc
  31. 203
      src/core/lib/channel/metrics.h
  32. 4
      src/core/lib/channel/promise_based_filter.h
  33. 2
      src/core/lib/channel/server_call_tracer_filter.cc
  34. 28
      src/core/lib/channel/server_call_tracer_filter.h
  35. 6
      src/core/lib/event_engine/default_event_engine.h
  36. 31
      src/core/lib/event_engine/event_engine_context.h
  37. 108
      src/core/lib/experiments/config.cc
  38. 6
      src/core/lib/experiments/experiments.yaml
  39. 1
      src/core/lib/promise/activity.h
  40. 2
      src/core/lib/promise/sleep.cc
  41. 25
      src/core/lib/surface/call.cc
  42. 17
      src/core/lib/surface/legacy_channel.cc
  43. 2
      src/core/plugin_registry/grpc_plugin_registry.cc
  44. 243
      src/cpp/ext/csm/metadata_exchange.cc
  45. 41
      src/cpp/ext/csm/metadata_exchange.h
  46. 6
      src/cpp/ext/otel/BUILD
  47. 23
      src/cpp/ext/otel/key_value_iterable.h
  48. 282
      src/cpp/ext/otel/otel_client_call_tracer.cc
  49. 35
      src/cpp/ext/otel/otel_client_call_tracer.h
  50. 328
      src/cpp/ext/otel/otel_client_filter.cc
  51. 68
      src/cpp/ext/otel/otel_client_filter.h
  52. 428
      src/cpp/ext/otel/otel_plugin.cc
  53. 288
      src/cpp/ext/otel/otel_plugin.h
  54. 198
      src/cpp/ext/otel/otel_server_call_tracer.cc
  55. 109
      src/cpp/ext/otel/otel_server_call_tracer.h
  56. 12
      src/objective-c/BoringSSL-GRPC.podspec
  57. 8
      src/objective-c/PrivacyInfo.xcprivacy
  58. 2
      test/core/client_channel/lb_policy/pick_first_test.cc
  59. 2
      test/core/client_channel/lb_policy/weighted_round_robin_test.cc
  60. 0
      test/core/end2end/end2end_test_corpus/retry_streaming/clusterfuzz-testcase-minimized-retry_streaming_fuzzer-6651400812036096
  61. 2
      test/core/end2end/grpc_core_end2end_test.bzl
  62. 52
      test/core/end2end/tests/http2_stats.cc
  63. 2
      test/core/end2end/tests/no_logging.cc
  64. 1
      test/core/transport/chaotic_good/client_transport_error_test.cc
  65. 1
      test/core/transport/chaotic_good/client_transport_test.cc
  66. 26
      test/core/util/fake_stats_plugin.cc
  67. 27
      test/core/util/fake_stats_plugin.h
  68. 15
      test/core/xds/BUILD
  69. 96
      test/core/xds/xds_bootstrap_test.cc
  70. 47
      test/core/xds/xds_client_fuzzer.cc
  71. 7
      test/core/xds/xds_client_fuzzer.proto
  72. 982
      test/core/xds/xds_client_test.cc
  73. 80
      test/core/xds/xds_client_test_peer.h
  74. 3
      test/core/xds/xds_cluster_resource_type_test.cc
  75. 3
      test/core/xds/xds_common_types_test.cc
  76. 3
      test/core/xds/xds_endpoint_resource_type_test.cc
  77. 3
      test/core/xds/xds_http_filters_test.cc
  78. 3
      test/core/xds/xds_listener_resource_type_test.cc
  79. 3
      test/core/xds/xds_route_config_resource_type_test.cc
  80. 1
      test/cpp/end2end/xds/BUILD
  81. 167
      test/cpp/end2end/xds/xds_core_end2end_test.cc
  82. 187
      test/cpp/ext/csm/metadata_exchange_test.cc
  83. 1
      test/cpp/ext/otel/BUILD
  84. 420
      test/cpp/ext/otel/otel_plugin_test.cc
  85. 84
      test/cpp/ext/otel/otel_test_library.cc
  86. 13
      test/cpp/ext/otel/otel_test_library.h
  87. 8
      tools/bazelify_tests/dockerimage_current_versions.bzl
  88. 2
      tools/bazelify_tests/test/supported_bazel_versions.bzl
  89. 1
      tools/distrib/fix_build_deps.py
  90. 2
      tools/dockerfile/test/bazel.current_version
  91. 2
      tools/dockerfile/test/bazel/Dockerfile
  92. 2
      tools/dockerfile/test/bazel_arm64.current_version
  93. 2
      tools/dockerfile/test/bazel_arm64/Dockerfile
  94. 2
      tools/dockerfile/test/binder_transport_apk.current_version
  95. 2
      tools/dockerfile/test/binder_transport_apk/Dockerfile
  96. 2
      tools/dockerfile/test/sanity.current_version
  97. 2
      tools/dockerfile/test/sanity/Dockerfile
  98. 3
      tools/doxygen/Doxyfile.c++.internal
  99. 3
      tools/doxygen/Doxyfile.core.internal

@ -1 +1 @@
6.5.0
7.1.0

50
BUILD

@ -1695,7 +1695,6 @@ grpc_cc_library(
language = "c++",
visibility = ["@grpc:alt_grpc_base_legacy"],
deps = [
"config",
"gpr",
"legacy_context",
"tcp_tracer",
@ -1962,20 +1961,15 @@ grpc_cc_library(
}),
external_deps = [
"absl/base:core_headers",
"absl/cleanup",
"absl/container:flat_hash_map",
"absl/container:flat_hash_set",
"absl/container:inlined_vector",
"absl/functional:any_invocable",
"absl/functional:function_ref",
"absl/hash",
"absl/meta:type_traits",
"absl/random",
"absl/status",
"absl/status:statusor",
"absl/strings",
"absl/strings:str_format",
"absl/time",
"absl/types:optional",
"absl/utility",
"madler_zlib",
@ -1989,7 +1983,6 @@ grpc_cc_library(
visibility = ["@grpc:alt_grpc_base_legacy"],
deps = [
"api_trace",
"byte_buffer",
"call_combiner",
"call_trace",
"call_tracer",
@ -1998,28 +1991,19 @@ grpc_cc_library(
"channel_stack_builder",
"channelz",
"config",
"config_vars",
"cpp_impl_of",
"debug_location",
"dynamic_annotations",
"exec_ctx",
"gpr",
"grpc_public_hdrs",
"grpc_trace",
"iomgr",
"iomgr_buffer_list",
"iomgr_internal_errqueue",
"iomgr_timer",
"legacy_context",
"orphanable",
"parse_address",
"promise",
"ref_counted_ptr",
"resource_quota_api",
"sockaddr_utils",
"stats",
"tcp_tracer",
"uri_parser",
"//src/core:1999",
"//src/core:activity",
"//src/core:all_ok",
@ -2027,14 +2011,11 @@ grpc_cc_library(
"//src/core:arena_promise",
"//src/core:atomic_utils",
"//src/core:bitset",
"//src/core:call_factory",
"//src/core:call_filters",
"//src/core:call_final_info",
"//src/core:call_finalization",
"//src/core:call_spine",
"//src/core:cancel_callback",
"//src/core:channel_args",
"//src/core:channel_args_endpoint_config",
"//src/core:channel_args_preconditioning",
"//src/core:channel_fwd",
"//src/core:channel_init",
@ -2043,75 +2024,47 @@ grpc_cc_library(
"//src/core:closure",
"//src/core:compression",
"//src/core:connectivity_state",
"//src/core:construct_destruct",
"//src/core:context",
"//src/core:default_event_engine",
"//src/core:dual_ref_counted",
"//src/core:error",
"//src/core:error_utils",
"//src/core:event_engine_common",
"//src/core:event_engine_extensions",
"//src/core:event_engine_memory_allocator_factory",
"//src/core:event_engine_query_extensions",
"//src/core:event_engine_shim",
"//src/core:event_engine_tcp_socket_utils",
"//src/core:event_engine_trace",
"//src/core:event_log",
"//src/core:event_engine_context",
"//src/core:experiments",
"//src/core:for_each",
"//src/core:gpr_atm",
"//src/core:gpr_manual_constructor",
"//src/core:gpr_spinlock",
"//src/core:grpc_sockaddr",
"//src/core:if",
"//src/core:init_internally",
"//src/core:iomgr_fwd",
"//src/core:iomgr_port",
"//src/core:json",
"//src/core:json_writer",
"//src/core:latch",
"//src/core:loop",
"//src/core:map",
"//src/core:match",
"//src/core:memory_quota",
"//src/core:message",
"//src/core:metadata",
"//src/core:metadata_batch",
"//src/core:metrics",
"//src/core:no_destruct",
"//src/core:per_cpu",
"//src/core:pipe",
"//src/core:poll",
"//src/core:pollset_set",
"//src/core:posix_event_engine_base_hdrs",
"//src/core:posix_event_engine_endpoint",
"//src/core:promise_status",
"//src/core:promise_trace",
"//src/core:race",
"//src/core:random_early_detection",
"//src/core:ref_counted",
"//src/core:resolved_address",
"//src/core:resource_quota",
"//src/core:resource_quota_trace",
"//src/core:seq",
"//src/core:server_interface",
"//src/core:slice",
"//src/core:slice_buffer",
"//src/core:slice_cast",
"//src/core:slice_refcount",
"//src/core:socket_mutator",
"//src/core:stats_data",
"//src/core:status_flag",
"//src/core:status_helper",
"//src/core:strerror",
"//src/core:thread_quota",
"//src/core:time",
"//src/core:transport_fwd",
"//src/core:try_join",
"//src/core:try_seq",
"//src/core:useful",
"//src/core:windows_event_engine",
"//src/core:windows_event_engine_listener",
],
)
@ -4276,6 +4229,7 @@ grpc_cc_library(
"//src/core:ext/xds/xds_channel_args.h",
"//src/core:ext/xds/xds_client.h",
"//src/core:ext/xds/xds_client_stats.h",
"//src/core:ext/xds/xds_metrics.h",
"//src/core:ext/xds/xds_resource_type.h",
"//src/core:ext/xds/xds_resource_type_impl.h",
"//src/core:ext/xds/xds_transport.h",

102
CMakeLists.txt generated

@ -5116,7 +5116,6 @@ add_library(grpc_authorization_provider
src/core/lib/address_utils/parse_address.cc
src/core/lib/address_utils/sockaddr_utils.cc
src/core/lib/backoff/backoff.cc
src/core/lib/backoff/random_early_detection.cc
src/core/lib/channel/call_tracer.cc
src/core/lib/channel/channel_args.cc
src/core/lib/channel/channel_args_preconditioning.cc
@ -5349,10 +5348,8 @@ add_library(grpc_authorization_provider
src/core/lib/surface/version.cc
src/core/lib/surface/wait_for_cq_end_op.cc
src/core/lib/transport/batch_builder.cc
src/core/lib/transport/call_factory.cc
src/core/lib/transport/call_filters.cc
src/core/lib/transport/call_final_info.cc
src/core/lib/transport/call_size_estimator.cc
src/core/lib/transport/call_spine.cc
src/core/lib/transport/connectivity_state.cc
src/core/lib/transport/error_utils.cc
@ -5442,8 +5439,6 @@ target_link_libraries(grpc_authorization_provider
absl::function_ref
absl::hash
absl::type_traits
absl::random_bit_gen_ref
absl::random_distributions
absl::statusor
absl::span
absl::utility
@ -5700,7 +5695,7 @@ endif()
if(gRPC_BUILD_GRPCPP_OTEL_PLUGIN)
add_library(grpcpp_otel_plugin
src/cpp/ext/otel/otel_client_filter.cc
src/cpp/ext/otel/otel_client_call_tracer.cc
src/cpp/ext/otel/otel_plugin.cc
src/cpp/ext/otel/otel_server_call_tracer.cc
)
@ -7565,6 +7560,7 @@ add_executable(bad_ping_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/bad_ping.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8040,6 +8036,7 @@ add_executable(binary_metadata_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/binary_metadata.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8479,6 +8476,7 @@ add_executable(call_creds_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/call_creds.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8685,6 +8683,7 @@ add_executable(call_host_override_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/call_host_override.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8790,6 +8789,7 @@ add_executable(cancel_after_accept_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_after_accept.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8852,6 +8852,7 @@ add_executable(cancel_after_client_done_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_after_client_done.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8914,6 +8915,7 @@ add_executable(cancel_after_invoke_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_after_invoke.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -8976,6 +8978,7 @@ add_executable(cancel_after_round_trip_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_after_round_trip.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -9085,6 +9088,7 @@ add_executable(cancel_before_invoke_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_before_invoke.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -9189,6 +9193,7 @@ add_executable(cancel_in_a_vacuum_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_in_a_vacuum.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -9251,6 +9256,7 @@ add_executable(cancel_with_status_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/cancel_with_status.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -10897,6 +10903,7 @@ add_executable(client_streaming_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/client_streaming.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -11235,6 +11242,7 @@ add_executable(compressed_payload_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/compressed_payload.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -11519,6 +11527,7 @@ add_executable(connectivity_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/connectivity.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -11947,6 +11956,7 @@ add_executable(default_host_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/default_host.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -12154,6 +12164,7 @@ add_executable(disappearing_server_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/disappearing_server.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -12473,6 +12484,7 @@ add_executable(empty_batch_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/empty_batch.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -13849,6 +13861,7 @@ add_executable(filter_causes_close_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/filter_causes_close.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -13911,6 +13924,7 @@ add_executable(filter_context_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/filter_context.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -13973,6 +13987,7 @@ add_executable(filter_init_fails_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/filter_init_fails.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -14085,6 +14100,7 @@ add_executable(filtered_metadata_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/filtered_metadata.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -14873,6 +14889,7 @@ add_executable(graceful_server_shutdown_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/graceful_server_shutdown.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -15238,6 +15255,7 @@ add_executable(grpc_authz_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/grpc_authz.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -16628,6 +16646,7 @@ add_executable(high_initial_seqno_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/high_initial_seqno.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -16936,6 +16955,7 @@ add_executable(hpack_size_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/hpack_size.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -17091,6 +17111,7 @@ add_executable(http2_stats_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/http2_stats.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -17983,6 +18004,7 @@ add_executable(invoke_large_request_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/invoke_large_request.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -18371,6 +18393,7 @@ add_executable(keepalive_timeout_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/keepalive_timeout.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -18476,6 +18499,7 @@ add_executable(large_metadata_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/large_metadata.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -19148,6 +19172,7 @@ add_executable(max_concurrent_streams_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/max_concurrent_streams.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -19210,6 +19235,7 @@ add_executable(max_connection_age_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/max_connection_age.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -19272,6 +19298,7 @@ add_executable(max_connection_idle_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/max_connection_idle.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -19334,6 +19361,7 @@ add_executable(max_message_length_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/max_message_length.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -20015,6 +20043,7 @@ add_executable(negative_deadline_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/negative_deadline.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -20109,6 +20138,7 @@ add_executable(no_logging_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/no_logging.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -20171,6 +20201,7 @@ add_executable(no_op_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/no_op.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -20638,9 +20669,10 @@ add_executable(otel_plugin_test
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/orca_load_report.grpc.pb.h
src/cpp/ext/otel/otel_client_filter.cc
src/cpp/ext/otel/otel_client_call_tracer.cc
src/cpp/ext/otel/otel_plugin.cc
src/cpp/ext/otel/otel_server_call_tracer.cc
test/core/util/fake_stats_plugin.cc
test/cpp/end2end/test_service_impl.cc
test/cpp/ext/otel/otel_plugin_test.cc
test/cpp/ext/otel/otel_test_library.cc
@ -21098,6 +21130,7 @@ add_executable(payload_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/payload.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -21472,6 +21505,7 @@ add_executable(ping_pong_streaming_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/ping_pong_streaming.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -21585,6 +21619,7 @@ add_executable(ping_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/ping.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -22578,6 +22613,7 @@ add_executable(proxy_auth_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/proxy_auth.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23153,6 +23189,7 @@ add_executable(registered_call_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/registered_call.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23259,6 +23296,7 @@ add_executable(request_with_flags_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/request_with_flags.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23321,6 +23359,7 @@ add_executable(request_with_payload_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/request_with_payload.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23654,6 +23693,7 @@ add_executable(resource_quota_server_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/resource_quota_server.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23758,6 +23798,7 @@ add_executable(retry_cancel_after_first_attempt_starts_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_cancel_after_first_attempt_starts.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23820,6 +23861,7 @@ add_executable(retry_cancel_during_delay_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_cancel_during_delay.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23882,6 +23924,7 @@ add_executable(retry_cancel_with_multiple_send_batches_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_cancel_with_multiple_send_batches.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -23944,6 +23987,7 @@ add_executable(retry_cancellation_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_cancellation.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24006,6 +24050,7 @@ add_executable(retry_disabled_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_disabled.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24068,6 +24113,7 @@ add_executable(retry_exceeds_buffer_size_in_delay_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_exceeds_buffer_size_in_delay.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24130,6 +24176,7 @@ add_executable(retry_exceeds_buffer_size_in_initial_batch_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_exceeds_buffer_size_in_initial_batch.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24192,6 +24239,7 @@ add_executable(retry_exceeds_buffer_size_in_subsequent_batch_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_exceeds_buffer_size_in_subsequent_batch.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24254,6 +24302,7 @@ add_executable(retry_lb_drop_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_lb_drop.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24316,6 +24365,7 @@ add_executable(retry_lb_fail_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_lb_fail.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24378,6 +24428,7 @@ add_executable(retry_non_retriable_status_before_trailers_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_non_retriable_status_before_trailers.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24440,6 +24491,7 @@ add_executable(retry_non_retriable_status_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_non_retriable_status.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24502,6 +24554,7 @@ add_executable(retry_per_attempt_recv_timeout_on_last_attempt_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_per_attempt_recv_timeout_on_last_attempt.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24564,6 +24617,7 @@ add_executable(retry_per_attempt_recv_timeout_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_per_attempt_recv_timeout.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24626,6 +24680,7 @@ add_executable(retry_recv_initial_metadata_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_recv_initial_metadata.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24688,6 +24743,7 @@ add_executable(retry_recv_message_replay_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_recv_message_replay.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24750,6 +24806,7 @@ add_executable(retry_recv_message_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_recv_message.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24812,6 +24869,7 @@ add_executable(retry_recv_trailing_metadata_error_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_recv_trailing_metadata_error.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24874,6 +24932,7 @@ add_executable(retry_send_initial_metadata_refs_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_send_initial_metadata_refs.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24936,6 +24995,7 @@ add_executable(retry_send_op_fails_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_send_op_fails.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -24998,6 +25058,7 @@ add_executable(retry_send_recv_batch_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_send_recv_batch.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25060,6 +25121,7 @@ add_executable(retry_server_pushback_delay_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_server_pushback_delay.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25122,6 +25184,7 @@ add_executable(retry_server_pushback_disabled_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_server_pushback_disabled.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25226,6 +25289,7 @@ add_executable(retry_streaming_after_commit_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_streaming_after_commit.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25288,6 +25352,7 @@ add_executable(retry_streaming_succeeds_before_replay_finished_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_streaming_succeeds_before_replay_finished.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25350,6 +25415,7 @@ add_executable(retry_streaming_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_streaming.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25412,6 +25478,7 @@ add_executable(retry_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25516,6 +25583,7 @@ add_executable(retry_throttled_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_throttled.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25578,6 +25646,7 @@ add_executable(retry_too_many_attempts_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_too_many_attempts.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25640,6 +25709,7 @@ add_executable(retry_transparent_goaway_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_transparent_goaway.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25702,6 +25772,7 @@ add_executable(retry_transparent_max_concurrent_streams_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_transparent_max_concurrent_streams.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25764,6 +25835,7 @@ add_executable(retry_transparent_not_sent_on_wire_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_transparent_not_sent_on_wire.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25826,6 +25898,7 @@ add_executable(retry_unref_before_finish_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_unref_before_finish.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -25888,6 +25961,7 @@ add_executable(retry_unref_before_recv_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/retry_unref_before_recv.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -26871,6 +26945,7 @@ add_executable(server_finishes_request_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/server_finishes_request.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -27154,6 +27229,7 @@ add_executable(server_streaming_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/server_streaming.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -27415,6 +27491,7 @@ add_executable(shutdown_finishes_calls_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/shutdown_finishes_calls.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -27477,6 +27554,7 @@ add_executable(shutdown_finishes_tags_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/shutdown_finishes_tags.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -27602,6 +27680,7 @@ add_executable(simple_delayed_request_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/simple_delayed_request.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -27664,6 +27743,7 @@ add_executable(simple_metadata_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/simple_metadata.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -27770,6 +27850,7 @@ add_executable(simple_request_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/simple_request.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -28695,6 +28776,7 @@ add_executable(streaming_error_response_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/streaming_error_response.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -29426,6 +29508,7 @@ add_executable(test_core_end2end_channelz_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/channelz.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -30684,6 +30767,7 @@ add_executable(timeout_before_request_call_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/timeout_before_request_call.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -31187,6 +31271,7 @@ add_executable(trailing_metadata_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/trailing_metadata.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -32422,6 +32507,7 @@ add_executable(write_buffering_at_end_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/write_buffering_at_end.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -32484,6 +32570,7 @@ add_executable(write_buffering_test
test/core/end2end/fixtures/proxy.cc
test/core/end2end/tests/write_buffering.cc
test/core/event_engine/event_engine_test_utils.cc
test/core/util/fake_stats_plugin.cc
test/core/util/test_lb_policies.cc
)
if(WIN32 AND MSVC)
@ -33582,6 +33669,7 @@ if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.cc
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.pb.h
${_gRPC_PROTO_GENS_DIR}/src/proto/grpc/testing/xds/v3/string.grpc.pb.h
test/core/util/fake_stats_plugin.cc
test/cpp/end2end/test_service_impl.cc
test/cpp/end2end/xds/xds_core_end2end_test.cc
test/cpp/end2end/xds/xds_end2end_test_lib.cc

3
Package.swift generated

@ -1110,6 +1110,7 @@ let package = Package(
"src/core/ext/xds/xds_lb_policy_registry.h",
"src/core/ext/xds/xds_listener.cc",
"src/core/ext/xds/xds_listener.h",
"src/core/ext/xds/xds_metrics.h",
"src/core/ext/xds/xds_resource_type.h",
"src/core/ext/xds/xds_resource_type_impl.h",
"src/core/ext/xds/xds_route_config.cc",
@ -1159,6 +1160,7 @@ let package = Package(
"src/core/lib/channel/promise_based_filter.cc",
"src/core/lib/channel/promise_based_filter.h",
"src/core/lib/channel/server_call_tracer_filter.cc",
"src/core/lib/channel/server_call_tracer_filter.h",
"src/core/lib/channel/status_util.cc",
"src/core/lib/channel/status_util.h",
"src/core/lib/channel/tcp_tracer.h",
@ -1201,6 +1203,7 @@ let package = Package(
"src/core/lib/event_engine/default_event_engine_factory.cc",
"src/core/lib/event_engine/default_event_engine_factory.h",
"src/core/lib/event_engine/event_engine.cc",
"src/core/lib/event_engine/event_engine_context.h",
"src/core/lib/event_engine/extensions/can_track_errors.h",
"src/core/lib/event_engine/extensions/chaotic_good_extension.h",
"src/core/lib/event_engine/extensions/supports_fd.h",

@ -1,2 +1,2 @@
6.5.0
7.1.0
6.5.0

File diff suppressed because it is too large Load Diff

@ -43,7 +43,7 @@ However individual releases may have a broader
compatibility range. The currently supported versions are captured by the
following list:
- [`6.5.0`](https://github.com/bazelbuild/bazel/releases/tag/6.5.0)
- [`7.1.0`](https://github.com/bazelbuild/bazel/releases/tag/7.1.0)
- [`6.5.0`](https://github.com/bazelbuild/bazel/releases/tag/6.5.0)
NOTE: gRPC doesn't support bzlmod yet.

@ -90,6 +90,15 @@ The inheritance of the compression configuration by child RPCs is left up to the
implementation. Note that in the absence of changes to the parent channel, its
configuration will be used.
### Deflate Compression
Like HTTP implementations, gRPC implementations MUST use the "deflate"
compression to mean the zlib structure (defined in
[RFC 1950](https://datatracker.ietf.org/doc/html/rfc1950))
with the deflate compression algorithm (defined in
[RFC 1951](https://datatracker.ietf.org/doc/html/rfc1951)).
Servers and clients MUST NOT send raw deflate data.
### Test cases
1. When a compression level is not specified for either the channel or the

6
gRPC-C++.podspec generated

@ -881,6 +881,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/xds_http_stateful_session_filter.h',
'src/core/ext/xds/xds_lb_policy_registry.h',
'src/core/ext/xds/xds_listener.h',
'src/core/ext/xds/xds_metrics.h',
'src/core/ext/xds/xds_resource_type.h',
'src/core/ext/xds/xds_resource_type_impl.h',
'src/core/ext/xds/xds_route_config.h',
@ -908,6 +909,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/context.h',
'src/core/lib/channel/metrics.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer_filter.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/channel/tcp_tracer.h',
'src/core/lib/compression/compression_internal.h',
@ -929,6 +931,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/common_closures.h',
'src/core/lib/event_engine/default_event_engine.h',
'src/core/lib/event_engine/default_event_engine_factory.h',
'src/core/lib/event_engine/event_engine_context.h',
'src/core/lib/event_engine/extensions/can_track_errors.h',
'src/core/lib/event_engine/extensions/chaotic_good_extension.h',
'src/core/lib/event_engine/extensions/supports_fd.h',
@ -2145,6 +2148,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/xds_http_stateful_session_filter.h',
'src/core/ext/xds/xds_lb_policy_registry.h',
'src/core/ext/xds/xds_listener.h',
'src/core/ext/xds/xds_metrics.h',
'src/core/ext/xds/xds_resource_type.h',
'src/core/ext/xds/xds_resource_type_impl.h',
'src/core/ext/xds/xds_route_config.h',
@ -2172,6 +2176,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/context.h',
'src/core/lib/channel/metrics.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer_filter.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/channel/tcp_tracer.h',
'src/core/lib/compression/compression_internal.h',
@ -2193,6 +2198,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/common_closures.h',
'src/core/lib/event_engine/default_event_engine.h',
'src/core/lib/event_engine/default_event_engine_factory.h',
'src/core/lib/event_engine/event_engine_context.h',
'src/core/lib/event_engine/extensions/can_track_errors.h',
'src/core/lib/event_engine/extensions/chaotic_good_extension.h',
'src/core/lib/event_engine/extensions/supports_fd.h',

6
gRPC-Core.podspec generated

@ -1226,6 +1226,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/xds_lb_policy_registry.h',
'src/core/ext/xds/xds_listener.cc',
'src/core/ext/xds/xds_listener.h',
'src/core/ext/xds/xds_metrics.h',
'src/core/ext/xds/xds_resource_type.h',
'src/core/ext/xds/xds_resource_type_impl.h',
'src/core/ext/xds/xds_route_config.cc',
@ -1275,6 +1276,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/promise_based_filter.cc',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer_filter.cc',
'src/core/lib/channel/server_call_tracer_filter.h',
'src/core/lib/channel/status_util.cc',
'src/core/lib/channel/status_util.h',
'src/core/lib/channel/tcp_tracer.h',
@ -1317,6 +1319,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/default_event_engine_factory.cc',
'src/core/lib/event_engine/default_event_engine_factory.h',
'src/core/lib/event_engine/event_engine.cc',
'src/core/lib/event_engine/event_engine_context.h',
'src/core/lib/event_engine/extensions/can_track_errors.h',
'src/core/lib/event_engine/extensions/chaotic_good_extension.h',
'src/core/lib/event_engine/extensions/supports_fd.h',
@ -2927,6 +2930,7 @@ Pod::Spec.new do |s|
'src/core/ext/xds/xds_http_stateful_session_filter.h',
'src/core/ext/xds/xds_lb_policy_registry.h',
'src/core/ext/xds/xds_listener.h',
'src/core/ext/xds/xds_metrics.h',
'src/core/ext/xds/xds_resource_type.h',
'src/core/ext/xds/xds_resource_type_impl.h',
'src/core/ext/xds/xds_route_config.h',
@ -2954,6 +2958,7 @@ Pod::Spec.new do |s|
'src/core/lib/channel/context.h',
'src/core/lib/channel/metrics.h',
'src/core/lib/channel/promise_based_filter.h',
'src/core/lib/channel/server_call_tracer_filter.h',
'src/core/lib/channel/status_util.h',
'src/core/lib/channel/tcp_tracer.h',
'src/core/lib/compression/compression_internal.h',
@ -2975,6 +2980,7 @@ Pod::Spec.new do |s|
'src/core/lib/event_engine/common_closures.h',
'src/core/lib/event_engine/default_event_engine.h',
'src/core/lib/event_engine/default_event_engine_factory.h',
'src/core/lib/event_engine/event_engine_context.h',
'src/core/lib/event_engine/extensions/can_track_errors.h',
'src/core/lib/event_engine/extensions/chaotic_good_extension.h',
'src/core/lib/event_engine/extensions/supports_fd.h',

3
grpc.gemspec generated

@ -1116,6 +1116,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/xds/xds_lb_policy_registry.h )
s.files += %w( src/core/ext/xds/xds_listener.cc )
s.files += %w( src/core/ext/xds/xds_listener.h )
s.files += %w( src/core/ext/xds/xds_metrics.h )
s.files += %w( src/core/ext/xds/xds_resource_type.h )
s.files += %w( src/core/ext/xds/xds_resource_type_impl.h )
s.files += %w( src/core/ext/xds/xds_route_config.cc )
@ -1165,6 +1166,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/channel/promise_based_filter.cc )
s.files += %w( src/core/lib/channel/promise_based_filter.h )
s.files += %w( src/core/lib/channel/server_call_tracer_filter.cc )
s.files += %w( src/core/lib/channel/server_call_tracer_filter.h )
s.files += %w( src/core/lib/channel/status_util.cc )
s.files += %w( src/core/lib/channel/status_util.h )
s.files += %w( src/core/lib/channel/tcp_tracer.h )
@ -1207,6 +1209,7 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/event_engine/default_event_engine_factory.cc )
s.files += %w( src/core/lib/event_engine/default_event_engine_factory.h )
s.files += %w( src/core/lib/event_engine/event_engine.cc )
s.files += %w( src/core/lib/event_engine/event_engine_context.h )
s.files += %w( src/core/lib/event_engine/extensions/can_track_errors.h )
s.files += %w( src/core/lib/event_engine/extensions/chaotic_good_extension.h )
s.files += %w( src/core/lib/event_engine/extensions/supports_fd.h )

@ -106,6 +106,9 @@ class OpenTelemetryPluginBuilder {
/// options can be added.
OpenTelemetryPluginBuilder& AddPluginOption(
std::unique_ptr<OpenTelemetryPluginOption> option);
// Records \a optional_label_key on all metrics that provide it.
OpenTelemetryPluginBuilder& AddOptionalLabel(
absl::string_view optional_label_key);
/// Registers a global plugin that acts on all channels and servers running on
/// the process.
absl::Status BuildAndRegisterGlobal();

3
package.xml generated

@ -1098,6 +1098,7 @@
<file baseinstalldir="/" name="src/core/ext/xds/xds_lb_policy_registry.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_listener.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_listener.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_metrics.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_resource_type.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_resource_type_impl.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/xds/xds_route_config.cc" role="src" />
@ -1147,6 +1148,7 @@
<file baseinstalldir="/" name="src/core/lib/channel/promise_based_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/promise_based_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/server_call_tracer_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/server_call_tracer_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/status_util.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/status_util.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/channel/tcp_tracer.h" role="src" />
@ -1189,6 +1191,7 @@
<file baseinstalldir="/" name="src/core/lib/event_engine/default_event_engine_factory.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/default_event_engine_factory.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/event_engine.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/event_engine_context.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/extensions/can_track_errors.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/extensions/chaotic_good_extension.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/event_engine/extensions/supports_fd.h" role="src" />

@ -119,6 +119,9 @@ grpc_cc_library(
srcs = [
"lib/channel/server_call_tracer_filter.cc",
],
hdrs = [
"lib/channel/server_call_tracer_filter.h",
],
external_deps = [
"absl/status",
"absl/status:statusor",
@ -592,7 +595,7 @@ grpc_cc_library(
deps = [
"activity",
"context",
"default_event_engine",
"event_engine_context",
"poll",
"time",
"//:event_engine_base_hdrs",
@ -906,6 +909,7 @@ grpc_cc_library(
"atomic_utils",
"construct_destruct",
"context",
"event_engine_context",
"no_destruct",
"poll",
"promise_factory",
@ -2608,6 +2612,21 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "event_engine_context",
hdrs = [
"lib/event_engine/event_engine_context.h",
],
visibility = [
"@grpc:alt_grpc_base_legacy",
],
deps = [
"context",
"//:event_engine_base_hdrs",
"//:gpr",
],
)
grpc_cc_library(
name = "default_event_engine",
srcs = [
@ -2622,7 +2641,6 @@ grpc_cc_library(
],
deps = [
"channel_args",
"context",
"default_event_engine_factory",
"event_engine_trace",
"no_destruct",
@ -3549,6 +3567,7 @@ grpc_cc_library(
"channel_args",
"channel_fwd",
"context",
"event_engine_context",
"grpc_server_config_selector",
"grpc_service_config",
"metadata_batch",
@ -5060,6 +5079,7 @@ grpc_cc_library(
"load_file",
"match",
"metadata_batch",
"metrics",
"pollset_set",
"protobuf_any_upb",
"protobuf_duration_upb",
@ -7442,10 +7462,10 @@ grpc_cc_library(
"chaotic_good_settings_metadata",
"closure",
"context",
"default_event_engine",
"error",
"error_utils",
"event_engine_common",
"event_engine_context",
"event_engine_extensions",
"event_engine_query_extensions",
"event_engine_tcp_socket_utils",
@ -7531,6 +7551,7 @@ grpc_cc_library(
"//:api_trace",
"//:channel",
"//:channel_create",
"//:config",
"//:debug_location",
"//:exec_ctx",
"//:gpr",
@ -7562,10 +7583,14 @@ grpc_cc_library(
],
language = "c++",
deps = [
"arena",
"channel_args",
"no_destruct",
"slice",
"time",
"//:call_tracer",
"//:gpr",
"//:legacy_context",
],
)

@ -1340,6 +1340,7 @@ ClientChannelFilter::CreateLoadBalancedCall(
const grpc_call_element_args& args, grpc_polling_entity* pollent,
grpc_closure* on_call_destruction_complete,
absl::AnyInvocable<void()> on_commit, bool is_transparent_retry) {
promise_detail::Context<Arena> arena_ctx(args.arena);
return OrphanablePtr<FilterBasedLoadBalancedCall>(
args.arena->New<FilterBasedLoadBalancedCall>(
this, args, pollent, on_call_destruction_complete,
@ -2894,6 +2895,15 @@ ClientChannelFilter::LoadBalancedCall::PickSubchannel(bool was_queued) {
set_picker(chand_->picker_);
}
while (true) {
// TODO(roth): Fix race condition in channel_idle filter and any
// other possible causes of this.
if (pickers.back() == nullptr) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) {
gpr_log(GPR_ERROR, "chand=%p lb_call=%p: picker is null, failing call",
chand_, this);
}
return absl::InternalError("picker is null -- shouldn't happen");
}
// Do pick.
if (GRPC_TRACE_FLAG_ENABLED(grpc_client_channel_lb_call_trace)) {
gpr_log(GPR_INFO, "chand=%p lb_call=%p: performing pick with picker=%p",

@ -31,6 +31,7 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/event_engine/event_engine_context.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/status_helper.h"
#include "src/core/lib/gprpp/sync.h"

@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
load("@build_bazel_rules_android//android:rules.bzl", "android_library")
# copybara: Import internal android_library rule here
licenses(["notice"])

@ -33,8 +33,8 @@
#include "src/core/ext/transport/chaotic_good/frame_header.h"
#include "src/core/ext/transport/chaotic_good/settings_metadata.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/event_engine/channel_args_endpoint_config.h"
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/event_engine/extensions/chaotic_good_extension.h"
#include "src/core/lib/event_engine/query_extensions.h"
#include "src/core/lib/event_engine/tcp_socket_utils.h"

@ -38,7 +38,7 @@
#include "src/core/ext/transport/chaotic_good/settings_metadata.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/event_engine/channel_args_endpoint_config.h"
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/event_engine/event_engine_context.h"
#include "src/core/lib/event_engine/extensions/chaotic_good_extension.h"
#include "src/core/lib/event_engine/query_extensions.h"
#include "src/core/lib/event_engine/resolved_address_internal.h"

@ -22,6 +22,7 @@
#include <grpc/support/log.h>
#include "src/core/ext/transport/inproc/legacy_inproc_transport.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/experiments/experiments.h"
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/promise/promise.h"

@ -38,6 +38,8 @@
#include <grpc/support/json.h>
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/env.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/json/json.h"
#include "src/core/lib/json/json_object_loader.h"
@ -47,6 +49,17 @@
namespace grpc_core {
namespace {
bool IsFallbackExperimentEnabled() {
auto fallback_enabled = GetEnv("GRPC_EXPERIMENTAL_XDS_FALLBACK");
bool enabled = false;
return gpr_parse_bool_value(fallback_enabled.value_or("0").c_str(),
&enabled) &&
enabled;
}
} // namespace
//
// GrpcXdsBootstrap::GrpcNode::Locality
//
@ -219,6 +232,16 @@ const JsonLoaderInterface* GrpcXdsBootstrap::GrpcAuthority::JsonLoader(
return loader;
}
void GrpcXdsBootstrap::GrpcAuthority::JsonPostLoad(
const Json& /*json*/, const JsonArgs& /*args*/,
ValidationErrors* /*errors*/) {
if (!IsFallbackExperimentEnabled()) {
if (servers_.size() > 1) {
servers_.resize(1);
}
}
}
//
// GrpcXdsBootstrap
//
@ -293,6 +316,11 @@ void GrpcXdsBootstrap::JsonPostLoad(const Json& /*json*/,
}
}
}
if (!IsFallbackExperimentEnabled()) {
if (servers_.size() > 1) {
servers_.resize(1);
}
}
}
std::string GrpcXdsBootstrap::ToString() const {

@ -118,6 +118,8 @@ class GrpcXdsBootstrap : public XdsBootstrap {
}
static const JsonLoaderInterface* JsonLoader(const JsonArgs&);
void JsonPostLoad(const Json& json, const JsonArgs& args,
ValidationErrors* errors);
private:
std::vector<GrpcXdsServer> servers_;

@ -150,7 +150,8 @@ class XdsClient::XdsChannel::AdsCall : public InternallyRefCounted<AdsCall> {
std::vector<std::string> errors;
std::map<std::string /*authority*/, std::set<XdsResourceKey>>
resources_seen;
bool have_valid_resources = false;
uint64_t num_valid_resources = 0;
uint64_t num_invalid_resources = 0;
RefCountedPtr<ReadDelayHandle> read_delay_handle;
};
@ -783,6 +784,7 @@ void XdsClient::XdsChannel::AdsCall::AdsResponseParser::ParseResource(
result_.errors.emplace_back(
absl::StrCat(error_prefix, "incorrect resource type \"", type_url,
"\" (should be \"", result_.type_url, "\")"));
++result_.num_invalid_resources;
return;
}
// Parse the resource.
@ -803,6 +805,7 @@ void XdsClient::XdsChannel::AdsCall::AdsResponseParser::ParseResource(
// there's nothing more we can do here.
result_.errors.emplace_back(absl::StrCat(
error_prefix, decode_result.resource.status().ToString()));
++result_.num_invalid_resources;
return;
}
}
@ -818,6 +821,7 @@ void XdsClient::XdsChannel::AdsCall::AdsResponseParser::ParseResource(
if (!parsed_resource_name.ok()) {
result_.errors.emplace_back(
absl::StrCat(error_prefix, "Cannot parse xDS resource name"));
++result_.num_invalid_resources;
return;
}
// Cancel resource-does-not-exist timer, if needed.
@ -877,10 +881,11 @@ void XdsClient::XdsChannel::AdsCall::AdsResponseParser::ParseResource(
result_.read_delay_handle);
UpdateResourceMetadataNacked(result_.version, decode_status.ToString(),
update_time_, &resource_state.meta);
++result_.num_invalid_resources;
return;
}
// Resource is valid.
result_.have_valid_resources = true;
++result_.num_valid_resources;
// If it didn't change, ignore it.
if (resource_state.resource != nullptr &&
result_.type->ResourcesEqual(resource_state.resource.get(),
@ -914,6 +919,7 @@ void XdsClient::XdsChannel::AdsCall::AdsResponseParser::
ResourceWrapperParsingFailed(size_t idx, absl::string_view message) {
result_.errors.emplace_back(
absl::StrCat("resource index ", idx, ": ", message));
++result_.num_invalid_resources;
}
//
@ -1157,13 +1163,19 @@ void XdsClient::XdsChannel::AdsCall::OnRecvMessage(absl::string_view payload) {
}
}
// If we had valid resources or the update was empty, update the version.
if (result.have_valid_resources || result.errors.empty()) {
if (result.num_valid_resources > 0 || result.errors.empty()) {
xds_channel()->resource_type_version_map_[result.type] =
std::move(result.version);
}
// Send ACK or NACK.
SendMessageLocked(result.type);
}
// Update metrics.
if (xds_client()->metrics_reporter_ != nullptr) {
xds_client()->metrics_reporter_->ReportResourceUpdates(
xds_channel()->server_.server_uri(), result.type_url,
result.num_valid_resources, result.num_invalid_resources);
}
}
xds_client()->work_serializer_.DrainQueue();
}
@ -1484,10 +1496,13 @@ bool XdsClient::XdsChannel::LrsCall::IsCurrentCallOnChannel() const {
// XdsClient
//
constexpr absl::string_view XdsClient::kOldStyleAuthority;
XdsClient::XdsClient(
std::unique_ptr<XdsBootstrap> bootstrap,
OrphanablePtr<XdsTransportFactory> transport_factory,
std::shared_ptr<grpc_event_engine::experimental::EventEngine> engine,
std::unique_ptr<XdsMetricsReporter> metrics_reporter,
std::string user_agent_name, std::string user_agent_version,
Duration resource_request_timeout)
: DualRefCounted<XdsClient>(
@ -1500,7 +1515,8 @@ XdsClient::XdsClient(
api_(this, &grpc_xds_client_trace, bootstrap_->node(), &def_pool_,
std::move(user_agent_name), std::move(user_agent_version)),
work_serializer_(engine),
engine_(std::move(engine)) {
engine_(std::move(engine)),
metrics_reporter_(std::move(metrics_reporter)) {
if (GRPC_TRACE_FLAG_ENABLED(grpc_xds_client_trace)) {
gpr_log(GPR_INFO, "[xds_client %p] creating xds client", this);
}
@ -1575,12 +1591,12 @@ void XdsClient::WatchResource(const XdsResourceType* type,
}
// Find server to use.
const XdsBootstrap::XdsServer* xds_server = nullptr;
absl::string_view authority_name = resource_name->authority;
if (absl::ConsumePrefix(&authority_name, "xdstp:")) {
auto* authority = bootstrap_->LookupAuthority(std::string(authority_name));
if (resource_name->authority != kOldStyleAuthority) {
auto* authority =
bootstrap_->LookupAuthority(std::string(resource_name->authority));
if (authority == nullptr) {
fail(absl::UnavailableError(
absl::StrCat("authority \"", authority_name,
absl::StrCat("authority \"", resource_name->authority,
"\" not present in bootstrap config")));
return;
}
@ -1738,10 +1754,11 @@ const XdsResourceType* XdsClient::GetResourceTypeLocked(
absl::StatusOr<XdsClient::XdsResourceName> XdsClient::ParseXdsResourceName(
absl::string_view name, const XdsResourceType* type) {
// Old-style names use the empty string for authority.
// authority is prefixed with "old:" to indicate that it's an old-style
// name.
// authority is set to kOldStyleAuthority to indicate that it's an
// old-style name.
if (!xds_federation_enabled_ || !absl::StartsWith(name, "xdstp:")) {
return XdsResourceName{"old:", {std::string(name), {}}};
return XdsResourceName{std::string(kOldStyleAuthority),
{std::string(name), {}}};
}
// New style name. Parse URI.
auto uri = URI::Parse(name);
@ -1760,14 +1777,14 @@ absl::StatusOr<XdsClient::XdsResourceName> XdsClient::ParseXdsResourceName(
URI::QueryParam{std::string(p.first), std::string(p.second)});
}
return XdsResourceName{
absl::StrCat("xdstp:", uri->authority()),
uri->authority(),
{std::string(path_parts.second), std::move(query_params)}};
}
std::string XdsClient::ConstructFullXdsResourceName(
absl::string_view authority, absl::string_view resource_type,
const XdsResourceKey& key) {
if (absl::ConsumePrefix(&authority, "xdstp:")) {
if (authority != kOldStyleAuthority) {
auto uri = URI::Create("xdstp", std::string(authority),
absl::StrCat("/", resource_type, "/", key.id),
key.query_params, /*fragment=*/"");
@ -2121,4 +2138,53 @@ void XdsClient::DumpClientConfig(
}
}
namespace {
absl::string_view CacheStateForEntry(const XdsApi::ResourceMetadata& metadata,
bool resource_cached) {
switch (metadata.client_status) {
case XdsApi::ResourceMetadata::REQUESTED:
return "requested";
case XdsApi::ResourceMetadata::DOES_NOT_EXIST:
return "does_not_exist";
case XdsApi::ResourceMetadata::ACKED:
return "acked";
case XdsApi::ResourceMetadata::NACKED:
return resource_cached ? "nacked_but_cached" : "nacked";
}
Crash("unknown resource state");
}
} // namespace
void XdsClient::ReportResourceCounts(
absl::FunctionRef<void(const ResourceCountLabels&, uint64_t)> func) {
ResourceCountLabels labels;
for (const auto& a : authority_state_map_) { // authority
labels.xds_authority = a.first;
for (const auto& t : a.second.resource_map) { // type
labels.resource_type = t.first->type_url();
// Count the number of entries in each state.
std::map<absl::string_view, uint64_t> counts;
for (const auto& r : t.second) { // resource id
absl::string_view cache_state =
CacheStateForEntry(r.second.meta, r.second.resource != nullptr);
++counts[cache_state];
}
// Report the count for each state.
for (const auto& c : counts) {
labels.cache_state = c.first;
func(labels, c.second);
}
}
}
}
void XdsClient::ReportServerConnections(
absl::FunctionRef<void(absl::string_view, bool)> func) {
for (const auto& p : xds_channel_map_) {
func(p.second->server_uri(), p.second->status().ok());
}
}
} // namespace grpc_core

@ -37,6 +37,7 @@
#include "src/core/ext/xds/xds_api.h"
#include "src/core/ext/xds/xds_bootstrap.h"
#include "src/core/ext/xds/xds_client_stats.h"
#include "src/core/ext/xds/xds_metrics.h"
#include "src/core/ext/xds/xds_resource_type.h"
#include "src/core/ext/xds/xds_transport.h"
#include "src/core/lib/debug/trace.h"
@ -60,6 +61,9 @@ extern TraceFlag grpc_xds_client_refcount_trace;
class XdsClient : public DualRefCounted<XdsClient> {
public:
// The authority reported for old-style (non-xdstp) resource names.
static constexpr absl::string_view kOldStyleAuthority = "#old";
class ReadDelayHandle : public RefCounted<ReadDelayHandle> {
public:
static RefCountedPtr<ReadDelayHandle> NoWait() { return nullptr; }
@ -87,6 +91,7 @@ class XdsClient : public DualRefCounted<XdsClient> {
std::unique_ptr<XdsBootstrap> bootstrap,
OrphanablePtr<XdsTransportFactory> transport_factory,
std::shared_ptr<grpc_event_engine::experimental::EventEngine> engine,
std::unique_ptr<XdsMetricsReporter> metrics_reporter,
std::string user_agent_name, std::string user_agent_version,
Duration resource_request_timeout = Duration::Seconds(15));
~XdsClient() override;
@ -156,6 +161,8 @@ class XdsClient : public DualRefCounted<XdsClient> {
}
protected:
Mutex* mu() ABSL_LOCK_RETURNED(&mu_) { return &mu_; }
// Dumps the active xDS config to the provided
// envoy.service.status.v3.ClientConfig message including the config status
// (e.g., CLIENT_REQUESTED, CLIENT_ACKED, CLIENT_NACKED).
@ -163,7 +170,22 @@ class XdsClient : public DualRefCounted<XdsClient> {
envoy_service_status_v3_ClientConfig* client_config)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_);
Mutex* mu() ABSL_LOCK_RETURNED(&mu_) { return &mu_; }
// Invokes func once for each combination of labels to report the
// resource count for those labels.
struct ResourceCountLabels {
absl::string_view xds_authority;
absl::string_view resource_type;
absl::string_view cache_state;
};
void ReportResourceCounts(
absl::FunctionRef<void(const ResourceCountLabels&, uint64_t)> func)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_);
// Invokes func once for each xDS server to report whether the
// connection to that server is working.
void ReportServerConnections(
absl::FunctionRef<void(absl::string_view /*xds_server*/, bool)> func)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&mu_);
private:
friend testing::XdsClientTestPeer;
@ -221,6 +243,8 @@ class XdsClient : public DualRefCounted<XdsClient> {
bool delay_unsubscription)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&XdsClient::mu_);
absl::string_view server_uri() const { return server_.server_uri(); }
private:
void OnConnectivityFailure(absl::Status status);
@ -327,6 +351,7 @@ class XdsClient : public DualRefCounted<XdsClient> {
XdsApi api_;
WorkSerializer work_serializer_;
std::shared_ptr<grpc_event_engine::experimental::EventEngine> engine_;
std::unique_ptr<XdsMetricsReporter> metrics_reporter_;
Mutex mu_;

@ -49,6 +49,7 @@
#include "src/core/ext/xds/xds_transport.h"
#include "src/core/ext/xds/xds_transport_grpc.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/metrics.h"
#include "src/core/lib/debug/trace.h"
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/gprpp/debug_location.h"
@ -64,8 +65,6 @@
#include "src/core/lib/slice/slice_internal.h"
#include "src/core/lib/transport/error_utils.h"
namespace grpc_core {
// If gRPC is built with -DGRPC_XDS_USER_AGENT_NAME_SUFFIX="...", that string
// will be appended to the user agent name reported to the xDS server.
#ifdef GRPC_XDS_USER_AGENT_NAME_SUFFIX
@ -84,10 +83,90 @@ namespace grpc_core {
#define GRPC_XDS_USER_AGENT_VERSION_SUFFIX_STRING ""
#endif
namespace grpc_core {
namespace {
// Metric labels.
constexpr absl::string_view kMetricLabelXdsServer = "grpc.xds.server";
constexpr absl::string_view kMetricLabelXdsAuthority = "grpc.xds.authority";
constexpr absl::string_view kMetricLabelXdsResourceType =
"grpc.xds.resource_type";
constexpr absl::string_view kMetricLabelXdsCacheState = "grpc.xds.cache_state";
const auto kMetricResourceUpdatesValid =
GlobalInstrumentsRegistry::RegisterUInt64Counter(
"grpc.xds_client.resource_updates_valid",
"EXPERIMENTAL. A counter of resources received that were considered "
"valid. The counter will be incremented even for resources that "
"have not changed.",
"{resource}",
{kMetricLabelTarget, kMetricLabelXdsServer,
kMetricLabelXdsResourceType},
{}, false);
const auto kMetricResourceUpdatesInvalid =
GlobalInstrumentsRegistry::RegisterUInt64Counter(
"grpc.xds_client.resource_updates_invalid",
"EXPERIMENTAL. A counter of resources received that were considered "
"invalid.",
"{resource}",
{kMetricLabelTarget, kMetricLabelXdsServer,
kMetricLabelXdsResourceType},
{}, false);
const auto kMetricConnected =
GlobalInstrumentsRegistry::RegisterCallbackInt64Gauge(
"grpc.xds_client.connected",
"EXPERIMENTAL. Whether or not the xDS client currently has a "
"working ADS stream to the xDS server. For a given server, this "
"will be set to 0 when we have a connectivity failure or when the "
"ADS stream fails without seeing a response message, as per gRFC "
"A57. It will be set to 1 when we receive the first response on "
"an ADS stream.",
"{bool}", {kMetricLabelTarget, kMetricLabelXdsServer}, {}, false);
const auto kMetricResources =
GlobalInstrumentsRegistry::RegisterCallbackInt64Gauge(
"grpc.xds_client.resources", "EXPERIMENTAL. Number of xDS resources.",
"{resource}",
{kMetricLabelTarget, kMetricLabelXdsAuthority,
kMetricLabelXdsResourceType, kMetricLabelXdsCacheState},
{}, false);
} // namespace
//
// GrpcXdsClient::MetricsReporter
//
class GrpcXdsClient::MetricsReporter : public XdsMetricsReporter {
public:
explicit MetricsReporter(GrpcXdsClient& xds_client)
: xds_client_(xds_client) {}
void ReportResourceUpdates(absl::string_view xds_server,
absl::string_view resource_type,
uint64_t num_valid_resources,
uint64_t num_invalid_resources) override {
xds_client_.stats_plugin_group_.AddCounter(
kMetricResourceUpdatesValid, num_valid_resources,
{xds_client_.key_, xds_server, resource_type}, {});
xds_client_.stats_plugin_group_.AddCounter(
kMetricResourceUpdatesInvalid, num_invalid_resources,
{xds_client_.key_, xds_server, resource_type}, {});
}
private:
GrpcXdsClient& xds_client_;
};
//
// GrpcXdsClient
//
constexpr absl::string_view GrpcXdsClient::kServerKey;
namespace {
Mutex* g_mu = new Mutex;
@ -97,10 +176,6 @@ NoDestruct<std::map<absl::string_view, GrpcXdsClient*>> g_xds_client_map
ABSL_GUARDED_BY(*g_mu);
char* g_fallback_bootstrap_config ABSL_GUARDED_BY(*g_mu) = nullptr;
} // namespace
namespace {
absl::StatusOr<std::string> GetBootstrapContents(const char* fallback_config) {
// First, try GRPC_XDS_BOOTSTRAP env var.
auto path = GetEnv("GRPC_XDS_BOOTSTRAP");
@ -138,19 +213,6 @@ absl::StatusOr<std::string> GetBootstrapContents(const char* fallback_config) {
"not defined");
}
std::vector<RefCountedPtr<GrpcXdsClient>> GetAllXdsClients() {
MutexLock lock(g_mu);
std::vector<RefCountedPtr<GrpcXdsClient>> xds_clients;
for (const auto& key_client : *g_xds_client_map) {
auto xds_client =
key_client.second->RefIfNonZero(DEBUG_LOCATION, "DumpAllClientConfigs");
if (xds_client != nullptr) {
xds_clients.emplace_back(xds_client.TakeAsSubclass<GrpcXdsClient>());
}
}
return xds_clients;
}
} // namespace
absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GrpcXdsClient::GetOrCreate(
@ -201,36 +263,20 @@ absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GrpcXdsClient::GetOrCreate(
return xds_client;
}
// ABSL_NO_THREAD_SAFETY_ANALYSIS because we have to manually manage locks for
// individual XdsClients and compiler struggles with checking the validity
grpc_slice GrpcXdsClient::DumpAllClientConfigs()
ABSL_NO_THREAD_SAFETY_ANALYSIS {
auto xds_clients = GetAllXdsClients();
upb::Arena arena;
// Contains strings that should survive till serialization
std::set<std::string> string_pool;
auto response = envoy_service_status_v3_ClientStatusResponse_new(arena.ptr());
// We lock each XdsClient mutex till we are done with the serialization to
// ensure that all data referenced from the UPB proto message stays alive.
for (const auto& xds_client : xds_clients) {
auto client_config =
envoy_service_status_v3_ClientStatusResponse_add_config(response,
arena.ptr());
xds_client->mu()->Lock();
xds_client->DumpClientConfig(&string_pool, arena.ptr(), client_config);
envoy_service_status_v3_ClientConfig_set_client_scope(
client_config, StdStringToUpbString(xds_client->key()));
}
// Serialize the upb message to bytes
size_t output_length;
char* output = envoy_service_status_v3_ClientStatusResponse_serialize(
response, arena.ptr(), &output_length);
for (const auto& xds_client : xds_clients) {
xds_client->mu()->Unlock();
namespace {
GlobalStatsPluginRegistry::StatsPluginGroup GetStatsPluginGroupForKey(
absl::string_view key) {
if (key == GrpcXdsClient::kServerKey) {
return GlobalStatsPluginRegistry::GetStatsPluginsForServer(ChannelArgs{});
}
return grpc_slice_from_cpp_string(std::string(output, output_length));
// TODO(roth): How do we set the authority here?
StatsPlugin::ChannelScope scope(key, "");
return GlobalStatsPluginRegistry::GetStatsPluginsForChannel(scope);
}
} // namespace
GrpcXdsClient::GrpcXdsClient(
absl::string_view key, std::unique_ptr<GrpcXdsBootstrap> bootstrap,
const ChannelArgs& args,
@ -238,6 +284,7 @@ GrpcXdsClient::GrpcXdsClient(
: XdsClient(
std::move(bootstrap), std::move(transport_factory),
grpc_event_engine::experimental::GetDefaultEventEngine(),
std::make_unique<MetricsReporter>(*this),
absl::StrCat("gRPC C-core ", GPR_PLATFORM_STRING,
GRPC_XDS_USER_AGENT_NAME_SUFFIX_STRING),
absl::StrCat("C-core ", grpc_version_string(),
@ -250,15 +297,22 @@ GrpcXdsClient::GrpcXdsClient(
key_(key),
certificate_provider_store_(MakeOrphanable<CertificateProviderStore>(
static_cast<const GrpcXdsBootstrap&>(this->bootstrap())
.certificate_providers())) {}
.certificate_providers())),
stats_plugin_group_(GetStatsPluginGroupForKey(key_)),
registered_metric_callback_(stats_plugin_group_.RegisterCallback(
[this](CallbackMetricReporter& reporter) {
ReportCallbackMetrics(reporter);
},
{kMetricConnected, kMetricResources})) {}
void GrpcXdsClient::Orphan() {
registered_metric_callback_.reset();
XdsClient::Orphan();
MutexLock lock(g_mu);
auto it = g_xds_client_map->find(key_);
if (it != g_xds_client_map->end() && it->second == this) {
g_xds_client_map->erase(it);
}
XdsClient::Orphan();
}
grpc_pollset_set* GrpcXdsClient::interested_parties() const {
@ -266,6 +320,66 @@ grpc_pollset_set* GrpcXdsClient::interested_parties() const {
->interested_parties();
}
namespace {
std::vector<RefCountedPtr<GrpcXdsClient>> GetAllXdsClients() {
MutexLock lock(g_mu);
std::vector<RefCountedPtr<GrpcXdsClient>> xds_clients;
for (const auto& key_client : *g_xds_client_map) {
auto xds_client =
key_client.second->RefIfNonZero(DEBUG_LOCATION, "DumpAllClientConfigs");
if (xds_client != nullptr) {
xds_clients.emplace_back(xds_client.TakeAsSubclass<GrpcXdsClient>());
}
}
return xds_clients;
}
} // namespace
// ABSL_NO_THREAD_SAFETY_ANALYSIS because we have to manually manage locks for
// individual XdsClients and compiler struggles with checking the validity
grpc_slice GrpcXdsClient::DumpAllClientConfigs()
ABSL_NO_THREAD_SAFETY_ANALYSIS {
auto xds_clients = GetAllXdsClients();
upb::Arena arena;
// Contains strings that should survive till serialization
std::set<std::string> string_pool;
auto response = envoy_service_status_v3_ClientStatusResponse_new(arena.ptr());
// We lock each XdsClient mutex till we are done with the serialization to
// ensure that all data referenced from the UPB proto message stays alive.
for (const auto& xds_client : xds_clients) {
auto client_config =
envoy_service_status_v3_ClientStatusResponse_add_config(response,
arena.ptr());
xds_client->mu()->Lock();
xds_client->DumpClientConfig(&string_pool, arena.ptr(), client_config);
envoy_service_status_v3_ClientConfig_set_client_scope(
client_config, StdStringToUpbString(xds_client->key()));
}
// Serialize the upb message to bytes
size_t output_length;
char* output = envoy_service_status_v3_ClientStatusResponse_serialize(
response, arena.ptr(), &output_length);
for (const auto& xds_client : xds_clients) {
xds_client->mu()->Unlock();
}
return grpc_slice_from_cpp_string(std::string(output, output_length));
}
void GrpcXdsClient::ReportCallbackMetrics(CallbackMetricReporter& reporter) {
MutexLock lock(mu());
ReportResourceCounts([&](const ResourceCountLabels& labels, uint64_t count) {
reporter.Report(
kMetricResources, count,
{key_, labels.xds_authority, labels.resource_type, labels.cache_state},
{});
});
ReportServerConnections([&](absl::string_view xds_server, bool connected) {
reporter.Report(kMetricConnected, connected, {key_, xds_server}, {});
});
}
namespace internal {
void SetXdsChannelArgsForTest(grpc_channel_args* args) {

@ -31,6 +31,7 @@
#include "src/core/ext/xds/xds_client.h"
#include "src/core/ext/xds/xds_transport.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/metrics.h"
#include "src/core/lib/gpr/useful.h"
#include "src/core/lib/gprpp/orphanable.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
@ -41,13 +42,13 @@ namespace grpc_core {
class GrpcXdsClient : public XdsClient {
public:
// The key to pass to GetOrCreate() for gRPC servers.
static constexpr absl::string_view kServerKey = "#server";
// Factory function to get or create the global XdsClient instance.
static absl::StatusOr<RefCountedPtr<GrpcXdsClient>> GetOrCreate(
absl::string_view key, const ChannelArgs& args, const char* reason);
// Builds ClientStatusResponse containing all resources from all XdsClients
static grpc_slice DumpAllClientConfigs();
// Do not instantiate directly -- use GetOrCreate() instead.
// TODO(roth): The transport factory is injectable here to support
// tests that want to use a fake transport factory with code that
@ -83,9 +84,18 @@ class GrpcXdsClient : public XdsClient {
absl::string_view key() const { return key_; }
// Builds ClientStatusResponse containing all resources from all XdsClients
static grpc_slice DumpAllClientConfigs();
private:
class MetricsReporter;
void ReportCallbackMetrics(CallbackMetricReporter& reporter);
std::string key_;
OrphanablePtr<CertificateProviderStore> certificate_provider_store_;
GlobalStatsPluginRegistry::StatsPluginGroup stats_plugin_group_;
std::unique_ptr<RegisteredMetricCallback> registered_metric_callback_;
};
namespace internal {

@ -0,0 +1,39 @@
//
// Copyright 2024 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_SRC_CORE_EXT_XDS_XDS_METRICS_H
#define GRPC_SRC_CORE_EXT_XDS_XDS_METRICS_H
#include <grpc/support/port_platform.h>
#include "absl/strings/string_view.h"
namespace grpc_core {
// An interface for XdsClient to report metrics.
class XdsMetricsReporter {
public:
virtual ~XdsMetricsReporter() = default;
virtual void ReportResourceUpdates(absl::string_view xds_server,
absl::string_view resource_type,
uint64_t num_valid,
uint64_t num_invalid) = 0;
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_EXT_XDS_XDS_METRICS_H

@ -94,8 +94,6 @@ namespace {
using ReadDelayHandle = XdsClient::ReadDelayHandle;
constexpr absl::string_view kServerXdsClientKey = "#server";
TraceFlag grpc_xds_server_config_fetcher_trace(false,
"xds_server_config_fetcher");
@ -1374,7 +1372,8 @@ grpc_server_config_fetcher* grpc_server_config_fetcher_xds_create(
"update=%p, user_data=%p}, args=%p)",
3, (notifier.on_serving_status_update, notifier.user_data, args));
auto xds_client = grpc_core::GrpcXdsClient::GetOrCreate(
grpc_core::kServerXdsClientKey, channel_args, "XdsServerConfigFetcher");
grpc_core::GrpcXdsClient::kServerKey, channel_args,
"XdsServerConfigFetcher");
if (!xds_client.ok()) {
gpr_log(GPR_ERROR, "Failed to create xds client: %s",
xds_client.status().ToString().c_str());

@ -32,7 +32,6 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/channel/tcp_tracer.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice_buffer.h"
@ -207,8 +206,6 @@ class ServerCallTracerFactory {
static absl::string_view ChannelArgName();
};
void RegisterServerCallTracerFilter(CoreConfiguration::Builder* builder);
// Convenience functions to add call tracers to a call context. Allows setting
// multiple call tracers to a single call. It is only valid to add client call
// tracers before the client_channel filter sees the send_initial_metadata op.

@ -16,7 +16,7 @@
#include "src/core/lib/channel/metrics.h"
#include "absl/container/flat_hash_map.h"
#include <memory>
#include "src/core/lib/gprpp/crash.h"
@ -24,11 +24,10 @@ namespace grpc_core {
// Uses the Construct-on-First-Use idiom to avoid the static initialization
// order fiasco.
absl::flat_hash_map<absl::string_view,
GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>&
std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>&
GlobalInstrumentsRegistry::GetInstrumentList() {
static NoDestruct<absl::flat_hash_map<
absl::string_view, GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>>
static NoDestruct<
std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>>
instruments;
return *instruments;
}
@ -40,8 +39,11 @@ GlobalInstrumentsRegistry::RegisterUInt64Counter(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -56,7 +58,7 @@ GlobalInstrumentsRegistry::RegisterUInt64Counter(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalUInt64CounterHandle handle;
handle.index = index;
return handle;
@ -69,8 +71,11 @@ GlobalInstrumentsRegistry::RegisterDoubleCounter(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -85,7 +90,7 @@ GlobalInstrumentsRegistry::RegisterDoubleCounter(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalDoubleCounterHandle handle;
handle.index = index;
return handle;
@ -98,8 +103,11 @@ GlobalInstrumentsRegistry::RegisterUInt64Histogram(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -114,7 +122,7 @@ GlobalInstrumentsRegistry::RegisterUInt64Histogram(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalUInt64HistogramHandle handle;
handle.index = index;
return handle;
@ -127,8 +135,11 @@ GlobalInstrumentsRegistry::RegisterDoubleHistogram(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -143,7 +154,7 @@ GlobalInstrumentsRegistry::RegisterDoubleHistogram(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalDoubleHistogramHandle handle;
handle.index = index;
return handle;
@ -156,8 +167,11 @@ GlobalInstrumentsRegistry::RegisterInt64Gauge(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -172,7 +186,7 @@ GlobalInstrumentsRegistry::RegisterInt64Gauge(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalInt64GaugeHandle handle;
handle.index = index;
return handle;
@ -185,8 +199,11 @@ GlobalInstrumentsRegistry::RegisterDoubleGauge(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -201,7 +218,7 @@ GlobalInstrumentsRegistry::RegisterDoubleGauge(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalDoubleGaugeHandle handle;
handle.index = index;
return handle;
@ -214,8 +231,11 @@ GlobalInstrumentsRegistry::RegisterCallbackInt64Gauge(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -230,7 +250,7 @@ GlobalInstrumentsRegistry::RegisterCallbackInt64Gauge(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalCallbackInt64GaugeHandle handle;
handle.index = index;
return handle;
@ -243,8 +263,11 @@ GlobalInstrumentsRegistry::RegisterCallbackDoubleGauge(
absl::Span<const absl::string_view> optional_label_keys,
bool enable_by_default) {
auto& instruments = GetInstrumentList();
if (instruments.find(name) != instruments.end()) {
Crash(absl::StrFormat("Metric name %s has already been registered.", name));
for (const auto& descriptor : instruments) {
if (descriptor.name == name) {
Crash(
absl::StrFormat("Metric name %s has already been registered.", name));
}
}
uint32_t index = instruments.size();
GPR_ASSERT(index < std::numeric_limits<uint32_t>::max());
@ -259,7 +282,7 @@ GlobalInstrumentsRegistry::RegisterCallbackDoubleGauge(
descriptor.label_keys = {label_keys.begin(), label_keys.end()};
descriptor.optional_label_keys = {optional_label_keys.begin(),
optional_label_keys.end()};
instruments.emplace(name, std::move(descriptor));
instruments.push_back(std::move(descriptor));
GlobalCallbackDoubleGaugeHandle handle;
handle.index = index;
return handle;
@ -268,10 +291,16 @@ GlobalInstrumentsRegistry::RegisterCallbackDoubleGauge(
void GlobalInstrumentsRegistry::ForEach(
absl::FunctionRef<void(const GlobalInstrumentDescriptor&)> f) {
for (const auto& instrument : GetInstrumentList()) {
f(instrument.second);
f(instrument);
}
}
const GlobalInstrumentsRegistry::GlobalInstrumentDescriptor&
GlobalInstrumentsRegistry::GetInstrumentDescriptor(
GlobalInstrumentHandle handle) {
return GetInstrumentList().at(handle.index);
}
RegisteredMetricCallback::RegisteredMetricCallback(
GlobalStatsPluginRegistry::StatsPluginGroup& stats_plugin_group,
absl::AnyInvocable<void(CallbackMetricReporter&)> callback,
@ -281,14 +310,14 @@ RegisteredMetricCallback::RegisteredMetricCallback(
callback_(std::move(callback)),
metrics_(std::move(metrics)),
min_interval_(min_interval) {
for (auto& plugin : stats_plugin_group_.plugins_) {
plugin->AddCallback(this);
for (auto& state : stats_plugin_group_.plugins_state_) {
state.plugin->AddCallback(this);
}
}
RegisteredMetricCallback::~RegisteredMetricCallback() {
for (auto& plugin : stats_plugin_group_.plugins_) {
plugin->RemoveCallback(this);
for (auto& state : stats_plugin_group_.plugins_state_) {
state.plugin->RemoveCallback(this);
}
}
@ -301,6 +330,28 @@ GlobalStatsPluginRegistry::StatsPluginGroup::RegisterCallback(
*this, std::move(callback), std::move(metrics), min_interval);
}
void GlobalStatsPluginRegistry::StatsPluginGroup::AddClientCallTracers(
const Slice& path, bool registered_method,
grpc_call_context_element* call_context) {
for (auto& state : plugins_state_) {
auto* call_tracer = state.plugin->GetClientCallTracer(
path, registered_method, state.scope_config);
if (call_tracer != nullptr) {
AddClientCallTracerToContext(call_context, call_tracer);
}
}
}
void GlobalStatsPluginRegistry::StatsPluginGroup::AddServerCallTracers(
grpc_call_context_element* call_context) {
for (auto& state : plugins_state_) {
auto* call_tracer = state.plugin->GetServerCallTracer(state.scope_config);
if (call_tracer != nullptr) {
AddServerCallTracerToContext(call_context, call_tracer);
}
}
}
NoDestruct<Mutex> GlobalStatsPluginRegistry::mutex_;
NoDestruct<std::vector<std::shared_ptr<StatsPlugin>>>
GlobalStatsPluginRegistry::plugins_;
@ -317,8 +368,26 @@ GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
MutexLock lock(&*mutex_);
StatsPluginGroup group;
for (const auto& plugin : *plugins_) {
if (plugin->IsEnabledForChannel(scope)) {
group.push_back(plugin);
bool is_enabled = false;
std::shared_ptr<StatsPlugin::ScopeConfig> config;
std::tie(is_enabled, config) = plugin->IsEnabledForChannel(scope);
if (is_enabled) {
group.AddStatsPlugin(plugin, std::move(config));
}
}
return group;
}
GlobalStatsPluginRegistry::StatsPluginGroup
GlobalStatsPluginRegistry::GetStatsPluginsForServer(const ChannelArgs& args) {
MutexLock lock(&*mutex_);
StatsPluginGroup group;
for (const auto& plugin : *plugins_) {
bool is_enabled = false;
std::shared_ptr<StatsPlugin::ScopeConfig> config;
std::tie(is_enabled, config) = plugin->IsEnabledForServer(args);
if (is_enabled) {
group.AddStatsPlugin(plugin, std::move(config));
}
}
return group;

@ -29,10 +29,13 @@
#include <grpc/support/log.h>
#include "src/core/lib/channel/call_tracer.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/gprpp/no_destruct.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/slice/slice.h"
namespace grpc_core {
@ -58,10 +61,11 @@ class GlobalInstrumentsRegistry {
kGauge,
kCallbackGauge,
};
using InstrumentID = uint32_t;
struct GlobalInstrumentDescriptor {
ValueType value_type;
InstrumentType instrument_type;
uint32_t index;
InstrumentID index;
bool enable_by_default;
absl::string_view name;
absl::string_view description;
@ -69,20 +73,20 @@ class GlobalInstrumentsRegistry {
std::vector<absl::string_view> label_keys;
std::vector<absl::string_view> optional_label_keys;
};
struct GlobalHandle {
struct GlobalInstrumentHandle {
// This is the index for the corresponding registered instrument that
// StatsPlugins can use to uniquely identify an instrument in the current
// process. Though this is not guaranteed to be stable between different
// runs or between different versions.
uint32_t index;
InstrumentID index;
};
struct GlobalUInt64CounterHandle : public GlobalHandle {};
struct GlobalDoubleCounterHandle : public GlobalHandle {};
struct GlobalUInt64HistogramHandle : public GlobalHandle {};
struct GlobalDoubleHistogramHandle : public GlobalHandle {};
struct GlobalInt64GaugeHandle : public GlobalHandle {};
struct GlobalDoubleGaugeHandle : public GlobalHandle {};
struct GlobalCallbackHandle : public GlobalHandle {};
struct GlobalUInt64CounterHandle : public GlobalInstrumentHandle {};
struct GlobalDoubleCounterHandle : public GlobalInstrumentHandle {};
struct GlobalUInt64HistogramHandle : public GlobalInstrumentHandle {};
struct GlobalDoubleHistogramHandle : public GlobalInstrumentHandle {};
struct GlobalInt64GaugeHandle : public GlobalInstrumentHandle {};
struct GlobalDoubleGaugeHandle : public GlobalInstrumentHandle {};
struct GlobalCallbackHandle : public GlobalInstrumentHandle {};
struct GlobalCallbackInt64GaugeHandle : public GlobalCallbackHandle {};
struct GlobalCallbackDoubleGaugeHandle : public GlobalCallbackHandle {};
@ -130,14 +134,15 @@ class GlobalInstrumentsRegistry {
static void ForEach(
absl::FunctionRef<void(const GlobalInstrumentDescriptor&)> f);
static const GlobalInstrumentDescriptor& GetInstrumentDescriptor(
GlobalInstrumentHandle handle);
private:
friend class GlobalInstrumentsRegistryTestPeer;
GlobalInstrumentsRegistry() = delete;
static absl::flat_hash_map<
absl::string_view, GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>&
static std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>&
GetInstrumentList();
};
@ -162,6 +167,7 @@ class RegisteredMetricCallback;
// The StatsPlugin interface.
class StatsPlugin {
public:
// Configuration (scope) for a specific client channel.
class ChannelScope {
public:
ChannelScope(absl::string_view target, absl::string_view authority)
@ -174,37 +180,74 @@ class StatsPlugin {
absl::string_view target_;
absl::string_view authority_;
};
// A general-purpose way for stats plugin to store per-channel or per-server
// state.
class ScopeConfig {
public:
virtual ~ScopeConfig() = default;
};
virtual ~StatsPlugin() = default;
virtual bool IsEnabledForChannel(const ChannelScope& scope) const = 0;
virtual bool IsEnabledForServer(const ChannelArgs& args) const = 0;
// Whether this stats plugin is enabled for the channel specified by \a scope.
// Returns true and a channel-specific ScopeConfig which may then be used to
// configure the ClientCallTracer in GetClientCallTracer().
virtual std::pair<bool, std::shared_ptr<ScopeConfig>> IsEnabledForChannel(
const ChannelScope& scope) const = 0;
// Whether this stats plugin is enabled for the server specified by \a args.
// Returns true and a server-specific ScopeConfig which may then be used to
// configure the ServerCallTracer in GetServerCallTracer().
virtual std::pair<bool, std::shared_ptr<ScopeConfig>> IsEnabledForServer(
const ChannelArgs& args) const = 0;
// Adds \a value to the uint64 counter specified by \a handle. \a label_values
// and \a optional_label_values specify attributes that are associated with
// this measurement and must match with their corresponding keys in
// GlobalInstrumentsRegistry::RegisterUInt64Counter().
virtual void AddCounter(
GlobalInstrumentsRegistry::GlobalUInt64CounterHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) = 0;
absl::Span<const absl::string_view> optional_label_values) = 0;
// Adds \a value to the double counter specified by \a handle. \a label_values
// and \a optional_label_values specify attributes that are associated with
// this measurement and must match with their corresponding keys in
// GlobalInstrumentsRegistry::RegisterDoubleCounter().
virtual void AddCounter(
GlobalInstrumentsRegistry::GlobalDoubleCounterHandle handle, double value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) = 0;
absl::Span<const absl::string_view> optional_label_values) = 0;
// Records a uint64 \a value to the histogram specified by \a handle. \a
// label_values and \a optional_label_values specify attributes that are
// associated with this measurement and must match with their corresponding
// keys in GlobalInstrumentsRegistry::RegisterUInt64Histogram().
virtual void RecordHistogram(
GlobalInstrumentsRegistry::GlobalUInt64HistogramHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) = 0;
absl::Span<const absl::string_view> optional_label_values) = 0;
// Records a double \a value to the histogram specified by \a handle. \a
// label_values and \a optional_label_values specify attributes that are
// associated with this measurement and must match with their corresponding
// keys in GlobalInstrumentsRegistry::RegisterDoubleHistogram().
virtual void RecordHistogram(
GlobalInstrumentsRegistry::GlobalDoubleHistogramHandle handle,
double value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) = 0;
absl::Span<const absl::string_view> optional_label_values) = 0;
// Sets an int64 \a value to the gauge specifed by \a handle. \a
// label_values and \a optional_label_values specify attributes that are
// associated with this measurement and must match with their corresponding
// keys in GlobalInstrumentsRegistry::RegisterInt64Gauge().
virtual void SetGauge(
GlobalInstrumentsRegistry::GlobalInt64GaugeHandle handle, int64_t value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) = 0;
absl::Span<const absl::string_view> optional_label_values) = 0;
// Sets a double \a value to the gauge specifed by \a handle. \a
// label_values and \a optional_label_values specify attributes that are
// associated with this measurement and must match with their corresponding
// keys in GlobalInstrumentsRegistry::RegisterDoubleGauge().
virtual void SetGauge(
GlobalInstrumentsRegistry::GlobalDoubleGaugeHandle handle, double value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) = 0;
absl::Span<const absl::string_view> optional_label_values) = 0;
// Adds a callback to be invoked when the stats plugin wants to
// populate the corresponding metrics (see callback->metrics() for list).
virtual void AddCallback(RegisteredMetricCallback* callback) = 0;
@ -212,13 +255,15 @@ class StatsPlugin {
// plugin may not use the callback after this method returns.
virtual void RemoveCallback(RegisteredMetricCallback* callback) = 0;
// TODO(yijiem): Details pending.
// std::unique_ptr<AsyncInstrument> GetObservableGauge(
// absl::string_view name, absl::string_view description,
// absl::string_view unit);
// AsyncInstrument* GetObservableCounter(
// absl::string_view name, absl::string_view description,
// absl::string_view unit);
// Gets a ClientCallTracer associated with this stats plugin which can be used
// in a call.
virtual ClientCallTracer* GetClientCallTracer(
const Slice& path, bool registered_method,
std::shared_ptr<ScopeConfig> scope_config) = 0;
// Gets a ServerCallTracer associated with this stats plugin which can be used
// in a call.
virtual ServerCallTracer* GetServerCallTracer(
std::shared_ptr<ScopeConfig> scope_config) = 0;
// TODO(yijiem): This is an optimization for the StatsPlugin to create its own
// representation of the label_values and use it multiple times. We would
@ -230,63 +275,55 @@ class StatsPlugin {
// absl::Span<absl::string_view> label_values) = 0;
};
// A global registry of StatsPlugins. It has shared ownership to the registered
// StatsPlugins. This API is supposed to be used during runtime after the main
// A global registry of stats plugins. It has shared ownership to the registered
// stats plugins. This API is supposed to be used during runtime after the main
// function begins. This API is thread-safe.
class GlobalStatsPluginRegistry {
public:
// A stats plugin group object is how the code in gRPC normally interacts with
// stats plugins. They got a stats plugin group which contains all the stats
// plugins for a specific scope and all operations on the stats plugin group
// will be applied to all the stats plugins within the group.
class StatsPluginGroup {
public:
void push_back(std::shared_ptr<StatsPlugin> plugin) {
plugins_.push_back(std::move(plugin));
}
void AddCounter(GlobalInstrumentsRegistry::GlobalUInt64CounterHandle handle,
uint64_t value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& plugin : plugins_) {
plugin->AddCounter(handle, value, label_values, optional_values);
}
// Adds a stats plugin and a scope config (per-channel or per-server) to the
// group.
void AddStatsPlugin(std::shared_ptr<StatsPlugin> plugin,
std::shared_ptr<StatsPlugin::ScopeConfig> config) {
PluginState plugin_state;
plugin_state.plugin = std::move(plugin);
plugin_state.scope_config = std::move(config);
plugins_state_.push_back(std::move(plugin_state));
}
void AddCounter(GlobalInstrumentsRegistry::GlobalDoubleCounterHandle handle,
double value,
// Adds a counter in all stats plugins within the group. See the StatsPlugin
// interface for more documentation and valid types.
template <class HandleType, class ValueType>
void AddCounter(HandleType handle, ValueType value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& plugin : plugins_) {
plugin->AddCounter(handle, value, label_values, optional_values);
}
}
void RecordHistogram(
GlobalInstrumentsRegistry::GlobalUInt64HistogramHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& plugin : plugins_) {
plugin->RecordHistogram(handle, value, label_values, optional_values);
}
}
void RecordHistogram(
GlobalInstrumentsRegistry::GlobalDoubleHistogramHandle handle,
double value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& plugin : plugins_) {
plugin->RecordHistogram(handle, value, label_values, optional_values);
for (auto& state : plugins_state_) {
state.plugin->AddCounter(handle, value, label_values, optional_values);
}
}
void SetGauge(GlobalInstrumentsRegistry::GlobalInt64GaugeHandle handle,
int64_t value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& plugin : plugins_) {
plugin->SetGauge(handle, value, label_values, optional_values);
// Records a value to a histogram in all stats plugins within the group. See
// the StatsPlugin interface for more documentation and valid types.
template <class HandleType, class ValueType>
void RecordHistogram(HandleType handle, ValueType value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& state : plugins_state_) {
state.plugin->RecordHistogram(handle, value, label_values,
optional_values);
}
}
void SetGauge(GlobalInstrumentsRegistry::GlobalDoubleGaugeHandle handle,
double value,
// Sets a value to a gauge in all stats plugins within the group. See the
// StatsPlugin interface for more documentation and valid types.
template <class HandleType, class ValueType>
void SetGauge(HandleType handle, ValueType value,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
for (auto& plugin : plugins_) {
plugin->SetGauge(handle, value, label_values, optional_values);
for (auto& state : plugins_state_) {
state.plugin->SetGauge(handle, value, label_values, optional_values);
}
}
@ -303,19 +340,33 @@ class GlobalStatsPluginRegistry {
std::vector<GlobalInstrumentsRegistry::GlobalCallbackHandle> metrics,
Duration min_interval = Duration::Seconds(5));
// Adds all available client call tracers associated with the stats plugins
// within the group to \a call_context.
void AddClientCallTracers(const Slice& path, bool registered_method,
grpc_call_context_element* call_context);
// Adds all available server call tracers associated with the stats plugins
// within the group to \a call_context.
void AddServerCallTracers(grpc_call_context_element* call_context);
private:
friend class RegisteredMetricCallback;
std::vector<std::shared_ptr<StatsPlugin>> plugins_;
struct PluginState {
std::shared_ptr<StatsPlugin::ScopeConfig> scope_config;
std::shared_ptr<StatsPlugin> plugin;
};
std::vector<PluginState> plugins_state_;
};
// Registers a stats plugin with the global stats plugin registry.
static void RegisterStatsPlugin(std::shared_ptr<StatsPlugin> plugin);
// The following two functions can be invoked to get a StatsPluginGroup for
// The following functions can be invoked to get a StatsPluginGroup for
// a specified scope.
static StatsPluginGroup GetStatsPluginsForChannel(
const StatsPlugin::ChannelScope& scope);
// TODO(yijiem): Implement this.
// StatsPluginsGroup GetStatsPluginsForServer(ChannelArgs& args);
static StatsPluginGroup GetStatsPluginsForServer(const ChannelArgs& args);
private:
friend class GlobalStatsPluginRegistryTestPeer;

@ -47,8 +47,8 @@
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/event_engine/default_event_engine.h" // IWYU pragma: keep
#include "src/core/lib/gprpp/crash.h"
#include "src/core/lib/event_engine/default_event_engine.h"
#include "src/core/lib/event_engine/event_engine_context.h" // IWYU pragma: keep
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/call_combiner.h"

@ -16,6 +16,8 @@
#include <grpc/support/port_platform.h>
#include "src/core/lib/channel/server_call_tracer_filter.h"
#include <functional>
#include <utility>

@ -0,0 +1,28 @@
// Copyright 2024 The gRPC Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_SRC_CORE_LIB_CHANNEL_SERVER_CALL_TRACER_FILTER_H
#define GRPC_SRC_CORE_LIB_CHANNEL_SERVER_CALL_TRACER_FILTER_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/config/core_configuration.h"
namespace grpc_core {
void RegisterServerCallTracerFilter(CoreConfiguration::Builder* builder);
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_CHANNEL_SERVER_CALL_TRACER_FILTER_H

@ -23,12 +23,6 @@
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/debug_location.h"
#include "src/core/lib/promise/context.h"
namespace grpc_core {
template <>
struct ContextType<grpc_event_engine::experimental::EventEngine> {};
} // namespace grpc_core
namespace grpc_event_engine {
namespace experimental {

@ -0,0 +1,31 @@
// Copyright 2024 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef GRPC_SRC_CORE_LIB_EVENT_ENGINE_EVENT_ENGINE_CONTEXT_H
#define GRPC_SRC_CORE_LIB_EVENT_ENGINE_EVENT_ENGINE_CONTEXT_H
#include <grpc/support/port_platform.h>
#include <grpc/event_engine/event_engine.h>
#include "src/core/lib/promise/context.h"
namespace grpc_core {
template <>
struct ContextType<grpc_event_engine::experimental::EventEngine> {};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_EVENT_ENGINE_EVENT_ENGINE_CONTEXT_H

@ -25,7 +25,7 @@
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_join.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
@ -49,9 +49,16 @@ struct ForcedExperiment {
bool forced = false;
bool value;
};
ForcedExperiment g_forced_experiments[kNumExperiments];
std::atomic<bool> g_loaded(false);
ForcedExperiment* ForcedExperiments() {
static NoDestruct<ForcedExperiment> forced_experiments[kNumExperiments];
return &**forced_experiments;
}
std::atomic<bool>* Loaded() {
static NoDestruct<std::atomic<bool>> loaded(false);
return &*loaded;
}
absl::AnyInvocable<bool(struct ExperimentMetadata)>* g_check_constraints_cb =
nullptr;
@ -98,7 +105,7 @@ GPR_ATTRIBUTE_NOINLINE Experiments LoadExperimentsFromConfigVariableInner() {
// Set defaults from metadata.
Experiments experiments;
for (size_t i = 0; i < kNumExperiments; i++) {
if (!g_forced_experiments[i].forced) {
if (!ForcedExperiments()[i].forced) {
if (g_check_constraints_cb != nullptr) {
experiments.enabled[i] =
(*g_check_constraints_cb)(g_experiment_metadata[i]);
@ -106,7 +113,7 @@ GPR_ATTRIBUTE_NOINLINE Experiments LoadExperimentsFromConfigVariableInner() {
experiments.enabled[i] = g_experiment_metadata[i].default_value;
}
} else {
experiments.enabled[i] = g_forced_experiments[i].value;
experiments.enabled[i] = ForcedExperiments()[i].value;
}
}
// For each comma-separated experiment in the global config:
@ -151,7 +158,7 @@ GPR_ATTRIBUTE_NOINLINE Experiments LoadExperimentsFromConfigVariableInner() {
}
Experiments LoadExperimentsFromConfigVariable() {
g_loaded.store(true, std::memory_order_relaxed);
Loaded()->store(true, std::memory_order_relaxed);
return LoadExperimentsFromConfigVariableInner();
}
@ -187,54 +194,65 @@ bool IsTestExperimentEnabled(size_t experiment_id) {
}
void PrintExperimentsList() {
size_t max_experiment_length = 0;
// Populate visitation order into a std::map so that iteration results in a
// lexical ordering of experiment names.
// The lexical ordering makes it nice and easy to find the experiment you're
// looking for in the output spam that we generate.
std::map<absl::string_view, size_t> visitation_order;
std::map<std::string, std::string> experiment_status;
std::set<std::string> defaulted_on_experiments;
for (size_t i = 0; i < kNumExperiments; i++) {
max_experiment_length =
std::max(max_experiment_length, strlen(g_experiment_metadata[i].name));
visitation_order[g_experiment_metadata[i].name] = i;
const char* name = g_experiment_metadata[i].name;
const bool enabled = IsExperimentEnabled(i);
const bool default_enabled = g_experiment_metadata[i].default_value;
const bool forced = ForcedExperiments()[i].forced;
if (!default_enabled && !enabled) continue;
if (default_enabled && enabled) {
defaulted_on_experiments.insert(name);
continue;
}
if (enabled) {
if (g_check_constraints_cb != nullptr &&
(*g_check_constraints_cb)(g_experiment_metadata[i])) {
experiment_status[name] = "on:constraints";
continue;
}
if (forced && ForcedExperiments()[i].value) {
experiment_status[name] = "on:forced";
continue;
}
experiment_status[name] = "on";
} else {
if (forced && !ForcedExperiments()[i].value) {
experiment_status[name] = "off:forced";
continue;
}
experiment_status[name] = "off";
}
}
for (auto name_index : visitation_order) {
const size_t i = name_index.second;
gpr_log(
GPR_INFO, "%s",
absl::StrCat(
"gRPC EXPERIMENT ", g_experiment_metadata[i].name,
std::string(max_experiment_length -
strlen(g_experiment_metadata[i].name) + 1,
' '),
IsExperimentEnabled(i) ? "ON " : "OFF",
" (default:", g_experiment_metadata[i].default_value ? "ON" : "OFF",
(g_check_constraints_cb != nullptr
? absl::StrCat(
" + ", g_experiment_metadata[i].additional_constaints,
" => ",
(*g_check_constraints_cb)(g_experiment_metadata[i])
? "ON "
: "OFF")
: std::string()),
g_forced_experiments[i].forced
? absl::StrCat(" force:",
g_forced_experiments[i].value ? "ON" : "OFF")
: std::string(),
")")
.c_str());
if (experiment_status.empty()) {
if (!defaulted_on_experiments.empty()) {
gpr_log(GPR_INFO, "gRPC experiments enabled: %s",
absl::StrJoin(defaulted_on_experiments, ", ").c_str());
}
} else {
if (defaulted_on_experiments.empty()) {
gpr_log(GPR_INFO, "gRPC experiments: %s",
absl::StrJoin(experiment_status, ", ", absl::PairFormatter(":"))
.c_str());
} else {
gpr_log(GPR_INFO, "gRPC experiments: %s; default-enabled: %s",
absl::StrJoin(experiment_status, ", ", absl::PairFormatter(":"))
.c_str(),
absl::StrJoin(defaulted_on_experiments, ", ").c_str());
}
}
}
void ForceEnableExperiment(absl::string_view experiment, bool enable) {
GPR_ASSERT(g_loaded.load(std::memory_order_relaxed) == false);
GPR_ASSERT(Loaded()->load(std::memory_order_relaxed) == false);
for (size_t i = 0; i < kNumExperiments; i++) {
if (g_experiment_metadata[i].name != experiment) continue;
if (g_forced_experiments[i].forced) {
GPR_ASSERT(g_forced_experiments[i].value == enable);
if (ForcedExperiments()[i].forced) {
GPR_ASSERT(ForcedExperiments()[i].value == enable);
} else {
g_forced_experiments[i].forced = true;
g_forced_experiments[i].value = enable;
ForcedExperiments()[i].forced = true;
ForcedExperiments()[i].value = enable;
}
return;
}

@ -80,21 +80,21 @@
allow_in_fuzzing_config: false
- name: event_engine_client
description: Use EventEngine clients instead of iomgr's grpc_tcp_client
expiry: 2024/04/01
expiry: 2024/07/01
owner: hork@google.com
test_tags: ["core_end2end_test", "event_engine_client_test"]
uses_polling: true
- name: event_engine_dns
description:
If set, use EventEngine DNSResolver for client channel resolution
expiry: 2024/04/01
expiry: 2024/07/01
owner: yijiem@google.com
test_tags: ["cancel_ares_query_test", "resolver_component_tests_runner_invoker"]
allow_in_fuzzing_config: false
uses_polling: true
- name: event_engine_listener
description: Use EventEngine listeners instead of iomgr's grpc_tcp_server
expiry: 2024/04/01
expiry: 2024/07/01
owner: vigneshbabu@google.com
test_tags: ["core_end2end_test", "event_engine_listener_test"]
uses_polling: true

@ -31,6 +31,7 @@
#include <grpc/support/log.h>
#include "src/core/lib/event_engine/event_engine_context.h"
#include "src/core/lib/gprpp/construct_destruct.h"
#include "src/core/lib/gprpp/no_destruct.h"
#include "src/core/lib/gprpp/orphanable.h"

@ -20,7 +20,7 @@
#include <grpc/event_engine/event_engine.h>
#include "src/core/lib/event_engine/default_event_engine.h" // IWYU pragma: keep
#include "src/core/lib/event_engine/event_engine_context.h" // IWYU pragma: keep
#include "src/core/lib/gprpp/time.h"
#include "src/core/lib/iomgr/exec_ctx.h"
#include "src/core/lib/promise/activity.h"

@ -588,6 +588,12 @@ class FilterStackCall final : public Call {
}
private:
class ScopedContext : public promise_detail::Context<Arena> {
public:
explicit ScopedContext(FilterStackCall* call)
: promise_detail::Context<Arena>(call->arena()) {}
};
static constexpr gpr_atm kRecvNone = 0;
static constexpr gpr_atm kRecvInitialMetadataFirst = 1;
@ -807,6 +813,7 @@ grpc_error_handle FilterStackCall::Create(grpc_call_create_args* args,
GPR_DEBUG_ASSERT(FromCallStack(call->call_stack()) == call);
*out_call = call->c_ptr();
grpc_slice path = grpc_empty_slice();
ScopedContext ctx(call);
if (call->is_client()) {
call->final_op_.client.status_details = nullptr;
call->final_op_.client.status = nullptr;
@ -822,6 +829,8 @@ grpc_error_handle FilterStackCall::Create(grpc_call_create_args* args,
call->send_initial_metadata_.Set(
GrpcRegisteredMethod(), reinterpret_cast<void*>(static_cast<uintptr_t>(
args->registered_method)));
channel_stack->stats_plugin_group->AddClientCallTracers(
Slice(CSliceRef(path)), args->registered_method, call->context_);
} else {
global_stats().IncrementServerCallsCreated();
call->final_op_.server.cancelled = nullptr;
@ -830,6 +839,9 @@ grpc_error_handle FilterStackCall::Create(grpc_call_create_args* args,
// collecting from when the call is created at the transport. The idea is
// that the transport would create the call tracer and pass it in as part of
// the metadata.
// TODO(yijiem): OpenCensus and internal Census is still using this way to
// set server call tracer. We need to refactor them to stats plugins
// (including removing the client channel filters).
if (args->server != nullptr &&
args->server->server_call_tracer_factory() != nullptr) {
auto* server_call_tracer =
@ -846,6 +858,7 @@ grpc_error_handle FilterStackCall::Create(grpc_call_create_args* args,
call->ContextSet(GRPC_CONTEXT_CALL_TRACER, server_call_tracer, nullptr);
}
}
channel_stack->stats_plugin_group->AddServerCallTracers(call->context_);
}
Call* parent = Call::FromC(args->parent);
@ -2725,6 +2738,8 @@ class ClientPromiseBasedCall final : public PromiseBasedCall {
"non-nullptr.");
}
ScopedContext context(this);
args->channel->channel_stack()->stats_plugin_group->AddClientCallTracers(
*args->path, args->registered_method, this->context());
send_initial_metadata_ =
GetContext<Arena>()->MakePooled<ClientMetadata>(GetContext<Arena>());
send_initial_metadata_->Set(HttpPathMetadata(), std::move(*args->path));
@ -3390,11 +3405,16 @@ ServerPromiseBasedCall::ServerPromiseBasedCall(Arena* arena,
if (channelz_node != nullptr) {
channelz_node->RecordCallStarted();
}
ScopedContext activity_context(this);
// TODO(yashykt): In the future, we want to also enable stats and trace
// collecting from when the call is created at the transport. The idea is that
// the transport would create the call tracer and pass it in as part of the
// metadata.
if (args->server->server_call_tracer_factory() != nullptr) {
// TODO(yijiem): OpenCensus and internal Census is still using this way to
// set server call tracer. We need to refactor them to stats plugins
// (including removing the client channel filters).
if (args->server != nullptr &&
args->server->server_call_tracer_factory() != nullptr) {
auto* server_call_tracer =
args->server->server_call_tracer_factory()->CreateNewServerCallTracer(
arena, args->server->channel_args());
@ -3409,7 +3429,8 @@ ServerPromiseBasedCall::ServerPromiseBasedCall(Arena* arena,
ContextSet(GRPC_CONTEXT_CALL_TRACER, server_call_tracer, nullptr);
}
}
ScopedContext activity_context(this);
args->channel->channel_stack()->stats_plugin_group->AddServerCallTracers(
context());
Spawn("server_promise",
channel()->channel_stack()->MakeServerCallPromise(
CallArgs{nullptr, ClientInitialMetadataOutstandingToken::Empty(),

@ -89,12 +89,17 @@ absl::StatusOr<OrphanablePtr<Channel>> LegacyChannel::Create(
status.ToString().c_str());
return status;
}
// TODO(roth): Figure out how to populate authority here.
// Or maybe just don't worry about this if no one needs it until after
// the call v3 stack lands.
StatsPlugin::ChannelScope scope(builder.target(), "");
*(*r)->stats_plugin_group =
GlobalStatsPluginRegistry::GetStatsPluginsForChannel(scope);
if (channel_stack_type == GRPC_SERVER_CHANNEL) {
*(*r)->stats_plugin_group =
GlobalStatsPluginRegistry::GetStatsPluginsForServer(args);
} else {
// TODO(roth): Figure out how to populate authority here.
// Or maybe just don't worry about this if no one needs it until after
// the call v3 stack lands.
StatsPlugin::ChannelScope scope(target, "");
*(*r)->stats_plugin_group =
GlobalStatsPluginRegistry::GetStatsPluginsForChannel(scope);
}
return MakeOrphanable<LegacyChannel>(
grpc_channel_stack_type_is_client(builder.channel_stack_type()),
builder.IsPromising(), std::move(target), args, std::move(*r));

@ -20,6 +20,7 @@
#include <grpc/grpc.h>
#include "src/core/lib/channel/server_call_tracer_filter.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/core/lib/surface/lame_client.h"
@ -27,7 +28,6 @@
#include "src/core/lib/transport/http_connect_handshaker.h"
#include "src/core/lib/transport/tcp_connect_handshaker.h"
namespace grpc_event_engine {
namespace experimental {
extern void RegisterEventEngineChannelArgPreconditioning(

@ -34,10 +34,8 @@
#include "absl/strings/strip.h"
#include "absl/types/optional.h"
#include "absl/types/variant.h"
#include "google/protobuf/struct.upb.h"
#include "opentelemetry/sdk/resource/semantic_conventions.h"
#include "upb/base/string_view.h"
#include "upb/mem/arena.hpp"
#include <grpc/slice.h>
@ -91,7 +89,24 @@ constexpr absl::string_view kPeerCanonicalServiceAttribute =
constexpr absl::string_view kGkeType = "gcp_kubernetes_engine";
constexpr absl::string_view kGceType = "gcp_compute_engine";
enum class GcpResourceType : std::uint8_t { kGke, kGce, kUnknown };
// A helper method that decodes the remote metadata \a slice as a protobuf
// Struct allocated on \a arena.
google_protobuf_Struct* DecodeMetadata(grpc_core::Slice slice,
upb_Arena* arena) {
// Treat an empty slice as an invalid metadata value.
if (slice.empty()) {
return nullptr;
}
// Decode the slice.
std::string decoded_metadata;
bool metadata_decoded =
absl::Base64Unescape(slice.as_string_view(), &decoded_metadata);
if (metadata_decoded) {
return google_protobuf_Struct_parse(decoded_metadata.c_str(),
decoded_metadata.size(), arena);
}
return nullptr;
}
// A minimal class for helping with the information we need from the xDS
// bootstrap file for GSM Observability reasons.
@ -145,13 +160,14 @@ std::string GetXdsBootstrapContents() {
return "";
}
GcpResourceType StringToGcpResourceType(absl::string_view type) {
MeshLabelsIterable::GcpResourceType StringToGcpResourceType(
absl::string_view type) {
if (type == kGkeType) {
return GcpResourceType::kGke;
return MeshLabelsIterable::GcpResourceType::kGke;
} else if (type == kGceType) {
return GcpResourceType::kGce;
return MeshLabelsIterable::GcpResourceType::kGce;
}
return GcpResourceType::kUnknown;
return MeshLabelsIterable::GcpResourceType::kUnknown;
}
upb_StringView AbslStrToUpbStr(absl::string_view str) {
@ -203,147 +219,96 @@ absl::string_view GetStringValueFromUpbStruct(google_protobuf_Struct* struct_pb,
return "unknown";
}
class MeshLabelsIterable : public LabelsIterable {
public:
explicit MeshLabelsIterable(
const std::vector<std::pair<absl::string_view, std::string>>&
local_labels,
grpc_core::Slice remote_metadata)
: local_labels_(local_labels), metadata_(std::move(remote_metadata)) {}
absl::optional<std::pair<absl::string_view, absl::string_view>> Next()
override {
auto& struct_pb = GetDecodedMetadata();
size_t local_labels_size = local_labels_.size();
if (pos_ < local_labels_size) {
return local_labels_[pos_++];
}
const size_t fixed_attribute_end =
local_labels_size + kFixedAttributes.size();
if (pos_ < fixed_attribute_end) {
return NextFromAttributeList(struct_pb, kFixedAttributes,
local_labels_size);
}
return NextFromAttributeList(struct_pb, GetAttributesForType(remote_type_),
fixed_attribute_end);
}
size_t Size() const override {
return local_labels_.size() + kFixedAttributes.size() +
GetAttributesForType(remote_type_).size();
}
struct RemoteAttribute {
absl::string_view otel_attribute;
absl::string_view metadata_attribute;
};
void ResetIteratorPosition() override { pos_ = 0; }
constexpr std::array<RemoteAttribute, 2> kFixedAttributes = {
RemoteAttribute{kPeerTypeAttribute, kMetadataExchangeTypeKey},
RemoteAttribute{kPeerCanonicalServiceAttribute,
kMetadataExchangeCanonicalServiceKey},
};
// Returns true if the peer sent a non-empty base64 encoded
// "x-envoy-peer-metadata" metadata.
bool GotRemoteLabels() const {
return GetDecodedMetadata().struct_pb != nullptr;
}
constexpr std::array<RemoteAttribute, 5> kGkeAttributeList = {
RemoteAttribute{kPeerWorkloadNameAttribute,
kMetadataExchangeWorkloadNameKey},
RemoteAttribute{kPeerNamespaceNameAttribute,
kMetadataExchangeNamespaceNameKey},
RemoteAttribute{kPeerClusterNameAttribute, kMetadataExchangeClusterNameKey},
RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey},
RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey},
};
private:
struct RemoteAttribute {
absl::string_view otel_attribute;
absl::string_view metadata_attribute;
};
constexpr std::array<RemoteAttribute, 3> kGceAttributeList = {
RemoteAttribute{kPeerWorkloadNameAttribute,
kMetadataExchangeWorkloadNameKey},
RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey},
RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey},
};
struct StructPb {
upb::Arena arena;
google_protobuf_Struct* struct_pb = nullptr;
};
absl::Span<const RemoteAttribute> GetAttributesForType(
MeshLabelsIterable::GcpResourceType remote_type) {
switch (remote_type) {
case MeshLabelsIterable::GcpResourceType::kGke:
return kGkeAttributeList;
case MeshLabelsIterable::GcpResourceType::kGce:
return kGceAttributeList;
default:
return {};
}
}
static constexpr std::array<RemoteAttribute, 2> kFixedAttributes = {
RemoteAttribute{kPeerTypeAttribute, kMetadataExchangeTypeKey},
RemoteAttribute{kPeerCanonicalServiceAttribute,
kMetadataExchangeCanonicalServiceKey},
};
absl::optional<std::pair<absl::string_view, absl::string_view>>
NextFromAttributeList(absl::Span<const RemoteAttribute> attributes,
size_t start_index, size_t curr,
google_protobuf_Struct* decoded_metadata,
upb_Arena* arena) {
GPR_DEBUG_ASSERT(curr >= start_index);
const size_t index = curr - start_index;
if (index >= attributes.size()) return absl::nullopt;
const auto& attribute = attributes[index];
return std::make_pair(
attribute.otel_attribute,
GetStringValueFromUpbStruct(decoded_metadata,
attribute.metadata_attribute, arena));
}
static constexpr std::array<RemoteAttribute, 5> kGkeAttributeList = {
RemoteAttribute{kPeerWorkloadNameAttribute,
kMetadataExchangeWorkloadNameKey},
RemoteAttribute{kPeerNamespaceNameAttribute,
kMetadataExchangeNamespaceNameKey},
RemoteAttribute{kPeerClusterNameAttribute,
kMetadataExchangeClusterNameKey},
RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey},
RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey},
};
static constexpr std::array<RemoteAttribute, 3> kGceAttributeList = {
RemoteAttribute{kPeerWorkloadNameAttribute,
kMetadataExchangeWorkloadNameKey},
RemoteAttribute{kPeerLocationAttribute, kMetadataExchangeLocationKey},
RemoteAttribute{kPeerProjectIdAttribute, kMetadataExchangeProjectIdKey},
};
} // namespace
static absl::Span<const RemoteAttribute> GetAttributesForType(
GcpResourceType remote_type) {
switch (remote_type) {
case GcpResourceType::kGke:
return kGkeAttributeList;
case GcpResourceType::kGce:
return kGceAttributeList;
default:
return {};
}
}
//
// MeshLabelsIterable
//
absl::optional<std::pair<absl::string_view, absl::string_view>>
NextFromAttributeList(const StructPb& struct_pb,
absl::Span<const RemoteAttribute> attributes,
size_t start_index) {
GPR_DEBUG_ASSERT(pos_ >= start_index);
const size_t index = pos_ - start_index;
if (index >= attributes.size()) return absl::nullopt;
++pos_;
const auto& attribute = attributes[index];
return std::make_pair(attribute.otel_attribute,
GetStringValueFromUpbStruct(
struct_pb.struct_pb, attribute.metadata_attribute,
struct_pb.arena.ptr()));
MeshLabelsIterable::MeshLabelsIterable(
const std::vector<std::pair<absl::string_view, std::string>>& local_labels,
grpc_core::Slice remote_metadata)
: struct_pb_(DecodeMetadata(std::move(remote_metadata), arena_.ptr())),
local_labels_(local_labels),
remote_type_(StringToGcpResourceType(GetStringValueFromUpbStruct(
struct_pb_, kMetadataExchangeTypeKey, arena_.ptr()))) {}
absl::optional<std::pair<absl::string_view, absl::string_view>>
MeshLabelsIterable::Next() {
size_t local_labels_size = local_labels_.size();
if (pos_ < local_labels_size) {
return local_labels_[pos_++];
}
StructPb& GetDecodedMetadata() const {
auto* slice = absl::get_if<grpc_core::Slice>(&metadata_);
if (slice == nullptr) {
return absl::get<StructPb>(metadata_);
}
// Treat an empty slice as an invalid metadata value.
if (slice->empty()) {
metadata_ = StructPb{};
auto& struct_pb = absl::get<StructPb>(metadata_);
return struct_pb;
}
std::string decoded_metadata;
bool metadata_decoded =
absl::Base64Unescape(slice->as_string_view(), &decoded_metadata);
metadata_ = StructPb{};
auto& struct_pb = absl::get<StructPb>(metadata_);
if (metadata_decoded) {
struct_pb.struct_pb = google_protobuf_Struct_parse(
decoded_metadata.c_str(), decoded_metadata.size(),
struct_pb.arena.ptr());
remote_type_ = StringToGcpResourceType(GetStringValueFromUpbStruct(
struct_pb.struct_pb, kMetadataExchangeTypeKey,
struct_pb.arena.ptr()));
}
return struct_pb;
const size_t fixed_attribute_end =
local_labels_size + kFixedAttributes.size();
if (pos_ < fixed_attribute_end) {
return NextFromAttributeList(kFixedAttributes, local_labels_size, pos_++,
struct_pb_, arena_.ptr());
}
return NextFromAttributeList(GetAttributesForType(remote_type_),
fixed_attribute_end, pos_++, struct_pb_,
arena_.ptr());
}
const std::vector<std::pair<absl::string_view, std::string>>& local_labels_;
// Holds either the metadata slice or the decoded proto struct.
mutable absl::variant<grpc_core::Slice, StructPb> metadata_;
mutable GcpResourceType remote_type_ = GcpResourceType::kUnknown;
uint32_t pos_ = 0;
};
constexpr std::array<MeshLabelsIterable::RemoteAttribute, 2>
MeshLabelsIterable::kFixedAttributes;
constexpr std::array<MeshLabelsIterable::RemoteAttribute, 5>
MeshLabelsIterable::kGkeAttributeList;
constexpr std::array<MeshLabelsIterable::RemoteAttribute, 3>
MeshLabelsIterable::kGceAttributeList;
} // namespace
size_t MeshLabelsIterable::Size() const {
return local_labels_.size() + kFixedAttributes.size() +
GetAttributesForType(remote_type_).size();
}
// Returns the mesh ID by reading and parsing the bootstrap file. Returns
// "unknown" if for some reason, mesh ID could not be figured out.
@ -370,6 +335,10 @@ std::string GetMeshId() {
return std::string(mesh_id);
}
//
// ServiceMeshLabelsInjector
//
ServiceMeshLabelsInjector::ServiceMeshLabelsInjector(
const opentelemetry::sdk::common::AttributeMap& map) {
upb::Arena arena;

@ -27,7 +27,9 @@
#include <vector>
#include "absl/strings/string_view.h"
#include "google/protobuf/struct.upb.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "upb/mem/arena.hpp"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/transport/metadata_batch.h"
@ -66,11 +68,50 @@ class ServiceMeshLabelsInjector : public LabelsInjector {
absl::Span<const std::shared_ptr<std::map<std::string, std::string>>>
optional_labels_span) const override;
const std::vector<std::pair<absl::string_view, std::string>>&
TestOnlyLocalLabels() const {
return local_labels_;
}
const grpc_core::Slice& TestOnlySerializedLabels() const {
return serialized_labels_to_send_;
}
private:
std::vector<std::pair<absl::string_view, std::string>> local_labels_;
grpc_core::Slice serialized_labels_to_send_;
};
// A LabelsIterable class provided by ServiceMeshLabelsInjector. EXPOSED FOR
// TESTING PURPOSES ONLY.
class MeshLabelsIterable : public LabelsIterable {
public:
enum class GcpResourceType : std::uint8_t { kGke, kGce, kUnknown };
MeshLabelsIterable(
const std::vector<std::pair<absl::string_view, std::string>>&
local_labels,
grpc_core::Slice remote_metadata);
absl::optional<std::pair<absl::string_view, absl::string_view>> Next()
override;
size_t Size() const override;
void ResetIteratorPosition() override { pos_ = 0; }
// Returns true if the peer sent a non-empty base64 encoded
// "x-envoy-peer-metadata" metadata.
bool GotRemoteLabels() const { return struct_pb_ != nullptr; }
private:
upb::Arena arena_;
google_protobuf_Struct* struct_pb_ = nullptr;
const std::vector<std::pair<absl::string_view, std::string>>& local_labels_;
GcpResourceType remote_type_ = GcpResourceType::kUnknown;
uint32_t pos_ = 0;
};
// Returns the mesh ID by reading and parsing the bootstrap file. Returns
// "unknown" if for some reason, mesh ID could not be figured out.
// EXPOSED FOR TESTING PURPOSES ONLY.

@ -31,14 +31,13 @@ package(
grpc_cc_library(
name = "otel_plugin",
srcs = [
"otel_client_filter.cc",
"otel_client_call_tracer.cc",
"otel_plugin.cc",
"otel_server_call_tracer.cc",
],
hdrs = [
"key_value_iterable.h",
"otel_call_tracer.h",
"otel_client_filter.h",
"otel_client_call_tracer.h",
"otel_plugin.h",
"otel_server_call_tracer.h",
"//:include/grpcpp/ext/otel_plugin.h",
@ -77,6 +76,7 @@ grpc_cc_library(
"//src/core:context",
"//src/core:error",
"//src/core:metadata_batch",
"//src/core:metrics",
"//src/core:slice",
"//src/core:slice_buffer",
],

@ -46,23 +46,26 @@ inline opentelemetry::nostd::string_view AbslStrViewToOpenTelemetryStrView(
// An iterable class based on opentelemetry::common::KeyValueIterable that
// allows gRPC to iterate on its various sources of attributes and avoid an
// allocation in cases wherever possible.
class KeyValueIterable : public opentelemetry::common::KeyValueIterable {
class OpenTelemetryPlugin::KeyValueIterable
: public opentelemetry::common::KeyValueIterable {
public:
explicit KeyValueIterable(
KeyValueIterable(
const std::vector<std::unique_ptr<LabelsIterable>>&
injected_labels_from_plugin_options,
absl::Span<const std::pair<absl::string_view, absl::string_view>>
additional_labels,
const ActivePluginOptionsView* active_plugin_options_view,
const OpenTelemetryPlugin::ActivePluginOptionsView*
active_plugin_options_view,
absl::Span<const std::shared_ptr<std::map<std::string, std::string>>>
optional_labels_span,
bool is_client)
bool is_client, const OpenTelemetryPlugin* otel_plugin)
: injected_labels_from_plugin_options_(
injected_labels_from_plugin_options),
additional_labels_(additional_labels),
active_plugin_options_view_(active_plugin_options_view),
optional_labels_(optional_labels_span),
is_client_(is_client) {}
is_client_(is_client),
otel_plugin_(otel_plugin) {}
bool ForEachKeyValue(opentelemetry::nostd::function_ref<
bool(opentelemetry::nostd::string_view,
@ -75,7 +78,8 @@ class KeyValueIterable : public opentelemetry::common::KeyValueIterable {
size_t /*index*/) {
return plugin_option.labels_injector()->AddOptionalLabels(
is_client_, optional_labels_, callback);
})) {
},
otel_plugin_)) {
return false;
}
for (const auto& plugin_option_injected_iterable :
@ -115,7 +119,8 @@ class KeyValueIterable : public opentelemetry::common::KeyValueIterable {
size += plugin_option.labels_injector()->GetOptionalLabelsSize(
is_client_, optional_labels_);
return true;
});
},
otel_plugin_);
}
return size;
}
@ -125,10 +130,12 @@ class KeyValueIterable : public opentelemetry::common::KeyValueIterable {
injected_labels_from_plugin_options_;
absl::Span<const std::pair<absl::string_view, absl::string_view>>
additional_labels_;
const ActivePluginOptionsView* active_plugin_options_view_;
const OpenTelemetryPlugin::ActivePluginOptionsView*
active_plugin_options_view_;
absl::Span<const std::shared_ptr<std::map<std::string, std::string>>>
optional_labels_;
bool is_client_;
const OpenTelemetryPlugin* otel_plugin_;
};
} // namespace internal

@ -0,0 +1,282 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpc/support/port_platform.h>
#include "src/cpp/ext/otel/otel_client_call_tracer.h"
#include <stdint.h>
#include <array>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "opentelemetry/context/context.h"
#include "opentelemetry/metrics/sync_instruments.h"
#include <grpc/status.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/client_channel/client_channel_filter.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/channel/tcp_tracer.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/cpp/ext/otel/key_value_iterable.h"
#include "src/cpp/ext/otel/otel_plugin.h"
namespace grpc {
namespace internal {
//
// OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer
//
OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::CallAttemptTracer(
const OpenTelemetryPlugin::ClientCallTracer* parent, bool arena_allocated)
: parent_(parent),
arena_allocated_(arena_allocated),
start_time_(absl::Now()) {
if (parent_->otel_plugin_->client_.attempt.started != nullptr) {
std::array<std::pair<absl::string_view, absl::string_view>, 2>
additional_labels = {
{{OpenTelemetryMethodKey(), parent_->MethodForStats()},
{OpenTelemetryTargetKey(),
parent_->scope_config_->filtered_target()}}};
// We might not have all the injected labels that we want at this point, so
// avoid recording a subset of injected labels here.
parent_->otel_plugin_->client_.attempt.started->Add(
1, KeyValueIterable(
/*injected_labels_from_plugin_options=*/{}, additional_labels,
/*active_plugin_options_view=*/nullptr,
/*optional_labels_span=*/{}, /*is_client=*/true,
parent_->otel_plugin_));
}
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordReceivedInitialMetadata(grpc_metadata_batch* recv_initial_metadata) {
parent_->scope_config_->active_plugin_options_view().ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t /*index*/) {
auto* labels_injector = plugin_option.labels_injector();
if (labels_injector != nullptr) {
injected_labels_from_plugin_options_.push_back(
labels_injector->GetLabels(recv_initial_metadata));
}
return true;
},
parent_->otel_plugin_);
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordSendInitialMetadata(grpc_metadata_batch* send_initial_metadata) {
parent_->scope_config_->active_plugin_options_view().ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t /*index*/) {
auto* labels_injector = plugin_option.labels_injector();
if (labels_injector != nullptr) {
labels_injector->AddLabels(send_initial_metadata, nullptr);
}
return true;
},
parent_->otel_plugin_);
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordSendMessage(const grpc_core::SliceBuffer& send_message) {
RecordAnnotation(
absl::StrFormat("Send message: %ld bytes", send_message.Length()));
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordSendCompressedMessage(
const grpc_core::SliceBuffer& send_compressed_message) {
RecordAnnotation(absl::StrFormat("Send compressed message: %ld bytes",
send_compressed_message.Length()));
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordReceivedMessage(const grpc_core::SliceBuffer& recv_message) {
RecordAnnotation(
absl::StrFormat("Received message: %ld bytes", recv_message.Length()));
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordReceivedDecompressedMessage(
const grpc_core::SliceBuffer& recv_decompressed_message) {
RecordAnnotation(absl::StrFormat("Received decompressed message: %ld bytes",
recv_decompressed_message.Length()));
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
RecordReceivedTrailingMetadata(
absl::Status status, grpc_metadata_batch* /*recv_trailing_metadata*/,
const grpc_transport_stream_stats* transport_stream_stats) {
std::array<std::pair<absl::string_view, absl::string_view>, 3>
additional_labels = {
{{OpenTelemetryMethodKey(), parent_->MethodForStats()},
{OpenTelemetryTargetKey(),
parent_->scope_config_->filtered_target()},
{OpenTelemetryStatusKey(),
grpc_status_code_to_string(
static_cast<grpc_status_code>(status.code()))}}};
KeyValueIterable labels(
injected_labels_from_plugin_options_, additional_labels,
&parent_->scope_config_->active_plugin_options_view(),
optional_labels_array_, /*is_client=*/true, parent_->otel_plugin_);
if (parent_->otel_plugin_->client_.attempt.duration != nullptr) {
parent_->otel_plugin_->client_.attempt.duration->Record(
absl::ToDoubleSeconds(absl::Now() - start_time_), labels,
opentelemetry::context::Context{});
}
if (parent_->otel_plugin_->client_.attempt
.sent_total_compressed_message_size != nullptr) {
parent_->otel_plugin_->client_.attempt.sent_total_compressed_message_size
->Record(transport_stream_stats != nullptr
? transport_stream_stats->outgoing.data_bytes
: 0,
labels, opentelemetry::context::Context{});
}
if (parent_->otel_plugin_->client_.attempt
.rcvd_total_compressed_message_size != nullptr) {
parent_->otel_plugin_->client_.attempt.rcvd_total_compressed_message_size
->Record(transport_stream_stats != nullptr
? transport_stream_stats->incoming.data_bytes
: 0,
labels, opentelemetry::context::Context{});
}
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::RecordCancel(
absl::Status /*cancel_error*/) {}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::RecordEnd(
const gpr_timespec& /*latency*/) {
if (arena_allocated_) {
this->~CallAttemptTracer();
} else {
delete this;
}
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::RecordAnnotation(
absl::string_view /*annotation*/) {
// Not implemented
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::RecordAnnotation(
const Annotation& /*annotation*/) {
// Not implemented
}
std::shared_ptr<grpc_core::TcpTracerInterface>
OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::StartNewTcpTrace() {
// No TCP trace.
return nullptr;
}
void OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer::
AddOptionalLabels(
OptionalLabelComponent component,
std::shared_ptr<std::map<std::string, std::string>> optional_labels) {
optional_labels_array_[static_cast<std::size_t>(component)] =
std::move(optional_labels);
}
//
// OpenTelemetryPlugin::ClientCallTracer
//
OpenTelemetryPlugin::ClientCallTracer::ClientCallTracer(
const grpc_core::Slice& path, grpc_core::Arena* arena,
bool registered_method, OpenTelemetryPlugin* otel_plugin,
std::shared_ptr<OpenTelemetryPlugin::ClientScopeConfig> scope_config)
: path_(path.Ref()),
arena_(arena),
registered_method_(registered_method),
otel_plugin_(otel_plugin),
scope_config_(std::move(scope_config)) {}
OpenTelemetryPlugin::ClientCallTracer::~ClientCallTracer() {}
OpenTelemetryPlugin::ClientCallTracer::CallAttemptTracer*
OpenTelemetryPlugin::ClientCallTracer::StartNewAttempt(
bool is_transparent_retry) {
// We allocate the first attempt on the arena and all subsequent attempts
// on the heap, so that in the common case we don't require a heap
// allocation, nor do we unnecessarily grow the arena.
bool is_first_attempt = true;
{
grpc_core::MutexLock lock(&mu_);
if (transparent_retries_ != 0 || retries_ != 0) {
is_first_attempt = false;
}
if (is_transparent_retry) {
++transparent_retries_;
} else {
++retries_;
}
}
if (is_first_attempt) {
return arena_->New<CallAttemptTracer>(this, /*arena_allocated=*/true);
}
return new CallAttemptTracer(this, /*arena_allocated=*/false);
}
absl::string_view OpenTelemetryPlugin::ClientCallTracer::MethodForStats()
const {
absl::string_view method = absl::StripPrefix(path_.as_string_view(), "/");
if (registered_method_ ||
(otel_plugin_->generic_method_attribute_filter() != nullptr &&
otel_plugin_->generic_method_attribute_filter()(method))) {
return method;
}
return "other";
}
void OpenTelemetryPlugin::ClientCallTracer::RecordAnnotation(
absl::string_view /*annotation*/) {
// Not implemented
}
void OpenTelemetryPlugin::ClientCallTracer::RecordAnnotation(
const Annotation& /*annotation*/) {
// Not implemented
}
} // namespace internal
} // namespace grpc

@ -16,8 +16,8 @@
//
//
#ifndef GRPC_SRC_CPP_EXT_OTEL_OTEL_CALL_TRACER_H
#define GRPC_SRC_CPP_EXT_OTEL_OTEL_CALL_TRACER_H
#ifndef GRPC_SRC_CPP_EXT_OTEL_OTEL_CLIENT_CALL_TRACER_H
#define GRPC_SRC_CPP_EXT_OTEL_OTEL_CLIENT_CALL_TRACER_H
#include <grpc/support/port_platform.h>
@ -42,18 +42,19 @@
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
#include "src/cpp/ext/otel/otel_client_filter.h"
#include "src/cpp/ext/otel/otel_plugin.h"
namespace grpc {
namespace internal {
class OpenTelemetryCallTracer : public grpc_core::ClientCallTracer {
class OpenTelemetryPlugin::ClientCallTracer
: public grpc_core::ClientCallTracer {
public:
class OpenTelemetryCallAttemptTracer : public CallAttemptTracer {
class CallAttemptTracer
: public grpc_core::ClientCallTracer::CallAttemptTracer {
public:
OpenTelemetryCallAttemptTracer(const OpenTelemetryCallTracer* parent,
bool arena_allocated);
CallAttemptTracer(const OpenTelemetryPlugin::ClientCallTracer* parent,
bool arena_allocated);
std::string TraceId() override {
// Not implemented
@ -96,7 +97,7 @@ class OpenTelemetryCallTracer : public grpc_core::ClientCallTracer {
optional_labels) override;
private:
const OpenTelemetryCallTracer* parent_;
const ClientCallTracer* parent_;
const bool arena_allocated_;
// Start time (for measuring latency).
absl::Time start_time_;
@ -109,11 +110,11 @@ class OpenTelemetryCallTracer : public grpc_core::ClientCallTracer {
injected_labels_from_plugin_options_;
};
explicit OpenTelemetryCallTracer(OpenTelemetryClientFilter* parent,
grpc_core::Slice path,
grpc_core::Arena* arena,
bool registered_method);
~OpenTelemetryCallTracer() override;
ClientCallTracer(
const grpc_core::Slice& path, grpc_core::Arena* arena,
bool registered_method, OpenTelemetryPlugin* otel_plugin,
std::shared_ptr<OpenTelemetryPlugin::ClientScopeConfig> scope_config);
~ClientCallTracer() override;
std::string TraceId() override {
// Not implemented
@ -130,19 +131,19 @@ class OpenTelemetryCallTracer : public grpc_core::ClientCallTracer {
return false;
}
OpenTelemetryCallAttemptTracer* StartNewAttempt(
bool is_transparent_retry) override;
CallAttemptTracer* StartNewAttempt(bool is_transparent_retry) override;
void RecordAnnotation(absl::string_view /*annotation*/) override;
void RecordAnnotation(const Annotation& /*annotation*/) override;
private:
absl::string_view MethodForStats() const;
const OpenTelemetryClientFilter* parent_;
// Client method.
grpc_core::Slice path_;
grpc_core::Arena* arena_;
const bool registered_method_;
OpenTelemetryPlugin* otel_plugin_;
std::shared_ptr<OpenTelemetryPlugin::ClientScopeConfig> scope_config_;
grpc_core::Mutex mu_;
// Non-transparent attempts per call
uint64_t retries_ ABSL_GUARDED_BY(&mu_) = 0;
@ -153,4 +154,4 @@ class OpenTelemetryCallTracer : public grpc_core::ClientCallTracer {
} // namespace internal
} // namespace grpc
#endif // GRPC_SRC_CPP_EXT_OTEL_OTEL_CALL_TRACER_H
#endif // GRPC_SRC_CPP_EXT_OTEL_OTEL_CLIENT_CALL_TRACER_H

@ -1,328 +0,0 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#include <grpc/support/port_platform.h>
#include "src/cpp/ext/otel/otel_client_filter.h"
#include <stdint.h>
#include <array>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "absl/functional/any_invocable.h"
#include "absl/status/status.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/optional.h"
#include "absl/types/span.h"
#include "opentelemetry/context/context.h"
#include "opentelemetry/metrics/sync_instruments.h"
#include <grpc/status.h>
#include <grpc/support/log.h>
#include <grpc/support/time.h>
#include "src/core/client_channel/client_channel_filter.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/channel/tcp_tracer.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/promise/context.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/core/lib/slice/slice.h"
#include "src/core/lib/slice/slice_buffer.h"
#include "src/core/lib/transport/metadata_batch.h"
#include "src/cpp/ext/otel/key_value_iterable.h"
#include "src/cpp/ext/otel/otel_call_tracer.h"
#include "src/cpp/ext/otel/otel_plugin.h"
namespace grpc {
namespace internal {
//
// OpenTelemetryClientFilter
//
const grpc_channel_filter OpenTelemetryClientFilter::kFilter =
grpc_core::MakePromiseBasedFilter<OpenTelemetryClientFilter,
grpc_core::FilterEndpoint::kClient>(
"otel_client");
absl::StatusOr<OpenTelemetryClientFilter> OpenTelemetryClientFilter::Create(
const grpc_core::ChannelArgs& args, ChannelFilter::Args /*filter_args*/) {
return OpenTelemetryClientFilter(
args.GetOwnedString(GRPC_ARG_SERVER_URI).value_or(""));
}
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle>
OpenTelemetryClientFilter::MakeCallPromise(
grpc_core::CallArgs call_args,
grpc_core::NextPromiseFactory next_promise_factory) {
auto* path = call_args.client_initial_metadata->get_pointer(
grpc_core::HttpPathMetadata());
bool registered_method = reinterpret_cast<uintptr_t>(
call_args.client_initial_metadata->get(grpc_core::GrpcRegisteredMethod())
.value_or(nullptr));
auto* call_context = grpc_core::GetContext<grpc_call_context_element>();
auto* tracer =
grpc_core::GetContext<grpc_core::Arena>()
->ManagedNew<OpenTelemetryCallTracer>(
this, path != nullptr ? path->Ref() : grpc_core::Slice(),
grpc_core::GetContext<grpc_core::Arena>(), registered_method);
GPR_DEBUG_ASSERT(
call_context[GRPC_CONTEXT_CALL_TRACER_ANNOTATION_INTERFACE].value ==
nullptr);
call_context[GRPC_CONTEXT_CALL_TRACER_ANNOTATION_INTERFACE].value = tracer;
call_context[GRPC_CONTEXT_CALL_TRACER_ANNOTATION_INTERFACE].destroy = nullptr;
return next_promise_factory(std::move(call_args));
}
OpenTelemetryClientFilter::OpenTelemetryClientFilter(std::string target)
: active_plugin_options_view_(
ActivePluginOptionsView::MakeForClient(target)) {
// Use the original target string only if a filter on the attribute is not
// registered or if the filter returns true, otherwise use "other".
if (OpenTelemetryPluginState().target_attribute_filter == nullptr ||
OpenTelemetryPluginState().target_attribute_filter(target)) {
filtered_target_ = std::move(target);
} else {
filtered_target_ = "other";
}
}
//
// OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer
//
OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
OpenTelemetryCallAttemptTracer(const OpenTelemetryCallTracer* parent,
bool arena_allocated)
: parent_(parent),
arena_allocated_(arena_allocated),
start_time_(absl::Now()) {
if (OpenTelemetryPluginState().client.attempt.started != nullptr) {
std::array<std::pair<absl::string_view, absl::string_view>, 2>
additional_labels = {
{{OpenTelemetryMethodKey(), parent_->MethodForStats()},
{OpenTelemetryTargetKey(), parent_->parent_->filtered_target()}}};
// We might not have all the injected labels that we want at this point, so
// avoid recording a subset of injected labels here.
OpenTelemetryPluginState().client.attempt.started->Add(
1, KeyValueIterable(/*injected_labels_from_plugin_options=*/{},
additional_labels,
/*active_plugin_options_view=*/nullptr,
/*optional_labels_span=*/{}, /*is_client=*/true));
}
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordReceivedInitialMetadata(grpc_metadata_batch* recv_initial_metadata) {
parent_->parent_->active_plugin_options_view().ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t /*index*/) {
auto* labels_injector = plugin_option.labels_injector();
if (labels_injector != nullptr) {
injected_labels_from_plugin_options_.push_back(
labels_injector->GetLabels(recv_initial_metadata));
}
return true;
});
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordSendInitialMetadata(grpc_metadata_batch* send_initial_metadata) {
parent_->parent_->active_plugin_options_view().ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t /*index*/) {
auto* labels_injector = plugin_option.labels_injector();
if (labels_injector != nullptr) {
labels_injector->AddLabels(send_initial_metadata, nullptr);
}
return true;
});
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::RecordSendMessage(
const grpc_core::SliceBuffer& send_message) {
RecordAnnotation(
absl::StrFormat("Send message: %ld bytes", send_message.Length()));
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordSendCompressedMessage(
const grpc_core::SliceBuffer& send_compressed_message) {
RecordAnnotation(absl::StrFormat("Send compressed message: %ld bytes",
send_compressed_message.Length()));
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordReceivedMessage(const grpc_core::SliceBuffer& recv_message) {
RecordAnnotation(
absl::StrFormat("Received message: %ld bytes", recv_message.Length()));
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordReceivedDecompressedMessage(
const grpc_core::SliceBuffer& recv_decompressed_message) {
RecordAnnotation(absl::StrFormat("Received decompressed message: %ld bytes",
recv_decompressed_message.Length()));
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::
RecordReceivedTrailingMetadata(
absl::Status status, grpc_metadata_batch* /*recv_trailing_metadata*/,
const grpc_transport_stream_stats* transport_stream_stats) {
std::array<std::pair<absl::string_view, absl::string_view>, 3>
additional_labels = {
{{OpenTelemetryMethodKey(), parent_->MethodForStats()},
{OpenTelemetryTargetKey(), parent_->parent_->filtered_target()},
{OpenTelemetryStatusKey(),
grpc_status_code_to_string(
static_cast<grpc_status_code>(status.code()))}}};
KeyValueIterable labels(injected_labels_from_plugin_options_,
additional_labels,
&parent_->parent_->active_plugin_options_view(),
optional_labels_array_, /*is_client=*/true);
if (OpenTelemetryPluginState().client.attempt.duration != nullptr) {
OpenTelemetryPluginState().client.attempt.duration->Record(
absl::ToDoubleSeconds(absl::Now() - start_time_), labels,
opentelemetry::context::Context{});
}
if (OpenTelemetryPluginState()
.client.attempt.sent_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.client.attempt.sent_total_compressed_message_size->Record(
transport_stream_stats != nullptr
? transport_stream_stats->outgoing.data_bytes
: 0,
labels, opentelemetry::context::Context{});
}
if (OpenTelemetryPluginState()
.client.attempt.rcvd_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.client.attempt.rcvd_total_compressed_message_size->Record(
transport_stream_stats != nullptr
? transport_stream_stats->incoming.data_bytes
: 0,
labels, opentelemetry::context::Context{});
}
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::RecordCancel(
absl::Status /*cancel_error*/) {}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::RecordEnd(
const gpr_timespec& /*latency*/) {
if (arena_allocated_) {
this->~OpenTelemetryCallAttemptTracer();
} else {
delete this;
}
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::RecordAnnotation(
absl::string_view /*annotation*/) {
// Not implemented
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::RecordAnnotation(
const Annotation& /*annotation*/) {
// Not implemented
}
std::shared_ptr<grpc_core::TcpTracerInterface>
OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::StartNewTcpTrace() {
// No TCP trace.
return nullptr;
}
void OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer::AddOptionalLabels(
OptionalLabelComponent component,
std::shared_ptr<std::map<std::string, std::string>> optional_labels) {
optional_labels_array_[static_cast<std::size_t>(component)] =
std::move(optional_labels);
}
//
// OpenTelemetryCallTracer
//
OpenTelemetryCallTracer::OpenTelemetryCallTracer(
OpenTelemetryClientFilter* parent, grpc_core::Slice path,
grpc_core::Arena* arena, bool registered_method)
: parent_(parent),
path_(std::move(path)),
arena_(arena),
registered_method_(registered_method) {}
OpenTelemetryCallTracer::~OpenTelemetryCallTracer() {}
OpenTelemetryCallTracer::OpenTelemetryCallAttemptTracer*
OpenTelemetryCallTracer::StartNewAttempt(bool is_transparent_retry) {
// We allocate the first attempt on the arena and all subsequent attempts
// on the heap, so that in the common case we don't require a heap
// allocation, nor do we unnecessarily grow the arena.
bool is_first_attempt = true;
{
grpc_core::MutexLock lock(&mu_);
if (transparent_retries_ != 0 || retries_ != 0) {
is_first_attempt = false;
}
if (is_transparent_retry) {
++transparent_retries_;
} else {
++retries_;
}
}
if (is_first_attempt) {
return arena_->New<OpenTelemetryCallAttemptTracer>(
this, /*arena_allocated=*/true);
}
return new OpenTelemetryCallAttemptTracer(this, /*arena_allocated=*/false);
}
absl::string_view OpenTelemetryCallTracer::MethodForStats() const {
absl::string_view method = absl::StripPrefix(path_.as_string_view(), "/");
if (registered_method_ ||
(OpenTelemetryPluginState().generic_method_attribute_filter != nullptr &&
OpenTelemetryPluginState().generic_method_attribute_filter(method))) {
return method;
}
return "other";
}
void OpenTelemetryCallTracer::RecordAnnotation(
absl::string_view /*annotation*/) {
// Not implemented
}
void OpenTelemetryCallTracer::RecordAnnotation(
const Annotation& /*annotation*/) {
// Not implemented
}
} // namespace internal
} // namespace grpc

@ -1,68 +0,0 @@
//
//
// Copyright 2023 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//
#ifndef GRPC_SRC_CPP_EXT_OTEL_OTEL_CLIENT_FILTER_H
#define GRPC_SRC_CPP_EXT_OTEL_OTEL_CLIENT_FILTER_H
#include <grpc/support/port_platform.h>
#include <string>
#include <utility>
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/promise/arena_promise.h"
#include "src/core/lib/transport/transport.h"
#include "src/cpp/ext/otel/otel_plugin.h"
namespace grpc {
namespace internal {
class OpenTelemetryClientFilter : public grpc_core::ChannelFilter {
public:
static const grpc_channel_filter kFilter;
static absl::StatusOr<OpenTelemetryClientFilter> Create(
const grpc_core::ChannelArgs& /*args*/,
ChannelFilter::Args /*filter_args*/);
grpc_core::ArenaPromise<grpc_core::ServerMetadataHandle> MakeCallPromise(
grpc_core::CallArgs call_args,
grpc_core::NextPromiseFactory next_promise_factory) override;
absl::string_view filtered_target() const { return filtered_target_; }
const ActivePluginOptionsView& active_plugin_options_view() const {
return active_plugin_options_view_;
}
private:
explicit OpenTelemetryClientFilter(std::string target);
std::string filtered_target_;
ActivePluginOptionsView active_plugin_options_view_;
};
} // namespace internal
} // namespace grpc
#endif // GRPC_SRC_CPP_EXT_OTEL_OTEL_CLIENT_FILTER_H

@ -20,11 +20,13 @@
#include "src/cpp/ext/otel/otel_plugin.h"
#include <memory>
#include <type_traits>
#include <utility>
#include "opentelemetry/metrics/meter.h"
#include "opentelemetry/metrics/meter_provider.h"
#include "opentelemetry/metrics/sync_instruments.h"
#include "opentelemetry/nostd/shared_ptr.h"
#include "opentelemetry/nostd/unique_ptr.h"
@ -37,21 +39,13 @@
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/surface/channel_stack_type.h"
#include "src/cpp/ext/otel/otel_client_filter.h"
#include "src/cpp/ext/otel/key_value_iterable.h"
#include "src/cpp/ext/otel/otel_client_call_tracer.h"
#include "src/cpp/ext/otel/otel_server_call_tracer.h"
namespace grpc {
namespace internal {
// TODO(yashykt): Extend this to allow multiple OpenTelemetry plugins to be
// registered in the same binary.
struct OpenTelemetryPluginState* g_otel_plugin_state_;
const struct OpenTelemetryPluginState& OpenTelemetryPluginState() {
GPR_DEBUG_ASSERT(g_otel_plugin_state_ != nullptr);
return *g_otel_plugin_state_;
}
absl::string_view OpenTelemetryMethodKey() { return "grpc.method"; }
absl::string_view OpenTelemetryStatusKey() { return "grpc.status"; }
@ -60,7 +54,7 @@ absl::string_view OpenTelemetryTargetKey() { return "grpc.target"; }
namespace {
absl::flat_hash_set<std::string> BaseMetrics() {
return absl::flat_hash_set<std::string>{
absl::flat_hash_set<std::string> base_metrics{
std::string(grpc::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName),
std::string(grpc::OpenTelemetryPluginBuilder::
@ -79,9 +73,67 @@ absl::flat_hash_set<std::string> BaseMetrics() {
kServerCallSentTotalCompressedMessageSizeInstrumentName),
std::string(grpc::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName)};
grpc_core::GlobalInstrumentsRegistry::ForEach(
[&](const grpc_core::GlobalInstrumentsRegistry::
GlobalInstrumentDescriptor& descriptor) {
if (descriptor.enable_by_default) {
base_metrics.emplace(descriptor.name);
}
});
return base_metrics;
}
} // namespace
class OpenTelemetryPlugin::NPCMetricsKeyValueIterable
: public opentelemetry::common::KeyValueIterable {
public:
NPCMetricsKeyValueIterable(
absl::Span<const absl::string_view> label_keys,
absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_label_keys,
absl::Span<const absl::string_view> optional_label_values,
const OptionalLabelsBitSet& optional_labels_bits)
: label_keys_(label_keys),
label_values_(label_values),
optional_label_keys_(optional_label_keys),
optional_label_values_(optional_label_values),
optional_labels_bits_(optional_labels_bits) {}
bool ForEachKeyValue(opentelemetry::nostd::function_ref<
bool(opentelemetry::nostd::string_view,
opentelemetry::common::AttributeValue)>
callback) const noexcept override {
for (size_t i = 0; i < label_keys_.size(); i++) {
if (!callback(AbslStrViewToOpenTelemetryStrView(label_keys_[i]),
AbslStrViewToOpenTelemetryStrView(label_values_[i]))) {
return false;
}
}
for (size_t i = 0; i < optional_label_keys_.size(); ++i) {
if (!optional_labels_bits_.test(i)) {
continue;
}
if (!callback(
AbslStrViewToOpenTelemetryStrView(optional_label_keys_[i]),
AbslStrViewToOpenTelemetryStrView(optional_label_values_[i]))) {
return false;
}
}
return true;
}
size_t size() const noexcept override {
return label_keys_.size() + optional_labels_bits_.count();
}
private:
absl::Span<const absl::string_view> label_keys_;
absl::Span<const absl::string_view> label_values_;
absl::Span<const absl::string_view> optional_label_keys_;
absl::Span<const absl::string_view> optional_label_values_;
const OptionalLabelsBitSet& optional_labels_bits_;
};
//
// OpenTelemetryPluginBuilderImpl
//
@ -156,112 +208,352 @@ OpenTelemetryPluginBuilderImpl& OpenTelemetryPluginBuilderImpl::AddPluginOption(
return *this;
}
OpenTelemetryPluginBuilderImpl&
OpenTelemetryPluginBuilderImpl::AddOptionalLabel(
absl::string_view optional_label_key) {
if (optional_label_keys_ == nullptr) {
optional_label_keys_ = std::make_shared<std::set<absl::string_view>>();
}
optional_label_keys_->emplace(optional_label_key);
return *this;
}
absl::Status OpenTelemetryPluginBuilderImpl::BuildAndRegisterGlobal() {
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider>
meter_provider = meter_provider_;
delete g_otel_plugin_state_;
g_otel_plugin_state_ = new struct OpenTelemetryPluginState;
if (meter_provider == nullptr) {
if (meter_provider_ == nullptr) {
return absl::OkStatus();
}
auto meter = meter_provider->GetMeter("grpc-c++", GRPC_CPP_VERSION_STRING);
if (metrics_.contains(grpc::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName)) {
g_otel_plugin_state_->client.attempt.started = meter->CreateUInt64Counter(
grpc_core::GlobalStatsPluginRegistry::RegisterStatsPlugin(
std::make_shared<OpenTelemetryPlugin>(
metrics_, meter_provider_, std::move(target_selector_),
std::move(target_attribute_filter_),
std::move(generic_method_attribute_filter_),
std::move(server_selector_), std::move(plugin_options_),
std::move(optional_label_keys_)));
return absl::OkStatus();
}
OpenTelemetryPlugin::OpenTelemetryPlugin(
const absl::flat_hash_set<std::string>& metrics,
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider>
meter_provider,
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_selector,
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter,
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter,
absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>
server_selector,
std::vector<std::unique_ptr<InternalOpenTelemetryPluginOption>>
plugin_options,
std::shared_ptr<std::set<absl::string_view>> optional_label_keys)
: meter_provider_(std::move(meter_provider)),
target_selector_(std::move(target_selector)),
server_selector_(std::move(server_selector)),
target_attribute_filter_(std::move(target_attribute_filter)),
generic_method_attribute_filter_(
std::move(generic_method_attribute_filter)),
plugin_options_(std::move(plugin_options)) {
auto meter = meter_provider_->GetMeter("grpc-c++", GRPC_CPP_VERSION_STRING);
// Per-call metrics.
if (metrics.contains(grpc::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName)) {
client_.attempt.started = meter->CreateUInt64Counter(
std::string(grpc::OpenTelemetryPluginBuilder::
kClientAttemptStartedInstrumentName),
"Number of client call attempts started", "{attempt}");
}
if (metrics_.contains(grpc::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName)) {
g_otel_plugin_state_->client.attempt.duration =
meter->CreateDoubleHistogram(
std::string(grpc::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName),
"End-to-end time taken to complete a client call attempt", "s");
if (metrics.contains(grpc::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName)) {
client_.attempt.duration = meter->CreateDoubleHistogram(
std::string(grpc::OpenTelemetryPluginBuilder::
kClientAttemptDurationInstrumentName),
"End-to-end time taken to complete a client call attempt", "s");
}
if (metrics_.contains(
if (metrics.contains(
grpc::OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->client.attempt.sent_total_compressed_message_size =
client_.attempt.sent_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
grpc::OpenTelemetryPluginBuilder::
kClientAttemptSentTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes sent per client call attempt", "By");
}
if (metrics_.contains(
if (metrics.contains(
grpc::OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->client.attempt.rcvd_total_compressed_message_size =
client_.attempt.rcvd_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
grpc::OpenTelemetryPluginBuilder::
kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes received per call attempt", "By");
}
if (metrics_.contains(
if (metrics.contains(
grpc::OpenTelemetryPluginBuilder::kServerCallStartedInstrumentName)) {
g_otel_plugin_state_->server.call.started = meter->CreateUInt64Counter(
server_.call.started = meter->CreateUInt64Counter(
std::string(
grpc::OpenTelemetryPluginBuilder::kServerCallStartedInstrumentName),
"Number of server calls started", "{call}");
}
if (metrics_.contains(grpc::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName)) {
g_otel_plugin_state_->server.call.duration = meter->CreateDoubleHistogram(
if (metrics.contains(grpc::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName)) {
server_.call.duration = meter->CreateDoubleHistogram(
std::string(grpc::OpenTelemetryPluginBuilder::
kServerCallDurationInstrumentName),
"End-to-end time taken to complete a call from server transport's "
"perspective",
"s");
}
if (metrics_.contains(
if (metrics.contains(
grpc::OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->server.call.sent_total_compressed_message_size =
server_.call.sent_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
grpc::OpenTelemetryPluginBuilder::
kServerCallSentTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes sent per server call", "By");
}
if (metrics_.contains(
if (metrics.contains(
grpc::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName)) {
g_otel_plugin_state_->server.call.rcvd_total_compressed_message_size =
server_.call.rcvd_total_compressed_message_size =
meter->CreateUInt64Histogram(
std::string(
grpc::OpenTelemetryPluginBuilder::
kServerCallRcvdTotalCompressedMessageSizeInstrumentName),
"Compressed message bytes received per server call", "By");
}
g_otel_plugin_state_->target_attribute_filter =
std::move(target_attribute_filter_);
g_otel_plugin_state_->server_selector = std::move(server_selector_);
g_otel_plugin_state_->generic_method_attribute_filter =
std::move(generic_method_attribute_filter_);
g_otel_plugin_state_->meter_provider = std::move(meter_provider);
g_otel_plugin_state_->plugin_options = std::move(plugin_options_);
grpc_core::ServerCallTracerFactory::RegisterGlobal(
new grpc::internal::OpenTelemetryServerCallTracerFactory());
grpc_core::CoreConfiguration::RegisterBuilder(
[target_selector = std::move(target_selector_)](
grpc_core::CoreConfiguration::Builder* builder) mutable {
builder->channel_init()
->RegisterFilter(
GRPC_CLIENT_CHANNEL,
&grpc::internal::OpenTelemetryClientFilter::kFilter)
.If([target_selector = std::move(target_selector)](
const grpc_core::ChannelArgs& args) {
// Only register the filter if no channel selector has been set or
// the target selector returns true for the target.
return target_selector == nullptr ||
target_selector(
args.GetString(GRPC_ARG_SERVER_URI).value_or(""));
});
// Non-per-call metrics.
grpc_core::GlobalInstrumentsRegistry::ForEach(
[&, this](const grpc_core::GlobalInstrumentsRegistry::
GlobalInstrumentDescriptor& descriptor) {
GPR_ASSERT(descriptor.optional_label_keys.size() <=
kOptionalLabelsSizeLimit);
if (instruments_data_.size() < descriptor.index + 1) {
instruments_data_.resize(descriptor.index + 1);
}
if (!metrics.contains(descriptor.name)) {
return;
}
switch (descriptor.instrument_type) {
case grpc_core::GlobalInstrumentsRegistry::InstrumentType::kCounter:
switch (descriptor.value_type) {
case grpc_core::GlobalInstrumentsRegistry::ValueType::kUInt64:
instruments_data_[descriptor.index].instrument =
meter->CreateUInt64Counter(
std::string(descriptor.name),
std::string(descriptor.description),
std::string(descriptor.unit));
break;
case grpc_core::GlobalInstrumentsRegistry::ValueType::kDouble:
instruments_data_[descriptor.index].instrument =
meter->CreateDoubleCounter(
std::string(descriptor.name),
std::string(descriptor.description),
std::string(descriptor.unit));
break;
default:
grpc_core::Crash(
absl::StrFormat("Unknown or unsupported value type: %d",
descriptor.value_type));
}
break;
case grpc_core::GlobalInstrumentsRegistry::InstrumentType::kHistogram:
switch (descriptor.value_type) {
case grpc_core::GlobalInstrumentsRegistry::ValueType::kUInt64:
instruments_data_[descriptor.index].instrument =
meter->CreateUInt64Histogram(
std::string(descriptor.name),
std::string(descriptor.description),
std::string(descriptor.unit));
break;
case grpc_core::GlobalInstrumentsRegistry::ValueType::kDouble:
instruments_data_[descriptor.index].instrument =
meter->CreateDoubleHistogram(
std::string(descriptor.name),
std::string(descriptor.description),
std::string(descriptor.unit));
break;
default:
grpc_core::Crash(
absl::StrFormat("Unknown or unsupported value type: %d",
descriptor.value_type));
}
break;
// TODO(yashkt, yijiem): implement gauges.
case grpc_core::GlobalInstrumentsRegistry::InstrumentType::kGauge:
switch (descriptor.value_type) {
case grpc_core::GlobalInstrumentsRegistry::ValueType::kInt64:
break;
case grpc_core::GlobalInstrumentsRegistry::ValueType::kDouble:
break;
default:
grpc_core::Crash(
absl::StrFormat("Unknown or unsupported value type: %d",
descriptor.value_type));
}
break;
case grpc_core::GlobalInstrumentsRegistry::InstrumentType::
kCallbackGauge:
switch (descriptor.value_type) {
case grpc_core::GlobalInstrumentsRegistry::ValueType::kInt64:
break;
case grpc_core::GlobalInstrumentsRegistry::ValueType::kDouble:
break;
default:
grpc_core::Crash(
absl::StrFormat("Unknown or unsupported value type: %d",
descriptor.value_type));
}
break;
default:
grpc_core::Crash(absl::StrFormat("Unknown instrument_type: %d",
descriptor.instrument_type));
}
for (size_t i = 0; i < descriptor.optional_label_keys.size(); ++i) {
if (optional_label_keys->find(descriptor.optional_label_keys[i]) !=
optional_label_keys->end()) {
instruments_data_[descriptor.index].optional_labels_bits.set(i);
}
}
});
return absl::OkStatus();
}
std::pair<bool, std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig>>
OpenTelemetryPlugin::IsEnabledForChannel(const ChannelScope& scope) const {
if (target_selector_ == nullptr || target_selector_(scope.target())) {
return {true, std::make_shared<ClientScopeConfig>(this, scope)};
}
return {false, nullptr};
}
std::pair<bool, std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig>>
OpenTelemetryPlugin::IsEnabledForServer(
const grpc_core::ChannelArgs& args) const {
// Return true only if there is no server selector registered or if the server
// selector returns true.
if (server_selector_ == nullptr || server_selector_(args)) {
return {true, std::make_shared<ServerScopeConfig>(this, args)};
}
return {false, nullptr};
}
void OpenTelemetryPlugin::AddCounter(
grpc_core::GlobalInstrumentsRegistry::GlobalUInt64CounterHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
const auto& instrument_data = instruments_data_.at(handle.index);
if (absl::holds_alternative<Disabled>(instrument_data.instrument)) {
// This instrument is disabled.
return;
}
GPR_ASSERT(absl::holds_alternative<
std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>>>(
instrument_data.instrument));
const auto& descriptor =
grpc_core::GlobalInstrumentsRegistry::GetInstrumentDescriptor(handle);
GPR_ASSERT(descriptor.label_keys.size() == label_values.size());
GPR_ASSERT(descriptor.optional_label_keys.size() == optional_values.size());
absl::get<std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>>>(
instrument_data.instrument)
->Add(value, NPCMetricsKeyValueIterable(
descriptor.label_keys, label_values,
descriptor.optional_label_keys, optional_values,
instrument_data.optional_labels_bits));
}
void OpenTelemetryPlugin::AddCounter(
grpc_core::GlobalInstrumentsRegistry::GlobalDoubleCounterHandle handle,
double value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
const auto& instrument_data = instruments_data_.at(handle.index);
if (absl::holds_alternative<Disabled>(instrument_data.instrument)) {
// This instrument is disabled.
return;
}
GPR_ASSERT(absl::holds_alternative<
std::unique_ptr<opentelemetry::metrics::Counter<double>>>(
instrument_data.instrument));
const auto& descriptor =
grpc_core::GlobalInstrumentsRegistry::GetInstrumentDescriptor(handle);
GPR_ASSERT(descriptor.label_keys.size() == label_values.size());
GPR_ASSERT(descriptor.optional_label_keys.size() == optional_values.size());
absl::get<std::unique_ptr<opentelemetry::metrics::Counter<double>>>(
instrument_data.instrument)
->Add(value, NPCMetricsKeyValueIterable(
descriptor.label_keys, label_values,
descriptor.optional_label_keys, optional_values,
instrument_data.optional_labels_bits));
}
void OpenTelemetryPlugin::RecordHistogram(
grpc_core::GlobalInstrumentsRegistry::GlobalUInt64HistogramHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
const auto& instrument_data = instruments_data_.at(handle.index);
if (absl::holds_alternative<Disabled>(instrument_data.instrument)) {
// This instrument is disabled.
return;
}
GPR_ASSERT(absl::holds_alternative<
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>>(
instrument_data.instrument));
const auto& descriptor =
grpc_core::GlobalInstrumentsRegistry::GetInstrumentDescriptor(handle);
GPR_ASSERT(descriptor.label_keys.size() == label_values.size());
GPR_ASSERT(descriptor.optional_label_keys.size() == optional_values.size());
absl::get<std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>>(
instrument_data.instrument)
->Record(value,
NPCMetricsKeyValueIterable(descriptor.label_keys, label_values,
descriptor.optional_label_keys,
optional_values,
instrument_data.optional_labels_bits),
opentelemetry::context::Context{});
}
void OpenTelemetryPlugin::RecordHistogram(
grpc_core::GlobalInstrumentsRegistry::GlobalDoubleHistogramHandle handle,
double value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) {
const auto& instrument_data = instruments_data_.at(handle.index);
if (absl::holds_alternative<Disabled>(instrument_data.instrument)) {
// This instrument is disabled.
return;
}
GPR_ASSERT(absl::holds_alternative<
std::unique_ptr<opentelemetry::metrics::Histogram<double>>>(
instrument_data.instrument));
const auto& descriptor =
grpc_core::GlobalInstrumentsRegistry::GetInstrumentDescriptor(handle);
GPR_ASSERT(descriptor.label_keys.size() == label_values.size());
GPR_ASSERT(descriptor.optional_label_keys.size() == optional_values.size());
absl::get<std::unique_ptr<opentelemetry::metrics::Histogram<double>>>(
instrument_data.instrument)
->Record(value,
NPCMetricsKeyValueIterable(descriptor.label_keys, label_values,
descriptor.optional_label_keys,
optional_values,
instrument_data.optional_labels_bits),
opentelemetry::context::Context{});
}
grpc_core::ClientCallTracer* OpenTelemetryPlugin::GetClientCallTracer(
const grpc_core::Slice& path, bool registered_method,
std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig> scope_config) {
return grpc_core::GetContext<grpc_core::Arena>()
->ManagedNew<ClientCallTracer>(
path, grpc_core::GetContext<grpc_core::Arena>(), registered_method,
this,
std::static_pointer_cast<OpenTelemetryPlugin::ClientScopeConfig>(
scope_config));
}
grpc_core::ServerCallTracer* OpenTelemetryPlugin::GetServerCallTracer(
std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig> scope_config) {
return grpc_core::GetContext<grpc_core::Arena>()
->ManagedNew<ServerCallTracer>(
this,
std::static_pointer_cast<OpenTelemetryPlugin::ServerScopeConfig>(
scope_config));
}
} // namespace internal
@ -324,6 +616,12 @@ OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::AddPluginOption(
return *this;
}
OpenTelemetryPluginBuilder& OpenTelemetryPluginBuilder::AddOptionalLabel(
absl::string_view optional_label_key) {
impl_->AddOptionalLabel(optional_label_key);
return *this;
}
absl::Status OpenTelemetryPluginBuilder::BuildAndRegisterGlobal() {
return impl_->BuildAndRegisterGlobal();
}

@ -40,6 +40,7 @@
#include <grpcpp/ext/otel_plugin.h>
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/metrics.h"
#include "src/core/lib/transport/metadata_batch.h"
namespace grpc {
@ -112,41 +113,6 @@ class InternalOpenTelemetryPluginOption
virtual const grpc::internal::LabelsInjector* labels_injector() const = 0;
};
struct OpenTelemetryPluginState {
struct Client {
struct Attempt {
std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>> started;
std::unique_ptr<opentelemetry::metrics::Histogram<double>> duration;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
sent_total_compressed_message_size;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
rcvd_total_compressed_message_size;
} attempt;
} client;
struct Server {
struct Call {
std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>> started;
std::unique_ptr<opentelemetry::metrics::Histogram<double>> duration;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
sent_total_compressed_message_size;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
rcvd_total_compressed_message_size;
} call;
} server;
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider>
meter_provider;
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter;
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter;
absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>
server_selector;
std::vector<std::unique_ptr<InternalOpenTelemetryPluginOption>>
plugin_options;
};
const struct OpenTelemetryPluginState& OpenTelemetryPluginState();
// Tags
absl::string_view OpenTelemetryMethodKey();
absl::string_view OpenTelemetryStatusKey();
@ -202,6 +168,9 @@ class OpenTelemetryPluginBuilderImpl {
generic_method_attribute_filter);
OpenTelemetryPluginBuilderImpl& AddPluginOption(
std::unique_ptr<InternalOpenTelemetryPluginOption> option);
// Records \a optional_label_key on all metrics that provide it.
OpenTelemetryPluginBuilderImpl& AddOptionalLabel(
absl::string_view optional_label_key);
absl::Status BuildAndRegisterGlobal();
private:
@ -217,53 +186,232 @@ class OpenTelemetryPluginBuilderImpl {
server_selector_;
std::vector<std::unique_ptr<InternalOpenTelemetryPluginOption>>
plugin_options_;
std::shared_ptr<std::set<absl::string_view>> optional_label_keys_;
};
// Creates a convenience wrapper to help iterate over only those plugin options
// that are active over a given channel/server.
class ActivePluginOptionsView {
class OpenTelemetryPlugin : public grpc_core::StatsPlugin {
public:
static ActivePluginOptionsView MakeForClient(absl::string_view target) {
return ActivePluginOptionsView(
[target](const InternalOpenTelemetryPluginOption& plugin_option) {
return plugin_option.IsActiveOnClientChannel(target);
});
}
OpenTelemetryPlugin(
const absl::flat_hash_set<std::string>& metrics,
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider>
meter_provider,
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_selector,
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter,
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter,
absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>
server_selector,
std::vector<std::unique_ptr<InternalOpenTelemetryPluginOption>>
plugin_options,
std::shared_ptr<std::set<absl::string_view>> optional_label_keys);
static ActivePluginOptionsView MakeForServer(
const grpc_core::ChannelArgs& args) {
return ActivePluginOptionsView(
[&args](const InternalOpenTelemetryPluginOption& plugin_option) {
return plugin_option.IsActiveOnServer(args);
});
}
private:
class ClientCallTracer;
class KeyValueIterable;
class NPCMetricsKeyValueIterable;
class ServerCallTracer;
// Creates a convenience wrapper to help iterate over only those plugin
// options that are active over a given channel/server.
class ActivePluginOptionsView {
public:
static ActivePluginOptionsView MakeForClient(
absl::string_view target, const OpenTelemetryPlugin* otel_plugin) {
return ActivePluginOptionsView(
[target](const InternalOpenTelemetryPluginOption& plugin_option) {
return plugin_option.IsActiveOnClientChannel(target);
},
otel_plugin);
}
static ActivePluginOptionsView MakeForServer(
const grpc_core::ChannelArgs& args,
const OpenTelemetryPlugin* otel_plugin) {
return ActivePluginOptionsView(
[&args](const InternalOpenTelemetryPluginOption& plugin_option) {
return plugin_option.IsActiveOnServer(args);
},
otel_plugin);
}
bool ForEach(
absl::FunctionRef<bool(const InternalOpenTelemetryPluginOption&, size_t)>
func) const {
for (size_t i = 0; i < OpenTelemetryPluginState().plugin_options.size();
++i) {
const auto& plugin_option = OpenTelemetryPluginState().plugin_options[i];
if (active_mask_[i] && !func(*plugin_option, i)) {
return false;
bool ForEach(absl::FunctionRef<
bool(const InternalOpenTelemetryPluginOption&, size_t)>
func,
const OpenTelemetryPlugin* otel_plugin) const {
for (size_t i = 0; i < otel_plugin->plugin_options().size(); ++i) {
const auto& plugin_option = otel_plugin->plugin_options()[i];
if (active_mask_[i] && !func(*plugin_option, i)) {
return false;
}
}
return true;
}
return true;
}
private:
explicit ActivePluginOptionsView(
absl::FunctionRef<bool(const InternalOpenTelemetryPluginOption&)> func) {
for (size_t i = 0; i < OpenTelemetryPluginState().plugin_options.size();
++i) {
const auto& plugin_option = OpenTelemetryPluginState().plugin_options[i];
if (plugin_option != nullptr && func(*plugin_option)) {
active_mask_.set(i);
private:
explicit ActivePluginOptionsView(
absl::FunctionRef<bool(const InternalOpenTelemetryPluginOption&)> func,
const OpenTelemetryPlugin* otel_plugin) {
for (size_t i = 0; i < otel_plugin->plugin_options().size(); ++i) {
const auto& plugin_option = otel_plugin->plugin_options()[i];
if (plugin_option != nullptr && func(*plugin_option)) {
active_mask_.set(i);
}
}
}
std::bitset<64> active_mask_;
};
class ClientScopeConfig : public grpc_core::StatsPlugin::ScopeConfig {
public:
ClientScopeConfig(const OpenTelemetryPlugin* otel_plugin,
const ChannelScope& scope)
: active_plugin_options_view_(ActivePluginOptionsView::MakeForClient(
scope.target(), otel_plugin)),
filtered_target_(
// Use the original target string only if a filter on the
// attribute is not registered or if the filter returns true,
// otherwise use "other".
otel_plugin->target_attribute_filter() == nullptr ||
otel_plugin->target_attribute_filter()(scope.target())
? scope.target()
: "other") {}
const ActivePluginOptionsView& active_plugin_options_view() const {
return active_plugin_options_view_;
}
absl::string_view filtered_target() const { return filtered_target_; }
private:
ActivePluginOptionsView active_plugin_options_view_;
std::string filtered_target_;
};
class ServerScopeConfig : public grpc_core::StatsPlugin::ScopeConfig {
public:
ServerScopeConfig(const OpenTelemetryPlugin* otel_plugin,
const grpc_core::ChannelArgs& args)
: active_plugin_options_view_(
ActivePluginOptionsView::MakeForServer(args, otel_plugin)) {}
const ActivePluginOptionsView& active_plugin_options_view() const {
return active_plugin_options_view_;
}
private:
ActivePluginOptionsView active_plugin_options_view_;
};
struct ClientMetrics {
struct Attempt {
std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>> started;
std::unique_ptr<opentelemetry::metrics::Histogram<double>> duration;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
sent_total_compressed_message_size;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
rcvd_total_compressed_message_size;
} attempt;
};
struct ServerMetrics {
struct Call {
std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>> started;
std::unique_ptr<opentelemetry::metrics::Histogram<double>> duration;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
sent_total_compressed_message_size;
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>
rcvd_total_compressed_message_size;
} call;
};
// StatsPlugin:
std::pair<bool, std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig>>
IsEnabledForChannel(const ChannelScope& scope) const override;
std::pair<bool, std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig>>
IsEnabledForServer(const grpc_core::ChannelArgs& args) const override;
void AddCounter(
grpc_core::GlobalInstrumentsRegistry::GlobalUInt64CounterHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) override;
void AddCounter(
grpc_core::GlobalInstrumentsRegistry::GlobalDoubleCounterHandle handle,
double value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) override;
void RecordHistogram(
grpc_core::GlobalInstrumentsRegistry::GlobalUInt64HistogramHandle handle,
uint64_t value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) override;
void RecordHistogram(
grpc_core::GlobalInstrumentsRegistry::GlobalDoubleHistogramHandle handle,
double value, absl::Span<const absl::string_view> label_values,
absl::Span<const absl::string_view> optional_values) override;
void SetGauge(
grpc_core::GlobalInstrumentsRegistry::GlobalInt64GaugeHandle /*handle*/,
int64_t /*value*/, absl::Span<const absl::string_view> /*label_values*/,
absl::Span<const absl::string_view> /*optional_values*/) override {}
void SetGauge(
grpc_core::GlobalInstrumentsRegistry::GlobalDoubleGaugeHandle /*handle*/,
double /*value*/, absl::Span<const absl::string_view> /*label_values*/,
absl::Span<const absl::string_view> /*optional_values*/) override {}
// TODO(yashkt, yijiem): implement async instrument.
void AddCallback(grpc_core::RegisteredMetricCallback* /*callback*/) override {
}
void RemoveCallback(
grpc_core::RegisteredMetricCallback* /*callback*/) override {}
grpc_core::ClientCallTracer* GetClientCallTracer(
const grpc_core::Slice& path, bool registered_method,
std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig> scope_config)
override;
grpc_core::ServerCallTracer* GetServerCallTracer(
std::shared_ptr<grpc_core::StatsPlugin::ScopeConfig> scope_config)
override;
const absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>&
server_selector() const {
return server_selector_;
}
const absl::AnyInvocable<bool(absl::string_view /*target*/) const>&
target_attribute_filter() const {
return target_attribute_filter_;
}
const absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>&
generic_method_attribute_filter() const {
return generic_method_attribute_filter_;
}
const std::vector<std::unique_ptr<InternalOpenTelemetryPluginOption>>&
plugin_options() const {
return plugin_options_;
}
std::bitset<64> active_mask_;
// Instruments for per-call metrics.
ClientMetrics client_;
ServerMetrics server_;
// Instruments for non-per-call metrics.
struct Disabled {};
using Instrument = absl::variant<
Disabled, std::unique_ptr<opentelemetry::metrics::Counter<uint64_t>>,
std::unique_ptr<opentelemetry::metrics::Counter<double>>,
std::unique_ptr<opentelemetry::metrics::Histogram<uint64_t>>,
std::unique_ptr<opentelemetry::metrics::Histogram<double>>>;
static constexpr int kOptionalLabelsSizeLimit = 64;
using OptionalLabelsBitSet = std::bitset<kOptionalLabelsSizeLimit>;
struct InstrumentData {
Instrument instrument;
OptionalLabelsBitSet optional_labels_bits;
};
std::vector<InstrumentData> instruments_data_;
opentelemetry::nostd::shared_ptr<opentelemetry::metrics::MeterProvider>
meter_provider_;
absl::AnyInvocable<bool(absl::string_view /*target*/) const> target_selector_;
absl::AnyInvocable<bool(const grpc_core::ChannelArgs& /*args*/) const>
server_selector_;
absl::AnyInvocable<bool(absl::string_view /*target*/) const>
target_attribute_filter_;
absl::AnyInvocable<bool(absl::string_view /*generic_method*/) const>
generic_method_attribute_filter_;
std::vector<std::unique_ptr<InternalOpenTelemetryPluginOption>>
plugin_options_;
};
} // namespace internal

@ -28,7 +28,6 @@
#include "absl/functional/any_invocable.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "absl/types/optional.h"
@ -50,127 +49,11 @@
namespace grpc {
namespace internal {
namespace {
// OpenTelemetryServerCallTracer implementation
class OpenTelemetryServerCallTracer : public grpc_core::ServerCallTracer {
public:
explicit OpenTelemetryServerCallTracer(const grpc_core::ChannelArgs& args)
: start_time_(absl::Now()),
active_plugin_options_view_(
ActivePluginOptionsView::MakeForServer(args)),
injected_labels_from_plugin_options_(
OpenTelemetryPluginState().plugin_options.size()) {}
std::string TraceId() override {
// Not implemented
return "";
}
std::string SpanId() override {
// Not implemented
return "";
}
bool IsSampled() override {
// Not implemented
return false;
}
// Please refer to `grpc_transport_stream_op_batch_payload` for details on
// arguments.
void RecordSendInitialMetadata(
grpc_metadata_batch* send_initial_metadata) override {
active_plugin_options_view_.ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t index) {
auto* labels_injector = plugin_option.labels_injector();
if (labels_injector != nullptr) {
labels_injector->AddLabels(
send_initial_metadata,
injected_labels_from_plugin_options_[index].get());
}
return true;
});
}
void RecordSendTrailingMetadata(
grpc_metadata_batch* /*send_trailing_metadata*/) override;
void RecordSendMessage(const grpc_core::SliceBuffer& send_message) override {
RecordAnnotation(
absl::StrFormat("Send message: %ld bytes", send_message.Length()));
}
void RecordSendCompressedMessage(
const grpc_core::SliceBuffer& send_compressed_message) override {
RecordAnnotation(absl::StrFormat("Send compressed message: %ld bytes",
send_compressed_message.Length()));
}
void RecordReceivedInitialMetadata(
grpc_metadata_batch* recv_initial_metadata) override;
void RecordReceivedMessage(
const grpc_core::SliceBuffer& recv_message) override {
RecordAnnotation(
absl::StrFormat("Received message: %ld bytes", recv_message.Length()));
}
void RecordReceivedDecompressedMessage(
const grpc_core::SliceBuffer& recv_decompressed_message) override {
RecordAnnotation(absl::StrFormat("Received decompressed message: %ld bytes",
recv_decompressed_message.Length()));
}
void RecordReceivedTrailingMetadata(
grpc_metadata_batch* /*recv_trailing_metadata*/) override {}
void RecordCancel(grpc_error_handle /*cancel_error*/) override {
elapsed_time_ = absl::Now() - start_time_;
}
void RecordEnd(const grpc_call_final_info* final_info) override;
void RecordAnnotation(absl::string_view /*annotation*/) override {
// Not implemented
}
void RecordAnnotation(const Annotation& /*annotation*/) override {
// Not implemented
}
std::shared_ptr<grpc_core::TcpTracerInterface> StartNewTcpTrace() override {
// No TCP trace.
return nullptr;
}
private:
absl::string_view MethodForStats() const {
absl::string_view method = absl::StripPrefix(path_.as_string_view(), "/");
if (registered_method_ ||
(OpenTelemetryPluginState().generic_method_attribute_filter !=
nullptr &&
OpenTelemetryPluginState().generic_method_attribute_filter(method))) {
return method;
}
return "other";
}
absl::Time start_time_;
absl::Duration elapsed_time_;
grpc_core::Slice path_;
bool registered_method_;
ActivePluginOptionsView active_plugin_options_view_;
// TODO(yashykt): It's wasteful to do this per call. When we re-haul the stats
// infrastructure, this should move to be done per server.
std::vector<std::unique_ptr<LabelsIterable>>
injected_labels_from_plugin_options_;
};
void OpenTelemetryServerCallTracer::RecordReceivedInitialMetadata(
void OpenTelemetryPlugin::ServerCallTracer::RecordReceivedInitialMetadata(
grpc_metadata_batch* recv_initial_metadata) {
path_ =
recv_initial_metadata->get_pointer(grpc_core::HttpPathMetadata())->Ref();
active_plugin_options_view_.ForEach(
scope_config_->active_plugin_options_view().ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t index) {
auto* labels_injector = plugin_option.labels_injector();
@ -179,31 +62,48 @@ void OpenTelemetryServerCallTracer::RecordReceivedInitialMetadata(
labels_injector->GetLabels(recv_initial_metadata);
}
return true;
});
},
otel_plugin_);
registered_method_ =
recv_initial_metadata->get(grpc_core::GrpcRegisteredMethod())
.value_or(nullptr) != nullptr;
std::array<std::pair<absl::string_view, absl::string_view>, 1>
additional_labels = {{{OpenTelemetryMethodKey(), MethodForStats()}}};
if (OpenTelemetryPluginState().server.call.started != nullptr) {
if (otel_plugin_->server_.call.started != nullptr) {
// We might not have all the injected labels that we want at this point, so
// avoid recording a subset of injected labels here.
OpenTelemetryPluginState().server.call.started->Add(
otel_plugin_->server_.call.started->Add(
1, KeyValueIterable(/*injected_labels_from_plugin_options=*/{},
additional_labels,
/*active_plugin_options_view=*/nullptr, {},
/*is_client=*/false));
/*is_client=*/false, otel_plugin_));
}
}
void OpenTelemetryServerCallTracer::RecordSendTrailingMetadata(
void OpenTelemetryPlugin::ServerCallTracer::RecordSendInitialMetadata(
grpc_metadata_batch* send_initial_metadata) {
scope_config_->active_plugin_options_view().ForEach(
[&](const InternalOpenTelemetryPluginOption& plugin_option,
size_t index) {
auto* labels_injector = plugin_option.labels_injector();
if (labels_injector != nullptr) {
labels_injector->AddLabels(
send_initial_metadata,
injected_labels_from_plugin_options_[index].get());
}
return true;
},
otel_plugin_);
}
void OpenTelemetryPlugin::ServerCallTracer::RecordSendTrailingMetadata(
grpc_metadata_batch* /*send_trailing_metadata*/) {
// We need to record the time when the trailing metadata was sent to
// mark the completeness of the request.
elapsed_time_ = absl::Now() - start_time_;
}
void OpenTelemetryServerCallTracer::RecordEnd(
void OpenTelemetryPlugin::ServerCallTracer::RecordEnd(
const grpc_call_final_info* final_info) {
std::array<std::pair<absl::string_view, absl::string_view>, 2>
additional_labels = {
@ -214,47 +114,25 @@ void OpenTelemetryServerCallTracer::RecordEnd(
KeyValueIterable labels(
injected_labels_from_plugin_options_, additional_labels,
/*active_plugin_options_view=*/nullptr, /*optional_labels_span=*/{},
/*is_client=*/false);
if (OpenTelemetryPluginState().server.call.duration != nullptr) {
OpenTelemetryPluginState().server.call.duration->Record(
/*is_client=*/false, otel_plugin_);
if (otel_plugin_->server_.call.duration != nullptr) {
otel_plugin_->server_.call.duration->Record(
absl::ToDoubleSeconds(elapsed_time_), labels,
opentelemetry::context::Context{});
}
if (OpenTelemetryPluginState()
.server.call.sent_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.server.call.sent_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.outgoing.data_bytes,
labels, opentelemetry::context::Context{});
if (otel_plugin_->server_.call.sent_total_compressed_message_size !=
nullptr) {
otel_plugin_->server_.call.sent_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.outgoing.data_bytes, labels,
opentelemetry::context::Context{});
}
if (OpenTelemetryPluginState()
.server.call.rcvd_total_compressed_message_size != nullptr) {
OpenTelemetryPluginState()
.server.call.rcvd_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.incoming.data_bytes,
labels, opentelemetry::context::Context{});
if (otel_plugin_->server_.call.rcvd_total_compressed_message_size !=
nullptr) {
otel_plugin_->server_.call.rcvd_total_compressed_message_size->Record(
final_info->stats.transport_stream_stats.incoming.data_bytes, labels,
opentelemetry::context::Context{});
}
}
} // namespace
//
// OpenTelemetryServerCallTracerFactory
//
grpc_core::ServerCallTracer*
OpenTelemetryServerCallTracerFactory::CreateNewServerCallTracer(
grpc_core::Arena* arena, const grpc_core::ChannelArgs& args) {
return arena->ManagedNew<OpenTelemetryServerCallTracer>(args);
}
bool OpenTelemetryServerCallTracerFactory::IsServerTraced(
const grpc_core::ChannelArgs& args) {
// Return true only if there is no server selector registered or if the server
// selector returns true.
return OpenTelemetryPluginState().server_selector == nullptr ||
OpenTelemetryPluginState().server_selector(args);
}
} // namespace internal
} // namespace grpc

@ -21,21 +21,116 @@
#include <grpc/support/port_platform.h>
#include "absl/strings/strip.h"
#include "src/core/lib/channel/call_tracer.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/resource_quota/arena.h"
#include "src/cpp/ext/otel/otel_plugin.h"
namespace grpc {
namespace internal {
class OpenTelemetryServerCallTracerFactory
: public grpc_core::ServerCallTracerFactory {
// OpenTelemetryPlugin::ServerCallTracer implementation
class OpenTelemetryPlugin::ServerCallTracer
: public grpc_core::ServerCallTracer {
public:
grpc_core::ServerCallTracer* CreateNewServerCallTracer(
grpc_core::Arena* arena,
const grpc_core::ChannelArgs& channel_args) override;
ServerCallTracer(
OpenTelemetryPlugin* otel_plugin,
std::shared_ptr<OpenTelemetryPlugin::ServerScopeConfig> scope_config)
: start_time_(absl::Now()),
injected_labels_from_plugin_options_(
otel_plugin->plugin_options().size()),
otel_plugin_(otel_plugin),
scope_config_(std::move(scope_config)) {}
std::string TraceId() override {
// Not implemented
return "";
}
std::string SpanId() override {
// Not implemented
return "";
}
bool IsSampled() override {
// Not implemented
return false;
}
// Please refer to `grpc_transport_stream_op_batch_payload` for details on
// arguments.
void RecordSendInitialMetadata(
grpc_metadata_batch* send_initial_metadata) override;
void RecordSendTrailingMetadata(
grpc_metadata_batch* /*send_trailing_metadata*/) override;
void RecordSendMessage(const grpc_core::SliceBuffer& send_message) override {
RecordAnnotation(
absl::StrFormat("Send message: %ld bytes", send_message.Length()));
}
void RecordSendCompressedMessage(
const grpc_core::SliceBuffer& send_compressed_message) override {
RecordAnnotation(absl::StrFormat("Send compressed message: %ld bytes",
send_compressed_message.Length()));
}
void RecordReceivedInitialMetadata(
grpc_metadata_batch* recv_initial_metadata) override;
void RecordReceivedMessage(
const grpc_core::SliceBuffer& recv_message) override {
RecordAnnotation(
absl::StrFormat("Received message: %ld bytes", recv_message.Length()));
}
void RecordReceivedDecompressedMessage(
const grpc_core::SliceBuffer& recv_decompressed_message) override {
RecordAnnotation(absl::StrFormat("Received decompressed message: %ld bytes",
recv_decompressed_message.Length()));
}
void RecordReceivedTrailingMetadata(
grpc_metadata_batch* /*recv_trailing_metadata*/) override {}
void RecordCancel(grpc_error_handle /*cancel_error*/) override {
elapsed_time_ = absl::Now() - start_time_;
}
void RecordEnd(const grpc_call_final_info* final_info) override;
void RecordAnnotation(absl::string_view /*annotation*/) override {
// Not implemented
}
void RecordAnnotation(const Annotation& /*annotation*/) override {
// Not implemented
}
std::shared_ptr<grpc_core::TcpTracerInterface> StartNewTcpTrace() override {
// No TCP trace.
return nullptr;
}
private:
absl::string_view MethodForStats() const {
absl::string_view method = absl::StripPrefix(path_.as_string_view(), "/");
if (registered_method_ ||
(otel_plugin_->generic_method_attribute_filter() != nullptr &&
otel_plugin_->generic_method_attribute_filter()(method))) {
return method;
}
return "other";
}
bool IsServerTraced(const grpc_core::ChannelArgs& args) override;
absl::Time start_time_;
absl::Duration elapsed_time_;
grpc_core::Slice path_;
bool registered_method_;
std::vector<std::unique_ptr<LabelsIterable>>
injected_labels_from_plugin_options_;
OpenTelemetryPlugin* otel_plugin_;
std::shared_ptr<OpenTelemetryPlugin::ServerScopeConfig> scope_config_;
};
} // namespace internal

@ -696,12 +696,12 @@ Pod::Spec.new do |s|
# PrivacyInfo.xcprivacy is not part of BoringSSL repo, inject it during pod installation
base64 --decode $opts <<EOF | gunzip > src/PrivacyInfo.xcprivacy
H4sICAAAAAAC/1ByaXZhY3lJbmZvLnhjcHJpdmFjeQC1kl9PwjAUxZ/Hp6h9Z1di/JsxAhskJAQXGQ8+
Nt0VG7a1aRuw395OHUhE8UHflrNzzj2/pNHgpSrJBrURsu7TXnhOCdZcFqJe9ekyn3Rv6CDuRGfpfZI/
ZmOiSmEsyZaj2TQhtAswVKpEgDRPSTabLnLiOwDGc0ros7XqDmC73YascYVcVo3RQKalQm3dzJd1fSAs
bEH9mff2gzleLQS3cSeI1uji+SLTYsO4yzXja78ygkb2f59YaRC++BJZlsgtFimzLHcKzS7BtGYOvm1O
ZcVEfdI+5ByNwWKYTY/U+4+gBQh+TrZBbzNW+wFHnQmzuJLaTUSJuajQWFapCD4SJ488IDNyDxV8mrm/
m1z1rsPeYSnscaDl+RewhTMWq5GUtsH7Y7KLy8ntL8h2WqtE8PY0484rAb5xoDEDAAA=
H4sICAAAAAAC/1ByaXZhY3lJbmZvLnhjcHJpdmFjeQCFUU1PwjAYPo9fUXtnr7uoMWMEN0iWEFykHDw2
3Ss2dGvTNuD+vUWdk4hya54+n3nT6VujyB6tk7qd0CS+pgRboWvZbid0wxbjOzrNRulV8Ziz52pOjJLO
k2rzsCxzQscAM2MUAhSsINWyXDMSPADmK0roq/fmHuBwOMT8yIqFbo5EB5XVBq3vlsFsHARx7WsaYj7d
T+oEtJbCZ6Mo3WGXrdaVlXsuOma52IWWKRzh8PvClUP4xcu1Uig81gX3nHUG3beCW8s7+NO50A2X7UX6
TAh0DutZVZ6xD4+oHxD9r+yFgea8DQXOMnPucattt5AKmWzQed6YFL4UF0OekDs9jIp+1Bxy85vkNk5O
TWGYA/1BeqxHUvg4YDZ6B1ry6jZXAgAA
EOF
# We are renaming openssl to openssl_grpc so that there is no conflict with openssl if it exists

@ -18,14 +18,6 @@
<string>C617.1</string>
</array>
</dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array>
</dict>
</plist>

@ -1168,7 +1168,7 @@ TEST_F(PickFirstTest, MetricValues) {
const absl::string_view kLabelValues[] = {target_};
auto stats_plugin = std::make_shared<FakeStatsPlugin>(
nullptr, /*use_disabled_by_default_metrics=*/true);
stats_plugin_group_.push_back(stats_plugin);
stats_plugin_group_.AddStatsPlugin(stats_plugin, nullptr);
// Send an update containing two addresses.
constexpr std::array<absl::string_view, 2> kAddresses = {
"ipv4:127.0.0.1:443", "ipv4:127.0.0.1:444"};

@ -1084,7 +1084,7 @@ TEST_F(WeightedRoundRobinTest, MetricValues) {
const absl::string_view kOptionalLabelValues[] = {kLocalityName};
auto stats_plugin = std::make_shared<FakeStatsPlugin>(
nullptr, /*use_disabled_by_default_metrics=*/true);
stats_plugin_group_.push_back(stats_plugin);
stats_plugin_group_.AddStatsPlugin(stats_plugin, nullptr);
// Send address list to LB policy.
const std::array<absl::string_view, 3> kAddresses = {
"ipv4:127.0.0.1:441", "ipv4:127.0.0.1:442", "ipv4:127.0.0.1:443"};

@ -90,6 +90,7 @@ def grpc_core_end2end_test(name, shard_count = 10, tags = []):
"//src/core:stats_data",
"//src/core:status_helper",
"//src/core:time",
"//test/core/util:fake_stats_plugin",
"//test/core/util:grpc_test_util",
"//test/core/util:test_lb_policies",
],
@ -170,6 +171,7 @@ def grpc_core_end2end_test(name, shard_count = 10, tags = []):
"//src/core:time",
"//test/core/event_engine/fuzzing_event_engine",
"//test/core/event_engine/fuzzing_event_engine:fuzzing_event_engine_proto",
"//test/core/util:fake_stats_plugin",
"//test/core/util:fuzz_config_vars",
"//test/core/util:fuzz_config_vars_proto",
"//test/core/util:grpc_test_util",

@ -33,6 +33,7 @@
#include "src/core/lib/channel/channel_fwd.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/lib/channel/context.h"
#include "src/core/lib/channel/metrics.h"
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/channel/tcp_tracer.h"
#include "src/core/lib/config/core_configuration.h"
@ -50,6 +51,7 @@
#include "src/core/lib/transport/metadata_batch.h"
#include "src/core/lib/transport/transport.h"
#include "test/core/end2end/end2end_tests.h"
#include "test/core/util/fake_stats_plugin.h"
namespace grpc_core {
namespace {
@ -130,33 +132,6 @@ class FakeCallTracer : public ClientCallTracer {
grpc_transport_stream_stats
FakeCallTracer::FakeCallAttemptTracer::transport_stream_stats_;
class FakeClientFilter : public ChannelFilter {
public:
static const grpc_channel_filter kFilter;
static absl::StatusOr<FakeClientFilter> Create(
const ChannelArgs& /*args*/, ChannelFilter::Args /*filter_args*/) {
return FakeClientFilter();
}
ArenaPromise<ServerMetadataHandle> MakeCallPromise(
CallArgs call_args, NextPromiseFactory next_promise_factory) override {
auto* call_context = GetContext<grpc_call_context_element>();
auto* tracer = GetContext<Arena>()->ManagedNew<FakeCallTracer>();
GPR_DEBUG_ASSERT(
call_context[GRPC_CONTEXT_CALL_TRACER_ANNOTATION_INTERFACE].value ==
nullptr);
call_context[GRPC_CONTEXT_CALL_TRACER_ANNOTATION_INTERFACE].value = tracer;
call_context[GRPC_CONTEXT_CALL_TRACER_ANNOTATION_INTERFACE].destroy =
nullptr;
return next_promise_factory(std::move(call_args));
}
};
const grpc_channel_filter FakeClientFilter::kFilter =
MakePromiseBasedFilter<FakeClientFilter, FilterEndpoint::kClient>(
"fake_client");
class FakeServerCallTracer : public ServerCallTracer {
public:
~FakeServerCallTracer() override {}
@ -203,11 +178,18 @@ class FakeServerCallTracer : public ServerCallTracer {
grpc_transport_stream_stats FakeServerCallTracer::transport_stream_stats_;
class FakeServerCallTracerFactory : public ServerCallTracerFactory {
// TODO(yijiem): figure out how to reuse FakeStatsPlugin instead of
// inheriting and overriding it here.
class NewFakeStatsPlugin : public FakeStatsPlugin {
public:
ServerCallTracer* CreateNewServerCallTracer(
Arena* arena, const ChannelArgs& /*args*/) override {
return arena->ManagedNew<FakeServerCallTracer>();
ClientCallTracer* GetClientCallTracer(
const Slice& /*path*/, bool /*registered_method*/,
std::shared_ptr<StatsPlugin::ScopeConfig> /*scope_config*/) override {
return GetContext<Arena>()->ManagedNew<FakeCallTracer>();
}
ServerCallTracer* GetServerCallTracer(
std::shared_ptr<StatsPlugin::ScopeConfig> /*scope_config*/) override {
return GetContext<Arena>()->ManagedNew<FakeServerCallTracer>();
}
};
@ -219,12 +201,8 @@ CORE_END2END_TEST(Http2FullstackSingleHopTest, StreamStats) {
g_mu = new Mutex();
g_client_call_ended_notify = new Notification();
g_server_call_ended_notify = new Notification();
CoreConfiguration::RegisterBuilder([](CoreConfiguration::Builder* builder) {
builder->channel_init()->RegisterFilter<FakeClientFilter>(
GRPC_CLIENT_CHANNEL);
});
ServerCallTracerFactory::RegisterGlobal(new FakeServerCallTracerFactory);
GlobalStatsPluginRegistry::RegisterStatsPlugin(
std::make_shared<NewFakeStatsPlugin>());
auto send_from_client = RandomSlice(10);
auto send_from_server = RandomSlice(20);
CoreEnd2endTest::IncomingStatusOnClient server_status;

@ -87,7 +87,7 @@ class Verifier {
}
auto it = allowed_logs_by_module->find(filename);
if (it != allowed_logs_by_module->end() &&
std::regex_match(args->message, it->second)) {
std::regex_search(args->message, it->second)) {
gpr_default_log(args);
return;
}

@ -37,6 +37,7 @@
#include <grpc/status.h>
#include "src/core/ext/transport/chaotic_good/client_transport.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/iomgr/timer_manager.h"
#include "src/core/lib/promise/activity.h"

@ -36,6 +36,7 @@
#include <grpc/grpc.h>
#include <grpc/status.h>
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/promise/if.h"
#include "src/core/lib/promise/loop.h"
#include "src/core/lib/promise/seq.h"

@ -122,17 +122,17 @@ namespace {
template <typename HandleType>
absl::optional<HandleType> FindInstrument(
const absl::flat_hash_map<
absl::string_view,
GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>& instruments,
const std::vector<GlobalInstrumentsRegistry::GlobalInstrumentDescriptor>&
instruments,
absl::string_view name, GlobalInstrumentsRegistry::ValueType value_type,
GlobalInstrumentsRegistry::InstrumentType instrument_type) {
auto it = instruments.find(name);
if (it != instruments.end() && it->second.value_type == value_type &&
it->second.instrument_type == instrument_type) {
HandleType handle;
handle.index = it->second.index;
return handle;
for (const auto& descriptor : instruments) {
if (descriptor.name == name && descriptor.value_type == value_type &&
descriptor.instrument_type == instrument_type) {
HandleType handle;
handle.index = descriptor.index;
return handle;
}
}
return absl::nullopt;
}
@ -216,9 +216,11 @@ GlobalInstrumentsRegistryTestPeer::FindCallbackDoubleGaugeHandleByName(
GlobalInstrumentsRegistry::GlobalInstrumentDescriptor*
GlobalInstrumentsRegistryTestPeer::FindMetricDescriptorByName(
absl::string_view name) {
auto& instruments = GlobalInstrumentsRegistry::GetInstrumentList();
auto it = instruments.find(name);
if (it != instruments.end()) return &it->second;
for (auto& descriptor : GlobalInstrumentsRegistry::GetInstrumentList()) {
if (descriptor.name == name) {
return &descriptor;
}
}
return nullptr;
}

@ -204,6 +204,8 @@ std::string MakeLabelString(
class FakeStatsPlugin : public StatsPlugin {
public:
class ScopeConfig : public StatsPlugin::ScopeConfig {};
explicit FakeStatsPlugin(
absl::AnyInvocable<bool(const ChannelScope& /*scope*/) const>
channel_filter = nullptr,
@ -266,13 +268,16 @@ class FakeStatsPlugin : public StatsPlugin {
});
}
bool IsEnabledForChannel(const ChannelScope& scope) const override {
if (channel_filter_ == nullptr) return true;
return channel_filter_(scope);
std::pair<bool, std::shared_ptr<StatsPlugin::ScopeConfig>>
IsEnabledForChannel(const ChannelScope& scope) const override {
if (channel_filter_ == nullptr || channel_filter_(scope)) {
return {true, nullptr};
}
return {false, nullptr};
}
bool IsEnabledForServer(const ChannelArgs& /*args*/) const override {
return false;
std::pair<bool, std::shared_ptr<StatsPlugin::ScopeConfig>> IsEnabledForServer(
const ChannelArgs& /*args*/) const override {
return {true, nullptr};
}
void AddCounter(
@ -382,6 +387,16 @@ class FakeStatsPlugin : public StatsPlugin {
callbacks_.erase(callback);
}
ClientCallTracer* GetClientCallTracer(
const Slice& /*path*/, bool /*registered_method*/,
std::shared_ptr<StatsPlugin::ScopeConfig> /*scope_config*/) override {
return nullptr;
}
ServerCallTracer* GetServerCallTracer(
std::shared_ptr<StatsPlugin::ScopeConfig> /*scope_config*/) override {
return nullptr;
}
absl::optional<uint64_t> GetCounterValue(
GlobalInstrumentsRegistry::GlobalUInt64CounterHandle handle,
absl::Span<const absl::string_view> label_values,

@ -37,6 +37,7 @@ grpc_cc_test(
"//:gpr",
"//src/core:grpc_xds_client",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],
)
@ -162,6 +163,18 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "xds_client_test_peer",
hdrs = ["xds_client_test_peer.h"],
external_deps = [
"absl/functional:function_ref",
],
language = "C++",
deps = [
"//:xds_client",
],
)
grpc_cc_test(
name = "xds_client_test",
srcs = ["xds_client_test.cc"],
@ -171,6 +184,7 @@ grpc_cc_test(
uses_event_engine = True,
uses_polling = False,
deps = [
":xds_client_test_peer",
":xds_transport_fake",
"//:xds_client",
"//src/proto/grpc/testing/xds/v3:discovery_proto",
@ -192,6 +206,7 @@ grpc_proto_fuzzer(
uses_event_engine = False,
uses_polling = False,
deps = [
":xds_client_test_peer",
":xds_transport_fake",
"//src/core:grpc_xds_client",
"//test/core/util:grpc_test_util",

@ -49,6 +49,7 @@
#include "src/core/lib/security/certificate_provider/certificate_provider_factory.h"
#include "src/core/lib/security/credentials/channel_creds_registry.h"
#include "src/core/lib/security/credentials/tls/grpc_tls_certificate_provider.h"
#include "test/core/util/scoped_env_var.h"
#include "test/core/util/test_config.h"
namespace grpc_core {
@ -163,8 +164,7 @@ TEST(XdsBootstrapTest, Basic) {
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
EXPECT_THAT(bootstrap->servers(),
::testing::ElementsAre(EqXdsServer("fake:///lb1", "fake"),
EqXdsServer("fake:///lb2", "fake")));
::testing::ElementsAre(EqXdsServer("fake:///lb1", "fake")));
EXPECT_EQ(bootstrap->authorities().size(), 2);
auto* authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
bootstrap->LookupAuthority("xds.example.com"));
@ -172,10 +172,8 @@ TEST(XdsBootstrapTest, Basic) {
EXPECT_EQ(authority->client_listener_resource_name_template(),
"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/"
"server/%s");
EXPECT_THAT(
authority->servers(),
::testing::ElementsAre(EqXdsServer("fake:///xds_server", "fake"),
EqXdsServer("fake:///xds_server2", "fake")));
EXPECT_THAT(authority->servers(), ::testing::ElementsAre(EqXdsServer(
"fake:///xds_server", "fake")));
authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
bootstrap->LookupAuthority("xds.example2.com"));
ASSERT_NE(authority, nullptr);
@ -726,6 +724,92 @@ TEST(XdsBootstrapTest, XdsServerToJsonAndParse) {
EXPECT_EQ(*xds_server, *output_xds_server);
}
TEST(XdsBootstrapTest, NoXdsServersEnvVar) {
ScopedEnvVar fallback_enabled("GRPC_EXPERIMENTAL_XDS_FALLBACK", "1");
const char* json_str =
"{"
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///lb1\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\","
" \"ignore\": 0"
" }"
" ],"
" \"ignore\": 0"
" },"
" {"
" \"server_uri\": \"fake:///lb2\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\","
" \"ignore\": 0"
" }"
" ],"
" \"ignore\": 0"
" }"
" ],"
" \"authorities\": {"
" \"xds.example.com\": {"
" \"client_listener_resource_name_template\": "
"\"xdstp://xds.example.com/envoy.config.listener.v3.Listener/grpc/server/"
"%s\","
" \"xds_servers\": ["
" {"
" \"server_uri\": \"fake:///xds_server\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\""
" }"
" ],"
" \"server_features\": [\"xds_v3\"]"
" },"
" {"
" \"server_uri\": \"fake:///xds_server2\","
" \"channel_creds\": ["
" {"
" \"type\": \"fake\""
" }"
" ],"
" \"server_features\": [\"xds_v3\"]"
" }"
" ]"
" }"
" },"
" \"node\": {"
" \"id\": \"foo\","
" \"cluster\": \"bar\","
" \"locality\": {"
" \"region\": \"milky_way\","
" \"zone\": \"sol_system\","
" \"sub_zone\": \"earth\","
" \"ignore\": {}"
" },"
" \"metadata\": {"
" \"foo\": 1,"
" \"bar\": 2"
" },"
" \"ignore\": \"whee\""
" },"
" \"server_listener_resource_name_template\": \"example/resource\","
" \"ignore\": {}"
"}";
auto bootstrap_or = GrpcXdsBootstrap::Create(json_str);
ASSERT_TRUE(bootstrap_or.ok()) << bootstrap_or.status();
auto bootstrap = std::move(*bootstrap_or);
EXPECT_THAT(bootstrap->servers(),
::testing::ElementsAre(EqXdsServer("fake:///lb1", "fake"),
EqXdsServer("fake:///lb2", "fake")));
auto* authority = static_cast<const GrpcXdsBootstrap::GrpcAuthority*>(
bootstrap->LookupAuthority("xds.example.com"));
ASSERT_NE(authority, nullptr);
EXPECT_THAT(
authority->servers(),
::testing::ElementsAre(EqXdsServer("fake:///xds_server", "fake"),
EqXdsServer("fake:///xds_server2", "fake")));
}
} // namespace
} // namespace testing
} // namespace grpc_core

@ -43,30 +43,11 @@
#include "src/libfuzzer/libfuzzer_macro.h"
#include "src/proto/grpc/testing/xds/v3/discovery.pb.h"
#include "test/core/xds/xds_client_fuzzer.pb.h"
#include "test/core/xds/xds_client_test_peer.h"
#include "test/core/xds/xds_transport_fake.h"
namespace grpc_core {
namespace testing {
class XdsClientTestPeer {
public:
explicit XdsClientTestPeer(XdsClient* xds_client) : xds_client_(xds_client) {}
void TestDumpClientConfig() {
upb::Arena arena;
auto client_config = envoy_service_status_v3_ClientConfig_new(arena.ptr());
std::set<std::string> string_pool;
MutexLock lock(xds_client_->mu());
xds_client_->DumpClientConfig(&string_pool, arena.ptr(), client_config);
}
private:
XdsClient* xds_client_;
};
} // namespace testing
class Fuzzer {
public:
explicit Fuzzer(absl::string_view bootstrap_json) {
@ -84,8 +65,8 @@ class Fuzzer {
transport_factory_ = transport_factory.get();
xds_client_ = MakeRefCounted<XdsClient>(
std::move(*bootstrap), std::move(transport_factory),
grpc_event_engine::experimental::GetDefaultEventEngine(), "foo agent",
"foo version");
grpc_event_engine::experimental::GetDefaultEventEngine(),
/*metrics_reporter=*/nullptr, "foo agent", "foo version");
}
void Act(const xds_client_fuzzer::Action& action) {
@ -135,6 +116,28 @@ class Fuzzer {
case xds_client_fuzzer::Action::kDumpCsdsData:
testing::XdsClientTestPeer(xds_client_.get()).TestDumpClientConfig();
break;
case xds_client_fuzzer::Action::kReportResourceCounts:
testing::XdsClientTestPeer(xds_client_.get())
.TestReportResourceCounts(
[](const testing::XdsClientTestPeer::ResourceCountLabels&
labels,
uint64_t count) {
gpr_log(GPR_INFO,
"xds_authority=\"%s\", resource_type=\"%s\", "
"cache_state=\"%s\" count=%" PRIu64,
std::string(labels.xds_authority).c_str(),
std::string(labels.resource_type).c_str(),
std::string(labels.cache_state).c_str(), count);
});
break;
case xds_client_fuzzer::Action::kReportServerConnections:
testing::XdsClientTestPeer(xds_client_.get())
.TestReportServerConnections(
[](absl::string_view xds_server, bool connected) {
gpr_log(GPR_INFO, "xds_server=\"%s\" connected=%d",
std::string(xds_server).c_str(), connected);
});
break;
case xds_client_fuzzer::Action::kTriggerConnectionFailure:
TriggerConnectionFailure(
action.trigger_connection_failure().authority(),

@ -59,6 +59,10 @@ message StopWatch {
message DumpCsdsData {}
message ReportResourceCounts {}
message ReportServerConnections {}
//
// interactions with fake transport
//
@ -96,12 +100,15 @@ message SendStatusToClient {
Status status = 2;
}
// Next free field: 10
message Action {
oneof action_type {
// interactions with XdsClient API
StartWatch start_watch = 1;
StopWatch stop_watch = 2;
DumpCsdsData dump_csds_data = 3;
ReportResourceCounts report_resource_counts = 8;
ReportServerConnections report_server_connections = 9;
// interactions with fake transport
TriggerConnectionFailure trigger_connection_failure = 4;
ReadMessageFromClient read_message_from_client = 5;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,80 @@
//
// Copyright 2022 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_TEST_CORE_XDS_XDS_CLIENT_TEST_PEER_H
#define GRPC_TEST_CORE_XDS_XDS_CLIENT_TEST_PEER_H
#include <grpc/support/port_platform.h>
#include <set>
#include "absl/functional/function_ref.h"
#include "absl/strings/str_cat.h"
#include "src/core/ext/xds/xds_client.h"
namespace grpc_core {
namespace testing {
class XdsClientTestPeer {
public:
explicit XdsClientTestPeer(XdsClient* xds_client) : xds_client_(xds_client) {}
void TestDumpClientConfig() {
upb::Arena arena;
auto client_config = envoy_service_status_v3_ClientConfig_new(arena.ptr());
std::set<std::string> string_pool;
MutexLock lock(xds_client_->mu());
xds_client_->DumpClientConfig(&string_pool, arena.ptr(), client_config);
}
struct ResourceCountLabels {
std::string xds_authority;
std::string resource_type;
std::string cache_state;
std::string ToString() const {
return absl::StrCat("xds_authority=\"", xds_authority,
"\" resource_type=\"", resource_type,
"\" cache_state=\"", cache_state, "\"");
}
};
void TestReportResourceCounts(
absl::FunctionRef<void(const ResourceCountLabels&, uint64_t)> func) {
MutexLock lock(xds_client_->mu());
xds_client_->ReportResourceCounts(
[&](const XdsClient::ResourceCountLabels& labels, uint64_t count) {
ResourceCountLabels labels_copy = {std::string(labels.xds_authority),
std::string(labels.resource_type),
std::string(labels.cache_state)};
func(labels_copy, count);
});
}
void TestReportServerConnections(
absl::FunctionRef<void(absl::string_view, bool)> func) {
MutexLock lock(xds_client_->mu());
xds_client_->ReportServerConnections(func);
}
private:
XdsClient* xds_client_;
};
} // namespace testing
} // namespace grpc_core
#endif // GRPC_TEST_CORE_XDS_XDS_CLIENT_TEST_PEER_H

@ -118,7 +118,8 @@ class XdsClusterTest : public ::testing::Test {
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr,
/*event_engine=*/nullptr, "foo agent",
/*event_engine=*/nullptr,
/*metrics_reporter=*/nullptr, "foo agent",
"foo version");
}

@ -104,7 +104,8 @@ class XdsCommonTypesTest : public ::testing::Test {
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr,
/*event_engine=*/nullptr, "foo agent",
/*event_engine=*/nullptr,
/*metrics_reporter=*/nullptr, "foo agent",
"foo version");
}

@ -95,7 +95,8 @@ class XdsEndpointTest : public ::testing::Test {
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr,
/*event_engine=*/nullptr, "foo agent",
/*event_engine=*/nullptr,
/*metrics_reporter=*/nullptr, "foo agent",
"foo version");
}

@ -117,7 +117,8 @@ class XdsHttpFilterTest : public ::testing::Test {
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr,
/*event_engine=*/nullptr, "foo agent",
/*event_engine=*/nullptr,
/*metrics_reporter=*/nullptr, "foo agent",
"foo version");
}

@ -118,7 +118,8 @@ class XdsListenerTest : public ::testing::Test {
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr,
/*event_engine=*/nullptr, "foo agent",
/*event_engine=*/nullptr,
/*metrics_reporter=*/nullptr, "foo agent",
"foo version");
}

@ -107,7 +107,8 @@ class XdsRouteConfigTest : public ::testing::Test {
}
return MakeRefCounted<XdsClient>(std::move(*bootstrap),
/*transport_factory=*/nullptr,
/*event_engine=*/nullptr, "foo agent",
/*event_engine=*/nullptr,
/*metrics_reporter=*/nullptr, "foo agent",
"foo version");
}

@ -223,6 +223,7 @@ grpc_cc_test(
"//:gpr",
"//:grpc",
"//:grpc++",
"//test/core/util:fake_stats_plugin",
"//test/core/util:grpc_test_util",
"//test/core/util:scoped_env_var",
],

@ -27,6 +27,7 @@
#include "src/core/client_channel/backup_poller.h"
#include "src/core/lib/config/config_vars.h"
#include "src/proto/grpc/testing/xds/v3/listener.pb.h"
#include "test/core/util/fake_stats_plugin.h"
#include "test/core/util/resolve_localhost_ip46.h"
#include "test/core/util/scoped_env_var.h"
#include "test/cpp/end2end/xds/xds_end2end_test_lib.h"
@ -1071,6 +1072,172 @@ TEST_P(XdsFederationTest, FederationServer) {
WaitForAllBackends(DEBUG_LOCATION);
}
//
// XdsMetricsTest - tests xDS metrics
//
class XdsMetricsTest : public XdsEnd2endTest {
protected:
void SetUp() override {
stats_plugin_ = grpc_core::FakeStatsPluginBuilder()
.UseDisabledByDefaultMetrics(true)
.BuildAndRegister();
InitClient();
}
std::shared_ptr<grpc_core::FakeStatsPlugin> stats_plugin_;
};
// Runs with RDS so that we know all resource types work properly.
INSTANTIATE_TEST_SUITE_P(
XdsTest, XdsMetricsTest,
::testing::Values(XdsTestType().set_enable_rds_testing()),
&XdsTestType::Name);
TEST_P(XdsMetricsTest, MetricDefinitionResourceUpdatesValid) {
const auto* descriptor =
grpc_core::GlobalInstrumentsRegistryTestPeer::FindMetricDescriptorByName(
"grpc.xds_client.resource_updates_valid");
ASSERT_NE(descriptor, nullptr);
EXPECT_EQ(descriptor->value_type,
grpc_core::GlobalInstrumentsRegistry::ValueType::kUInt64);
EXPECT_EQ(descriptor->instrument_type,
grpc_core::GlobalInstrumentsRegistry::InstrumentType::kCounter);
EXPECT_EQ(descriptor->enable_by_default, false);
EXPECT_EQ(descriptor->name, "grpc.xds_client.resource_updates_valid");
EXPECT_EQ(descriptor->unit, "{resource}");
EXPECT_THAT(descriptor->label_keys,
::testing::ElementsAre("grpc.target", "grpc.xds.server",
"grpc.xds.resource_type"));
EXPECT_THAT(descriptor->optional_label_keys, ::testing::ElementsAre());
}
TEST_P(XdsMetricsTest, MetricDefinitionResourceUpdatesInvalid) {
const auto* descriptor =
grpc_core::GlobalInstrumentsRegistryTestPeer::FindMetricDescriptorByName(
"grpc.xds_client.resource_updates_invalid");
ASSERT_NE(descriptor, nullptr);
EXPECT_EQ(descriptor->value_type,
grpc_core::GlobalInstrumentsRegistry::ValueType::kUInt64);
EXPECT_EQ(descriptor->instrument_type,
grpc_core::GlobalInstrumentsRegistry::InstrumentType::kCounter);
EXPECT_EQ(descriptor->enable_by_default, false);
EXPECT_EQ(descriptor->name, "grpc.xds_client.resource_updates_invalid");
EXPECT_EQ(descriptor->unit, "{resource}");
EXPECT_THAT(descriptor->label_keys,
::testing::ElementsAre("grpc.target", "grpc.xds.server",
"grpc.xds.resource_type"));
EXPECT_THAT(descriptor->optional_label_keys, ::testing::ElementsAre());
}
TEST_P(XdsMetricsTest, MetricDefinitionConnected) {
const auto* descriptor =
grpc_core::GlobalInstrumentsRegistryTestPeer::FindMetricDescriptorByName(
"grpc.xds_client.connected");
ASSERT_NE(descriptor, nullptr);
EXPECT_EQ(descriptor->value_type,
grpc_core::GlobalInstrumentsRegistry::ValueType::kInt64);
EXPECT_EQ(
descriptor->instrument_type,
grpc_core::GlobalInstrumentsRegistry::InstrumentType::kCallbackGauge);
EXPECT_EQ(descriptor->enable_by_default, false);
EXPECT_EQ(descriptor->name, "grpc.xds_client.connected");
EXPECT_EQ(descriptor->unit, "{bool}");
EXPECT_THAT(descriptor->label_keys,
::testing::ElementsAre("grpc.target", "grpc.xds.server"));
EXPECT_THAT(descriptor->optional_label_keys, ::testing::ElementsAre());
}
TEST_P(XdsMetricsTest, MetricDefinitionResources) {
const auto* descriptor =
grpc_core::GlobalInstrumentsRegistryTestPeer::FindMetricDescriptorByName(
"grpc.xds_client.resources");
ASSERT_NE(descriptor, nullptr);
EXPECT_EQ(descriptor->value_type,
grpc_core::GlobalInstrumentsRegistry::ValueType::kInt64);
EXPECT_EQ(
descriptor->instrument_type,
grpc_core::GlobalInstrumentsRegistry::InstrumentType::kCallbackGauge);
EXPECT_EQ(descriptor->enable_by_default, false);
EXPECT_EQ(descriptor->name, "grpc.xds_client.resources");
EXPECT_EQ(descriptor->unit, "{resource}");
EXPECT_THAT(
descriptor->label_keys,
::testing::ElementsAre("grpc.target", "grpc.xds.authority",
"grpc.xds.resource_type", "grpc.xds.cache_state"));
EXPECT_THAT(descriptor->optional_label_keys, ::testing::ElementsAre());
}
TEST_P(XdsMetricsTest, MetricValues) {
const auto kMetricResourceUpdatesValid =
grpc_core::GlobalInstrumentsRegistryTestPeer::
FindUInt64CounterHandleByName(
"grpc.xds_client.resource_updates_valid")
.value();
const auto kMetricResourceUpdatesInvalid =
grpc_core::GlobalInstrumentsRegistryTestPeer::
FindUInt64CounterHandleByName(
"grpc.xds_client.resource_updates_invalid")
.value();
const auto kMetricConnected =
grpc_core::GlobalInstrumentsRegistryTestPeer::
FindCallbackInt64GaugeHandleByName("grpc.xds_client.connected")
.value();
const auto kMetricResources =
grpc_core::GlobalInstrumentsRegistryTestPeer::
FindCallbackInt64GaugeHandleByName("grpc.xds_client.resources")
.value();
const std::string kTarget = absl::StrCat("xds:", kServerName);
const std::string kXdsServer = absl::StrCat("localhost:", balancer_->port());
CreateAndStartBackends(1, /*xds_enabled=*/true);
EdsResourceArgs args =
EdsResourceArgs({{"locality0", CreateEndpointsForBackends()}});
balancer_->ads_service()->SetEdsResource(BuildEdsResource(args));
CheckRpcSendOk(DEBUG_LOCATION);
stats_plugin_->TriggerCallbacks();
// Check client metrics.
EXPECT_THAT(stats_plugin_->GetCallbackGaugeValue(kMetricConnected,
{kTarget, kXdsServer}, {}),
::testing::Optional(1));
for (absl::string_view type_url :
{"envoy.config.listener.v3.Listener",
"envoy.config.route.v3.RouteConfiguration",
"envoy.config.cluster.v3.Cluster",
"envoy.config.endpoint.v3.ClusterLoadAssignment"}) {
EXPECT_THAT(
stats_plugin_->GetCounterValue(kMetricResourceUpdatesValid,
{kTarget, kXdsServer, type_url}, {}),
::testing::Optional(1));
EXPECT_THAT(
stats_plugin_->GetCounterValue(kMetricResourceUpdatesInvalid,
{kTarget, kXdsServer, type_url}, {}),
::testing::Optional(0));
EXPECT_THAT(stats_plugin_->GetCallbackGaugeValue(
kMetricResources, {kTarget, "#old", type_url, "acked"}, {}),
::testing::Optional(1));
}
// Check server metrics.
EXPECT_THAT(stats_plugin_->GetCallbackGaugeValue(kMetricConnected,
{"#server", kXdsServer}, {}),
::testing::Optional(1));
for (absl::string_view type_url :
{"envoy.config.listener.v3.Listener",
"envoy.config.route.v3.RouteConfiguration"}) {
EXPECT_THAT(
stats_plugin_->GetCounterValue(kMetricResourceUpdatesValid,
{"#server", kXdsServer, type_url}, {}),
::testing::Optional(1));
EXPECT_THAT(
stats_plugin_->GetCounterValue(kMetricResourceUpdatesInvalid,
{"#server", kXdsServer, type_url}, {}),
::testing::Optional(0));
EXPECT_THAT(
stats_plugin_->GetCallbackGaugeValue(
kMetricResources, {"#server", "#old", type_url, "acked"}, {}),
::testing::Optional(1));
}
}
//
// XdsFederationDisabledTest
//

@ -43,6 +43,35 @@ namespace grpc {
namespace testing {
namespace {
using ::testing::ElementsAre;
using ::testing::Pair;
opentelemetry::sdk::resource::Resource TestGkeResource() {
opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "gcp_kubernetes_engine");
attributes.SetAttribute("k8s.pod.name", "pod");
attributes.SetAttribute("k8s.container.name", "container");
attributes.SetAttribute("k8s.namespace.name", "namespace");
attributes.SetAttribute("k8s.cluster.name", "cluster");
attributes.SetAttribute("cloud.region", "region");
attributes.SetAttribute("cloud.account.id", "id");
return opentelemetry::sdk::resource::Resource::Create(attributes);
}
opentelemetry::sdk::resource::Resource TestGceResource() {
opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "gcp_compute_engine");
attributes.SetAttribute("cloud.availability_zone", "zone");
attributes.SetAttribute("cloud.account.id", "id");
return opentelemetry::sdk::resource::Resource::Create(attributes);
}
opentelemetry::sdk::resource::Resource TestUnknownResource() {
opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "random");
return opentelemetry::sdk::resource::Resource::Create(attributes);
}
class TestScenario {
public:
enum class ResourceType : std::uint8_t { kGke, kGce, kUnknown };
@ -91,32 +120,6 @@ class TestScenario {
XdsBootstrapSource bootstrap_source() const { return bootstrap_source_; }
private:
static opentelemetry::sdk::resource::Resource TestGkeResource() {
opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "gcp_kubernetes_engine");
attributes.SetAttribute("k8s.pod.name", "pod");
attributes.SetAttribute("k8s.container.name", "container");
attributes.SetAttribute("k8s.namespace.name", "namespace");
attributes.SetAttribute("k8s.cluster.name", "cluster");
attributes.SetAttribute("cloud.region", "region");
attributes.SetAttribute("cloud.account.id", "id");
return opentelemetry::sdk::resource::Resource::Create(attributes);
}
static opentelemetry::sdk::resource::Resource TestGceResource() {
opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "gcp_compute_engine");
attributes.SetAttribute("cloud.availability_zone", "zone");
attributes.SetAttribute("cloud.account.id", "id");
return opentelemetry::sdk::resource::Resource::Create(attributes);
}
static opentelemetry::sdk::resource::Resource TestUnknownResource() {
opentelemetry::sdk::common::AttributeMap attributes;
attributes.SetAttribute("cloud.platform", "random");
return opentelemetry::sdk::resource::Resource::Create(attributes);
}
ResourceType type_;
XdsBootstrapSource bootstrap_source_;
};
@ -163,7 +166,7 @@ class MetadataExchangeTest
case TestScenario::XdsBootstrapSource::kFromFile: {
ASSERT_EQ(bootstrap_file_name_, nullptr);
FILE* bootstrap_file =
gpr_tmpfile("gcp_observability_config", &bootstrap_file_name_);
gpr_tmpfile("xds_bootstrap", &bootstrap_file_name_);
fputs(kBootstrap, bootstrap_file);
fclose(bootstrap_file);
grpc_core::SetEnv("GRPC_XDS_BOOTSTRAP", bootstrap_file_name_);
@ -186,7 +189,7 @@ class MetadataExchangeTest
}
~MetadataExchangeTest() override {
grpc_core::UnsetEnv("GRPC_GCP_OBSERVABILITY_CONFIG");
grpc_core::UnsetEnv("GRPC_XDS_BOOTSTRAP_CONFIG");
grpc_core::UnsetEnv("GRPC_XDS_BOOTSTRAP");
if (bootstrap_file_name_ != nullptr) {
remove(bootstrap_file_name_);
@ -418,6 +421,134 @@ TEST_P(MetadataExchangeTest, VerifyCsmServiceLabels) {
"mynamespace");
}
// Creates a serialized slice with labels for metadata exchange based on \a
// resource.
grpc_core::Slice RemoteMetadataSliceFromResource(
const opentelemetry::sdk::resource::Resource& resource) {
return grpc::internal::ServiceMeshLabelsInjector(resource.GetAttributes())
.TestOnlySerializedLabels()
.Ref();
}
std::vector<std::pair<absl::string_view, absl::string_view>> LabelsFromIterable(
grpc::internal::MeshLabelsIterable* iterable) {
std::vector<std::pair<absl::string_view, absl::string_view>> labels;
while (true) {
auto label = iterable->Next();
if (!label.has_value()) break;
labels.push_back(*std::move(label));
}
EXPECT_EQ(labels.size(), iterable->Size());
return labels;
}
std::string PrettyPrintLabels(
const std::vector<std::pair<absl::string_view, absl::string_view>>&
labels) {
std::vector<std::string> strings;
strings.reserve(labels.size());
for (const auto& pair : labels) {
strings.push_back(
absl::StrFormat("{\"%s\" : \"%s\"}", pair.first, pair.second));
}
return absl::StrJoin(strings, ", ");
}
TEST(MeshLabelsIterableTest, NoRemoteMetadata) {
std::vector<std::pair<absl::string_view, std::string>> local_labels = {
{"csm.workload_canonical_service", "canonical_service"},
{"csm.mesh_id", "mesh"}};
grpc::internal::MeshLabelsIterable iterable(local_labels, grpc_core::Slice());
auto labels = LabelsFromIterable(&iterable);
EXPECT_FALSE(iterable.GotRemoteLabels());
EXPECT_THAT(
labels,
ElementsAre(Pair("csm.workload_canonical_service", "canonical_service"),
Pair("csm.mesh_id", "mesh"),
Pair("csm.remote_workload_type", "unknown"),
Pair("csm.remote_workload_canonical_service", "unknown")))
<< PrettyPrintLabels(labels);
}
TEST(MeshLabelsIterableTest, RemoteGceTypeMetadata) {
std::vector<std::pair<absl::string_view, std::string>> local_labels = {
{"csm.workload_canonical_service", "canonical_service"},
{"csm.mesh_id", "mesh"}};
grpc::internal::MeshLabelsIterable iterable(
local_labels, RemoteMetadataSliceFromResource(TestGceResource()));
auto labels = LabelsFromIterable(&iterable);
EXPECT_TRUE(iterable.GotRemoteLabels());
EXPECT_THAT(
labels,
ElementsAre(
Pair("csm.workload_canonical_service", "canonical_service"),
Pair("csm.mesh_id", "mesh"),
Pair("csm.remote_workload_type", "gcp_compute_engine"),
Pair("csm.remote_workload_canonical_service", "canonical_service"),
Pair("csm.remote_workload_name", "workload"),
Pair("csm.remote_workload_location", "zone"),
Pair("csm.remote_workload_project_id", "id")))
<< PrettyPrintLabels(labels);
}
TEST(MeshLabelsIterableTest, RemoteGkeTypeMetadata) {
std::vector<std::pair<absl::string_view, std::string>> local_labels = {
{"csm.workload_canonical_service", "canonical_service"},
{"csm.mesh_id", "mesh"}};
grpc::internal::MeshLabelsIterable iterable(
local_labels, RemoteMetadataSliceFromResource(TestGkeResource()));
auto labels = LabelsFromIterable(&iterable);
EXPECT_TRUE(iterable.GotRemoteLabels());
EXPECT_THAT(
labels,
ElementsAre(
Pair("csm.workload_canonical_service", "canonical_service"),
Pair("csm.mesh_id", "mesh"),
Pair("csm.remote_workload_type", "gcp_kubernetes_engine"),
Pair("csm.remote_workload_canonical_service", "canonical_service"),
Pair("csm.remote_workload_name", "workload"),
Pair("csm.remote_workload_namespace_name", "namespace"),
Pair("csm.remote_workload_cluster_name", "cluster"),
Pair("csm.remote_workload_location", "region"),
Pair("csm.remote_workload_project_id", "id")))
<< PrettyPrintLabels(labels);
}
TEST(MeshLabelsIterableTest, RemoteUnknownTypeMetadata) {
std::vector<std::pair<absl::string_view, std::string>> local_labels = {
{"csm.workload_canonical_service", "canonical_service"},
{"csm.mesh_id", "mesh"}};
grpc::internal::MeshLabelsIterable iterable(
local_labels, RemoteMetadataSliceFromResource(TestUnknownResource()));
auto labels = LabelsFromIterable(&iterable);
EXPECT_TRUE(iterable.GotRemoteLabels());
EXPECT_THAT(
labels,
ElementsAre(
Pair("csm.workload_canonical_service", "canonical_service"),
Pair("csm.mesh_id", "mesh"),
Pair("csm.remote_workload_type", "random"),
Pair("csm.remote_workload_canonical_service", "canonical_service")))
<< PrettyPrintLabels(labels);
}
TEST(MeshLabelsIterableTest, TestResetIteratorPosition) {
std::vector<std::pair<absl::string_view, std::string>> local_labels = {
{"csm.workload_canonical_service", "canonical_service"},
{"csm.mesh_id", "mesh"}};
grpc::internal::MeshLabelsIterable iterable(local_labels, grpc_core::Slice());
auto labels = LabelsFromIterable(&iterable);
auto expected_labels_matcher = ElementsAre(
Pair("csm.workload_canonical_service", "canonical_service"),
Pair("csm.mesh_id", "mesh"), Pair("csm.remote_workload_type", "unknown"),
Pair("csm.remote_workload_canonical_service", "unknown"));
EXPECT_THAT(labels, expected_labels_matcher) << PrettyPrintLabels(labels);
// Resetting the iterable should return the entire list again.
iterable.ResetIteratorPosition();
labels = LabelsFromIterable(&iterable);
EXPECT_THAT(labels, expected_labels_matcher) << PrettyPrintLabels(labels);
}
INSTANTIATE_TEST_SUITE_P(
MetadataExchange, MetadataExchangeTest,
::testing::Values(

@ -41,6 +41,7 @@ grpc_cc_library(
deps = [
"//:grpc++",
"//src/cpp/ext/otel:otel_plugin",
"//test/core/util:fake_stats_plugin",
"//test/core/util:grpc_test_util",
"//test/cpp/end2end:test_service_impl",
],

@ -18,10 +18,15 @@
#include "src/cpp/ext/otel/otel_plugin.h"
#include <type_traits>
#include "absl/functional/any_invocable.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "opentelemetry/metrics/provider.h"
#include "opentelemetry/nostd/variant.h"
#include "opentelemetry/sdk/common/attribute_utils.h"
#include "opentelemetry/sdk/metrics/data/point_data.h"
#include "opentelemetry/sdk/metrics/meter_provider.h"
#include "opentelemetry/sdk/metrics/metric_reader.h"
@ -38,6 +43,73 @@ namespace grpc {
namespace testing {
namespace {
#define GRPC_ARG_SERVER_SELECTOR_KEY "grpc.testing.server_selector_key"
#define GRPC_ARG_SERVER_SELECTOR_VALUE "grpc.testing.server_selector_value"
template <typename T>
void PopulateLabelMap(
T label_keys, T label_values,
std::unordered_map<std::string,
opentelemetry::sdk::common::OwnedAttributeValue>*
label_maps) {
for (size_t i = 0; i < label_keys.size(); ++i) {
(*label_maps)[std::string(label_keys[i])] = std::string(label_values[i]);
}
}
MATCHER_P4(AttributesEq, label_keys, label_values, optional_label_keys,
optional_label_values, "") {
std::unordered_map<std::string,
opentelemetry::sdk::common::OwnedAttributeValue>
label_map;
PopulateLabelMap(label_keys, label_values, &label_map);
PopulateLabelMap(optional_label_keys, optional_label_values, &label_map);
return ::testing::ExplainMatchResult(
::testing::UnorderedElementsAreArray(label_map),
arg.attributes.GetAttributes(), result_listener);
}
template <typename T>
auto IntOrDoubleEq(T result) {
return ::testing::Eq(result);
}
template <>
auto IntOrDoubleEq(double result) {
return ::testing::DoubleEq(result);
}
MATCHER_P(CounterResultEq, result, "") {
return ::testing::ExplainMatchResult(
::testing::VariantWith<opentelemetry::sdk::metrics::SumPointData>(
::testing::Field(
&opentelemetry::sdk::metrics::SumPointData::value_,
::testing::VariantWith<std::remove_cv_t<decltype(result)>>(
IntOrDoubleEq(result)))),
arg.point_data, result_listener);
}
MATCHER_P4(HistogramResultEq, sum, min, max, count, "") {
return ::testing::ExplainMatchResult(
::testing::VariantWith<opentelemetry::sdk::metrics::HistogramPointData>(
::testing::AllOf(
::testing::Field(
&opentelemetry::sdk::metrics::HistogramPointData::sum_,
::testing::VariantWith<std::remove_cv_t<decltype(sum)>>(
IntOrDoubleEq(sum))),
::testing::Field(
&opentelemetry::sdk::metrics::HistogramPointData::min_,
::testing::VariantWith<std::remove_cv_t<decltype(min)>>(
IntOrDoubleEq(min))),
::testing::Field(
&opentelemetry::sdk::metrics::HistogramPointData::max_,
::testing::VariantWith<std::remove_cv_t<decltype(max)>>(
IntOrDoubleEq(max))),
::testing::Field(
&opentelemetry::sdk::metrics::HistogramPointData::count_,
::testing::Eq(count)))),
arg.point_data, result_listener);
}
TEST(OpenTelemetryPluginBuildTest, ApiDependency) {
opentelemetry::metrics::Provider::GetMeterProvider();
}
@ -933,6 +1005,354 @@ TEST_F(OpenTelemetryPluginOptionEnd2EndTest,
EXPECT_EQ(absl::get<std::string>(server_attributes.at("key5")), "value5");
}
using OpenTelemetryPluginNPCMetricsTest = OpenTelemetryPluginEnd2EndTest;
TEST_F(OpenTelemetryPluginNPCMetricsTest, RecordUInt64Counter) {
constexpr absl::string_view kMetricName = "uint64_counter";
constexpr int kCounterValues[] = {1, 2, 3};
constexpr int64_t kCounterResult = 6;
constexpr std::array<absl::string_view, 2> kLabelKeys = {"label_key_1",
"label_key_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2"};
constexpr std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
"label_value_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2"};
auto handle = grpc_core::GlobalInstrumentsRegistry::RegisterUInt64Counter(
kMetricName, "A simple uint64 counter.", "unit", kLabelKeys,
kOptionalLabelKeys, /*enable_by_default=*/true);
Init(std::move(Options()
.set_metric_names({kMetricName})
.set_target_selector([](absl::string_view target) {
return absl::StartsWith(target, "dns:///");
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
grpc_core::StatsPlugin::ChannelScope("dns:///localhost:8080", ""));
for (auto v : kCounterValues) {
stats_plugins.AddCounter(handle, v, kLabelValues, kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); });
EXPECT_THAT(data, ::testing::ElementsAre(::testing::Pair(
kMetricName, ::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues,
kOptionalLabelKeys,
kOptionalLabelValues),
CounterResultEq(kCounterResult))))));
}
TEST_F(OpenTelemetryPluginNPCMetricsTest, RecordDoubleCounter) {
constexpr absl::string_view kMetricName = "double_counter";
constexpr double kCounterValues[] = {1.23, 2.34, 3.45};
constexpr double kCounterResult = 7.02;
constexpr std::array<absl::string_view, 2> kLabelKeys = {"label_key_1",
"label_key_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2"};
constexpr std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
"label_value_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2"};
auto handle = grpc_core::GlobalInstrumentsRegistry::RegisterDoubleCounter(
kMetricName, "A simple double counter.", "unit", kLabelKeys,
kOptionalLabelKeys, /*enable_by_default=*/false);
Init(std::move(Options()
.set_metric_names({kMetricName})
.set_target_selector([](absl::string_view target) {
return absl::StartsWith(target, "dns:///");
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForChannel(
grpc_core::StatsPlugin::ChannelScope("dns:///localhost:8080", ""));
for (auto v : kCounterValues) {
stats_plugins.AddCounter(handle, v, kLabelValues, kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); });
EXPECT_THAT(data, ::testing::ElementsAre(::testing::Pair(
kMetricName, ::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues,
kOptionalLabelKeys,
kOptionalLabelValues),
CounterResultEq(kCounterResult))))));
}
TEST_F(OpenTelemetryPluginNPCMetricsTest, RecordUInt64Histogram) {
constexpr absl::string_view kMetricName = "uint64_histogram";
constexpr int kHistogramValues[] = {1, 1, 2, 3, 4, 4, 5, 6};
constexpr int64_t kSum = 26;
constexpr int64_t kMin = 1;
constexpr int64_t kMax = 6;
constexpr int64_t kCount = 8;
constexpr std::array<absl::string_view, 2> kLabelKeys = {"label_key_1",
"label_key_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2"};
constexpr std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
"label_value_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2"};
auto handle = grpc_core::GlobalInstrumentsRegistry::RegisterUInt64Histogram(
kMetricName, "A simple uint64 histogram.", "unit", kLabelKeys,
kOptionalLabelKeys, /*enable_by_default=*/true);
Init(std::move(
Options()
.set_metric_names({kMetricName})
.set_server_selector([](const grpc_core::ChannelArgs& args) {
return args.GetString(GRPC_ARG_SERVER_SELECTOR_KEY) ==
GRPC_ARG_SERVER_SELECTOR_VALUE;
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
grpc_core::ChannelArgs args;
args = args.Set(GRPC_ARG_SERVER_SELECTOR_KEY, GRPC_ARG_SERVER_SELECTOR_VALUE);
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForServer(args);
for (auto v : kHistogramValues) {
stats_plugins.RecordHistogram(handle, v, kLabelValues,
kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); });
EXPECT_THAT(data,
::testing::ElementsAre(::testing::Pair(
kMetricName,
::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues, kOptionalLabelKeys,
kOptionalLabelValues),
HistogramResultEq(kSum, kMin, kMax, kCount))))));
}
TEST_F(OpenTelemetryPluginNPCMetricsTest, RecordDoubleHistogram) {
constexpr absl::string_view kMetricName = "double_histogram";
constexpr double kHistogramValues[] = {1.1, 1.2, 2.2, 3.3,
4.4, 4.5, 5.5, 6.6};
constexpr double kSum = 28.8;
constexpr double kMin = 1.1;
constexpr double kMax = 6.6;
constexpr double kCount = 8;
constexpr std::array<absl::string_view, 2> kLabelKeys = {"label_key_1",
"label_key_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2"};
constexpr std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
"label_value_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2"};
auto handle = grpc_core::GlobalInstrumentsRegistry::RegisterDoubleHistogram(
kMetricName, "A simple double histogram.", "unit", kLabelKeys,
kOptionalLabelKeys, /*enable_by_default=*/true);
Init(std::move(
Options()
.set_metric_names({kMetricName})
.set_server_selector([](const grpc_core::ChannelArgs& args) {
return args.GetString(GRPC_ARG_SERVER_SELECTOR_KEY) ==
GRPC_ARG_SERVER_SELECTOR_VALUE;
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
grpc_core::ChannelArgs args;
args = args.Set(GRPC_ARG_SERVER_SELECTOR_KEY, GRPC_ARG_SERVER_SELECTOR_VALUE);
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForServer(args);
for (auto v : kHistogramValues) {
stats_plugins.RecordHistogram(handle, v, kLabelValues,
kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); });
EXPECT_THAT(data,
::testing::ElementsAre(::testing::Pair(
kMetricName,
::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues, kOptionalLabelKeys,
kOptionalLabelValues),
HistogramResultEq(kSum, kMin, kMax, kCount))))));
}
TEST_F(OpenTelemetryPluginNPCMetricsTest,
RegisterMultipleOpenTelemetryPlugins) {
constexpr absl::string_view kMetricName = "yet_another_double_histogram";
constexpr std::array<absl::string_view, 2> kLabelKeys = {"label_key_1",
"label_key_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2"};
constexpr std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
"label_value_2"};
constexpr std::array<absl::string_view, 2> kOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2"};
auto handle = grpc_core::GlobalInstrumentsRegistry::RegisterDoubleHistogram(
kMetricName, "A simple double histogram.", "unit", kLabelKeys,
kOptionalLabelKeys, /*enable_by_default=*/true);
// Build and register a separate OpenTelemetryPlugin and verify its histogram
// recording.
grpc::internal::OpenTelemetryPluginBuilderImpl ot_builder;
auto reader = BuildAndRegisterOpenTelemetryPlugin(std::move(
Options()
.set_metric_names({kMetricName})
.set_server_selector([](const grpc_core::ChannelArgs& args) {
return args.GetString(GRPC_ARG_SERVER_SELECTOR_KEY) ==
GRPC_ARG_SERVER_SELECTOR_VALUE;
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
EXPECT_EQ(ot_builder.BuildAndRegisterGlobal(), absl::OkStatus());
grpc_core::ChannelArgs args;
args = args.Set(GRPC_ARG_SERVER_SELECTOR_KEY, GRPC_ARG_SERVER_SELECTOR_VALUE);
{
constexpr double kHistogramValues[] = {1.23, 2.34, 3.45, 4.56};
constexpr double kSum = 11.58;
constexpr double kMin = 1.23;
constexpr double kMax = 4.56;
constexpr int kCount = 4;
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForServer(args);
for (auto v : kHistogramValues) {
stats_plugins.RecordHistogram(handle, v, kLabelValues,
kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); },
reader.get());
EXPECT_THAT(
data, ::testing::ElementsAre(::testing::Pair(
kMetricName,
::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues, kOptionalLabelKeys,
kOptionalLabelValues),
HistogramResultEq(kSum, kMin, kMax, kCount))))));
}
// Now build and register another OpenTelemetryPlugin using the test fixture
// and record histogram.
constexpr double kHistogramValues[] = {1.1, 1.2, 2.2, 3.3,
4.4, 4.5, 5.5, 6.6};
constexpr double kSum = 28.8;
constexpr double kMin = 1.1;
constexpr double kMax = 6.6;
constexpr int kCount = 8;
Init(std::move(
Options()
.set_metric_names({kMetricName})
.set_server_selector([](const grpc_core::ChannelArgs& args) {
return args.GetString(GRPC_ARG_SERVER_SELECTOR_KEY) ==
GRPC_ARG_SERVER_SELECTOR_VALUE;
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForServer(args);
for (auto v : kHistogramValues) {
stats_plugins.RecordHistogram(handle, v, kLabelValues,
kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); });
EXPECT_THAT(data,
::testing::ElementsAre(::testing::Pair(
kMetricName,
::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues, kOptionalLabelKeys,
kOptionalLabelValues),
HistogramResultEq(kSum, kMin, kMax, kCount))))));
// Verify that the first plugin gets the data as well.
data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); },
reader.get());
EXPECT_THAT(data,
::testing::ElementsAre(::testing::Pair(
kMetricName,
::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues, kOptionalLabelKeys,
kOptionalLabelValues),
HistogramResultEq(kSum, kMin, kMax, kCount))))));
}
TEST_F(OpenTelemetryPluginNPCMetricsTest,
DisabledOptionalLabelKeysShouldNotBeRecorded) {
constexpr absl::string_view kMetricName =
"yet_another_yet_another_double_histogram";
constexpr double kHistogramValues[] = {1.1, 1.2, 2.2, 3.3,
4.4, 4.5, 5.5, 6.6};
constexpr double kSum = 28.8;
constexpr double kMin = 1.1;
constexpr double kMax = 6.6;
constexpr double kCount = 8;
constexpr std::array<absl::string_view, 2> kLabelKeys = {"label_key_1",
"label_key_2"};
constexpr std::array<absl::string_view, 4> kOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2", "optional_label_key_3",
"optional_label_key_4"};
constexpr std::array<absl::string_view, 2> kActualOptionalLabelKeys = {
"optional_label_key_1", "optional_label_key_2"};
constexpr std::array<absl::string_view, 2> kLabelValues = {"label_value_1",
"label_value_2"};
constexpr std::array<absl::string_view, 4> kOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2",
"optional_label_value_3", "optional_label_value_4"};
constexpr std::array<absl::string_view, 2> kActualOptionalLabelValues = {
"optional_label_value_1", "optional_label_value_2"};
auto handle = grpc_core::GlobalInstrumentsRegistry::RegisterDoubleHistogram(
kMetricName, "A simple double histogram.", "unit", kLabelKeys,
kOptionalLabelKeys, /*enable_by_default=*/true);
Init(std::move(
Options()
.set_metric_names({kMetricName})
.set_server_selector([](const grpc_core::ChannelArgs& args) {
return args.GetString(GRPC_ARG_SERVER_SELECTOR_KEY) ==
GRPC_ARG_SERVER_SELECTOR_VALUE;
})
.add_optional_label(kOptionalLabelKeys[0])
.add_optional_label(kOptionalLabelKeys[1])));
grpc_core::ChannelArgs args;
args = args.Set(GRPC_ARG_SERVER_SELECTOR_KEY, GRPC_ARG_SERVER_SELECTOR_VALUE);
auto stats_plugins =
grpc_core::GlobalStatsPluginRegistry::GetStatsPluginsForServer(args);
for (auto v : kHistogramValues) {
stats_plugins.RecordHistogram(handle, v, kLabelValues,
kOptionalLabelValues);
}
auto data = ReadCurrentMetricsData(
[&](const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&
data) { return !data.contains(kMetricName); });
EXPECT_THAT(
data,
::testing::ElementsAre(::testing::Pair(
kMetricName,
::testing::ElementsAre(::testing::AllOf(
AttributesEq(kLabelKeys, kLabelValues, kActualOptionalLabelKeys,
kActualOptionalLabelValues),
HistogramResultEq(kSum, kMin, kMax, kCount))))));
}
} // namespace
} // namespace testing
} // namespace grpc

@ -32,6 +32,7 @@
#include "src/core/lib/channel/promise_based_filter.h"
#include "src/core/lib/config/core_configuration.h"
#include "src/core/lib/gprpp/notification.h"
#include "test/core/util/fake_stats_plugin.h"
#include "test/core/util/test_config.h"
#include "test/cpp/end2end/test_service_impl.h"
#include "test/cpp/util/byte_buffer_proto_helper.h"
@ -83,38 +84,7 @@ const grpc_channel_filter AddServiceLabelsFilter::kFilter =
"add_service_labels_filter");
void OpenTelemetryPluginEnd2EndTest::Init(Options config) {
// We are resetting the MeterProvider and OpenTelemetry plugin at the start
// of each test to avoid test results from one test carrying over to another
// test. (Some measurements can get arbitrarily delayed.)
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>(
std::make_unique<opentelemetry::sdk::metrics::ViewRegistry>(),
*config.resource);
reader_.reset(new grpc::testing::MockMetricReader);
meter_provider->AddMetricReader(reader_);
grpc_core::CoreConfiguration::Reset();
grpc::internal::OpenTelemetryPluginBuilderImpl ot_builder;
ot_builder.DisableAllMetrics();
for (const auto& metric_name : config.metric_names) {
ot_builder.EnableMetric(metric_name);
}
if (config.use_meter_provider) {
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
reader_.reset(new grpc::testing::MockMetricReader);
meter_provider->AddMetricReader(reader_);
ot_builder.SetMeterProvider(std::move(meter_provider));
}
ot_builder.SetTargetSelector(std::move(config.target_selector));
ot_builder.SetServerSelector(std::move(config.server_selector));
ot_builder.SetTargetAttributeFilter(
std::move(config.target_attribute_filter));
ot_builder.SetGenericMethodAttributeFilter(
std::move(config.generic_method_attribute_filter));
for (auto& option : config.plugin_options) {
ot_builder.AddPluginOption(std::move(option));
}
ASSERT_EQ(ot_builder.BuildAndRegisterGlobal(), absl::OkStatus());
ChannelArguments channel_args;
if (!config.labels_to_inject.empty()) {
labels_to_inject_ = config.labels_to_inject;
@ -125,6 +95,7 @@ void OpenTelemetryPluginEnd2EndTest::Init(Options config) {
});
channel_args.SetPointer(GRPC_ARG_LABELS_TO_INJECT, &labels_to_inject_);
}
reader_ = BuildAndRegisterOpenTelemetryPlugin(std::move(config));
grpc_init();
grpc::ServerBuilder builder;
int port;
@ -148,6 +119,8 @@ void OpenTelemetryPluginEnd2EndTest::TearDown() {
server_->Shutdown();
grpc_shutdown_blocking();
grpc_core::ServerCallTracerFactory::TestOnlyReset();
grpc_core::GlobalStatsPluginRegistryTestPeer::
ResetGlobalStatsPluginRegistry();
}
void OpenTelemetryPluginEnd2EndTest::ResetStub(
@ -183,14 +156,18 @@ OpenTelemetryPluginEnd2EndTest::ReadCurrentMetricsData(
bool(const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&)>
continue_predicate) {
continue_predicate,
opentelemetry::sdk::metrics::MetricReader* reader) {
if (reader == nullptr) {
reader = reader_.get();
}
absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>
data;
auto deadline = absl::Now() + absl::Seconds(5);
do {
reader_->Collect([&](opentelemetry::sdk::metrics::ResourceMetrics& rm) {
reader->Collect([&](opentelemetry::sdk::metrics::ResourceMetrics& rm) {
for (const opentelemetry::sdk::metrics::ScopeMetrics& smd :
rm.scope_metric_data_) {
for (const opentelemetry::sdk::metrics::MetricData& md :
@ -207,5 +184,46 @@ OpenTelemetryPluginEnd2EndTest::ReadCurrentMetricsData(
return data;
}
std::shared_ptr<opentelemetry::sdk::metrics::MetricReader>
OpenTelemetryPluginEnd2EndTest::BuildAndRegisterOpenTelemetryPlugin(
OpenTelemetryPluginEnd2EndTest::Options options) {
grpc::internal::OpenTelemetryPluginBuilderImpl ot_builder;
// We are resetting the MeterProvider and OpenTelemetry plugin at the start
// of each test to avoid test results from one test carrying over to another
// test. (Some measurements can get arbitrarily delayed.)
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>(
std::make_unique<opentelemetry::sdk::metrics::ViewRegistry>(),
*options.resource);
std::shared_ptr<opentelemetry::sdk::metrics::MetricReader> reader =
std::make_shared<grpc::testing::MockMetricReader>();
meter_provider->AddMetricReader(reader);
ot_builder.DisableAllMetrics();
for (const auto& metric_name : options.metric_names) {
ot_builder.EnableMetric(metric_name);
}
if (options.use_meter_provider) {
auto meter_provider =
std::make_shared<opentelemetry::sdk::metrics::MeterProvider>();
reader.reset(new grpc::testing::MockMetricReader);
meter_provider->AddMetricReader(reader);
ot_builder.SetMeterProvider(std::move(meter_provider));
}
ot_builder.SetTargetSelector(std::move(options.target_selector));
ot_builder.SetServerSelector(std::move(options.server_selector));
ot_builder.SetTargetAttributeFilter(
std::move(options.target_attribute_filter));
ot_builder.SetGenericMethodAttributeFilter(
std::move(options.generic_method_attribute_filter));
for (auto& option : options.plugin_options) {
ot_builder.AddPluginOption(std::move(option));
}
for (auto& optional_label_key : options.optional_label_keys) {
ot_builder.AddOptionalLabel(optional_label_key);
}
EXPECT_EQ(ot_builder.BuildAndRegisterGlobal(), absl::OkStatus());
return reader;
}
} // namespace testing
} // namespace grpc

@ -114,6 +114,11 @@ class OpenTelemetryPluginEnd2EndTest : public ::testing::Test {
return *this;
}
Options& add_optional_label(absl::string_view optional_label_key) {
optional_label_keys.emplace(optional_label_key);
return *this;
}
absl::flat_hash_set<absl::string_view> metric_names;
// TODO(yashykt): opentelemetry::sdk::resource::Resource doesn't have a copy
// assignment operator so wrapping it in a unique_ptr till it is fixed.
@ -135,6 +140,7 @@ class OpenTelemetryPluginEnd2EndTest : public ::testing::Test {
std::vector<
std::unique_ptr<grpc::internal::InternalOpenTelemetryPluginOption>>
plugin_options;
absl::flat_hash_set<absl::string_view> optional_label_keys;
};
// Note that we can't use SetUp() here since we want to send in parameters.
@ -147,6 +153,10 @@ class OpenTelemetryPluginEnd2EndTest : public ::testing::Test {
void SendRPC();
void SendGenericRPC();
std::shared_ptr<opentelemetry::sdk::metrics::MetricReader>
BuildAndRegisterOpenTelemetryPlugin(
OpenTelemetryPluginEnd2EndTest::Options options);
absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>
@ -155,7 +165,8 @@ class OpenTelemetryPluginEnd2EndTest : public ::testing::Test {
bool(const absl::flat_hash_map<
std::string,
std::vector<opentelemetry::sdk::metrics::PointDataAttributes>>&)>
continue_predicate);
continue_predicate,
opentelemetry::sdk::metrics::MetricReader* reader = nullptr);
const absl::string_view kMethodName = "grpc.testing.EchoTestService/Echo";
const absl::string_view kGenericMethodName = "foo/bar";

@ -88,9 +88,9 @@ DOCKERIMAGE_CURRENT_VERSIONS = {
"tools/dockerfile/interoptest/grpc_interop_ruby.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/grpc_interop_ruby@sha256:efd7f41a736dd4b8f73b32f5215b86f6bfe9013c422dfcd77978de0253aaee45",
"tools/dockerfile/interoptest/lb_interop_fake_servers.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/lb_interop_fake_servers@sha256:b89a51dd9147e1293f50ee64dd719fce5929ca7894d3770a3d80dbdecb99fd52",
"tools/dockerfile/test/android_ndk.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/android_ndk@sha256:ab154ecb062af2111d2d3550c4d3da3384201d9893bbd37d49e8160fc34bc137",
"tools/dockerfile/test/bazel.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/bazel@sha256:eb327f8e44f2712f557de1d8918c41c3cba1112c4b39b13104c29211ed8f827a",
"tools/dockerfile/test/bazel_arm64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/bazel_arm64@sha256:6cddc0ecdb42a7db7105b73fc3192edb911702102d1bac671e26d44a17d7aa95",
"tools/dockerfile/test/binder_transport_apk.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/binder_transport_apk@sha256:ff64e263dfa5491ee6fddd7f0d7c1a20ba756636655849a3b923c665d78c8ef2",
"tools/dockerfile/test/bazel.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/bazel@sha256:13ac6e425df68bcbee5898bb349171781597416bb93a84c4b923390e078602be",
"tools/dockerfile/test/bazel_arm64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/bazel_arm64@sha256:112e4f5085b43df2c02852f23c665aa5310e5a44ea3f4ae553a21b882a9b56aa",
"tools/dockerfile/test/binder_transport_apk.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/binder_transport_apk@sha256:498798c02e414c19c85781c245ca641f94f1494a47edec7861316d91b2d635e5",
"tools/dockerfile/test/csharp_debian11_arm64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/csharp_debian11_arm64@sha256:4d4bc5f15e03f3d3d8fd889670ecde2c66a2e4d2dd9db80733c05c1d90c8a248",
"tools/dockerfile/test/csharp_debian11_x64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/csharp_debian11_x64@sha256:b2e5c47d986312ea0850e2f2e696b45d23ee0aabceea161d31e28559e19ec4a5",
"tools/dockerfile/test/cxx_alpine_x64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/cxx_alpine_x64@sha256:f2019edf9f2afd5042567f11afb1aa78a789fc9acdcce5ee0c14cc11f6830ed7",
@ -112,5 +112,5 @@ DOCKERIMAGE_CURRENT_VERSIONS = {
"tools/dockerfile/test/rbe_ubuntu2004.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/rbe_ubuntu2004@sha256:d3951aeadf43e3bee6adc5b86d26cdaf0b9d1b5baf790d7b2530d1c197adc9f8",
"tools/dockerfile/test/ruby_debian11_arm64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/ruby_debian11_arm64@sha256:d2e79919b2e2d4cc36a29682ecb5170641df4fb506cfb453978ffdeb8a841bd9",
"tools/dockerfile/test/ruby_debian11_x64.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/ruby_debian11_x64@sha256:f8fc0ec22065278e5bc02ad7f9a68191e46d083035b3a90ed587561dba9c58c5",
"tools/dockerfile/test/sanity.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/sanity@sha256:a83cf1344c166834d76f49e08448f4253b535cdbc47efffb195cfa710f5114d6",
"tools/dockerfile/test/sanity.current_version": "docker://us-docker.pkg.dev/grpc-testing/testing-images-public/sanity@sha256:f9f5bdecfb8ea805b697e55b64a64827c683b507b3cbcc683e2f0101c9bc1643",
}

@ -17,6 +17,6 @@ This file is generated from the supported_bazel_versions.bzl.template
"""
SUPPORTED_BAZEL_VERSIONS = [
"6.5.0",
"7.1.0",
"6.5.0",
]

@ -439,6 +439,7 @@ for dirname in [
"grpc_package": lambda **kwargs: None,
"filegroup": lambda name, **kwargs: None,
"sh_library": lambda name, **kwargs: None,
"platform": lambda name, **kwargs: None,
},
{},
)

@ -1 +1 @@
us-docker.pkg.dev/grpc-testing/testing-images-public/bazel:71afcbd2698751336bd9890d00eb37ed790b3ac6@sha256:eb327f8e44f2712f557de1d8918c41c3cba1112c4b39b13104c29211ed8f827a
us-docker.pkg.dev/grpc-testing/testing-images-public/bazel:f49cc7eb69c326a62a936c5b4cdadc5f651df857@sha256:13ac6e425df68bcbee5898bb349171781597416bb93a84c4b923390e078602be

@ -38,7 +38,7 @@ RUN apt-get update && apt-get -y install \
# Bazel installation
# Must be in sync with tools/bazel
ENV BAZEL_VERSION 6.5.0
ENV BAZEL_VERSION 7.1.0
# The correct bazel version is already preinstalled, no need to use //tools/bazel wrapper.
ENV DISABLE_BAZEL_WRAPPER 1

@ -1 +1 @@
us-docker.pkg.dev/grpc-testing/testing-images-public/bazel_arm64:415b428a3108de92d1d819cd053181a110a8078b@sha256:6cddc0ecdb42a7db7105b73fc3192edb911702102d1bac671e26d44a17d7aa95
us-docker.pkg.dev/grpc-testing/testing-images-public/bazel_arm64:359ba8a4ea06e806f244b9488794fa23d0ae339f@sha256:112e4f5085b43df2c02852f23c665aa5310e5a44ea3f4ae553a21b882a9b56aa

@ -97,7 +97,7 @@ RUN apt-get update && apt-get -y install libc++-dev clang && apt-get clean
# Bazel installation
# Must be in sync with tools/bazel
ENV BAZEL_VERSION 6.5.0
ENV BAZEL_VERSION 7.1.0
# The correct bazel version is already preinstalled, no need to use //tools/bazel wrapper.
ENV DISABLE_BAZEL_WRAPPER 1

@ -1 +1 @@
us-docker.pkg.dev/grpc-testing/testing-images-public/binder_transport_apk:611a0d410d3032bc41175599a5f1495a07759c1f@sha256:ff64e263dfa5491ee6fddd7f0d7c1a20ba756636655849a3b923c665d78c8ef2
us-docker.pkg.dev/grpc-testing/testing-images-public/binder_transport_apk:d67135c8089a0721c8918828145d949127c34e7f@sha256:498798c02e414c19c85781c245ca641f94f1494a47edec7861316d91b2d635e5

@ -38,7 +38,7 @@ RUN apt-get update && apt-get -y install \
# Bazel installation
# Must be in sync with tools/bazel
ENV BAZEL_VERSION 6.5.0
ENV BAZEL_VERSION 7.1.0
# The correct bazel version is already preinstalled, no need to use //tools/bazel wrapper.
ENV DISABLE_BAZEL_WRAPPER 1

@ -1 +1 @@
us-docker.pkg.dev/grpc-testing/testing-images-public/sanity:38e0bdb9e26335d2d947263d33efa0f688dcd2fc@sha256:a83cf1344c166834d76f49e08448f4253b535cdbc47efffb195cfa710f5114d6
us-docker.pkg.dev/grpc-testing/testing-images-public/sanity:ed5f64033b5ac0038b25be49f8e53439e000461f@sha256:f9f5bdecfb8ea805b697e55b64a64827c683b507b3cbcc683e2f0101c9bc1643

@ -108,7 +108,7 @@ RUN apt-get update && apt-get install -y jq git && apt-get clean
# Bazel installation
# Must be in sync with tools/bazel
ENV BAZEL_VERSION 6.5.0
ENV BAZEL_VERSION 7.1.0
# The correct bazel version is already preinstalled, no need to use //tools/bazel wrapper.
ENV DISABLE_BAZEL_WRAPPER 1

@ -2115,6 +2115,7 @@ src/core/ext/xds/xds_lb_policy_registry.cc \
src/core/ext/xds/xds_lb_policy_registry.h \
src/core/ext/xds/xds_listener.cc \
src/core/ext/xds/xds_listener.h \
src/core/ext/xds/xds_metrics.h \
src/core/ext/xds/xds_resource_type.h \
src/core/ext/xds/xds_resource_type_impl.h \
src/core/ext/xds/xds_route_config.cc \
@ -2164,6 +2165,7 @@ src/core/lib/channel/metrics.h \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/promise_based_filter.h \
src/core/lib/channel/server_call_tracer_filter.cc \
src/core/lib/channel/server_call_tracer_filter.h \
src/core/lib/channel/status_util.cc \
src/core/lib/channel/status_util.h \
src/core/lib/channel/tcp_tracer.h \
@ -2206,6 +2208,7 @@ src/core/lib/event_engine/default_event_engine.h \
src/core/lib/event_engine/default_event_engine_factory.cc \
src/core/lib/event_engine/default_event_engine_factory.h \
src/core/lib/event_engine/event_engine.cc \
src/core/lib/event_engine/event_engine_context.h \
src/core/lib/event_engine/extensions/can_track_errors.h \
src/core/lib/event_engine/extensions/chaotic_good_extension.h \
src/core/lib/event_engine/extensions/supports_fd.h \

@ -1885,6 +1885,7 @@ src/core/ext/xds/xds_lb_policy_registry.cc \
src/core/ext/xds/xds_lb_policy_registry.h \
src/core/ext/xds/xds_listener.cc \
src/core/ext/xds/xds_listener.h \
src/core/ext/xds/xds_metrics.h \
src/core/ext/xds/xds_resource_type.h \
src/core/ext/xds/xds_resource_type_impl.h \
src/core/ext/xds/xds_route_config.cc \
@ -1936,6 +1937,7 @@ src/core/lib/channel/metrics.h \
src/core/lib/channel/promise_based_filter.cc \
src/core/lib/channel/promise_based_filter.h \
src/core/lib/channel/server_call_tracer_filter.cc \
src/core/lib/channel/server_call_tracer_filter.h \
src/core/lib/channel/status_util.cc \
src/core/lib/channel/status_util.h \
src/core/lib/channel/tcp_tracer.h \
@ -1978,6 +1980,7 @@ src/core/lib/event_engine/default_event_engine.h \
src/core/lib/event_engine/default_event_engine_factory.cc \
src/core/lib/event_engine/default_event_engine_factory.h \
src/core/lib/event_engine/event_engine.cc \
src/core/lib/event_engine/event_engine_context.h \
src/core/lib/event_engine/extensions/can_track_errors.h \
src/core/lib/event_engine/extensions/chaotic_good_extension.h \
src/core/lib/event_engine/extensions/supports_fd.h \

Loading…
Cancel
Save