55
66package  io .opentelemetry .contrib .awsxray .propagator ;
77
8+ import  static  io .opentelemetry .api .internal .OtelEncodingUtils .isValidBase16String ;
9+ 
810import  io .opentelemetry .api .baggage .Baggage ;
911import  io .opentelemetry .api .baggage .BaggageBuilder ;
10- import  io .opentelemetry .api .baggage .BaggageEntry ;
1112import  io .opentelemetry .api .internal .StringUtils ;
1213import  io .opentelemetry .api .trace .Span ;
1314import  io .opentelemetry .api .trace .SpanContext ;
2122import  io .opentelemetry .context .propagation .TextMapSetter ;
2223import  java .util .Collections ;
2324import  java .util .List ;
24- import  java .util .function . BiConsumer ;
25+ import  java .util .Set ;
2526import  java .util .logging .Logger ;
2627import  javax .annotation .Nullable ;
2728
@@ -68,6 +69,17 @@ public final class AwsXrayPropagator implements TextMapPropagator {
6869  private  static  final  char  IS_SAMPLED  = '1' ;
6970  private  static  final  char  NOT_SAMPLED  = '0' ;
7071
72+   private  static  final  String  LINEAGE_KEY  = "Lineage" ;
73+   private  static  final  char  LINEAGE_DELIMITER  = ':' ;
74+   private  static  final  int  LINEAGE_MAX_LENGTH  = 18 ;
75+   private  static  final  int  LINEAGE_MIN_LENGTH  = 12 ;
76+   private  static  final  int  LINEAGE_HASH_LENGTH  = 8 ;
77+   private  static  final  int  LINEAGE_MAX_COUNTER1  = 32767 ;
78+   private  static  final  int  LINEAGE_MAX_COUNTER2  = 255 ;
79+   private  static  final  int  LINEAGE_MIN_COUNTER  = 0 ;
80+   private  static  final  String  INVALID_LINEAGE  = "-1:11111111:0" ;
81+   private  static  final  int  NUM_OF_LINEAGE_DELIMITERS  = 2 ;
82+ 
7183  private  static  final  List <String > FIELDS  = Collections .singletonList (TRACE_HEADER_KEY );
7284
7385  private  static  final  AwsXrayPropagator  INSTANCE  = new  AwsXrayPropagator ();
@@ -127,34 +139,19 @@ public <C> void inject(Context context, @Nullable C carrier, TextMapSetter<C> se
127139        .append (samplingFlag );
128140
129141    Baggage  baggage  = Baggage .fromContext (context );
130-     // Truncate baggage to 256 chars per X-Ray spec. 
131-     baggage .forEach (
132-         new  BiConsumer <String , BaggageEntry >() {
133- 
134-           private  int  baggageWrittenBytes ;
135- 
136-           @ Override 
137-           public  void  accept (String  key , BaggageEntry  entry ) {
138-             if  (key .equals (TRACE_ID_KEY )
139-                 || key .equals (PARENT_ID_KEY )
140-                 || key .equals (SAMPLED_FLAG_KEY )) {
141-               return ;
142-             }
143-             // Size is key/value pair, excludes delimiter. 
144-             int  size  = key .length () + entry .getValue ().length () + 1 ;
145-             if  (baggageWrittenBytes  + size  > 256 ) {
146-               return ;
147-             }
148-             traceHeader 
149-                 .append (TRACE_HEADER_DELIMITER )
150-                 .append (key )
151-                 .append (KV_DELIMITER )
152-                 .append (entry .getValue ());
153-             baggageWrittenBytes  += size ;
154-           }
155-         });
156- 
157-     setter .set (carrier , TRACE_HEADER_KEY , traceHeader .toString ());
142+     String  lineageHeader  = baggage .getEntryValue (LINEAGE_KEY );
143+ 
144+     if  (lineageHeader  != null ) {
145+       traceHeader 
146+           .append (TRACE_HEADER_DELIMITER )
147+           .append (LINEAGE_KEY )
148+           .append (KV_DELIMITER )
149+           .append (lineageHeader );
150+     }
151+ 
152+     // add 256 character truncation 
153+     String  truncatedTraceHeader  = traceHeader .substring (0 , Math .min (traceHeader .length (), 256 ));
154+     setter .set (carrier , TRACE_HEADER_KEY , truncatedTraceHeader );
158155  }
159156
160157  @ Override 
@@ -183,10 +180,20 @@ private static <C> Context getContextFromHeader(
183180
184181    String  traceId  = TraceId .getInvalid ();
185182    String  spanId  = SpanId .getInvalid ();
183+     String  lineageHeader ;
186184    Boolean  isSampled  = false ;
187185
188-     BaggageBuilder  baggage  = null ;
189-     int  baggageReadBytes  = 0 ;
186+     Baggage  contextBaggage  = Baggage .fromContext (context );
187+     BaggageBuilder  baggageBuilder  = Baggage .builder ();
188+     Set <String > baggageMap  = contextBaggage .asMap ().keySet ();
189+ 
190+     // Copying baggage over to new Baggage object to add Lineage key 
191+     for  (String  baggageKey  : baggageMap ) {
192+       String  baggageValue  = contextBaggage .getEntryValue (baggageKey );
193+       if  (baggageValue  != null ) {
194+         baggageBuilder .put (baggageKey , baggageValue );
195+       }
196+     }
190197
191198    int  pos  = 0 ;
192199    while  (pos  < traceHeader .length ()) {
@@ -215,12 +222,13 @@ private static <C> Context getContextFromHeader(
215222        spanId  = parseSpanId (value );
216223      } else  if  (trimmedPart .startsWith (SAMPLED_FLAG_KEY )) {
217224        isSampled  = parseTraceFlag (value );
218-       } else  if  (baggageReadBytes  + trimmedPart .length () <= 256 ) {
219-         if  (baggage  == null ) {
220-           baggage  = Baggage .builder ();
225+       } else  if  (trimmedPart .startsWith (LINEAGE_KEY )) {
226+         lineageHeader  = parseLineageHeader (value );
227+         if  (isValidLineage (lineageHeader )) {
228+           baggageBuilder .put (LINEAGE_KEY , lineageHeader );
229+         } else  {
230+           logger .fine ("Invalid Lineage header: "  + value );
221231        }
222-         baggage .put (trimmedPart .substring (0 , equalsIndex ), value );
223-         baggageReadBytes  += trimmedPart .length ();
224232      }
225233    }
226234    if  (isSampled  == null ) {
@@ -243,12 +251,17 @@ private static <C> Context getContextFromHeader(
243251            spanId ,
244252            isSampled  ? TraceFlags .getSampled () : TraceFlags .getDefault (),
245253            TraceState .getDefault ());
254+ 
246255    if  (spanContext .isValid ()) {
247256      context  = context .with (Span .wrap (spanContext ));
248257    }
249-     if  (baggage  != null ) {
250-       context  = context .with (baggage .build ());
258+ 
259+     Baggage  baggage  = baggageBuilder .build ();
260+ 
261+     if  (!baggage .isEmpty ()) {
262+       context  = context .with (baggage );
251263    }
264+ 
252265    return  context ;
253266  }
254267
@@ -316,6 +329,31 @@ private static String parseSpanId(String xrayParentId) {
316329    return  xrayParentId ;
317330  }
318331
332+   private  static  String  parseLineageHeader (String  xrayLineageHeader ) {
333+     long  numOfDelimiters  = xrayLineageHeader .chars ().filter (ch  -> ch  == LINEAGE_DELIMITER ).count ();
334+ 
335+     if  (xrayLineageHeader .length () < LINEAGE_MIN_LENGTH 
336+         || xrayLineageHeader .length () > LINEAGE_MAX_LENGTH 
337+         || numOfDelimiters  != NUM_OF_LINEAGE_DELIMITERS ) {
338+       return  INVALID_LINEAGE ;
339+     }
340+ 
341+     return  xrayLineageHeader ;
342+   }
343+ 
344+   private  static  boolean  isValidLineage (String  key ) {
345+     String [] split  = key .split (String .valueOf (LINEAGE_DELIMITER ));
346+     String  hash  = split [1 ];
347+     int  counter1  = parseIntOrReturnNegative (split [0 ]);
348+     int  counter2  = parseIntOrReturnNegative (split [2 ]);
349+ 
350+     boolean  isHashValid  = hash .length () == LINEAGE_HASH_LENGTH  && isValidBase16String (hash );
351+     boolean  isValidCounter2  = counter2  <= LINEAGE_MAX_COUNTER2  && counter2  >= LINEAGE_MIN_COUNTER ;
352+     boolean  isValidCounter1  = counter1  <= LINEAGE_MAX_COUNTER1  && counter1  >= LINEAGE_MIN_COUNTER ;
353+ 
354+     return  isHashValid  && isValidCounter2  && isValidCounter1 ;
355+   }
356+ 
319357  @ Nullable 
320358  private  static  Boolean  parseTraceFlag (String  xraySampledFlag ) {
321359    if  (xraySampledFlag .length () != SAMPLED_FLAG_LENGTH ) {
@@ -332,4 +370,12 @@ private static Boolean parseTraceFlag(String xraySampledFlag) {
332370      return  null ;
333371    }
334372  }
373+ 
374+   private  static  int  parseIntOrReturnNegative (String  num ) {
375+     try  {
376+       return  Integer .parseInt (num );
377+     } catch  (NumberFormatException  e ) {
378+       return  -1 ;
379+     }
380+   }
335381}
0 commit comments