Skip to content

Commit 49b6177

Browse files
authored
Merge branch 'main' into selective-gapic-p1
2 parents 7acbbf2 + dddf797 commit 49b6177

File tree

8 files changed

+694
-118
lines changed
  • gapic/templates/%namespace/%name_%version/%sub/services/%service/transports
  • tests/integration/goldens
    • asset/google/cloud/asset_v1/services/asset_service/transports
    • credentials/google/iam/credentials_v1/services/iam_credentials/transports
    • eventarc/google/cloud/eventarc_v1/services/eventarc/transports
    • logging/google/cloud/logging_v2/services
      • config_service_v2/transports
      • logging_service_v2/transports
      • metrics_service_v2/transports
    • redis/google/cloud/redis_v1/services/cloud_redis/transports

8 files changed

+694
-118
lines changed

gapic/templates/%namespace/%name_%version/%sub/services/%service/transports/grpc.py.j2

Lines changed: 86 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
{% block content %}
44

5+
import logging as std_logging
6+
import pickle
57
import warnings
68
from typing import Callable, Dict, Optional, Sequence, Tuple, Union
79

@@ -13,8 +15,11 @@ from google.api_core import gapic_v1
1315
import google.auth # type: ignore
1416
from google.auth import credentials as ga_credentials # type: ignore
1517
from google.auth.transport.grpc import SslCredentials # type: ignore
18+
from google.protobuf.json_format import MessageToJson
19+
import google.protobuf.message
1620

1721
import grpc # type: ignore
22+
import proto # type: ignore
1823

1924
{% filter sort_lines %}
2025
{% set import_ns = namespace(has_operations_mixin=false) %}
@@ -42,6 +47,77 @@ from google.longrunning import operations_pb2 # type: ignore
4247
{% endfilter %}
4348
from .base import {{ service.name }}Transport, DEFAULT_CLIENT_INFO
4449

