@@ -221,7 +221,9 @@ def to_json(self, indent=4) -> str:
221
221
return json .dumps (
222
222
{
223
223
"body" : self .body ,
224
- "severity_number" : repr (self .severity_number ),
224
+ "severity_number" : self .severity_number .value
225
+ if self .severity_number is not None
226
+ else None ,
225
227
"severity_text" : self .severity_text ,
226
228
"attributes" : (
227
229
dict (self .attributes ) if bool (self .attributes ) else None
@@ -438,6 +440,7 @@ def force_flush(self, timeout_millis: int = 30000) -> bool:
438
440
"exc_text" ,
439
441
"filename" ,
440
442
"funcName" ,
443
+ "getMessage" ,
441
444
"message" ,
442
445
"levelname" ,
443
446
"levelno" ,
@@ -503,51 +506,26 @@ def _translate(self, record: logging.LogRecord) -> LogRecord:
503
506
observered_timestamp = time_ns ()
504
507
span_context = get_current_span ().get_span_context ()
505
508
attributes = self ._get_attributes (record )
506
- # This comment is taken from GanyedeNil's PR #3343, I have redacted it
507
- # slightly for clarity:
508
- # According to the definition of the Body field type in the
509
- # OTel 1.22.0 Logs Data Model article, the Body field should be of
510
- # type 'any' and should not use the str method to directly translate
511
- # the msg. This is because str only converts non-text types into a
512
- # human-readable form, rather than a standard format, which leads to
513
- # the need for additional operations when collected through a log
514
- # collector.
515
- # Considering that he Body field should be of type 'any' and should not
516
- # use the str method but record.msg is also a string type, then the
517
- # difference is just the self.args formatting?
518
- # The primary consideration depends on the ultimate purpose of the log.
519
- # Converting the default log directly into a string is acceptable as it
520
- # will be required to be presented in a more readable format. However,
521
- # this approach might not be as "standard" when hoping to aggregate
522
- # logs and perform subsequent data analysis. In the context of log
523
- # extraction, it would be more appropriate for the msg to be
524
- # converted into JSON format or remain unchanged, as it will eventually
525
- # be transformed into JSON. If the final output JSON data contains a
526
- # structure that appears similar to JSON but is not, it may confuse
527
- # users. This is particularly true for operation and maintenance
528
- # personnel who need to deal with log data in various languages.
529
- # Where is the JSON converting occur? and what about when the msg
530
- # represents something else but JSON, the expected behavior change?
531
- # For the ConsoleLogExporter, it performs the to_json operation in
532
- # opentelemetry.sdk._logs._internal.export.ConsoleLogExporter.__init__,
533
- # so it can handle any type of input without problems. As for the
534
- # OTLPLogExporter, it also handles any type of input encoding in
535
- # _encode_log located in
536
- # opentelemetry.exporter.otlp.proto.common._internal._log_encoder.
537
- # Therefore, no extra operation is needed to support this change.
538
- # The only thing to consider is the users who have already been using
539
- # this SDK. If they upgrade the SDK after this change, they will need
540
- # to readjust their logging collection rules to adapt to the latest
541
- # output format. Therefore, this change is considered a breaking
542
- # change and needs to be upgraded at an appropriate time.
543
509
severity_number = std_to_otel (record .levelno )
544
510
if self .formatter :
545
511
body = self .format (record )
546
512
else :
547
- if isinstance (record .msg , str ) and record .args :
548
- body = record .msg % record .args
549
- else :
513
+ # `record.getMessage()` uses `record.msg` as a template to format
514
+ # `record.args` into. There is a special case in `record.getMessage()`
515
+ # where it will only attempt formatting if args are provided,
516
+ # otherwise, it just stringifies `record.msg`.
517
+ #
518
+ # Since the OTLP body field has a type of 'any' and the logging module
519
+ # is sometimes used in such a way that objects incorrectly end up
520
+ # set as record.msg, in those cases we would like to bypass
521
+ # `record.getMessage()` completely and set the body to the object
522
+ # itself instead of its string representation.
523
+ # For more background, see: https://github.com/open-telemetry/opentelemetry-python/pull/4216
524
+ if not record .args and not isinstance (record .msg , str ):
525
+ # no args are provided so it's *mostly* safe to use the message template as the body
550
526
body = record .msg
527
+ else :
528
+ body = record .getMessage ()
551
529
552
530
# related to https://github.com/open-telemetry/opentelemetry-python/issues/3548
553
531
# Severity Text = WARN as defined in https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/logs/data-model.md#displaying-severity.
0 commit comments