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