Unnecessary/noncompliant attributes added via logging instrumentation
Running the app in https://opentelemetry.io/docs/languages/python/getting-started/ with
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
opentelemetry-instrument \
--traces_exporter console \
--metrics_exporter console \
--logs_exporter console \
--service_name dice-server \
flask run -p 8080
results in logs like:
{
"body": "Anonymous player is rolling the dice: 1",
"severity_number": 13,
"severity_text": "WARN",
"attributes": {
"otelSpanID": "57a328e3a762f95e",
"otelTraceID": "a794f5ff6f076ca3e8744dd222d27760",
"otelTraceSampled": true,
"otelServiceName": "dice-server",
"code.file.path": "/tmp/tmp.ZHMcsMeRYL/otel-getting-started/app.py",
"code.function.name": "roll_dice",
"code.line.number": 17
},
"dropped_attributes": 0,
"timestamp": "2025-07-01T15:07:59.544312Z",
"observed_timestamp": "2025-07-01T15:07:59.544731Z",
"trace_id": "0xa794f5ff6f076ca3e8744dd222d27760",
"span_id": "0x57a328e3a762f95e",
"trace_flags": 1,
"resource": {
"attributes": {
"telemetry.sdk.language": "python",
"telemetry.sdk.name": "opentelemetry",
"telemetry.sdk.version": "1.34.1",
"service.name": "dice-server",
"telemetry.auto.version": "0.55b1"
},
"schema_url": ""
}
}
You can see the trace_id, span_id , trace_flags and resource::attributes::service.name are also present as log-level attributes with the names otelSpanID, otelTraceID , otelTraceSampled and otelServiceName respectively. This is mentioned here: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/b1c2c7941b9f5aa4762c648b0e3644de80c1e0a4/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/constants.py#L25-L30
I spoke with @xrmx on Slack about this (see https://cloud-native.slack.com/archives/C01PD4HUVBL/p1751382766109069) and this seems to be historical artifact that could be removed. Since this is reported by opentelemetry-demo and it's on our official docs, it has some weight as an 'official' thing when it seems like it is not, so I would suggest removing it.
I'm taking a closer look at this and I think the issue is the following. opentelemetry-instrumentation-logging is injecting otel span context data (otel*) into Python logging records. Then the log shipping in the sdk (OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true) is forwarding these attributes into OTel logs too. Now adding these attributes into the Python logs looks legit so maybe we should just filter them out in the log shipping code?
Now adding these attributes into the Python logs looks legit
My main concern/question is that these attributes seem redundant and feel 'official' since they are coming from one of our official examples. I don't think it is wrong per se that this attributes are there, just potentially confusing
Just found out that the attributes name in non-OTLP logs are documented in https://opentelemetry.io/docs/specs/otel/compatibility/logging_trace_context/ and we should probably make these attributes at least compliant with that.
UPDATE: actually we are already doing that respecting the spec when OTEL_PYTHON_LOG_CORRELATION=true.
Just found out that the attributes name in non-OTLP logs are documented in https://opentelemetry.io/docs/specs/otel/compatibility/logging_trace_context/ and we should probably make these attributes at least compliant with that.
UPDATE: actually we are already doing that respecting the spec when
OTEL_PYTHON_LOG_CORRELATION=true.
So with OTEL_PYTHON_LOG_CORRELATION=true the fields should be added as trace_id rather than otelSpanId etc.? I can't reproduce this with opentelemetry-instrumentation-logging 0.60b1.
Just found out that the attributes name in non-OTLP logs are documented in https://opentelemetry.io/docs/specs/otel/compatibility/logging_trace_context/ and we should probably make these attributes at least compliant with that. UPDATE: actually we are already doing that respecting the spec when
OTEL_PYTHON_LOG_CORRELATION=true.So with
OTEL_PYTHON_LOG_CORRELATION=truethe fields should be added astrace_idrather thanotelSpanIdetc.? I can't reproduce this with opentelemetry-instrumentation-logging 0.60b1.
The logs have already the proper trace context, OTEL_PYTHON_LOG_CORRELATION=true IIRC changes the formatting of the message to include them also in the text.
I think we can workaround this behavior with something like the following: we are injecting these out of spec attributes only if user enabled log correlation or has a log hook configured that is not the out of the box use case or the examples we have in the docs.
diff --git a/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/__init__.py b/instrumentation/o
pentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/__init__.py
index 226108325..4e493a69f 100644
--- a/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/__init__.py
+++ b/instrumentation/opentelemetry-instrumentation-logging/src/opentelemetry/instrumentation/logging/__init__.py
@@ -130,9 +130,33 @@ class LoggingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring
service_name = None
+ set_logging_format = kwargs.get(
+ "set_logging_format",
+ environ.get(OTEL_PYTHON_LOG_CORRELATION, "false").lower()
+ == "true",
+ )
+
+ if set_logging_format:
+ log_format = kwargs.get(
+ "logging_format", environ.get(OTEL_PYTHON_LOG_FORMAT, None)
+ )
+ log_format = log_format or DEFAULT_LOGGING_FORMAT
+
+ log_level = kwargs.get(
+ "log_level", LEVELS.get(environ.get(OTEL_PYTHON_LOG_LEVEL))
+ )
+ log_level = log_level or logging.INFO
+
+ logging.basicConfig(format=log_format, level=log_level)
+
def record_factory(*args, **kwargs):
record = old_factory(*args, **kwargs)
+ # inject out of spec attributes only if the user is asking to do so or if a
+ # a log hook is configured
+ if not set_logging_format and not callable(LoggingInstrumentor._log_hook):
+ return record
+
record.otelSpanID = "0"
record.otelTraceID = "0"
record.otelTraceSampled = False
@@ -168,25 +192,6 @@ class LoggingInstrumentor(BaseInstrumentor): # pylint: disable=empty-docstring
logging.setLogRecordFactory(record_factory)
- set_logging_format = kwargs.get(
- "set_logging_format",
- environ.get(OTEL_PYTHON_LOG_CORRELATION, "false").lower()
- == "true",
- )
-
- if set_logging_format:
- log_format = kwargs.get(
- "logging_format", environ.get(OTEL_PYTHON_LOG_FORMAT, None)
- )
- log_format = log_format or DEFAULT_LOGGING_FORMAT
-
- log_level = kwargs.get(
- "log_level", LEVELS.get(environ.get(OTEL_PYTHON_LOG_LEVEL))
- )
- log_level = log_level or logging.INFO
-
- logging.basicConfig(format=log_format, level=log_level)
-
def _uninstrument(self, **kwargs):
if LoggingInstrumentor._old_factory:
logging.setLogRecordFactory(LoggingInstrumentor._old_factory)