2525 pytest .skip ("No GRPC" , allow_module_level = True )
2626
2727
28- import google .api_core .gapic_v1 .client_info
2928import google .api_core .gapic_v1 .method
3029import google .api_core .page_iterator
3130from google .api_core import client_options as client_options_lib
3231from google .api_core import exceptions , retry , timeout
32+ from google .api_core .gapic_v1 import client_info
3333
3434
3535def _utcnow_monotonic ():
@@ -477,6 +477,8 @@ def test_wrap_method_otel_tracing_enabled_success(mock_otel, method_name):
477477 "rpc.system" : "grpc" ,
478478 "rpc.service" : "google.cloud.secretmanager.v1.SecretManagerService" ,
479479 "rpc.method" : "ListSecrets" ,
480+ "gcp.client.service" : "SecretManagerService" ,
481+ "gcp.client.repo" : "googleapis/google-cloud-python" ,
480482 },
481483 )
482484
@@ -506,6 +508,8 @@ def test_wrap_method_otel_tracing_custom_client_options(mock_otel):
506508 "rpc.system" : "grpc" ,
507509 "rpc.service" : "google.test.Service" ,
508510 "rpc.method" : "TestMethod" ,
511+ "gcp.client.service" : "Service" ,
512+ "gcp.client.repo" : "googleapis/google-cloud-python" ,
509513 },
510514 )
511515
@@ -630,6 +634,8 @@ def test_wrap_method_async_otel_tracing(mock_otel):
630634 "rpc.system" : "grpc" ,
631635 "rpc.service" : "google.test.AsyncService" ,
632636 "rpc.method" : "AsyncMethod" ,
637+ "gcp.client.service" : "AsyncService" ,
638+ "gcp.client.repo" : "googleapis/google-cloud-python" ,
633639 },
634640 )
635641
@@ -655,3 +661,143 @@ def test_wrap_method_async_otel_tracing_streaming_skips_span(mock_otel):
655661 mock_trace = mock_otel .trace ,
656662 expected_result = "async_success" ,
657663 )
664+
665+
666+ def test_wrap_method_otel_tracing_attributes_with_client_info (mock_otel ):
667+ """Proves that client_info version, repo, and artifact attributes are included in the T3 span."""
668+ mock_target = mock .Mock (return_value = "success" )
669+
670+ info = client_info .ClientInfo (
671+ client_library_version = "2.16.0" ,
672+ )
673+ info .client_repo = "googleapis/google-cloud-python-test"
674+ info .client_artifact = "google-cloud-secretmanager"
675+
676+ wrapped = google .api_core .gapic_v1 .method .wrap_method (
677+ mock_target ,
678+ client_info = info ,
679+ method_name = "/google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
680+ )
681+ result = wrapped ()
682+
683+ assert result == "success"
684+ mock_otel .tracer .start_as_current_span .assert_called_once_with (
685+ "google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
686+ kind = "CLIENT" ,
687+ attributes = {
688+ "rpc.system" : "grpc" ,
689+ "rpc.service" : "google.cloud.secretmanager.v1.SecretManagerService" ,
690+ "rpc.method" : "ListSecrets" ,
691+ "gcp.client.service" : "SecretManagerService" ,
692+ "gcp.client.repo" : "googleapis/google-cloud-python-test" ,
693+ "gcp.client.version" : "2.16.0" ,
694+ "gcp.client.artifact" : "google-cloud-secretmanager" ,
695+ },
696+ )
697+
698+
699+ def test_wrap_method_otel_tracing_attributes_fallback_gapic_version (mock_otel ):
700+ """Proves that gapic_version is used when client_library_version is not set."""
701+ mock_target = mock .Mock (return_value = "success" )
702+
703+ info = client_info .ClientInfo (
704+ gapic_version = "1.5.0" ,
705+ )
706+
707+ wrapped = google .api_core .gapic_v1 .method .wrap_method (
708+ mock_target ,
709+ client_info = info ,
710+ method_name = "/google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
711+ )
712+ result = wrapped ()
713+
714+ assert result == "success"
715+ mock_otel .tracer .start_as_current_span .assert_called_once_with (
716+ "google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
717+ kind = "CLIENT" ,
718+ attributes = {
719+ "rpc.system" : "grpc" ,
720+ "rpc.service" : "google.cloud.secretmanager.v1.SecretManagerService" ,
721+ "rpc.method" : "ListSecrets" ,
722+ "gcp.client.service" : "SecretManagerService" ,
723+ "gcp.client.repo" : "googleapis/google-cloud-python" ,
724+ "gcp.client.version" : "1.5.0" ,
725+ },
726+ )
727+
728+
729+ def test_wrap_method_otel_tracing_attributes_no_client_info (mock_otel ):
730+ """Proves that when client_info is None, gcp.client repo, version, and artifact are omitted."""
731+ mock_target = mock .Mock (return_value = "success" )
732+
733+ wrapped = google .api_core .gapic_v1 .method .wrap_method (
734+ mock_target ,
735+ client_info = None ,
736+ method_name = "/google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
737+ )
738+ result = wrapped ()
739+
740+ assert result == "success"
741+ mock_otel .tracer .start_as_current_span .assert_called_once_with (
742+ "google.cloud.secretmanager.v1.SecretManagerService/ListSecrets" ,
743+ kind = "CLIENT" ,
744+ attributes = {
745+ "rpc.system" : "grpc" ,
746+ "rpc.service" : "google.cloud.secretmanager.v1.SecretManagerService" ,
747+ "rpc.method" : "ListSecrets" ,
748+ "gcp.client.service" : "SecretManagerService" ,
749+ },
750+ )
751+
752+
753+ def test_wrap_method_otel_tracing_attributes_no_service (mock_otel ):
754+ """Proves that when method_name has no service prefix, gcp.client.service is omitted."""
755+ mock_target = mock .Mock (return_value = "success" )
756+
757+ wrapped = google .api_core .gapic_v1 .method .wrap_method (
758+ mock_target ,
759+ client_info = None ,
760+ method_name = "ListSecrets" ,
761+ )
762+ result = wrapped ()
763+
764+ assert result == "success"
765+ mock_otel .tracer .start_as_current_span .assert_called_once_with (
766+ "ListSecrets" ,
767+ kind = "CLIENT" ,
768+ attributes = {
769+ "rpc.system" : "grpc" ,
770+ "rpc.service" : "" ,
771+ "rpc.method" : "ListSecrets" ,
772+ },
773+ )
774+
775+
776+ def test_wrap_method_async_otel_tracing_with_client_info (mock_otel ):
777+ """Proves that method_async.wrap_method passes client_info to _GapicCallable."""
778+ from google .api_core .gapic_v1 import method_async
779+
780+ mock_target = mock .Mock (return_value = "async_success" )
781+ info = client_info .ClientInfo (client_library_version = "3.0.0" )
782+
783+ wrapped = method_async .wrap_method (
784+ mock_target ,
785+ kind = None ,
786+ client_info = info ,
787+ method_name = "google.test.AsyncService/AsyncMethod" ,
788+ )
789+ result = wrapped ()
790+
791+ assert result == "async_success"
792+ mock_otel .tracer .start_as_current_span .assert_called_once_with (
793+ "google.test.AsyncService/AsyncMethod" ,
794+ kind = "CLIENT" ,
795+ attributes = {
796+ "rpc.system" : "grpc" ,
797+ "rpc.service" : "google.test.AsyncService" ,
798+ "rpc.method" : "AsyncMethod" ,
799+ "gcp.client.service" : "AsyncService" ,
800+ "gcp.client.repo" : "googleapis/google-cloud-python" ,
801+ "gcp.client.version" : "3.0.0" ,
802+ },
803+ )
0 commit comments