Skip to content

Commit ae07484

Browse files
committed
feat: add client debug logging support for unary-stream gRPC calls
1 parent 7fbd5fd commit ae07484

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

google/api_core/grpc_helpers.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,13 @@
1717

1818
import collections
1919
import functools
20+
import logging
21+
import pickle
2022
import warnings
2123

24+
import google.protobuf.json_format
2225
import grpc
26+
import proto
2327

2428
from google.api_core import exceptions
2529
import google.auth
@@ -48,6 +52,7 @@
4852
else:
4953
HAS_GRPC_GCP = False
5054

55+
_LOGGER = logging.getLogger(__name__)
5156

5257
# The list of gRPC Callable interfaces that return iterators.
5358
_STREAM_WRAP_CLASSES = (grpc.UnaryStreamMultiCallable, grpc.StreamStreamMultiCallable)
@@ -113,7 +118,31 @@ def __next__(self) -> P:
113118
result = self._stored_first_result
114119
del self._stored_first_result
115120
return result
116-
return next(self._wrapped)
121+
result = next(self._wrapped)
122+
123+
logging_enabled = _LOGGER.isEnabledFor(
124+
logging.DEBUG
125+
)
126+
if logging_enabled: # pragma: NO COVER
127+
if isinstance(result, proto.Message):
128+
response_payload = type(result).to_json(result)
129+
elif isinstance(result, google.protobuf.message.Message):
130+
response_payload = google.protobuf.json_format.MessageToJson(result)
131+
else:
132+
response_payload = (
133+
f"{type(result).__name__}: {pickle.dumps(result)}"
134+
)
135+
grpc_response = {
136+
"payload": response_payload,
137+
"status": "OK",
138+
}
139+
_LOGGER.debug(
140+
f"Received response of type {type(result)} via gRPC stream",
141+
extra={
142+
"response": grpc_response,
143+
},
144+
)
145+
return result
117146
except grpc.RpcError as exc:
118147
# If the stream has already returned data, we cannot recover here.
119148
raise exceptions.from_grpc_error(exc) from exc

google/api_core/grpc_helpers_async.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,23 @@
2020

2121
import asyncio
2222
import functools
23+
import logging
24+
import pickle
2325

2426
from typing import AsyncGenerator, Generic, Iterator, Optional, TypeVar
2527

28+
import google.protobuf.json_format
2629
import grpc
2730
from grpc import aio
31+
import proto
2832

2933
from google.api_core import exceptions, grpc_helpers
3034

3135
# denotes the proto response type for grpc calls
3236
P = TypeVar("P")
3337

38+
_LOGGER = logging.getLogger(__name__)
39+
3440
# NOTE(lidiz) Alternatively, we can hack "__getattribute__" to perform
3541
# automatic patching for us. But that means the overhead of creating an
3642
# extra Python function spreads to every single send and receive.
@@ -94,7 +100,30 @@ def __init__(self):
94100

95101
async def read(self) -> P:
96102
try:
97-
return await self._call.read()
103+
result = await self._call.read()
104+
logging_enabled =_LOGGER.isEnabledFor(
105+
logging.DEBUG
106+
)
107+
if logging_enabled: # pragma: NO COVER
108+
if isinstance(result, proto.Message):
109+
response_payload = type(result).to_json(result)
110+
elif isinstance(result, google.protobuf.message.Message):
111+
response_payload = google.protobuf.json_format.MessageToJson(result)
112+
else:
113+
response_payload = (
114+
f"{type(result).__name__}: {pickle.dumps(result)}"
115+
)
116+
grpc_response = {
117+
"payload": response_payload,
118+
"status": "OK",
119+
}
120+
_LOGGER.debug(
121+
f"Received response of type {type(result)} via gRPC stream",
122+
extra={
123+
"response": grpc_response,
124+
},
125+
)
126+
return result
98127
except grpc.RpcError as rpc_error:
99128
raise exceptions.from_grpc_error(rpc_error) from rpc_error
100129

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy