Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

{% block content %}

import logging
import warnings
from typing import Callable, Dict, Optional, Sequence, Tuple, Union

Expand Down Expand Up @@ -42,6 +43,60 @@ from google.longrunning import operations_pb2 # type: ignore
{% endfilter %}
from .base import {{ service.name }}Transport, DEFAULT_CLIENT_INFO

try: # pragma: NO COVER
from google.api_core import client_logging # type: ignore
CLIENT_LOGGING_SUPPORTED = True
except ImportError:
CLIENT_LOGGING_SUPPORTED = False

_LOGGER = logging.getLogger(__name__)


class _LoggingClientInterceptor(grpc.UnaryUnaryClientInterceptor): # pragma: NO COVER
def intercept_unary_unary(self, continuation, client_call_details, request):
request_metadata = client_call_details.metadata
# if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(logging.DEBUG):
request_payload = {% if method.output.ident.is_proto_plus_type %}{type(request).to_json(request){% else %}json_format.MessageToJson(request){% endif %}
grpc_request = {
"payload": request_payload,
"requestMethod": "grpc",
"metadata": dict(request_metadata),
}
_LOGGER.debug(
f"Sending request for {client_call_details.method}",
extra = {
"serviceName": "{{ service.meta.address.proto }}",
"rpcName": client_call_details.method,
"request": grpc_request,
{# 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.' #}
"metadata": grpc_request["metadata"],
},
)

response = continuation(client_call_details, request)
response_metadata = response.trailing_metadata()
# Convert gRPC metadata `<class 'grpc.aio._metadata.Metadata'>` to list of tuples
metadata = [(k, v) for k, v in response_metadata]
result = response.result()
# if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(logging.DEBUG): # pragma: NO COVER
response_payload = {% if method.output.ident.is_proto_plus_type %}{type(response).to_json(response){% else %}json_format.MessageToJson(response){% endif %}
grpc_response = {
"payload": response_payload,
"metadata": dict(metadata),
"status": "OK",
}
_LOGGER.debug(
f"Received response for {client_call_details.method}.",
extra = {
"serviceName": "{{ service.meta.address.proto }}",
"rpcName": client_call_details.method,
"response": grpc_response,
{# 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.' #}
"metadata": grpc_response["metadata"],
},
)
return response


class {{ service.name }}GrpcTransport({{ service.name }}Transport):
"""gRPC backend transport for {{ service.name }}.
Expand Down Expand Up @@ -196,6 +251,8 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
)

# Wrap messages. This must be done after self._grpc_channel exists
self._interceptor = _LoggingClientInterceptor()
self._grpc_intercept_channel = grpc.intercept_channel(self._grpc_channel, self._interceptor)
self._prep_wrapped_messages(client_info)


Expand Down Expand Up @@ -262,7 +319,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
# Quick check: Only create a new client if we do not already have one.
if self._operations_client is None:
self._operations_client = operations_v1.OperationsClient(
self.grpc_channel
self._grpc_intercept_channel
)

# Return the client from cache.
Expand Down Expand Up @@ -292,7 +349,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if '{{ method.transport_safe_name|snake_case }}' not in self._stubs:
self._stubs['{{ method.transport_safe_name|snake_case }}'] = self.grpc_channel.{{ method.grpc_stub_type }}(
self._stubs['{{ method.transport_safe_name|snake_case }}'] = self._grpc_intercept_channel.{{ method.grpc_stub_type }}(
'/{{ '.'.join(method.meta.address.package) }}.{{ service.name }}/{{ method.name }}',
request_serializer={{ method.input.ident }}.{% if method.input.ident.python_import.module.endswith('_pb2') %}SerializeToString{% else %}serialize{% endif %},
response_deserializer={{ method.output.ident }}.{% if method.output.ident.python_import.module.endswith('_pb2') %}FromString{% else %}deserialize{% endif %},
Expand Down Expand Up @@ -320,7 +377,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "set_iam_policy" not in self._stubs:
self._stubs["set_iam_policy"] = self.grpc_channel.unary_unary(
self._stubs["set_iam_policy"] = self._grpc_intercept_channel.unary_unary(
"/google.iam.v1.IAMPolicy/SetIamPolicy",
request_serializer=iam_policy_pb2.SetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
Expand All @@ -346,7 +403,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "get_iam_policy" not in self._stubs:
self._stubs["get_iam_policy"] = self.grpc_channel.unary_unary(
self._stubs["get_iam_policy"] = self._grpc_intercept_channel.unary_unary(
"/google.iam.v1.IAMPolicy/GetIamPolicy",
request_serializer=iam_policy_pb2.GetIamPolicyRequest.SerializeToString,
response_deserializer=policy_pb2.Policy.FromString,
Expand Down Expand Up @@ -374,7 +431,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
# gRPC handles serialization and deserialization, so we just need
# to pass in the functions for each.
if "test_iam_permissions" not in self._stubs:
self._stubs["test_iam_permissions"] = self.grpc_channel.unary_unary(
self._stubs["test_iam_permissions"] = self._grpc_intercept_channel.unary_unary(
"/google.iam.v1.IAMPolicy/TestIamPermissions",
request_serializer=iam_policy_pb2.TestIamPermissionsRequest.SerializeToString,
response_deserializer=iam_policy_pb2.TestIamPermissionsResponse.FromString,
Expand All @@ -383,7 +440,7 @@ class {{ service.name }}GrpcTransport({{ service.name }}Transport):
{% endif %}

def close(self):
self.grpc_channel.close()
self._grpc_intercept_channel.close()

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,59 @@ from google.longrunning import operations_pb2 # type: ignore
from .base import {{ service.name }}Transport, DEFAULT_CLIENT_INFO
from .grpc import {{ service.name }}GrpcTransport

import logging

try: # pragma: NO COVER
from google.api_core import client_logging # type: ignore
CLIENT_LOGGING_SUPPORTED = True
except ImportError:
CLIENT_LOGGING_SUPPORTED = False

_LOGGER = logging.getLogger(__name__)


class MetadataAsyncClientInterceptor(grpc.aio.UnaryUnaryClientInterceptor): # pragma: NO COVER
async def intercept_unary_unary(self, continuation, client_call_details, request):
request_metadata = client_call_details.metadata
if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(logging.DEBUG):
grpc_request = {
"payload": type(request).to_json(request),
"requestMethod": "grpc",
"metadata": dict(request_metadata),
}
_LOGGER.debug(
f"Sending request for {client_call_details.method}",
extra = {
"serviceName": "{{ service.meta.address.proto }}",
"rpcName": str(client_call_details.method),
"request": grpc_request,
{# 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.' #}
"metadata": grpc_request["metadata"],
},
)
response = await continuation(client_call_details, request)
response_metadata = await response.trailing_metadata()
# Convert gRPC metadata `<class 'grpc.aio._metadata.Metadata'>` to list of tuples
metadata = [(k, v) for k, v in response_metadata]
result = await response
if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(logging.DEBUG):
grpc_response = {
"payload": type(result).to_json(result),
"metadata": dict(metadata),
"status": "OK",
}
_LOGGER.debug(
f"Received response to rpc {client_call_details.method}.",
extra = {
"serviceName": "{{ service.meta.address.proto }}",
"rpcName": str(client_call_details.method),
"response": grpc_response,
{# 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.' #}
"metadata": grpc_response["metadata"],
},
)
return response


class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):
"""gRPC AsyncIO backend transport for {{ service.name }}.
Expand Down Expand Up @@ -244,6 +297,9 @@ class {{ service.grpc_asyncio_transport_name }}({{ service.name }}Transport):

# Wrap messages. This must be done after self._grpc_channel exists
self._wrap_with_kind = "kind" in inspect.signature(gapic_v1.method_async.wrap_method).parameters
if CLIENT_LOGGING_SUPPORTED and _LOGGER.isEnabledFor(logging.DEBUG):
self._interceptor = MetadataAsyncClientInterceptor()
self._grpc_channel._unary_unary_interceptors.append(self._interceptor)
self._prep_wrapped_messages(client_info)

@property
Expand Down
Loading
Loading