Skip to content

Commit 5309478

Browse files
fix: Made write_entries raise ValueError on ParseErrors (#958)
* fix: Log Logger errors internally rather than crashing * Documentation. * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * Made write_entries raise ValueError on ParseErrors * Finished fixing up merge conflicts * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * linting pt.2 * docstring change * Improved docstring message --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent dfcce1f commit 5309478

File tree

3 files changed

+64
-4
lines changed

3 files changed

+64
-4
lines changed

google/cloud/logging_v2/_gapic.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030

3131
from google.protobuf.json_format import MessageToDict
3232
from google.protobuf.json_format import ParseDict
33+
from google.protobuf.json_format import ParseError
3334

3435
from google.cloud.logging_v2._helpers import entry_from_resource
3536
from google.cloud.logging_v2.sink import Sink
@@ -151,7 +152,10 @@ def write_entries(
151152
Useful for checking whether the logging API endpoints are working
152153
properly before sending valuable data.
153154
"""
154-
log_entry_pbs = [_log_entry_mapping_to_pb(entry) for entry in entries]
155+
try:
156+
log_entry_pbs = [_log_entry_mapping_to_pb(entry) for entry in entries]
157+
except ParseError as e:
158+
raise ValueError(f"Invalid log entry: {str(e)}") from e
155159

156160
request = WriteLogEntriesRequest(
157161
log_name=logger_name,

google/cloud/logging_v2/logger.py

+42-3
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def _do_log(self, client, _entry_class, payload=None, **kw):
162162

163163
api_repr = entry.to_api_repr()
164164
entries = [api_repr]
165+
165166
if google.cloud.logging_v2._instrumentation_emitted is False:
166167
entries = _add_instrumentation(entries, **kw)
167168
google.cloud.logging_v2._instrumentation_emitted = True
@@ -200,18 +201,38 @@ def log_text(self, text, *, client=None, **kw):
200201
self._do_log(client, TextEntry, text, **kw)
201202

202203
def log_struct(self, info, *, client=None, **kw):
203-
"""Log a dictionary message
204+
"""Logs a dictionary message.
204205
205206
See
206207
https://cloud.google.com/logging/docs/reference/v2/rest/v2/entries/write
207208
209+
The message must be able to be serializable to a Protobuf Struct.
210+
It must be a dictionary of strings to one of the following:
211+
212+
- :class:`str`
213+
- :class:`int`
214+
- :class:`float`
215+
- :class:`bool`
216+
- :class:`list[str|float|int|bool|list|dict|None]`
217+
- :class:`dict[str, str|float|int|bool|list|dict|None]`
218+
219+
For more details on Protobuf structs, see https://protobuf.dev/reference/protobuf/google.protobuf/#value.
220+
If the provided dictionary cannot be serialized into a Protobuf struct,
221+
it will not be logged, and a :class:`ValueError` will be raised.
222+
208223
Args:
209-
info (dict): the log entry information
224+
info (dict[str, str|float|int|bool|list|dict|None]):
225+
the log entry information.
210226
client (Optional[~logging_v2.client.Client]):
211227
The client to use. If not passed, falls back to the
212228
``client`` stored on the current sink.
213229
kw (Optional[dict]): additional keyword arguments for the entry.
214230
See :class:`~logging_v2.entries.LogEntry`.
231+
232+
Raises:
233+
ValueError:
234+
if the dictionary message provided cannot be serialized into a Protobuf
235+
struct.
215236
"""
216237
for field in _STRUCT_EXTRACTABLE_FIELDS:
217238
# attempt to copy relevant fields from the payload into the LogEntry body
@@ -405,8 +426,22 @@ def log_text(self, text, **kw):
405426
def log_struct(self, info, **kw):
406427
"""Add a struct entry to be logged during :meth:`commit`.
407428
429+
The message must be able to be serializable to a Protobuf Struct.
430+
It must be a dictionary of strings to one of the following:
431+
432+
- :class:`str`
433+
- :class:`int`
434+
- :class:`float`
435+
- :class:`bool`
436+
- :class:`list[str|float|int|bool|list|dict|None]`
437+
- :class:`dict[str, str|float|int|bool|list|dict|None]`
438+
439+
For more details on Protobuf structs, see https://protobuf.dev/reference/protobuf/google.protobuf/#value.
440+
If the provided dictionary cannot be serialized into a Protobuf struct,
441+
it will not be logged, and a :class:`ValueError` will be raised during :meth:`commit`.
442+
408443
Args:
409-
info (dict): The struct entry,
444+
info (dict[str, str|float|int|bool|list|dict|None]): The struct entry,
410445
kw (Optional[dict]): Additional keyword arguments for the entry.
411446
See :class:`~logging_v2.entries.LogEntry`.
412447
"""
@@ -451,6 +486,10 @@ def commit(self, *, client=None, partial_success=True):
451486
Whether a batch's valid entries should be written even
452487
if some other entry failed due to a permanent error such
453488
as INVALID_ARGUMENT or PERMISSION_DENIED.
489+
490+
Raises:
491+
ValueError:
492+
if one of the messages in the batch cannot be successfully parsed.
454493
"""
455494
if client is None:
456495
client = self.client

tests/unit/test__gapic.py

+17
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import google.auth.credentials
1818
import mock
1919

20+
from datetime import datetime
21+
2022
import google.cloud.logging
2123
from google.cloud import logging_v2
2224
from google.cloud.logging_v2 import _gapic
@@ -173,6 +175,21 @@ def test_write_entries_single(self):
173175
assert request.entries[0].resource.type == entry["resource"]["type"]
174176
assert request.entries[0].text_payload == "text"
175177

178+
def test_write_entries_parse_error(self):
179+
client = self.make_logging_api()
180+
with self.assertRaises(ValueError):
181+
with mock.patch.object(
182+
type(client._gapic_api.transport.write_log_entries), "__call__"
183+
) as call:
184+
entry = {
185+
"logName": self.LOG_PATH,
186+
"resource": {"type": "global"},
187+
"jsonPayload": {"time": datetime.now()},
188+
}
189+
client.write_entries([entry])
190+
191+
call.assert_not_called()
192+
176193
def test_logger_delete(self):
177194
client = self.make_logging_api()
178195

0 commit comments

Comments
 (0)