Skip to content

Commit 7c5fac0

Browse files
authored
fix(otel): disable log injection when otlp logs are used (#14427)
1 parent fdd3934 commit 7c5fac0

File tree

3 files changed

+95
-15
lines changed

3 files changed

+95
-15
lines changed

ddtrace/internal/opentelemetry/logs.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ def set_otel_logs_provider() -> None:
4747
telemetry_writer.add_count_metric(TELEMETRY_NAMESPACE.TRACERS, "logging_provider_configured", 1, (("type", "dd"),))
4848
global DD_LOGS_PROVIDER_CONFIGURED
4949
DD_LOGS_PROVIDER_CONFIGURED = True
50+
# Disable log injection to prevent duplicate log attributes from being sent.
51+
config._logs_injection = False
5052

5153

5254
def _should_configure_logs_exporter() -> bool:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
features:
3+
- |
4+
fix: Ensures that ddtrace log injection is disabled when OpenTelemetry logs are enabled (DD_LOGS_OTEL_ENABLED=true).

tests/opentelemetry/test_logs.py

Lines changed: 89 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,28 +48,43 @@ def decode_logs_request(request_body: bytes):
4848
return export_request
4949

5050

51-
def extract_log_correlation_attributes(captured_logs, log_message: str):
52-
"""Extract log correlation attributes from captured logs."""
53-
attributes = {}
51+
def find_log_record_by_message(captured_logs, log_message: str):
52+
"""Find a log record and its resource by matching log message content."""
5453
for resource_logs in captured_logs.resource_logs:
55-
for attr in resource_logs.resource.attributes:
56-
if attr.key == "service.name":
57-
attributes["service"] = attr.value.string_value
58-
elif attr.key == "deployment.environment":
59-
attributes["env"] = attr.value.string_value
60-
elif attr.key == "service.version":
61-
attributes["version"] = attr.value.string_value
62-
elif attr.key == "host.name":
63-
attributes["host_name"] = attr.value.string_value
6454
for scope_logs in resource_logs.scope_logs:
6555
for record in scope_logs.log_records:
6656
if log_message in record.body.string_value:
67-
attributes["trace_id"] = record.trace_id.hex()
68-
attributes["span_id"] = record.span_id.hex()
69-
break
57+
return record, resource_logs.resource
58+
return None, None
59+
60+
61+
def extract_resource_attributes(log_record, resource) -> dict:
62+
"""Extract resource attributes from log record and resource."""
63+
attributes = {}
64+
65+
for attr in resource.attributes:
66+
if attr.key == "service.name":
67+
attributes["service"] = attr.value.string_value
68+
elif attr.key == "deployment.environment":
69+
attributes["env"] = attr.value.string_value
70+
elif attr.key == "service.version":
71+
attributes["version"] = attr.value.string_value
72+
elif attr.key == "host.name":
73+
attributes["host_name"] = attr.value.string_value
74+
75+
if log_record:
76+
attributes["trace_id"] = log_record.trace_id.hex()
77+
attributes["span_id"] = log_record.span_id.hex()
78+
7079
return attributes
7180

7281

82+
def extract_log_correlation_attributes(captured_logs, log_message: str) -> dict:
83+
"""Extract log correlation attributes and trace/span IDs from captured logs."""
84+
log_record, resource = find_log_record_by_message(captured_logs, log_message)
85+
return extract_resource_attributes(log_record, resource)
86+
87+
7388
@pytest.mark.skipif(API_VERSION >= (1, 15, 0), reason="OpenTelemetry API >= 1.15.0 supports logs collection")
7489
def test_otel_api_version_not_supported(ddtrace_run_python_code_in_subprocess):
7590
"""Test error when OpenTelemetry API version is too old."""
@@ -294,6 +309,65 @@ def test_otel_logs_exporter_auto_configured_grpc():
294309
), "Expected log message not found in exported gRPC payload"
295310

296311

312+
@pytest.mark.skipif(
313+
EXPORTER_VERSION < MINIMUM_SUPPORTED_VERSION,
314+
reason=f"OpenTelemetry exporter version {MINIMUM_SUPPORTED_VERSION} is required to export logs",
315+
)
316+
@pytest.mark.subprocess(
317+
ddtrace_run=True,
318+
env={
319+
"DD_LOGS_OTEL_ENABLED": "true",
320+
"DD_SERVICE": "test_service",
321+
"DD_VERSION": "1.0",
322+
"DD_ENV": "test_env",
323+
},
324+
parametrize={"DD_LOGS_INJECTION": [None, "true"]},
325+
)
326+
def test_ddtrace_log_injection_otlp_enabled():
327+
"""Test that ddtrace log injection is disabled when OpenTelemetry logs are enabled."""
328+
from logging import getLogger
329+
330+
from opentelemetry._logs import get_logger_provider
331+
332+
from ddtrace import tracer
333+
from ddtrace.internal.constants import LOG_ATTR_ENV
334+
from ddtrace.internal.constants import LOG_ATTR_SERVICE
335+
from ddtrace.internal.constants import LOG_ATTR_SPAN_ID
336+
from ddtrace.internal.constants import LOG_ATTR_TRACE_ID
337+
from ddtrace.internal.constants import LOG_ATTR_VERSION
338+
from tests.opentelemetry.test_logs import create_mock_grpc_server
339+
from tests.opentelemetry.test_logs import find_log_record_by_message
340+
341+
log = getLogger()
342+
mock_service, server = create_mock_grpc_server()
343+
344+
try:
345+
server.start()
346+
with tracer.trace("test_trace"):
347+
log.error("test_ddtrace_log_correlation")
348+
logger_provider = get_logger_provider()
349+
logger_provider.force_flush()
350+
finally:
351+
server.stop(0)
352+
353+
log_record = None
354+
for request in mock_service.received_requests:
355+
log_record, _ = find_log_record_by_message(request, "test_ddtrace_log_correlation")
356+
if log_record:
357+
break
358+
else:
359+
assert False, f"No log record with message 'test_ddtrace_log_correlation' found in the request: {request}"
360+
361+
ddtrace_attributes = {}
362+
for attr in log_record.attributes:
363+
if attr.key in (LOG_ATTR_ENV, LOG_ATTR_SERVICE, LOG_ATTR_VERSION, LOG_ATTR_TRACE_ID, LOG_ATTR_SPAN_ID):
364+
ddtrace_attributes[attr.key] = attr.value
365+
366+
assert (
367+
ddtrace_attributes == {}
368+
), f"Log Injection attributes should not be present in the log record: {ddtrace_attributes}"
369+
370+
297371
@pytest.mark.skipif(
298372
EXPORTER_VERSION < MINIMUM_SUPPORTED_VERSION,
299373
reason=f"OpenTelemetry exporter version {MINIMUM_SUPPORTED_VERSION} is required to export logs",

0 commit comments

Comments
 (0)