11package dev .vality .woody .api ;
22
3- import dev .vality .woody .api .trace .Span ;
3+ import dev .vality .woody .api .event .CallType ;
4+ import dev .vality .woody .api .proxy .InstanceMethodCaller ;
5+ import dev .vality .woody .api .trace .*;
46import org .slf4j .MDC ;
57
68import java .time .Instant ;
9+ import java .util .HashSet ;
10+ import java .util .Locale ;
11+ import java .util .Objects ;
12+ import java .util .Set ;
713
814@ SuppressWarnings ("checkstyle:AbbreviationAsWordInName" )
915public class MDCUtils {
@@ -15,48 +21,248 @@ public class MDCUtils {
1521 public static final String TRACE_ID = "trace_id" ;
1622 public static final String PARENT_ID = "parent_id" ;
1723 public static final String DEADLINE = "deadline" ;
24+ public static final String TRACE_RPC_PREFIX = "rpc." ;
25+ public static final String TRACE_RPC_CLIENT_PREFIX = TRACE_RPC_PREFIX + "client." ;
26+ public static final String TRACE_RPC_SERVER_PREFIX = TRACE_RPC_PREFIX + "server." ;
27+ public static final String TRACE_RPC_METADATA_SUFFIX = "metadata." ;
28+ public static final String EXTENDED_MDC_PROPERTY = "woody.mdc.extended" ;
29+ private static final ThreadLocal <Set <String >> EXTENDED_MDC_KEYS = ThreadLocal .withInitial (HashSet ::new );
30+ private static volatile boolean extendedFieldsEnabled =
31+ Boolean .parseBoolean (System .getProperty (EXTENDED_MDC_PROPERTY , "true" ));
1832
19- /**
20- * Put span data in MDC
21- *
22- * @param span - service or client span
23- */
24- public static void putSpanData (Span span , io .opentelemetry .api .trace .Span otelSpan ) {
25- MDC .put (SPAN_ID , span .getId () != null ? span .getId () : "" );
26- MDC .put (TRACE_ID , span .getTraceId () != null ? span .getTraceId () : "" );
27- MDC .put (PARENT_ID , span .getParentId () != null ? span .getParentId () : "" );
28- MDC .put (OTEL_TRACE_ID ,
29- otelSpan .getSpanContext ().getTraceId () != null ? otelSpan .getSpanContext ().getTraceId () : "" );
30- MDC .put (OTEL_SPAN_ID ,
31- otelSpan .getSpanContext ().getSpanId () != null ? otelSpan .getSpanContext ().getSpanId () : "" );
32- MDC .put (OTEL_TRACE_FLAGS ,
33- otelSpan .getSpanContext ().getTraceFlags () != null ? otelSpan .getSpanContext ().getTraceFlags ().asHex () :
34- "" );
35- if (span .hasDeadline ()) {
36- MDC .put (DEADLINE , span .getDeadline ().toString ());
37- }
38- }
39-
40- /**
41- * Remove span data from MDC
42- */
43- public static void removeSpanData () {
33+ public static void putTraceData (TraceData traceData , ContextSpan contextSpan ) {
34+ if (traceData == null || contextSpan == null || contextSpan .getSpan () == null ) {
35+ removeTraceData ();
36+ return ;
37+ }
38+
39+ io .opentelemetry .api .trace .Span otelSpan = traceData .getOtelSpan ();
40+ io .opentelemetry .api .trace .SpanContext spanContext = otelSpan != null ? otelSpan .getSpanContext () : null ;
41+
42+ populateSpanIdentifiers (contextSpan .getSpan ());
43+ populateOtelIdentifiers (spanContext );
44+
45+ clearExtendedEntries (false );
46+ if (isExtendedFieldsEnabled ()) {
47+ populateExtendedFields (traceData );
48+ }
49+
50+ updateDeadlineEntries (traceData , contextSpan );
51+ }
52+
53+ private static void populateSpanIdentifiers (Span span ) {
54+ putMdcValue (SPAN_ID , span .getId ());
55+ putMdcValue (TRACE_ID , span .getTraceId ());
56+ putMdcValue (PARENT_ID , span .getParentId ());
57+ }
58+
59+ private static void populateOtelIdentifiers (io .opentelemetry .api .trace .SpanContext spanContext ) {
60+ if (spanContext == null ) {
61+ putMdcValue (OTEL_TRACE_ID , null );
62+ putMdcValue (OTEL_SPAN_ID , null );
63+ putMdcValue (OTEL_TRACE_FLAGS , null );
64+ return ;
65+ }
66+ putMdcValue (OTEL_TRACE_ID , spanContext .getTraceId ());
67+ putMdcValue (OTEL_SPAN_ID , spanContext .getSpanId ());
68+ putMdcValue (OTEL_TRACE_FLAGS ,
69+ spanContext .getTraceFlags () != null ? spanContext .getTraceFlags ().asHex () : null );
70+ }
71+
72+ public static void removeTraceData () {
4473 MDC .remove (SPAN_ID );
4574 MDC .remove (TRACE_ID );
4675 MDC .remove (OTEL_TRACE_ID );
4776 MDC .remove (OTEL_SPAN_ID );
4877 MDC .remove (OTEL_TRACE_FLAGS );
4978 MDC .remove (DEADLINE );
79+ clearExtendedEntries (true );
5080 }
5181
52- public static void putDeadline (Instant deadline ) {
53- if (deadline != null ) {
54- MDC .put (DEADLINE , deadline .toString ());
82+ public static void putDeadline (TraceData traceData , ContextSpan contextSpan , Instant deadline ) {
83+ if (deadline == null ) {
84+ removeDeadline (traceData , contextSpan );
85+ return ;
5586 }
87+
88+ updateDeadlineEntries (traceData , contextSpan );
5689 }
5790
58- public static void removeDeadline () {
59- MDC .remove (DEADLINE );
91+ public static void removeDeadline (TraceData traceData , ContextSpan contextSpan ) {
92+ updateDeadlineEntries (traceData , contextSpan );
93+ }
94+
95+ public static void enableExtendedFields () {
96+ extendedFieldsEnabled = true ;
97+ }
98+
99+ public static void disableExtendedFields () {
100+ extendedFieldsEnabled = false ;
101+ clearExtendedEntries (false );
102+ }
103+
104+ public static boolean isExtendedFieldsEnabled () {
105+ return extendedFieldsEnabled ;
106+ }
107+
108+ private static void populateExtendedFields (TraceData traceData ) {
109+ addSpanDetails (traceData .getClientSpan (), TRACE_RPC_CLIENT_PREFIX );
110+ addSpanDetails (traceData .getServiceSpan (), TRACE_RPC_SERVER_PREFIX );
111+ }
112+
113+ private static void addSpanDetails (ContextSpan contextSpan , String prefix ) {
114+ if (contextSpan == null || !contextSpan .isFilled ()) {
115+ return ;
116+ }
117+
118+ addExtendedEntry (prefix + "service" , resolveServiceName (contextSpan ));
119+ addExtendedEntry (prefix + "function" , resolveFunctionName (contextSpan ));
120+ addExtendedEntry (prefix + "type" , resolveCallType (contextSpan ));
121+ addExtendedEntry (prefix + "event" , resolveEvent (contextSpan ));
122+ addExtendedEntry (prefix + "url" , resolveEndpoint (contextSpan ));
123+
124+ long duration = contextSpan .getSpan ().getDuration ();
125+ if (duration > 0 ) {
126+ addExtendedEntry (prefix + "execution_duration_ms" , Long .toString (duration ));
127+ }
128+
129+ addCustomMetadataEntries (contextSpan , prefix + TRACE_RPC_METADATA_SUFFIX );
130+ }
131+
132+ private static void addCustomMetadataEntries (ContextSpan contextSpan , String prefix ) {
133+ Metadata metadata = contextSpan .getCustomMetadata ();
134+ if (metadata == null ) {
135+ return ;
136+ }
137+ for (String key : metadata .getKeys ()) {
138+ Object value = metadata .getValue (key );
139+ if (value != null ) {
140+ addExtendedEntry (prefix + key , Objects .toString (value ));
141+ }
142+ }
143+ }
144+
145+ private static String resolveServiceName (ContextSpan contextSpan ) {
146+ InstanceMethodCaller caller = contextSpan .getMetadata ().getValue (MetadataProperties .INSTANCE_METHOD_CALLER );
147+ if (caller == null || caller .getTargetMethod () == null ) {
148+ return null ;
149+ }
150+ Class <?> declaringClass = caller .getTargetMethod ().getDeclaringClass ();
151+ if (declaringClass == null ) {
152+ return null ;
153+ }
154+ Class <?> serviceClass = declaringClass ;
155+ if (declaringClass .getEnclosingClass () != null ) {
156+ String simple = declaringClass .getSimpleName ();
157+ if ("Iface" .equals (simple ) || "AsyncIface" .equals (simple )) {
158+ serviceClass = declaringClass .getEnclosingClass ();
159+ }
160+ }
161+ String simpleName = serviceClass .getSimpleName ();
162+ if (simpleName .endsWith ("Srv" )) {
163+ simpleName = simpleName .substring (0 , simpleName .length () - 3 );
164+ }
165+ return simpleName ;
166+ }
167+
168+ private static String resolveFunctionName (ContextSpan contextSpan ) {
169+ String callName = contextSpan .getMetadata ().getValue (MetadataProperties .CALL_NAME );
170+ if (callName != null && !callName .isEmpty ()) {
171+ return callName ;
172+ }
173+ InstanceMethodCaller caller = contextSpan .getMetadata ().getValue (MetadataProperties .INSTANCE_METHOD_CALLER );
174+ if (caller != null && caller .getTargetMethod () != null ) {
175+ return caller .getTargetMethod ().getName ();
176+ }
177+ return null ;
178+ }
179+
180+ private static String resolveCallType (ContextSpan contextSpan ) {
181+ CallType callType = contextSpan .getMetadata ().getValue (MetadataProperties .CALL_TYPE );
182+ if (callType != null ) {
183+ return callType .name ().toLowerCase (Locale .ROOT );
184+ }
185+ return null ;
186+ }
187+
188+ private static String resolveEvent (ContextSpan contextSpan ) {
189+ Object event = contextSpan .getMetadata ().getValue (MetadataProperties .EVENT_TYPE );
190+ if (event instanceof Enum <?>) {
191+ return formatEnum ((Enum <?>) event );
192+ }
193+ return null ;
194+ }
195+
196+ private static String resolveEndpoint (ContextSpan contextSpan ) {
197+ Object endpoint = contextSpan .getMetadata ().getValue (MetadataProperties .CALL_ENDPOINT );
198+ if (endpoint instanceof Endpoint ) {
199+ return ((Endpoint <?>) endpoint ).getStringValue ();
200+ }
201+ return endpoint != null ? endpoint .toString () : null ;
202+ }
203+
204+ private static String formatEnum (Enum <?> value ) {
205+ return value == null ? null : value .name ().toLowerCase (Locale .ROOT ).replace ('_' , ' ' );
206+ }
207+
208+ private static void addExtendedEntry (String key , String value ) {
209+ if (key == null || value == null || value .isEmpty ()) {
210+ return ;
211+ }
212+ MDC .put (key , value );
213+ EXTENDED_MDC_KEYS .get ().add (key );
214+ }
215+
216+ private static void putMdcValue (String key , String value ) {
217+ MDC .put (key , value != null ? value : "" );
218+ }
219+
220+ private static void removeExtendedEntry (String key ) {
221+ MDC .remove (key );
222+ EXTENDED_MDC_KEYS .get ().remove (key );
223+ }
224+
225+ private static void updateDeadlineEntries (TraceData traceData , ContextSpan contextSpan ) {
226+ Instant activeDeadline = contextSpan != null ? ContextUtils .getDeadline (contextSpan ) : null ;
227+ if (activeDeadline != null ) {
228+ MDC .put (DEADLINE , activeDeadline .toString ());
229+ } else {
230+ MDC .remove (DEADLINE );
231+ }
232+
233+ removeExtendedEntry (TRACE_RPC_CLIENT_PREFIX + "deadline" );
234+ removeExtendedEntry (TRACE_RPC_SERVER_PREFIX + "deadline" );
235+
236+ if (!isExtendedFieldsEnabled ()) {
237+ return ;
238+ }
239+
240+ if (traceData != null ) {
241+ addDeadlineEntry (traceData .getClientSpan (), TRACE_RPC_CLIENT_PREFIX );
242+ addDeadlineEntry (traceData .getServiceSpan (), TRACE_RPC_SERVER_PREFIX );
243+ }
244+ }
245+
246+ private static void addDeadlineEntry (ContextSpan span , String prefix ) {
247+ if (span == null ) {
248+ return ;
249+ }
250+ Instant deadline = ContextUtils .getDeadline (span );
251+ if (deadline != null ) {
252+ addExtendedEntry (prefix + "deadline" , deadline .toString ());
253+ }
60254 }
61255
256+ private static void clearExtendedEntries (boolean removeThreadLocal ) {
257+ Set <String > keys = EXTENDED_MDC_KEYS .get ();
258+ for (String key : keys ) {
259+ MDC .remove (key );
260+ }
261+
262+ if (removeThreadLocal ) {
263+ EXTENDED_MDC_KEYS .remove ();
264+ } else {
265+ keys .clear ();
266+ }
267+ }
62268}
0 commit comments