diff --git a/google/cloud/logging_v2/handlers/handlers.py b/google/cloud/logging_v2/handlers/handlers.py index f6fa90d71..28960ae71 100644 --- a/google/cloud/logging_v2/handlers/handlers.py +++ b/google/cloud/logging_v2/handlers/handlers.py @@ -252,6 +252,7 @@ def _format_and_parse_message(record, formatter_handler): pass # if json_fields was set, create a dictionary using that if passed_json_fields and isinstance(passed_json_fields, collections.abc.Mapping): + passed_json_fields = passed_json_fields.copy() if message != "None": passed_json_fields["message"] = message return passed_json_fields diff --git a/tests/unit/handlers/test_handlers.py b/tests/unit/handlers/test_handlers.py index ad70061a6..1e431f1aa 100644 --- a/tests/unit/handlers/test_handlers.py +++ b/tests/unit/handlers/test_handlers.py @@ -846,6 +846,24 @@ def test_json_fields_with_json_message(self): self.assertEqual(result["key_m"], message["key_m"]) self.assertEqual(result["key_j"], json_fields["key_j"]) + def test_json_fields_input_unmodified(self): + # Related issue: https://github.com/googleapis/python-logging/issues/652 + from google.cloud.logging_v2.handlers.handlers import _format_and_parse_message + + message = "hello world" + json_fields = {"hello": "world"} + json_fields_orig = json_fields.copy() + record = logging.LogRecord("logname", None, None, None, message, None, None) + setattr(record, "json_fields", json_fields) + handler = logging.StreamHandler() + _format_and_parse_message(record, handler) + # ensure json_fields has no side-effects + self.assertEqual(set(json_fields.keys()), set(json_fields_orig.keys())) + for (key, value) in json_fields_orig.items(): + self.assertEqual( + value, json_fields[key], f"expected_payload[{key}] != result[{key}]" + ) + class TestSetupLogging(unittest.TestCase): def _call_fut(self, handler, excludes=None): diff --git a/tests/unit/handlers/test_structured_log.py b/tests/unit/handlers/test_structured_log.py index 3fe322fe3..61bf36f65 100644 --- a/tests/unit/handlers/test_structured_log.py +++ b/tests/unit/handlers/test_structured_log.py @@ -511,6 +511,36 @@ def test_format_with_nested_json(self): result = json.loads(handler.format(record)) self.assertEqual(result["outer"], json_fields["outer"]) + def test_json_fields_input_unmodified(self): + # Related issue: https://github.com/googleapis/python-logging/issues/652 + import logging + + handler = self._make_one() + message = "hello world" + json_fields = { + "hello": "world", + } + json_fields_orig = json_fields.copy() + record = logging.LogRecord( + None, + logging.INFO, + None, + None, + message, + None, + None, + ) + record.created = None + setattr(record, "json_fields", json_fields) + handler.filter(record) + handler.format(record) + # ensure json_fields has no side-effects + self.assertEqual(set(json_fields.keys()), set(json_fields_orig.keys())) + for (key, value) in json_fields_orig.items(): + self.assertEqual( + value, json_fields[key], f"expected_payload[{key}] != result[{key}]" + ) + def test_emits_instrumentation_info(self): import logging import mock