Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always use LogRecord.getMessage to get the log body #4327

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4260](https://github.com/open-telemetry/opentelemetry-python/pull/4260))
- semantic-conventions: Bump to 1.29.0
([#4337](https://github.com/open-telemetry/opentelemetry-python/pull/4337))
- Further improve compatibility with other logging libraries that override
`LogRecord.getMessage()` in order to customize message formatting
([#4327](https://github.com/open-telemetry/opentelemetry-python/pull/4327))

## Version 1.28.0/0.49b0 (2024-11-05)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -510,22 +510,7 @@ def _translate(self, record: logging.LogRecord) -> LogRecord:
if self.formatter:
body = self.format(record)
else:
# `record.getMessage()` uses `record.msg` as a template to format
# `record.args` into. There is a special case in `record.getMessage()`
# where it will only attempt formatting if args are provided,
# otherwise, it just stringifies `record.msg`.
#
# Since the OTLP body field has a type of 'any' and the logging module
# is sometimes used in such a way that objects incorrectly end up
# set as record.msg, in those cases we would like to bypass
# `record.getMessage()` completely and set the body to the object
# itself instead of its string representation.
# For more background, see: https://github.com/open-telemetry/opentelemetry-python/pull/4216
if not record.args and not isinstance(record.msg, str):
# no args are provided so it's *mostly* safe to use the message template as the body
body = record.msg
else:
body = record.getMessage()
body = record.getMessage()

# related to https://github.com/open-telemetry/opentelemetry-python/issues/3548
# Severity Text = WARN as defined in https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#displaying-severity.
Expand Down
13 changes: 5 additions & 8 deletions opentelemetry-sdk/tests/logs/test_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ def test_simple_log_record_processor_different_msg_types(self):
"Temperature hits high 420 C in Hyderabad",
"CRITICAL",
),
(["list", "of", "strings"], "WARN"),
({"key": "value"}, "ERROR"),
("['list', 'of', 'strings']", "WARN"),
("{'key': 'value'}", "ERROR"),
]
emitted = [
(item.log_record.body, item.log_record.severity_text)
Expand All @@ -232,8 +232,7 @@ def test_simple_log_record_processor_different_msg_types(self):

def test_simple_log_record_processor_custom_single_obj(self):
"""
Tests that special-case handling for logging a single non-string object
is correctly applied.
Tests that logging a single non-string object uses getMessage
"""
exporter = InMemoryLogExporter()
log_record_processor = BatchLogRecordProcessor(exporter)
Expand All @@ -259,9 +258,7 @@ def test_simple_log_record_processor_custom_single_obj(self):
logger.warning("a string with a percent-s: %s", "and arg")
# non-string msg with args - getMessage stringifies msg and formats args into it
logger.warning(["a non-string with a percent-s", "%s"], "and arg")
# non-string msg with no args:
# - normally getMessage would stringify the object and bypass formatting
# - SPECIAL CASE: bypass stringification as well to keep the raw object
# non-string msg with no args - getMessage stringifies the object and bypasses formatting
logger.warning(["a non-string with a percent-s", "%s"])
log_record_processor.shutdown()

Expand All @@ -270,7 +267,7 @@ def test_simple_log_record_processor_custom_single_obj(self):
("a string with a percent-s: %s"),
("a string with a percent-s: and arg"),
("['a non-string with a percent-s', 'and arg']"),
(["a non-string with a percent-s", "%s"]),
("['a non-string with a percent-s', '%s']"),
]
for emitted, expected in zip(finished_logs, expected):
self.assertEqual(emitted.log_record.body, expected)
Expand Down
Loading