Skip to content

Commit 63275f6

Browse files
committed
use trace ID to map to individual stacks of active contexts
1 parent d2b33d9 commit 63275f6

File tree

12 files changed

+177
-161
lines changed

12 files changed

+177
-161
lines changed

communication/build.gradle

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ dependencies {
44
implementation libs.slf4j
55

66
api project(':remote-config:remote-config-api')
7-
implementation project(':components:json')
87
implementation project(':remote-config:remote-config-core')
98
implementation project(':internal-api')
109
implementation project(':utils:container-utils')

communication/src/main/java/datadog/communication/serialization/json/JSONWritableFormatter.java

Lines changed: 0 additions & 131 deletions
This file was deleted.

dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/LLMObsServices.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@
44
import datadog.communication.BackendApiFactory;
55
import datadog.communication.ddagent.SharedCommunicationObjects;
66
import datadog.trace.api.Config;
7+
import datadog.trace.api.DDTraceId;
8+
import datadog.trace.bootstrap.instrumentation.api.AgentScope;
9+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext;
10+
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
11+
import datadog.trace.llmobs.domain.SpanContextInfo;
12+
import java.util.Deque;
13+
import java.util.Map;
14+
import java.util.NoSuchElementException;
15+
import java.util.concurrent.ConcurrentHashMap;
16+
import java.util.concurrent.ConcurrentLinkedDeque;
17+
import javax.annotation.Nonnull;
718
import org.slf4j.Logger;
819
import org.slf4j.LoggerFactory;
920

@@ -14,9 +25,86 @@ public class LLMObsServices {
1425
final Config config;
1526
final BackendApi backendApi;
1627

28+
Map<DDTraceId, Deque<SpanContextInfo>> activeSpanContextByTID = new ConcurrentHashMap<>();
29+
1730
LLMObsServices(Config config, SharedCommunicationObjects sco) {
1831
this.config = config;
1932
this.backendApi =
2033
new BackendApiFactory(config, sco).createBackendApi(BackendApiFactory.Intake.LLMOBS_API);
2134
}
35+
36+
@Nonnull
37+
public SpanContextInfo getActiveSpanContext() {
38+
// Valid case: possibly start root llm obs span/trace while there is NOT an active apm trace
39+
AgentScope activeScope = AgentTracer.activeScope();
40+
if (activeScope == null) {
41+
return new SpanContextInfo();
42+
}
43+
44+
// Unexpected case: null active scope span, log to avoid crashes
45+
if (activeScope.span() == null) {
46+
logger.warn("active span scope found but no null span");
47+
return new SpanContextInfo();
48+
}
49+
50+
// Unexpected case: null trace ID, log to avoid crashes
51+
DDTraceId traceId = activeScope.span().getTraceId();
52+
if (traceId == null) {
53+
logger.warn("active scope found but unexpectedly null trace ID");
54+
return new SpanContextInfo();
55+
}
56+
57+
Deque<SpanContextInfo> activeSpanCtxForTID = activeSpanContextByTID.get(traceId);
58+
// Valid case: possibly start root llm obs span/trace while there's an active apm trace
59+
if (activeSpanCtxForTID == null || activeSpanCtxForTID.isEmpty()) {
60+
return new SpanContextInfo();
61+
}
62+
63+
// Valid case: possibly start child llm obs span for a given trace ID
64+
return activeSpanCtxForTID.peek();
65+
}
66+
67+
public void setActiveSpanContext(SpanContextInfo spanContext) {
68+
AgentSpanContext activeCtx = spanContext.getActiveContext();
69+
if (activeCtx == null) {
70+
logger.warn("unexpected null active context");
71+
return;
72+
}
73+
74+
DDTraceId traceId = activeCtx.getTraceId();
75+
if (traceId == null) {
76+
logger.warn("unexpected null trace ID");
77+
return;
78+
}
79+
80+
Deque<SpanContextInfo> contexts = activeSpanContextByTID.get(activeCtx.getTraceId());
81+
if (contexts == null) {
82+
contexts = new ConcurrentLinkedDeque<>();
83+
}
84+
contexts.push(spanContext);
85+
this.activeSpanContextByTID.put(traceId, contexts);
86+
}
87+
88+
public void removeActiveSpanContext(DDTraceId traceId) {
89+
if (!activeSpanContextByTID.containsKey(traceId)) {
90+
logger.debug("active span contexts not found for trace {}", traceId);
91+
return;
92+
}
93+
Deque<SpanContextInfo> contexts = activeSpanContextByTID.get(traceId);
94+
if (contexts == null) {
95+
return;
96+
}
97+
if (!contexts.isEmpty()) {
98+
try {
99+
contexts.pop();
100+
if (contexts.isEmpty()) {
101+
// the trace MAY still be active, however, the next set should re-create the hierarchy as
102+
// needed
103+
activeSpanContextByTID.remove(traceId);
104+
}
105+
} catch (NoSuchElementException noSuchElementException) {
106+
logger.debug("failed to pop context stack for trace {}", traceId);
107+
}
108+
}
109+
}
22110
}

dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/LLMObsSystem.java

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,12 @@ public LLMObsSpan startLLMSpan(
5757

5858
DDLLMObsSpan span =
5959
new DDLLMObsSpan(
60-
Tags.LLMOBS_LLM_SPAN_KIND, spanName, getMLApp(mlApp), sessionID, serviceName);
60+
Tags.LLMOBS_LLM_SPAN_KIND,
61+
spanName,
62+
getMLApp(mlApp),
63+
sessionID,
64+
serviceName,
65+
llmObsServices);
6166

6267
if (modelName == null || modelName.isEmpty()) {
6368
modelName = CUSTOM_MODEL_VAL;
@@ -75,28 +80,48 @@ public LLMObsSpan startLLMSpan(
7580
public LLMObsSpan startAgentSpan(
7681
String spanName, @Nullable String mlApp, @Nullable String sessionID) {
7782
return new DDLLMObsSpan(
78-
Tags.LLMOBS_AGENT_SPAN_KIND, spanName, getMLApp(mlApp), sessionID, serviceName);
83+
Tags.LLMOBS_AGENT_SPAN_KIND,
84+
spanName,
85+
getMLApp(mlApp),
86+
sessionID,
87+
serviceName,
88+
llmObsServices);
7989
}
8090

8191
@Override
8292
public LLMObsSpan startToolSpan(
8393
String spanName, @Nullable String mlApp, @Nullable String sessionID) {
8494
return new DDLLMObsSpan(
85-
Tags.LLMOBS_TOOL_SPAN_KIND, spanName, getMLApp(mlApp), sessionID, serviceName);
95+
Tags.LLMOBS_TOOL_SPAN_KIND,
96+
spanName,
97+
getMLApp(mlApp),
98+
sessionID,
99+
serviceName,
100+
llmObsServices);
86101
}
87102

88103
@Override
89104
public LLMObsSpan startTaskSpan(
90105
String spanName, @Nullable String mlApp, @Nullable String sessionID) {
91106
return new DDLLMObsSpan(
92-
Tags.LLMOBS_TASK_SPAN_KIND, spanName, getMLApp(mlApp), sessionID, serviceName);
107+
Tags.LLMOBS_TASK_SPAN_KIND,
108+
spanName,
109+
getMLApp(mlApp),
110+
sessionID,
111+
serviceName,
112+
llmObsServices);
93113
}
94114

