22
33{% block content %}
44
5+ import logging as std_logging
56import warnings
67from typing import Callable, Dict, Optional, Sequence, Tuple, Union
78
@@ -13,6 +14,7 @@ from google.api_core import gapic_v1
1314import google.auth # type: ignore
1415from google.auth import credentials as ga_credentials # type: ignore
1516from google.auth.transport.grpc import SslCredentials # type: ignore
17+ from google.protobuf.json_format import MessageToJson
1618
1719import grpc # type: ignore
1820
@@ -42,6 +44,66 @@ from google.longrunning import operations_pb2 # type: ignore
4244{% endfilter %}
4345from .base import {{ service.name }}Transport, DEFAULT_CLIENT_INFO
4446
47+ try:
48+ from google.api_core import client_logging # type: ignore
49+ CLIENT_LOGGING_SUPPORTED = True # pragma: NO COVER
50+ except ImportError: # pragma: NO COVER
51+ CLIENT_LOGGING_SUPPORTED = False
52+
53+ _LOGGER = std_logging.getLogger(__name__)
54+
55+
56+ class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER
57+ def intercept_unary_unary(self, continuation, client_call_details, request):
58+ request_metadata = client_call_details.metadata
59+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(std_logging.DEBUG):
60+ try:
61+ request_payload = type(request).to_json(request)
62+ except:
63+ request_payload = MessageToJson(request)
64+ grpc_request = {
65+ "payload": request_payload,
66+ "requestMethod": "grpc",
67+ "metadata": dict(request_metadata),
68+ }
69+ _LOGGER.debug(
70+ f"Sending request for {client_call_details.method}",
71+ extra = {
72+ "serviceName": "{{ service.meta.address.proto }}",
73+ "rpcName": client_call_details.method,
74+ "request": grpc_request,
75+ {# 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.' #}
76+ "metadata": grpc_request["metadata"],
77+ },
78+ )
79+
80+ response = continuation(client_call_details, request)
81+ response_metadata = response.trailing_metadata()
82+ # Convert gRPC metadata `<class ' grpc.aio._metadata.Metadata' >` to list of tuples
83+ metadata = dict([(k, v) for k, v in response_metadata]) if response_metadata else None
84+ result = response.result()
85+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(std_logging.DEBUG): # pragma: NO COVER
86+ try:
87+ response_payload = type(result).to_json(result)
88+ except:
89+ response_payload = MessageToJson(result)
90+ grpc_response = {
91+ "payload": response_payload,
92+ "metadata": metadata,
93+ "status": "OK",
94+ }
95+ _LOGGER.debug(
96+ f"Received response for {client_call_details.method}.",
97+ extra = {
98+ "serviceName": "{{ service.meta.address.proto }}",
99+ "rpcName": client_call_details.method,
100+ "response": grpc_response,
101+ {# 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.' #}
102+ "metadata": grpc_response["metadata"],
103+ },
104+ )
105+ return response
106+
45107
46108class {{ service.name }}GrpcTransport({{ service.name }}Transport):
47109 """gRPC backend transport for {{ service.name }}.
@@ -123,7 +185,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
123185 google.api_core.exceptions.DuplicateCredentialArgs: If both ``credentials``
124186 and ``credentials_file`` are passed.
125187 """
126- self._grpc_channel = None
188+ self._base_channel = None
127189 self._ssl_channel_credentials = ssl_channel_credentials
128190 self._stubs: Dict[str, Callable] = {}
129191 {% if service .has_lro %}
@@ -140,7 +202,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
140202 credentials = None
141203 self._ignore_credentials = True
142204 # If a channel was explicitly provided, set it.
143- self._grpc_channel = channel
205+ self._base_channel = channel
144206 self._ssl_channel_credentials = None
145207
146208 else:
@@ -176,10 +238,10 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
176238 api_audience=api_audience,
177239 )
178240
179- if not self._grpc_channel :
241+ if not self._base_channel :
180242 # initialize with the provided callable or the default channel
181243 channel_init = channel or type(self).create_channel
182- self._grpc_channel = channel_init(
244+ self._base_channel = channel_init(
183245 self._host,
184246 # use the credentials which are saved
185247 credentials=self._credentials,
@@ -195,6 +257,11 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
195257 ],
196258 )
197259
260+ self._grpc_channel = self._base_channel
261+ if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(std_logging.DEBUG): # pragma: NO COVER
262+ self._interceptor = _LoggingClientInterceptor()
263+ self._grpc_channel = grpc.intercept_channel(self._base_channel, self._interceptor)
264+
198265 # Wrap messages. This must be done after self._grpc_channel exists
199266 self._prep_wrapped_messages(client_info)
200267
@@ -248,7 +315,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
248315 def grpc_channel(self) -> grpc.Channel:
249316 """Return the channel designed to connect to this service.
250317 """
251- return self._grpc_channel
318+ return self._base_channel
252319
253320 {% if service .has_lro %}
254321
@@ -262,7 +329,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
262329 # Quick check: Only create a new client if we do not already have one.
263330 if self._operations_client is None:
264331 self._operations_client = operations_v1.OperationsClient(
265- self.grpc_channel
332+ self._grpc_channel
266333 )
267334
268335 # Return the client from cache.
@@ -292,7 +359,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
292359 # gRPC handles serialization and deserialization, so we just need
293360 # to pass in the functions for each.
294361 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 }}(
362+ self._stubs['{{ method.transport_safe_name|snake_case }}'] = self._grpc_channel .{{ method.grpc_stub_type }}(
296363 '/{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}',
297364 request_serializer={{ method.input.ident }}.{% if method .input .ident .python_import .module .endswith ('_pb2' ) %} SerializeToString{% else %} serialize{% endif %} ,
298365 response_deserializer={{ method.output.ident }}.{% if method .output .ident .python_import .module .endswith ('_pb2' ) %} FromString{% else %} deserialize{% endif %} ,
@@ -320,7 +387,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
320387 # gRPC handles serialization and deserialization, so we just need
321388 # to pass in the functions for each.
322389 if "set_iam_policy" not in self._stubs:
323- self._stubs["set_iam_policy"] = self.grpc_channel .unary_unary(
390+ self._stubs["set_iam_policy"] = self._grpc_channel .unary_unary(
324391 "/google.iam.v1.IAMPolicy/SetIamPolicy",
325392 request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
326393 response_deserializer=policy_pb2.Policy.FromString,
@@ -346,7 +413,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
346413 # gRPC handles serialization and deserialization, so we just need
347414 # to pass in the functions for each.
348415 if "get_iam_policy" not in self._stubs:
349- self._stubs["get_iam_policy"] = self.grpc_channel .unary_unary(
416+ self._stubs["get_iam_policy"] = self._grpc_channel .unary_unary(
350417 "/google.iam.v1.IAMPolicy/GetIamPolicy",
351418 request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
352419 response_deserializer=policy_pb2.Policy.FromString,
@@ -374,7 +441,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
374441 # gRPC handles serialization and deserialization, so we just need
375442 # to pass in the functions for each.
376443 if "test_iam_permissions" not in self._stubs:
377- self._stubs["test_iam_permissions"] = self.grpc_channel .unary_unary(
444+ self._stubs["test_iam_permissions"] = self._grpc_channel .unary_unary(
378445 "/google.iam.v1.IAMPolicy/TestIamPermissions",
379446 request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
380447 response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
@@ -383,7 +450,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
383450 {% endif %}
384451
385452 def close(self):
386- self.grpc_channel .close()
453+ self._grpc_channel .close()
387454
388455 {% include '%namespace/%name_%version/%sub/services/%service/transports/_mixins.py.j2' %}
389456
0 commit comments