Skip to content

Commit 7bb21f5

Browse files
GanymedeNilshalevrsrikanthccv
authored
Update the body type in the log (#3343)
Co-authored-by: Shalev Roda <[email protected]> Co-authored-by: Srikanth Chekuri <[email protected]>
1 parent 98985b5 commit 7bb21f5

File tree

3 files changed

+80
-3
lines changed

3 files changed

+80
-3
lines changed

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10-
10+
- Update the body type in the log
11+
([$3343](https://github.com/open-telemetry/opentelemetry-python/pull/3343))
1112
- Add max_scale option to Exponential Bucket Histogram Aggregation
1213
([#3323](https://github.com/open-telemetry/opentelemetry-python/pull/3323))
1314
- Use BoundedAttributes instead of raw dict to extract attributes from LogRecord
@@ -23,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2324
- Fixed bug where logging export is tracked as trace
2425
[#3375](https://github.com/open-telemetry/opentelemetry-python/pull/3375))
2526

27+
2628
## Version 1.18.0/0.39b0 (2023-05-19)
2729

2830
- Select histogram aggregation with an environment variable

opentelemetry-sdk/src/opentelemetry/sdk/_logs/_internal/__init__.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import traceback
2222
from os import environ
2323
from time import time_ns
24-
from typing import Any, Callable, Optional, Tuple, Union
24+
from typing import Any, Callable, Optional, Tuple, Union # noqa
2525

2626
from opentelemetry._logs import Logger as APILogger
2727
from opentelemetry._logs import LoggerProvider as APILoggerProvider
@@ -476,15 +476,56 @@ def _translate(self, record: logging.LogRecord) -> LogRecord:
476476
timestamp = int(record.created * 1e9)
477477
span_context = get_current_span().get_span_context()
478478
attributes = self._get_attributes(record)
479+
# This comment is taken from GanyedeNil's PR #3343, I have redacted it
480+
# slightly for clarity:
481+
# According to the definition of the Body field type in the
482+
# OTel 1.22.0 Logs Data Model article, the Body field should be of
483+
# type 'any' and should not use the str method to directly translate
484+
# the msg. This is because str only converts non-text types into a
485+
# human-readable form, rather than a standard format, which leads to
486+
# the need for additional operations when collected through a log
487+
# collector.
488+
# Considering that he Body field should be of type 'any' and should not
489+
# use the str method but record.msg is also a string type, then the
490+
# difference is just the self.args formatting?
491+
# The primary consideration depends on the ultimate purpose of the log.
492+
# Converting the default log directly into a string is acceptable as it
493+
# will be required to be presented in a more readable format. However,
494+
# this approach might not be as "standard" when hoping to aggregate
495+
# logs and perform subsequent data analysis. In the context of log
496+
# extraction, it would be more appropriate for the msg to be
497+
# converted into JSON format or remain unchanged, as it will eventually
498+
# be transformed into JSON. If the final output JSON data contains a
499+
# structure that appears similar to JSON but is not, it may confuse
500+
# users. This is particularly true for operation and maintenance
501+
# personnel who need to deal with log data in various languages.
502+
# Where is the JSON converting occur? and what about when the msg
503+
# represents something else but JSON, the expected behavior change?
504+
# For the ConsoleLogExporter, it performs the to_json operation in
505+
# opentelemetry.sdk._logs._internal.export.ConsoleLogExporter.__init__,
506+
# so it can handle any type of input without problems. As for the
507+
# OTLPLogExporter, it also handles any type of input encoding in
508+
# _encode_log located in
509+
# opentelemetry.exporter.otlp.proto.common._internal._log_encoder.
510+
# Therefore, no extra operation is needed to support this change.
511+
# The only thing to consider is the users who have already been using
512+
# this SDK. If they upgrade the SDK after this change, they will need
513+
# to readjust their logging collection rules to adapt to the latest
514+
# output format. Therefore, this change is considered a breaking
515+
# change and needs to be upgraded at an appropriate time.
479516
severity_number = std_to_otel(record.levelno)
517+
if isinstance(record.msg, str) and record.args:
518+
body = record.msg % record.args
519+
else:
520+
body = record.msg
480521
return LogRecord(
481522
timestamp=timestamp,
482523
trace_id=span_context.trace_id,
483524
span_id=span_context.span_id,
484525
trace_flags=span_context.trace_flags,
485526
severity_text=record.levelname,
486527
severity_number=severity_number,
487-
body=record.getMessage(),
528+
body=body,
488529
resource=self._logger.resource,
489530
attributes=attributes,
490531
)

opentelemetry-sdk/tests/logs/test_export.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,40 @@ def test_simple_log_record_processor_shutdown(self):
173173
finished_logs = exporter.get_finished_logs()
174174
self.assertEqual(len(finished_logs), 0)
175175

176+
def test_simple_log_record_processor_different_msg_types(self):
177+
exporter = InMemoryLogExporter()
178+
log_record_processor = BatchLogRecordProcessor(exporter)
179+
180+
provider = LoggerProvider()
181+
provider.add_log_record_processor(log_record_processor)
182+
183+
logger = logging.getLogger("different_msg_types")
184+
logger.addHandler(LoggingHandler(logger_provider=provider))
185+
186+
logger.warning("warning message: %s", "possible upcoming heatwave")
187+
logger.error("Very high rise in temperatures across the globe")
188+
logger.critical("Temperature hits high 420 C in Hyderabad")
189+
logger.warning(["list", "of", "strings"])
190+
logger.error({"key": "value"})
191+
log_record_processor.shutdown()
192+
193+
finished_logs = exporter.get_finished_logs()
194+
expected = [
195+
("warning message: possible upcoming heatwave", "WARNING"),
196+
("Very high rise in temperatures across the globe", "ERROR"),
197+
(
198+
"Temperature hits high 420 C in Hyderabad",
199+
"CRITICAL",
200+
),
201+
(["list", "of", "strings"], "WARNING"),
202+
({"key": "value"}, "ERROR"),
203+
]
204+
emitted = [
205+
(item.log_record.body, item.log_record.severity_text)
206+
for item in finished_logs
207+
]
208+
self.assertEqual(expected, emitted)
209+
176210

177211
class TestBatchLogRecordProcessor(ConcurrencyTestBase):
178212
def test_emit_call_log_record(self):

0 commit comments

Comments
 (0)