// // // 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 "src/cpp/ext/otel/otel_plugin.h" #include "absl/functional/any_invocable.h" #include "api/include/opentelemetry/metrics/provider.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include #include #include "src/core/lib/channel/call_tracer.h" #include "src/core/lib/config/core_configuration.h" #include "test/core/util/test_config.h" #include "test/cpp/end2end/test_service_impl.h" #include "test/cpp/ext/otel/otel_test_library.h" namespace grpc { namespace testing { namespace { TEST(OpenTelemetryPluginBuildTest, ApiDependency) { opentelemetry::metrics::Provider::GetMeterProvider(); } TEST(OpenTelemetryPluginBuildTest, SdkDependency) { opentelemetry::sdk::metrics::MeterProvider(); } TEST(OpenTelemetryPluginBuildTest, Basic) { grpc::OpenTelemetryPluginBuilder builder; } TEST_F(OpenTelemetryPluginEnd2EndTest, ClientAttemptStarted) { Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName}); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } TEST_F(OpenTelemetryPluginEnd2EndTest, ClientAttemptDuration) { Init( {grpc::OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName}); SendRPC(); const char* kMetricName = "grpc.client.attempt.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OpenTelemetryPluginEnd2EndTest, ClientAttemptSentTotalCompressedMessageSize) { Init({grpc::OpenTelemetryPluginBuilder:: kClientAttemptSentTotalCompressedMessageSizeInstrumentName}); SendRPC(); const char* kMetricName = "grpc.client.attempt.sent_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OpenTelemetryPluginEnd2EndTest, ClientAttemptRcvdTotalCompressedMessageSize) { Init({grpc::OpenTelemetryPluginBuilder:: kClientAttemptRcvdTotalCompressedMessageSizeInstrumentName}); SendRPC(); const char* kMetricName = "grpc.client.attempt.rcvd_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OpenTelemetryPluginEnd2EndTest, ServerCallStarted) { Init({grpc::OpenTelemetryPluginBuilder::kServerCallStartedInstrumentName}); SendRPC(); const char* kMetricName = "grpc.server.call.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto server_started_value = absl::get_if(&point_data->value_); ASSERT_NE(server_started_value, nullptr); ASSERT_EQ(*server_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 1); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); } TEST_F(OpenTelemetryPluginEnd2EndTest, ServerCallDuration) { Init({grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}); SendRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OpenTelemetryPluginEnd2EndTest, ServerCallSentTotalCompressedMessageSize) { Init({grpc::OpenTelemetryPluginBuilder:: kServerCallSentTotalCompressedMessageSizeInstrumentName}); SendRPC(); const char* kMetricName = "grpc.server.call.sent_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); EXPECT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } TEST_F(OpenTelemetryPluginEnd2EndTest, ServerCallRcvdTotalCompressedMessageSize) { Init({grpc::OpenTelemetryPluginBuilder:: kServerCallRcvdTotalCompressedMessageSizeInstrumentName}); SendRPC(); const char* kMetricName = "grpc.server.call.rcvd_total_compressed_message_size"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); ASSERT_EQ(absl::get(point_data->max_), 5); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "OK"); } // Make sure that no meter provider results in normal operations. TEST_F(OpenTelemetryPluginEnd2EndTest, NoMeterProviderRegistered) { Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/true); SendRPC(); } // Test that a channel selector returning true records metrics on the channel. TEST_F(OpenTelemetryPluginEnd2EndTest, TargetSelectorReturnsTrue) { Init({grpc::OpenTelemetryPluginBuilder:: kClientAttemptStartedInstrumentName}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/ [](absl::string_view /*target*/) { return true; }); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that a target selector returning false does not record metrics on the // channel. TEST_F(OpenTelemetryPluginEnd2EndTest, TargetSelectorReturnsFalse) { Init({grpc::OpenTelemetryPluginBuilder:: kClientAttemptStartedInstrumentName}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/ [](absl::string_view /*target*/) { return false; }); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_TRUE(data.empty()); } // Test that a server selector returning true records metrics on the server. TEST_F(OpenTelemetryPluginEnd2EndTest, ServerSelectorReturnsTrue) { Init({grpc::OpenTelemetryPluginBuilder:: kServerCallDurationInstrumentName}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ [](const grpc_core::ChannelArgs& /*channel_args*/) { return true; }); SendRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); const auto& server_attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(server_attributes.size(), 2); EXPECT_EQ(absl::get(server_attributes.at("grpc.method")), kMethodName); EXPECT_EQ(absl::get(server_attributes.at("grpc.status")), "OK"); } // Test that a server selector returning false does not record metrics on the // server. TEST_F(OpenTelemetryPluginEnd2EndTest, ServerSelectorReturnsFalse) { Init({grpc::OpenTelemetryPluginBuilder:: kServerCallDurationInstrumentName}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ [](const grpc_core::ChannelArgs& /*channel_args*/) { return false; }); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_TRUE(data.empty()); } // Test that a target attribute filter returning true records metrics with the // target as is on the channel. TEST_F(OpenTelemetryPluginEnd2EndTest, TargetAttributeFilterReturnsTrue) { Init({grpc::OpenTelemetryPluginBuilder:: kClientAttemptStartedInstrumentName}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/[](absl::string_view /*target*/) { return true; }); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that a target attribute filter returning false records metrics with the // target as "other". TEST_F(OpenTelemetryPluginEnd2EndTest, TargetAttributeFilterReturnsFalse) { Init({grpc::OpenTelemetryPluginBuilder:: kClientAttemptStartedInstrumentName}, /*resource=*/ opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ [server_address = canonical_server_address_]( absl::string_view /*target*/) { return false; }); SendRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, "other"); } // Test that generic method names are scrubbed properly on the client side. TEST_F(OpenTelemetryPluginEnd2EndTest, GenericClientRpc) { Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName}); SendGenericRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that generic method names are scrubbed properly on the client side if // the method attribute filter is set and it returns false. TEST_F(OpenTelemetryPluginEnd2EndTest, GenericClientRpcWithMethodAttributeFilterReturningFalse) { Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return false; }); SendGenericRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that generic method names is not scrubbed on the client side if // the method attribute filter is set and it returns true. TEST_F(OpenTelemetryPluginEnd2EndTest, GenericClientRpcWithMethodAttributeFilterReturningTrue) { Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptStartedInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return true; }); SendGenericRPC(); const char* kMetricName = "grpc.client.attempt.started"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& /*data*/) { return false; }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); auto client_started_value = absl::get_if(&point_data->value_); ASSERT_NE(client_started_value, nullptr); EXPECT_EQ(*client_started_value, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kGenericMethodName); const auto* target_value = absl::get_if(&attributes.at("grpc.target")); ASSERT_NE(target_value, nullptr); EXPECT_EQ(*target_value, canonical_server_address_); } // Test that generic method names are scrubbed properly on the server side. TEST_F(OpenTelemetryPluginEnd2EndTest, GenericServerRpc) { Init({grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}); SendGenericRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "UNIMPLEMENTED"); } // Test that generic method names are scrubbed properly on the server side if // the method attribute filter is set and it returns false. TEST_F(OpenTelemetryPluginEnd2EndTest, GenericServerRpcWithMethodAttributeFilterReturningFalse) { Init({grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return false; }); SendGenericRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, "other"); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "UNIMPLEMENTED"); } // Test that generic method names are not scrubbed on the server side if // the method attribute filter is set and it returns true. TEST_F(OpenTelemetryPluginEnd2EndTest, GenericServerRpcWithMethodAttributeFilterReturningTrue) { Init({grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ [](absl::string_view /*generic_method*/) { return true; }); SendGenericRPC(); const char* kMetricName = "grpc.server.call.duration"; auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains(kMetricName); }); ASSERT_EQ(data[kMetricName].size(), 1); auto point_data = absl::get_if( &data[kMetricName][0].point_data); ASSERT_NE(point_data, nullptr); ASSERT_EQ(point_data->count_, 1); const auto& attributes = data[kMetricName][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 2); const auto* method_value = absl::get_if(&attributes.at("grpc.method")); ASSERT_NE(method_value, nullptr); EXPECT_EQ(*method_value, kGenericMethodName); const auto* status_value = absl::get_if(&attributes.at("grpc.status")); ASSERT_NE(status_value, nullptr); EXPECT_EQ(*status_value, "UNIMPLEMENTED"); } using OpenTelemetryPluginOptionEnd2EndTest = OpenTelemetryPluginEnd2EndTest; class SimpleLabelIterable : public grpc::internal::LabelsIterable { public: explicit SimpleLabelIterable( std::pair label) : label_(label) {} absl::optional> Next() override { if (iterated_) { return absl::nullopt; } iterated_ = true; return label_; } size_t Size() const override { return 1; } void ResetIteratorPosition() override { iterated_ = false; } private: bool iterated_ = false; std::pair label_; }; class CustomLabelInjector : public grpc::internal::LabelsInjector { public: explicit CustomLabelInjector(std::pair label) : label_(std::move(label)) {} ~CustomLabelInjector() override {} std::unique_ptr GetLabels( grpc_metadata_batch* /*incoming_initial_metadata*/) const override { return std::make_unique(label_); } void AddLabels( grpc_metadata_batch* /*outgoing_initial_metadata*/, grpc::internal::LabelsIterable* /*labels_from_incoming_metadata*/) const override {} bool AddOptionalLabels( absl::Span>> /*optional_labels_span*/, opentelemetry::nostd::function_ref< bool(opentelemetry::nostd::string_view, opentelemetry::common::AttributeValue)> /*callback*/) const override { return true; } size_t GetOptionalLabelsSize( absl::Span>> /*optional_labels_span*/) const override { return 0; } private: std::pair label_; }; class CustomPluginOption : public grpc::internal::InternalOpenTelemetryPluginOption { public: CustomPluginOption(bool enabled_on_client, bool enabled_on_server, std::pair label) : enabled_on_client_(enabled_on_client), enabled_on_server_(enabled_on_server), label_injector_( std::make_unique(std::move(label))) {} ~CustomPluginOption() override {} bool IsActiveOnClientChannel(absl::string_view /*target*/) const override { return enabled_on_client_; } bool IsActiveOnServer(const grpc_core::ChannelArgs& /*args*/) const override { return enabled_on_server_; } const grpc::internal::LabelsInjector* labels_injector() const override { return label_injector_.get(); } private: bool enabled_on_client_; bool enabled_on_server_; std::unique_ptr label_injector_; }; TEST_F(OpenTelemetryPluginOptionEnd2EndTest, Basic) { std::vector< std::unique_ptr> plugin_option_list; plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ true, /*enabled_on_server*/ true, std::make_pair("key", "value"))); Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName, grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ absl::AnyInvocable(), /*plugin_options=*/std::move(plugin_option_list)); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains("grpc.client.attempt.duration") || !data.contains("grpc.server.call.duration"); }); // Verify client side metric ASSERT_EQ(data["grpc.client.attempt.duration"].size(), 1); const auto& client_attributes = data["grpc.client.attempt.duration"][0].attributes.GetAttributes(); EXPECT_EQ(client_attributes.size(), 4); EXPECT_EQ(absl::get(client_attributes.at("key")), "value"); // Verify server side metric ASSERT_EQ(data["grpc.server.call.duration"].size(), 1); const auto& server_attributes = data["grpc.server.call.duration"][0].attributes.GetAttributes(); EXPECT_EQ(server_attributes.size(), 3); EXPECT_EQ(absl::get(server_attributes.at("key")), "value"); } TEST_F(OpenTelemetryPluginOptionEnd2EndTest, ClientOnlyPluginOption) { std::vector< std::unique_ptr> plugin_option_list; plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ true, /*enabled_on_server*/ false, std::make_pair("key", "value"))); Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName, grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ absl::AnyInvocable(), /*plugin_options=*/std::move(plugin_option_list)); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains("grpc.client.attempt.duration") || !data.contains("grpc.server.call.duration"); }); // Verify client side metric ASSERT_EQ(data["grpc.client.attempt.duration"].size(), 1); const auto& client_attributes = data["grpc.client.attempt.duration"][0].attributes.GetAttributes(); EXPECT_EQ(client_attributes.size(), 4); EXPECT_EQ(absl::get(client_attributes.at("key")), "value"); // Verify server side metric ASSERT_EQ(data["grpc.server.call.duration"].size(), 1); const auto& server_attributes = data["grpc.server.call.duration"][0].attributes.GetAttributes(); EXPECT_EQ(server_attributes.size(), 2); EXPECT_THAT(server_attributes, ::testing::Not(::testing::Contains(::testing::Key("key")))); } TEST_F(OpenTelemetryPluginOptionEnd2EndTest, ServerOnlyPluginOption) { std::vector< std::unique_ptr> plugin_option_list; plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ false, /*enabled_on_server*/ true, std::make_pair("key", "value"))); Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName, grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ absl::AnyInvocable(), /*plugin_options=*/std::move(plugin_option_list)); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains("grpc.client.attempt.duration") || !data.contains("grpc.server.call.duration"); }); // Verify client side metric ASSERT_EQ(data["grpc.client.attempt.duration"].size(), 1); const auto& attributes = data["grpc.client.attempt.duration"][0].attributes.GetAttributes(); EXPECT_EQ(attributes.size(), 3); EXPECT_THAT(attributes, ::testing::Not(::testing::Contains(::testing::Key("key")))); // Verify server side metric ASSERT_EQ(data["grpc.server.call.duration"].size(), 1); const auto& server_attributes = data["grpc.server.call.duration"][0].attributes.GetAttributes(); EXPECT_EQ(server_attributes.size(), 3); EXPECT_EQ(absl::get(server_attributes.at("key")), "value"); } TEST_F(OpenTelemetryPluginOptionEnd2EndTest, MultipleEnabledAndDisabledPluginOptions) { std::vector< std::unique_ptr> plugin_option_list; plugin_option_list.reserve(5); plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ true, /*enabled_on_server*/ true, std::make_pair("key1", "value1"))); plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ true, /*enabled_on_server*/ false, std::make_pair("key2", "value2"))); plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ true, /*enabled_on_server*/ false, std::make_pair("key3", "value3"))); plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ false, /*enabled_on_server*/ true, std::make_pair("key4", "value4"))); plugin_option_list.emplace_back(std::make_unique( /*enabled_on_client*/ false, /*enabled_on_server*/ true, std::make_pair("key5", "value5"))); Init({grpc::OpenTelemetryPluginBuilder::kClientAttemptDurationInstrumentName, grpc::OpenTelemetryPluginBuilder::kServerCallDurationInstrumentName}, /*resource=*/opentelemetry::sdk::resource::Resource::Create({}), /*labels_injector=*/nullptr, /*test_no_meter_provider=*/false, /*labels_to_inject=*/{}, /*target_selector=*/absl::AnyInvocable(), /*server_selector=*/ absl::AnyInvocable(), /*target_attribute_filter=*/ absl::AnyInvocable(), /*generic_method_attribute_filter=*/ absl::AnyInvocable(), /*plugin_options=*/std::move(plugin_option_list)); SendRPC(); auto data = ReadCurrentMetricsData( [&](const absl::flat_hash_map< std::string, std::vector>& data) { return !data.contains("grpc.client.attempt.duration") || !data.contains("grpc.server.call.duration"); }); // Verify client side metric ASSERT_EQ(data["grpc.client.attempt.duration"].size(), 1); const auto& client_attributes = data["grpc.client.attempt.duration"][0].attributes.GetAttributes(); EXPECT_EQ(client_attributes.size(), 6); EXPECT_EQ(absl::get(client_attributes.at("key1")), "value1"); EXPECT_EQ(absl::get(client_attributes.at("key2")), "value2"); EXPECT_EQ(absl::get(client_attributes.at("key3")), "value3"); EXPECT_THAT(client_attributes, ::testing::Not(::testing::Contains(::testing::Key("key4")))); EXPECT_THAT(client_attributes, ::testing::Not(::testing::Contains(::testing::Key("key5")))); // Verify server side metric ASSERT_EQ(data["grpc.server.call.duration"].size(), 1); const auto& server_attributes = data["grpc.server.call.duration"][0].attributes.GetAttributes(); EXPECT_EQ(server_attributes.size(), 5); EXPECT_EQ(absl::get(server_attributes.at("key1")), "value1"); EXPECT_THAT(server_attributes, ::testing::Not(::testing::Contains(::testing::Key("key2")))); EXPECT_THAT(server_attributes, ::testing::Not(::testing::Contains(::testing::Key("key3")))); EXPECT_EQ(absl::get(server_attributes.at("key4")), "value4"); EXPECT_EQ(absl::get(server_attributes.at("key5")), "value5"); } } // namespace } // namespace testing } // namespace grpc int main(int argc, char** argv) { grpc::testing::TestEnvironment env(&argc, argv); ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); }