50+
try:
51+
from google.api_core import client_logging # type: ignore
52+
CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
53+
except ImportError: # pragma: NO COVER
54+
CLIENT_LOGGING_SUPPORTED = False
55+
56+
_LOGGER = std_logging.getLogger(__name__)
57+
58+
59+
class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER
60+
def intercept_unary_unary(self, continuation, client_call_details, request):
61+
logging_enabled = CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(std_logging.DEBUG)
62+
if logging_enabled: # pragma: NO COVER
63+
request_metadata = client_call_details.metadata
64+
if isinstance(request, proto.Message):
65+
{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2293): Investigate if we can improve this logic
66+
or wait for next gen protobuf.
67+
#}
68+
request_payload = type(request).to_json(request)
69+
elif isinstance(request, google.protobuf.message.Message):
70+
request_payload = MessageToJson(request)
71+
else:
72+
request_payload = f"{type(result).__name__}: {pickle.dumps(request)}"
73+
grpc_request = {
74+
"payload": request_payload,
75+
"requestMethod": "grpc",
76+
"metadata": dict(request_metadata),
77+
}
78+
_LOGGER.debug(
79+
f"Sending request for {client_call_details.method}",
80+
extra = {
81+
"serviceName": "{{ service.meta.address.proto }}",
82+
"rpcName": client_call_details.method,
83+
"request": grpc_request,
84+
{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2275): logging `metadata` seems repetitive and may need to be cleaned up. We're including it within "request" for consistency with REST transport. #}
85+
"metadata": grpc_request["metadata"],
86+
},
87+
)
88+
89+
response = continuation(client_call_details, request)
90+
if logging_enabled: # pragma: NO COVER
91+
response_metadata = response.trailing_metadata()
92+
# Convert gRPC metadata `<class 'grpc.aio._metadata.Metadata'>` to list of tuples
93+
metadata = dict([(k, v) for k, v in response_metadata]) if response_metadata else None
94+
result = response.result()
95+
if isinstance(result, proto.Message):
96+
{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2293): Investigate if we can improve this logic
97+
or wait for next gen protobuf.
98+
#}
99+
response_payload = type(result).to_json(result)
100+
elif isinstance(result, google.protobuf.message.Message):
101+
response_payload = MessageToJson(result)
102+
else:
103+
response_payload = f"{type(result).__name__}: {pickle.dumps(result)}"
104+
grpc_response = {
105+
"payload": response_payload,
106+
"metadata": metadata,
107+
"status": "OK",
108+
}
109+
_LOGGER.debug(
110+
f"Received response for {client_call_details.method}.",
111+
extra = {
112+
"serviceName": "{{ service.meta.address.proto }}",
113+
"rpcName": client_call_details.method,
114+
"response": grpc_response,
115+
{# TODO(https://github.com/googleapis/gapic-generator-python/issues/2275): logging `metadata` seems repetitive and may need to be cleaned up. We're including it within "request" for consistency with REST transport. #}
116+
"metadata": grpc_response["metadata"],
117+
},
118+
)
119+
return response
120+
45121

46122
class {{ service.name }}GrpcTransport({{ service.name }}Transport):
47123
"""gRPC backend transport for {{ service.name }}.
@@ -195,7 +271,10 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
195271
],
196272
)
197273

198-
# Wrap messages. This must be done after self._grpc_channel exists
274+
self._interceptor = _LoggingClientInterceptor()
275+
self._logged_channel = grpc.intercept_channel(self._grpc_channel, self._interceptor)
276+
277+
# Wrap messages. This must be done after self._logged_channel exists
199278
self._prep_wrapped_messages(client_info)
200279

201280

@@ -262,7 +341,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
262341
# Quick check: Only create a new client if we do not already have one.
263342
if self._operations_client is None:
264343
self._operations_client = operations_v1.OperationsClient(
265-
self.grpc_channel
344+
self._logged_channel
266345
)
267346

268347
# Return the client from cache.
@@ -292,7 +371,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
292371
# gRPC handles serialization and deserialization, so we just need
293372
# to pass in the functions for each.
294373
if '{{ method.transport_safe_name|snake_case }}' not in self._stubs:
295-
self._stubs['{{ method.transport_safe_name|snake_case }}'] = self.grpc_channel.{{ method.grpc_stub_type }}(
374+
self._stubs['{{ method.transport_safe_name|snake_case }}'] = self._logged_channel.{{ method.grpc_stub_type }}(
296375
'/{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}',
297376
request_serializer={{ method.input.ident }}.{% if method.input.ident.python_import.module.endswith('_pb2') %}SerializeToString{% else %}serialize{% endif %},
298377
response_deserializer={{ method.output.ident }}.{% if method.output.ident.python_import.module.endswith('_pb2') %}FromString{% else %}deserialize{% endif %},
@@ -320,7 +399,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
320399
# gRPC handles serialization and deserialization, so we just need
321400
# to pass in the functions for each.
322401
if "set_iam_policy" not in self._stubs:
323-
self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
402+
self._stubs["set_iam_policy"] = self._logged_channel.unary_unary(
324403
"/google.iam.v1.IAMPolicy/SetIamPolicy",
325404
request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
326405
response_deserializer=policy_pb2.Policy.FromString,
@@ -346,7 +425,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
346425
# gRPC handles serialization and deserialization, so we just need
347426
# to pass in the functions for each.
348427
if "get_iam_policy" not in self._stubs:
349-
self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
428+
self._stubs["get_iam_policy"] = self._logged_channel.unary_unary(
350429
"/google.iam.v1.IAMPolicy/GetIamPolicy",
351430
request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
352431
response_deserializer=policy_pb2.Policy.FromString,
@@ -374,7 +453,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
374453
# gRPC handles serialization and deserialization, so we just need
375454
# to pass in the functions for each.
376455
if "test_iam_permissions" not in self._stubs:
377-
self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
456+
self._stubs["test_iam_permissions"] = self._logged_channel.unary_unary(
378457
"/google.iam.v1.IAMPolicy/TestIamPermissions",
379458
request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
380459
response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
@@ -383,7 +462,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
383462
{% endif %}
384463

385464
def close(self):
386-
self.grpc_channel.close()
465+
self._logged_channel.close()
387466

388467
{% include '%namespace/%name_%version/%sub/services/%service/transports/_mixins.py.j2' %}
389468

0 commit comments

Comments
 (0)