95115
@Override
96116
public LLMObsSpan startWorkflowSpan(
97117
String spanName, @Nullable String mlApp, @Nullable String sessionID) {
98118
return new DDLLMObsSpan(
99-
Tags.LLMOBS_WORKFLOW_SPAN_KIND, spanName, getMLApp(mlApp), sessionID, serviceName);
119+
Tags.LLMOBS_WORKFLOW_SPAN_KIND,
120+
spanName,
121+
getMLApp(mlApp),
122+
sessionID,
123+
serviceName,
124+
llmObsServices);
100125
}
101126

102127
private String getMLApp(String mlApp) {

dd-java-agent/agent-llmobs/src/main/java/datadog/trace/llmobs/domain/DDLLMObsSpan.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package datadog.trace.llmobs.domain;
22

33
import datadog.trace.api.DDSpanTypes;
4+
import datadog.trace.api.DDTraceId;
45
import datadog.trace.api.llmobs.LLMObs;
56
import datadog.trace.api.llmobs.LLMObsSpan;
67
import datadog.trace.api.llmobs.LLMObsTags;
78
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
9+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext;
810
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
911
import datadog.trace.bootstrap.instrumentation.api.Tags;
12+
import datadog.trace.llmobs.LLMObsServices;
1013
import java.util.Collections;
1114
import java.util.HashMap;
1215
import java.util.List;
@@ -32,36 +35,51 @@ public class DDLLMObsSpan implements LLMObsSpan {
3235

3336
private static final String LLM_OBS_INSTRUMENTATION_NAME = "llmobs";
3437

38+
private static final String PARENT_ID_TAG_INTERNAL = "parent_id";
39+
3540
private static final Logger LOGGER = LoggerFactory.getLogger(DDLLMObsSpan.class);
3641

3742
private final AgentSpan span;
3843
private final String spanKind;
3944

4045
private boolean finished = false;
4146

42-
private static final Logger LOGGER = LoggerFactory.getLogger(DDLLMObsSpan.class);
47+
private final LLMObsServices llmObsServices;
4348

4449
public DDLLMObsSpan(
4550
@Nonnull String kind,
4651
String spanName,
4752
@Nonnull String mlApp,
4853
String sessionID,
49-
@Nonnull String serviceName) {
54+
@Nonnull String serviceName,
55+
@Nonnull LLMObsServices llmObsServices) {
5056

5157
if (null == spanName || spanName.isEmpty()) {
5258
spanName = kind;
5359
}
5460

61+
this.llmObsServices = llmObsServices;
62+
5563
AgentTracer.SpanBuilder spanBuilder =
5664
AgentTracer.get()
5765
.buildSpan(LLM_OBS_INSTRUMENTATION_NAME, spanName)
5866
.withServiceName(serviceName)
5967
.withSpanType(DDSpanTypes.LLMOBS);
6068

69+
SpanContextInfo activeSpanCtxInfo = this.llmObsServices.getActiveSpanContext();
70+
AgentSpanContext activeCtx = activeSpanCtxInfo.getActiveContext();
71+
if (!activeSpanCtxInfo.isRoot() && null != activeCtx) {
72+
spanBuilder.asChildOf(activeCtx);
73+
}
6174
this.span = spanBuilder.start();
75+
this.llmObsServices.setActiveSpanContext(
76+
new SpanContextInfo(span.context(), String.valueOf(span.context().getSpanId())));
77+
6278
this.span.setTag(SPAN_KIND, kind);
6379
this.spanKind = kind;
6480
this.span.setTag(LLMOBS_TAG_PREFIX + LLMObsTags.ML_APP, mlApp);
81+
this.span.setTag(
82+
LLMOBS_TAG_PREFIX + PARENT_ID_TAG_INTERNAL, activeSpanCtxInfo.getParentSpanID());
6583
if (sessionID != null && !sessionID.isEmpty()) {
6684
this.span.setTag(LLMOBS_TAG_PREFIX + LLMObsTags.SESSION_ID, sessionID);
6785
}
@@ -87,7 +105,6 @@ public void annotateIO(List<LLMObs.LLMMessage> inputData, List<LLMObs.LLMMessage
87105
if (finished) {
88106
return;
89107
}
90-
LOGGER.warn("ANNOTATE IN {} OUT {}", inputData, outputData);
91108
if (inputData != null && !inputData.isEmpty()) {
92109
this.span.setTag(INPUT, inputData);
93110
}
@@ -273,7 +290,9 @@ public void finish() {
273290
if (finished) {
274291
return;
275292
}
293+
DDTraceId traceId = this.span.getTraceId();
276294
this.span.finish();
277295
this.finished = true;
296+
this.llmObsServices.removeActiveSpanContext(traceId);
278297
}
279298
}

0 commit comments

Comments
 (0)