From 0d3ab144d208438ecffb667661cd45cded34d79e Mon Sep 17 00:00:00 2001 From: owent Date: Sun, 12 Apr 2026 19:10:25 +0800 Subject: [PATCH 1/2] Allow user use custom grpc::ChannelArguments --- .../exporters/otlp/otlp_grpc_client.h | 5 ++ .../exporters/otlp/otlp_grpc_client_options.h | 10 ++- exporters/otlp/src/otlp_grpc_client.cc | 83 ++++++++++--------- exporters/otlp/test/otlp_grpc_target_test.cc | 61 +++++++++++++- 4 files changed, 120 insertions(+), 39 deletions(-) diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h index 367e8ebbff..df33a9e24f 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h @@ -254,6 +254,11 @@ class OtlpGrpcClient bool IsShutdown() const noexcept; private: + friend class OtlpGrpcClientTestPeer; + + // Build gRPC channel arguments from exporter options. Shared by MakeChannel() and unit tests. + static grpc::ChannelArguments BuildChannelArguments(const OtlpGrpcClientOptions &options); + // Stores if this gRPC client had its Shutdown() method called std::atomic is_shutdown_; diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client_options.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client_options.h index baec1350e7..a35aee605f 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client_options.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client_options.h @@ -13,7 +13,8 @@ namespace grpc { class ChannelCredentials; -} +class ChannelArguments; +} // namespace grpc OPENTELEMETRY_BEGIN_NAMESPACE namespace exporter @@ -90,6 +91,13 @@ struct OtlpGrpcClientOptions /** The backoff will be multiplied by this value after each retry attempt. */ float retry_policy_backoff_multiplier{}; + + /** + * Optional caller-provided gRPC channel arguments. + * This is a non-owning pointer, and the pointed-to arguments are copied when the channel is + * created. + */ + const grpc::ChannelArguments *channel_arguments{}; }; } // namespace otlp diff --git a/exporters/otlp/src/otlp_grpc_client.cc b/exporters/otlp/src/otlp_grpc_client.cc index a5fa6b7b95..35443528c6 100644 --- a/exporters/otlp/src/otlp_grpc_client.cc +++ b/exporters/otlp/src/otlp_grpc_client.cc @@ -355,7 +355,52 @@ std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO return nullptr; } + grpc::ChannelArguments grpc_arguments = BuildChannelArguments(options); + + if (options.use_ssl_credentials) + { + grpc::SslCredentialsOptions ssl_opts; + ssl_opts.pem_root_certs = GetFileContentsOrInMemoryContents( + options.ssl_credentials_cacert_path, options.ssl_credentials_cacert_as_string); +#ifdef ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW + ssl_opts.pem_private_key = GetFileContentsOrInMemoryContents(options.ssl_client_key_path, + options.ssl_client_key_string); + ssl_opts.pem_cert_chain = GetFileContentsOrInMemoryContents(options.ssl_client_cert_path, + options.ssl_client_cert_string); + +#endif + channel = + grpc::CreateCustomChannel(grpc_target, grpc::SslCredentials(ssl_opts), grpc_arguments); + } + else + { + channel = + grpc::CreateCustomChannel(grpc_target, grpc::InsecureChannelCredentials(), grpc_arguments); + } + +#ifdef ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW + if (options.credentials) + { + if (options.use_ssl_credentials) + { + OTEL_INTERNAL_LOG_WARN( + "[OTLP GRPC Client] Both 'credentials' and 'use_ssl_credentials' options are set. " + "The former takes priority."); + } + channel = grpc::CreateCustomChannel(grpc_target, options.credentials, grpc_arguments); + } +#endif // ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW + + return channel; +} + +grpc::ChannelArguments OtlpGrpcClient::BuildChannelArguments(const OtlpGrpcClientOptions &options) +{ grpc::ChannelArguments grpc_arguments; + if (options.channel_arguments != nullptr) + { + grpc_arguments = *options.channel_arguments; + } grpc_arguments.SetUserAgentPrefix(options.user_agent); if (options.max_threads > 0) @@ -399,9 +444,7 @@ std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO ] })"}; - // Allocate string with buffer large enough to hold the formatted json config auto service_config = std::string(kServiceConfigJson.size(), '\0'); - // Prior to C++17, need to explicitly cast away constness from `data()` buffer std::snprintf( const_cast(service_config.data()), service_config.size(), kServiceConfigJson.data(), options.retry_policy_max_attempts, @@ -413,41 +456,7 @@ std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO } #endif // ENABLE_OTLP_RETRY_PREVIEW - if (options.use_ssl_credentials) - { - grpc::SslCredentialsOptions ssl_opts; - ssl_opts.pem_root_certs = GetFileContentsOrInMemoryContents( - options.ssl_credentials_cacert_path, options.ssl_credentials_cacert_as_string); -#ifdef ENABLE_OTLP_GRPC_SSL_MTLS_PREVIEW - ssl_opts.pem_private_key = GetFileContentsOrInMemoryContents(options.ssl_client_key_path, - options.ssl_client_key_string); - ssl_opts.pem_cert_chain = GetFileContentsOrInMemoryContents(options.ssl_client_cert_path, - options.ssl_client_cert_string); - -#endif - channel = - grpc::CreateCustomChannel(grpc_target, grpc::SslCredentials(ssl_opts), grpc_arguments); - } - else - { - channel = - grpc::CreateCustomChannel(grpc_target, grpc::InsecureChannelCredentials(), grpc_arguments); - } - -#ifdef ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW - if (options.credentials) - { - if (options.use_ssl_credentials) - { - OTEL_INTERNAL_LOG_WARN( - "[OTLP GRPC Client] Both 'credentials' and 'use_ssl_credentials' options are set. " - "The former takes priority."); - } - channel = grpc::CreateCustomChannel(grpc_target, options.credentials, grpc_arguments); - } -#endif // ENABLE_OTLP_GRPC_CREDENTIAL_PREVIEW - - return channel; + return grpc_arguments; } std::unique_ptr OtlpGrpcClient::MakeClientContext( diff --git a/exporters/otlp/test/otlp_grpc_target_test.cc b/exporters/otlp/test/otlp_grpc_target_test.cc index a10282a199..65a1298f99 100644 --- a/exporters/otlp/test/otlp_grpc_target_test.cc +++ b/exporters/otlp/test/otlp_grpc_target_test.cc @@ -1,8 +1,10 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#include #include -#include + +#include #include "opentelemetry/exporters/otlp/otlp_grpc_client.h" #include "opentelemetry/exporters/otlp/otlp_grpc_client_options.h" @@ -14,6 +16,34 @@ namespace exporter namespace otlp { +namespace +{ + +const grpc_arg *FindChannelArg(const grpc_channel_args &channel_args, const char *key) +{ + for (std::size_t i = 0; i < channel_args.num_args; ++i) + { + const grpc_arg &arg = channel_args.args[i]; + if (arg.key != nullptr && std::strcmp(arg.key, key) == 0) + { + return &arg; + } + } + + return nullptr; +} + +} // namespace + +class OtlpGrpcClientTestPeer : public ::testing::Test +{ +public: + static grpc::ChannelArguments BuildChannelArguments(const OtlpGrpcClientOptions &options) + { + return OtlpGrpcClient::BuildChannelArguments(options); + } +}; + TEST(OtlpGrpcClientEndpointTest, GrpcClientTest) { OtlpGrpcClientOptions opts1; @@ -34,6 +64,35 @@ TEST(OtlpGrpcClientEndpointTest, GrpcClientTest) EXPECT_EQ(target3, "localhost:4317"); } +TEST_F(OtlpGrpcClientTestPeer, ChannelArgumentsOptionTest) +{ + OtlpGrpcClientOptions options; + options.user_agent = "otlp-grpc-target-test"; + + grpc::ChannelArguments channel_arguments; + channel_arguments.SetMaxReceiveMessageSize(1); + options.channel_arguments = &channel_arguments; + + auto built_arguments = BuildChannelArguments(options); + channel_arguments.SetMaxReceiveMessageSize(-1); + + auto built_channel_args = built_arguments.c_channel_args(); + + const grpc_arg *max_receive_arg = + FindChannelArg(built_channel_args, GRPC_ARG_MAX_RECEIVE_MESSAGE_LENGTH); + ASSERT_NE(max_receive_arg, nullptr); + EXPECT_EQ(max_receive_arg->type, GRPC_ARG_INTEGER); + EXPECT_EQ(max_receive_arg->value.integer, 1); + + const grpc_arg *user_agent_arg = + FindChannelArg(built_channel_args, GRPC_ARG_PRIMARY_USER_AGENT_STRING); + ASSERT_NE(user_agent_arg, nullptr); + EXPECT_EQ(user_agent_arg->type, GRPC_ARG_STRING); + EXPECT_EQ(std::strncmp(user_agent_arg->value.string, options.user_agent.c_str(), + options.user_agent.size()), + 0); +} + } // namespace otlp } // namespace exporter OPENTELEMETRY_END_NAMESPACE From cc5816473ba23264d37a9723cecb20e1bddba092 Mon Sep 17 00:00:00 2001 From: owent Date: Sun, 12 Apr 2026 19:49:36 +0800 Subject: [PATCH 2/2] Fixes IWYU issue --- .../opentelemetry/exporters/otlp/otlp_grpc_client.h | 11 +++++++++-- exporters/otlp/src/otlp_grpc_client.cc | 11 ++++++----- exporters/otlp/test/otlp_grpc_target_test.cc | 6 +++++- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h index df33a9e24f..6185879277 100644 --- a/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h +++ b/exporters/otlp/include/opentelemetry/exporters/otlp/otlp_grpc_client.h @@ -35,6 +35,11 @@ class Arena; } } // namespace google +namespace grpc +{ +class ChannelArguments; +} // namespace grpc + namespace opentelemetry { namespace proto @@ -256,8 +261,10 @@ class OtlpGrpcClient private: friend class OtlpGrpcClientTestPeer; - // Build gRPC channel arguments from exporter options. Shared by MakeChannel() and unit tests. - static grpc::ChannelArguments BuildChannelArguments(const OtlpGrpcClientOptions &options); + // Populate gRPC channel arguments from exporter options. Shared by MakeChannel() and unit + // tests. + static void PopulateChannelArguments(const OtlpGrpcClientOptions &options, + grpc::ChannelArguments &grpc_arguments); // Stores if this gRPC client had its Shutdown() method called std::atomic is_shutdown_; diff --git a/exporters/otlp/src/otlp_grpc_client.cc b/exporters/otlp/src/otlp_grpc_client.cc index 35443528c6..5f29fce7a9 100644 --- a/exporters/otlp/src/otlp_grpc_client.cc +++ b/exporters/otlp/src/otlp_grpc_client.cc @@ -355,7 +355,8 @@ std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO return nullptr; } - grpc::ChannelArguments grpc_arguments = BuildChannelArguments(options); + grpc::ChannelArguments grpc_arguments; + PopulateChannelArguments(options, grpc_arguments); if (options.use_ssl_credentials) { @@ -394,9 +395,11 @@ std::shared_ptr OtlpGrpcClient::MakeChannel(const OtlpGrpcClientO return channel; } -grpc::ChannelArguments OtlpGrpcClient::BuildChannelArguments(const OtlpGrpcClientOptions &options) +void OtlpGrpcClient::PopulateChannelArguments(const OtlpGrpcClientOptions &options, + grpc::ChannelArguments &grpc_arguments) { - grpc::ChannelArguments grpc_arguments; + grpc_arguments = grpc::ChannelArguments{}; + if (options.channel_arguments != nullptr) { grpc_arguments = *options.channel_arguments; @@ -455,8 +458,6 @@ grpc::ChannelArguments OtlpGrpcClient::BuildChannelArguments(const OtlpGrpcClien grpc_arguments.SetServiceConfigJSON(service_config); } #endif // ENABLE_OTLP_RETRY_PREVIEW - - return grpc_arguments; } std::unique_ptr OtlpGrpcClient::MakeClientContext( diff --git a/exporters/otlp/test/otlp_grpc_target_test.cc b/exporters/otlp/test/otlp_grpc_target_test.cc index 65a1298f99..b71ea93118 100644 --- a/exporters/otlp/test/otlp_grpc_target_test.cc +++ b/exporters/otlp/test/otlp_grpc_target_test.cc @@ -1,10 +1,12 @@ // Copyright The OpenTelemetry Authors // SPDX-License-Identifier: Apache-2.0 +#include #include #include #include +#include #include "opentelemetry/exporters/otlp/otlp_grpc_client.h" #include "opentelemetry/exporters/otlp/otlp_grpc_client_options.h" @@ -40,7 +42,9 @@ class OtlpGrpcClientTestPeer : public ::testing::Test public: static grpc::ChannelArguments BuildChannelArguments(const OtlpGrpcClientOptions &options) { - return OtlpGrpcClient::BuildChannelArguments(options); + grpc::ChannelArguments grpc_arguments; + OtlpGrpcClient::PopulateChannelArguments(options, grpc_arguments); + return grpc_arguments; } };