diff --git a/packages/google-cloud-logging/google/cloud/logging_v2/handlers/handlers.py b/packages/google-cloud-logging/google/cloud/logging_v2/handlers/handlers.py index 8cab7d1e3f0c..280ce319fcf1 100644 --- a/packages/google-cloud-logging/google/cloud/logging_v2/handlers/handlers.py +++ b/packages/google-cloud-logging/google/cloud/logging_v2/handlers/handlers.py @@ -281,14 +281,19 @@ def _format_and_parse_message(record, formatter_handler): except (json.decoder.JSONDecodeError, IndexError): # log string is not valid json pass + # A record whose ``msg`` is the Python object ``None`` is treated as + # having no content. ``logging.Formatter`` renders such a record as the + # string ``"None"``; detect that case from ``record.msg`` directly so that + # a legitimate user message of the literal string ``"None"`` is preserved. + is_empty_message = record.msg is None and message == "None" # 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": + if not is_empty_message: passed_json_fields["message"] = message return passed_json_fields # if formatted message contains no content, return None - return message if message != "None" else None + return None if is_empty_message else message def setup_logging( diff --git a/packages/google-cloud-logging/tests/unit/handlers/test_handlers.py b/packages/google-cloud-logging/tests/unit/handlers/test_handlers.py index 4b3e09cdd629..b7e719fdff88 100644 --- a/packages/google-cloud-logging/tests/unit/handlers/test_handlers.py +++ b/packages/google-cloud-logging/tests/unit/handlers/test_handlers.py @@ -922,6 +922,34 @@ def test_none(self): result = _format_and_parse_message(record, handler) self.assertEqual(result, None) + def test_literal_none_string_preserved(self): + """ + A literal string message of "None" should be preserved, not dropped. + Only a record whose msg is the Python object None is treated as empty. + """ + from google.cloud.logging_v2.handlers.handlers import _format_and_parse_message + + message = "None" + record = logging.LogRecord("logname", None, None, None, message, None, None) + handler = logging.StreamHandler() + result = _format_and_parse_message(record, handler) + self.assertEqual(result, "None") + + def test_literal_none_string_preserved_with_json_fields(self): + """ + A literal string message of "None" should populate the message field + even when json_fields are present. + """ + from google.cloud.logging_v2.handlers.handlers import _format_and_parse_message + + message = "None" + json_fields = {"key": "val"} + record = logging.LogRecord("logname", None, None, None, message, None, None) + setattr(record, "json_fields", json_fields) + handler = logging.StreamHandler() + result = _format_and_parse_message(record, handler) + self.assertEqual(result, {"message": "None", "key": "val"}) + def test_none_formatted(self): """ None messages with formatting rules should return formatted string