Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions ddtrace/_trace/apm_filter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import os
from typing import List
from typing import Optional

from ddtrace._trace.processor import TraceProcessor
from ddtrace._trace.span import Span
from ddtrace.internal.utils.formats import asbool


class APMTracingEnabledFilter(TraceProcessor):
"""
Trace processor that drops all APM traces when DD_APM_TRACING_ENABLED is set to a falsy value.
"""

def __init__(self) -> None:
super().__init__()
self._apm_tracing_enabled = asbool(os.getenv("DD_APM_TRACING_ENABLED", "true"))

def process_trace(self, trace: List[Span]) -> Optional[List[Span]]:
if not self._apm_tracing_enabled:
return None
return trace
6 changes: 6 additions & 0 deletions ddtrace/llmobs/_llmobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import ddtrace
from ddtrace import config
from ddtrace import patch
from ddtrace._trace.apm_filter import APMTracingEnabledFilter
from ddtrace._trace.context import Context
from ddtrace._trace.span import Span
from ddtrace._trace.tracer import Tracer
Expand Down Expand Up @@ -603,6 +604,11 @@ def enable(

# override the default _instance with a new tracer
cls._instance = cls(tracer=_tracer, span_processor=span_processor)

# Add APM trace filter to drop all APM traces when DD_APM_TRACING_ENABLED is falsy
apm_filter = APMTracingEnabledFilter()
cls._instance.tracer._span_aggregator.user_processors.append(apm_filter)

cls.enabled = True
cls._instance.start()

Expand Down
4 changes: 4 additions & 0 deletions releasenotes/notes/llmobs-apm-tracing-8cd612f8a3af4960.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
fixes:
- |
LLM Observability: ensures APM is disabled when DD_APM_TRACING_ENABLED=0 when using LLM Observability.
24 changes: 24 additions & 0 deletions tests/llmobs/test_llmobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -628,3 +628,27 @@ def test_trace_id_propagation_with_non_llm_parent(llmobs, llmobs_events):
# LLMObs trace IDs should be different from APM trace ID
assert first_child_event["trace_id"] != first_child_event["_dd"]["apm_trace_id"]
assert second_child_event["trace_id"] != second_child_event["_dd"]["apm_trace_id"]


@pytest.mark.parametrize("llmobs_env", [{"DD_APM_TRACING_ENABLED": "false"}])
def test_apm_traces_dropped_when_disabled(llmobs, llmobs_events, tracer, llmobs_env):
from tests.utils import DummyWriter

dummy_writer = DummyWriter()
tracer._span_aggregator.writer = dummy_writer

with tracer.trace("apm_span") as apm_span:
apm_span.set_tag("operation", "test")

# Create an LLMObs span (should be sent to LLMObs but not APM)
with llmobs.llm(model_name="test-model") as llm_span:
llmobs.annotate(llm_span, input_data="test input", output_data="test output")

# Check that no APM traces were sent to the writer
assert len(dummy_writer.traces) == 0, "APM traces should be dropped when DD_APM_TRACING_ENABLED=false"

# But LLMObs events should still be sent
assert len(llmobs_events) == 1
llm_event = llmobs_events[0]
assert llm_event["meta"]["span.kind"] == "llm"
assert llm_event["meta"]["model_name"] == "test-model"
Loading