diff --git a/dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/domain/DDLLMObsSpan.java b/dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/domain/DDLLMObsSpan.java index ab337bd6923..37a5c11d164 100644 --- a/dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/domain/DDLLMObsSpan.java +++ b/dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/domain/DDLLMObsSpan.java @@ -1,6 +1,7 @@ package datadog.trace.llmobs.domain; import datadog.context.ContextScope; +import datadog.trace.api.Config; import datadog.trace.api.DDSpanTypes; import datadog.trace.api.DDTraceId; import datadog.trace.api.WellKnownTags; @@ -72,6 +73,11 @@ public DDLLMObsSpan( span = spanBuilder.start(); + // set global dd_tags as base layer so UST and span-level tags can override them + for (Map.Entry entry : Config.get().getGlobalTags().entrySet()) { + span.setTag(LLMOBS_TAG_PREFIX + entry.getKey(), entry.getValue()); + } + // set UST (unified service tags, env, service, version) span.setTag(ENV, wellKnownTags.getEnv()); span.setTag(SERVICE, wellKnownTags.getService()); diff --git a/dd-java-agent/agent-llmobs/src/test/groovy/datadog/trace/llmobs/domain/DDLLMObsSpanTest.groovy b/dd-java-agent/agent-llmobs/src/test/groovy/datadog/trace/llmobs/domain/DDLLMObsSpanTest.groovy index e667e3599d6..632eb971bf9 100644 --- a/dd-java-agent/agent-llmobs/src/test/groovy/datadog/trace/llmobs/domain/DDLLMObsSpanTest.groovy +++ b/dd-java-agent/agent-llmobs/src/test/groovy/datadog/trace/llmobs/domain/DDLLMObsSpanTest.groovy @@ -402,6 +402,17 @@ class DDLLMObsSpanTest extends DDSpecification{ null | "has_session_id:0" } + def "global dd_tags are included in LLMObs span tags"() { + setup: + injectSysConfig("trace.global.tags", "team:backend,owner:ml-platform") + def test = llmObsSpan(Tags.LLMOBS_WORKFLOW_SPAN_KIND, "test-span") + + expect: + def innerSpan = (AgentSpan) test.span + innerSpan.getTag(LLMOBS_TAG_PREFIX + "team") == "backend" + innerSpan.getTag(LLMOBS_TAG_PREFIX + "owner") == "ml-platform" + } + private LLMObsSpan llmObsSpan(String kind, name) { llmObsSpan(kind, name, null) } diff --git a/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/OpenAiDecorator.java b/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/OpenAiDecorator.java index e6fca7e8979..abba4170d44 100644 --- a/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/OpenAiDecorator.java +++ b/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/main/java/datadog/trace/instrumentation/openai_java/OpenAiDecorator.java @@ -13,6 +13,7 @@ import datadog.trace.bootstrap.instrumentation.api.UTF8BytesString; import datadog.trace.bootstrap.instrumentation.decorator.ClientDecorator; import java.util.List; +import java.util.Map; public class OpenAiDecorator extends ClientDecorator { public static final OpenAiDecorator DECORATE = new OpenAiDecorator(); @@ -91,6 +92,11 @@ protected CharSequence component() { @Override public AgentSpan afterStart(AgentSpan span) { if (llmObsEnabled) { + // set global dd_tags as base layer so UST and span-level tags can override them + for (Map.Entry entry : Config.get().getGlobalTags().entrySet()) { + span.setTag(CommonTags.TAG_PREFIX + entry.getKey(), entry.getValue()); + } + // set UST (unified service tags, env, service, version) span.setTag(CommonTags.ENV, wellKnownTags.getEnv()); span.setTag(CommonTags.SERVICE, wellKnownTags.getService()); diff --git a/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/test/groovy/GlobalTagsTest.groovy b/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/test/groovy/GlobalTagsTest.groovy new file mode 100644 index 00000000000..420bf13c295 --- /dev/null +++ b/dd-java-agent/instrumentation/openai-java/openai-java-3.0/src/test/groovy/GlobalTagsTest.groovy @@ -0,0 +1,26 @@ +import static datadog.trace.agent.test.utils.TraceUtils.runUnderTrace + +/** + * Verifies that global tags (dd.trace.global.tags / DD_TAGS) are propagated to LLMObs span tags in the OpenAI instrumentation. + */ +class GlobalTagsTest extends OpenAiTest { + + @Override + void configurePreAgent() { + super.configurePreAgent() + injectSysConfig("trace.global.tags", "team:backend,owner:ml-platform") + } + + def "global dd_tags are included in OpenAI LLMObs span tags"() { + when: + runUnderTrace("parent") { + openAiClient.chat().completions().create(chatCompletionCreateParams(false)) + } + TEST_WRITER.waitForTraces(1) + def openAiSpan = TEST_WRITER.flatten().find { it.operationName.toString() == "openai.request" } + + then: + openAiSpan.getTag("_ml_obs_tag.team") == "backend" + openAiSpan.getTag("_ml_obs_tag.owner") == "ml-platform" + } +}