4
4
import datadog .communication .BackendApiFactory ;
5
5
import datadog .communication .ddagent .SharedCommunicationObjects ;
6
6
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 ;
7
18
import org .slf4j .Logger ;
8
19
import org .slf4j .LoggerFactory ;
9
20
@@ -14,9 +25,89 @@ public class LLMObsServices {
14
25
final Config config ;
15
26
final BackendApi backendApi ;
16
27
28
+ Map <DDTraceId , Deque <SpanContextInfo >> activeSpanContextByTID = new ConcurrentHashMap <>();
29
+
17
30
LLMObsServices (Config config , SharedCommunicationObjects sco ) {
18
31
this .config = config ;
19
32
this .backendApi =
20
33
new BackendApiFactory (config , sco ).createBackendApi (BackendApiFactory .Intake .LLMOBS_API );
21
34
}
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
+ }
22
113
}
0 commit comments