diff --git a/litellm/integrations/datadog/datadog_handler.py b/litellm/integrations/datadog/datadog_handler.py index d360dac121c..cde5f64dd8e 100644 --- a/litellm/integrations/datadog/datadog_handler.py +++ b/litellm/integrations/datadog/datadog_handler.py @@ -62,7 +62,11 @@ def get_datadog_tags( "POD_NAME": get_datadog_pod_name(), } - tags: Final[list[str]] = [f"{k}:{v}" for k, v in base_tags.items()] + custom_tags: Final = (tag.strip() for tag in os.getenv("DD_TAGS", "").split(",")) + tags: Final[list[str]] = [ + *(f"{k}:{v}" for k, v in base_tags.items()), + *(tag for tag in custom_tags if tag), + ] if standard_logging_object: request_tags: Final = standard_logging_object.get("request_tags", []) or [] diff --git a/tests/unit/integrations/datadog/test_datadog_llm_obs.py b/tests/unit/integrations/datadog/test_datadog_llm_obs.py index a7826e11483..b18c5bb467a 100644 --- a/tests/unit/integrations/datadog/test_datadog_llm_obs.py +++ b/tests/unit/integrations/datadog/test_datadog_llm_obs.py @@ -91,6 +91,15 @@ def build(logger: DataDogLLMObsLogger, **kwargs: Any) -> dict[str, Any]: return json.loads(safe_dumps(payload)) +def test_dd_tags_are_exported_on_llm_observability_spans(logger: DataDogLLMObsLogger) -> None: + with patch.dict(os.environ, {"DD_TAGS": "team:platform,cost_center:engineering"}): + payload = build(logger) + + assert "cost_center:engineering" in payload["tags"] + assert "team:platform" in payload["tags"] + assert "request_tag:cost_center:engineering" not in payload["tags"] + + def test_output_tool_calls_use_the_datadog_tool_call_schema(logger: DataDogLLMObsLogger) -> None: """Datadog reads name/arguments/tool_id off the tool call; OpenAI nests them under `function`.""" payload = build( diff --git a/tests/unit/integrations/datadog/test_datadog_tags_regression.py b/tests/unit/integrations/datadog/test_datadog_tags_regression.py index 110ac75e73b..8eec0d9b81f 100644 --- a/tests/unit/integrations/datadog/test_datadog_tags_regression.py +++ b/tests/unit/integrations/datadog/test_datadog_tags_regression.py @@ -4,13 +4,12 @@ from unittest.mock import patch import pytest - from litellm.integrations.datadog.datadog import DataDogLogger -from litellm.integrations.datadog.datadog_handler import get_datadog_tags, normalize_datadog_tag_value from litellm.integrations.datadog.datadog_cost_management import ( DatadogCostManagementLogger, ) -from litellm.types.utils import StandardLoggingPayload, StandardLoggingMetadata +from litellm.integrations.datadog.datadog_handler import get_datadog_tags, normalize_datadog_tag_value +from litellm.types.utils import StandardLoggingMetadata, StandardLoggingPayload class TestDatadogTagsRegression: @@ -94,6 +93,22 @@ class TestDatadogTagsRegression: assert "request_tag:capability:p_t" in tags assert "team:67890" in tags + def test_dd_tags_are_exported_as_individual_tags_for_logs(self, mock_env_vars): + with patch.dict(os.environ, {"DD_TAGS": " team:platform , cost_center:engineering ,, "}): + tags = get_datadog_tags() + with patch("asyncio.create_task"): + logger = DataDogLogger() + log = logger.create_datadog_logging_payload( + kwargs={"standard_logging_object": StandardLoggingPayload(metadata=StandardLoggingMetadata())}, + response_obj=None, + start_time=datetime.datetime(2026, 1, 1), + end_time=datetime.datetime(2026, 1, 1), + ) + + assert "env:test-env" in tags + assert tags[-2:] == ["team:platform", "cost_center:engineering"] + assert "cost_center:engineering" in log["ddtags"].split(",") + @pytest.mark.asyncio async def test_non_string_request_tag_still_emits_the_datadog_payload(self, mock_env_vars): with patch("asyncio.create_task"):