44import datadog .communication .BackendApiFactory ;
55import datadog .communication .ddagent .SharedCommunicationObjects ;
66import 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 ;
718import org .slf4j .Logger ;
819import org .slf4j .LoggerFactory ;
920
@@ -14,9 +25,89 @@ 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+ logger .warn ("NO ACTIVE SPAN CONTEXT" );
42+ return new SpanContextInfo ();
43+ }
44+
45+ // Unexpected case: null active scope span, log to avoid crashes
46+ if (activeScope .span () == null ) {
47+ logger .warn ("active span scope found but no null span" );
48+ return new SpanContextInfo ();
49+ }
50+
51+ // Unexpected case: null trace ID, log to avoid crashes
52+ DDTraceId traceId = activeScope .span ().getTraceId ();
53+ if (traceId == null ) {
54+ logger .warn ("active scope found but unexpectedly null trace ID" );
55+ return new SpanContextInfo ();
56+ }
57+
58+ Deque <SpanContextInfo > activeSpanCtxForTID = activeSpanContextByTID .get (traceId );
59+ // Valid case: possibly start root llm obs span/trace while there's an active apm trace
60+ if (activeSpanCtxForTID == null || activeSpanCtxForTID .isEmpty ()) {
61+ logger .warn ("NO ACTIVE SPAN CONTEXT FOR TRACE ID {}" , traceId );
62+ return new SpanContextInfo ();
63+ }
64+
65+ // Valid case: possibly start child llm obs span for a given trace ID
66+ return activeSpanCtxForTID .peek ();
67+ }
68+
69+ public void setActiveSpanContext (SpanContextInfo spanContext ) {
70+ AgentSpanContext activeCtx = spanContext .getActiveContext ();
71+ if (activeCtx == null ) {
72+ logger .warn ("unexpected null active context" );
73+ return ;
74+ }
75+
76+ DDTraceId traceId = activeCtx .getTraceId ();
77+ if (traceId == null ) {
78+ logger .warn ("unexpected null trace ID" );
79+ return ;
80+ }
81+
82+ Deque <SpanContextInfo > contexts = activeSpanContextByTID .get (activeCtx .getTraceId ());
83+ if (contexts == null ) {
84+ contexts = new ConcurrentLinkedDeque <>();
85+ }
86+ contexts .push (spanContext );
87+ this .activeSpanContextByTID .put (traceId , contexts );
88+ logger .warn ("ADDED TRACE ID: {} SPAN CONTEXTS: {}" , traceId , contexts );
89+ }
90+
91+ public void removeActiveSpanContext (DDTraceId traceId ) {
92+ if (!activeSpanContextByTID .containsKey (traceId )) {
93+ logger .debug ("active span contexts not found for trace {}" , traceId );
94+ return ;
95+ }
96+ Deque <SpanContextInfo > contexts = activeSpanContextByTID .get (traceId );
97+ if (contexts == null ) {
98+ return ;
99+ }
100+ if (!contexts .isEmpty ()) {
101+ try {
102+ contexts .pop ();
103+ if (contexts .isEmpty ()) {
104+ // the trace MAY still be active, however, the next set should re-create the hierarchy as
105+ // needed
106+ activeSpanContextByTID .remove (traceId );
107+ }
108+ } catch (NoSuchElementException noSuchElementException ) {
109+ logger .debug ("failed to pop context stack for trace {}" , traceId );
110+ }
111+ }
112+ }
22113}
0 commit comments