Skip to content

Commit 68240b5

Browse files
fix(otlp): allow multiple signals [backport 3.17] (#15128)
Backport 06e349b from #15107 to 3.17. ## Description Fixed a circular import issue when enabling both OpenTelemetry metrics and logs simultaneously (`DD_METRICS_OTEL_ENABLED=true` + `DD_LOGS_OTEL_ENABLED=true`). Update lazy initialization to enable full opentelemetry support when `opentelemetry` is first imported. ## Testing Verified that enabling multiple OpenTelemetry signals together no longer produces circular import errors. Confirmed individual signals (metrics-only, logs-only, traces-only) still work correctly. ## Risks Low risk. Changes initialization timing from lazy to eager, but no functional behavior changes for users. ## Additional Notes Enables proper multi-signal OpenTelemetry support for comprehensive observability (traces + metrics + logs). Co-authored-by: Munir Abdinur <[email protected]>
1 parent 5566af1 commit 68240b5

File tree

6 files changed

+42
-18
lines changed

6 files changed

+42
-18
lines changed

ddtrace/bootstrap/preload.py

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -64,36 +64,27 @@ def register_post_preload(func: t.Callable) -> None:
6464
if config._runtime_metrics_enabled:
6565
RuntimeWorker.enable()
6666

67-
if config._otel_trace_enabled:
6867

69-
@ModuleWatchdog.after_module_imported("opentelemetry.trace")
70-
def _ot_traces(_):
68+
@ModuleWatchdog.after_module_imported("opentelemetry")
69+
def _otel_signals(_):
70+
if config._otel_trace_enabled:
7171
from opentelemetry.trace import set_tracer_provider
7272

7373
from ddtrace.opentelemetry import TracerProvider
7474

7575
set_tracer_provider(TracerProvider())
7676

77+
if config._otel_logs_enabled:
78+
from ddtrace.internal.opentelemetry.logs import set_otel_logs_provider
7779

78-
if config._otel_metrics_enabled:
80+
set_otel_logs_provider()
7981

80-
@ModuleWatchdog.after_module_imported("opentelemetry.metrics")
81-
def _otel_metrics(_):
82+
if config._otel_metrics_enabled:
8283
from ddtrace.internal.opentelemetry.metrics import set_otel_meter_provider
8384

8485
set_otel_meter_provider()
8586

8687

87-
if config._otel_logs_enabled:
88-
89-
@register_post_preload
90-
def _otel_logs():
91-
# Post load to ensure that the logger provider is not overridden by module unloading
92-
from ddtrace.internal.opentelemetry.logs import set_otel_logs_provider
93-
94-
set_otel_logs_provider()
95-
96-
9788
if config._llmobs_enabled:
9889
from ddtrace.llmobs import LLMObs
9990

ddtrace/opentelemetry/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
All operations defined the opentelemetry trace api are configured to use the ddtrace global tracer (``ddtrace.tracer``)
1010
and generate datadog compatible traces. By default all opentelemetry traces are submitted to a Datadog agent.
1111
12+
**Note:** OpenTelemetry support is lazily loaded when the ``opentelemetry`` package is first imported. Ensure
13+
``import opentelemetry...`` or ``from opentelemetry....`` is present in your code.
14+
1215
1316
Configuration
1417
-------------
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
fixes:
3+
- |
4+
opentelemetry: Fixed circular import when enabling multiple OpenTelemetry signals (metrics + logs) simultaneously.

tests/opentelemetry/test_context.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@
1111
import ddtrace
1212
from ddtrace.constants import MANUAL_DROP_KEY
1313
from ddtrace.constants import MANUAL_KEEP_KEY
14+
from ddtrace.internal.opentelemetry.logs import MINIMUM_SUPPORTED_VERSION
15+
16+
from .test_logs import EXPORTER_VERSION
1417

1518

1619
@pytest.mark.snapshot(wait_for_num_traces=1)
@@ -204,3 +207,25 @@ def test_otel_baggage_removal_propagation_to_ddtrace(oteltracer):
204207
assert ddspan.context.get_baggage_item("key4") == "value4"
205208
assert ddspan.context.get_baggage_item("key1") is None
206209
assert ddspan.context.get_baggage_item("key2") is None
210+
211+
212+
@pytest.mark.skipif(
213+
EXPORTER_VERSION < MINIMUM_SUPPORTED_VERSION,
214+
reason=f"OpenTelemetry exporter version {MINIMUM_SUPPORTED_VERSION} is required to export logs",
215+
)
216+
@pytest.mark.subprocess(
217+
env={"DD_TRACE_OTEL_ENABLED": "true", "DD_LOGS_OTEL_ENABLED": "true", "DD_METRICS_OTEL_ENABLED": "true"},
218+
ddtrace_run=True,
219+
)
220+
def test_providers_are_set():
221+
from opentelemetry._logs import get_logger_provider
222+
from opentelemetry.metrics import get_meter_provider
223+
from opentelemetry.trace import get_tracer_provider
224+
225+
tracer_provider = get_tracer_provider()
226+
meter_provider = get_meter_provider()
227+
logger_provider = get_logger_provider()
228+
229+
assert tracer_provider.get_tracer(__name__) is not None
230+
assert meter_provider.get_meter(__name__) is not None
231+
assert logger_provider.get_logger(__name__) is not None

tests/opentelemetry/test_logs.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_otel_api_version_not_supported(ddtrace_run_python_code_in_subprocess):
9090
"""Test error when OpenTelemetry API version is too old."""
9191
env = os.environ.copy()
9292
env["DD_LOGS_OTEL_ENABLED"] = "true"
93-
stdout, stderr, status, _ = ddtrace_run_python_code_in_subprocess(code="", env=env)
93+
stdout, stderr, status, _ = ddtrace_run_python_code_in_subprocess(code="import opentelemetry", env=env)
9494
assert status == 0, (stdout, stderr)
9595
assert (
9696
"OpenTelemetry API requires version 1.15.0 or higher to enable logs collection. "
@@ -107,7 +107,7 @@ def test_otel_sdk_not_installed(ddtrace_run_python_code_in_subprocess):
107107
"""Test error when OpenTelemetry SDK is not installed."""
108108
env = os.environ.copy()
109109
env["DD_LOGS_OTEL_ENABLED"] = "true"
110-
stdout, stderr, status, _ = ddtrace_run_python_code_in_subprocess(code="", env=env)
110+
stdout, stderr, status, _ = ddtrace_run_python_code_in_subprocess(code="import opentelemetry", env=env)
111111
assert status == 0, (stdout, stderr)
112112

113113
assert (

tests/telemetry/test_writer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
8383
import ddtrace.settings.symbol_db
8484
import ddtrace.settings.dynamic_instrumentation
8585
import ddtrace.settings.exception_replay
86+
import opentelemetry
8687
"""
8788

8889
env = os.environ.copy()

0 commit comments

Comments
 (0)