5
5
6
6
package io .opentelemetry .contrib .awsxray .propagator ;
7
7
8
+ import static io .opentelemetry .api .internal .OtelEncodingUtils .isValidBase16String ;
9
+
8
10
import io .opentelemetry .api .baggage .Baggage ;
9
11
import io .opentelemetry .api .baggage .BaggageBuilder ;
10
- import io .opentelemetry .api .baggage .BaggageEntry ;
11
12
import io .opentelemetry .api .internal .StringUtils ;
12
13
import io .opentelemetry .api .trace .Span ;
13
14
import io .opentelemetry .api .trace .SpanContext ;
21
22
import io .opentelemetry .context .propagation .TextMapSetter ;
22
23
import java .util .Collections ;
23
24
import java .util .List ;
24
- import java .util .function . BiConsumer ;
25
+ import java .util .Set ;
25
26
import java .util .logging .Logger ;
26
27
import javax .annotation .Nullable ;
27
28
@@ -68,6 +69,17 @@ public final class AwsXrayPropagator implements TextMapPropagator {
68
69
private static final char IS_SAMPLED = '1' ;
69
70
private static final char NOT_SAMPLED = '0' ;
70
71
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
+
71
83
private static final List <String > FIELDS = Collections .singletonList (TRACE_HEADER_KEY );
72
84
73
85
private static final AwsXrayPropagator INSTANCE = new AwsXrayPropagator ();
@@ -127,34 +139,19 @@ public <C> void inject(Context context, @Nullable C carrier, TextMapSetter<C> se
127
139
.append (samplingFlag );
128
140
129
141
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 );
158
155
}
159
156
160
157
@ Override
@@ -183,10 +180,20 @@ private static <C> Context getContextFromHeader(
183
180
184
181
String traceId = TraceId .getInvalid ();
185
182
String spanId = SpanId .getInvalid ();
183
+ String lineageHeader ;
186
184
Boolean isSampled = false ;
187
185
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
+ }
190
197
191
198
int pos = 0 ;
192
199
while (pos < traceHeader .length ()) {
@@ -215,12 +222,13 @@ private static <C> Context getContextFromHeader(
215
222
spanId = parseSpanId (value );
216
223
} else if (trimmedPart .startsWith (SAMPLED_FLAG_KEY )) {
217
224
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 );
221
231
}
222
- baggage .put (trimmedPart .substring (0 , equalsIndex ), value );
223
- baggageReadBytes += trimmedPart .length ();
224
232
}
225
233
}
226
234
if (isSampled == null ) {
@@ -243,12 +251,17 @@ private static <C> Context getContextFromHeader(
243
251
spanId ,
244
252
isSampled ? TraceFlags .getSampled () : TraceFlags .getDefault (),
245
253
TraceState .getDefault ());
254
+
246
255
if (spanContext .isValid ()) {
247
256
context = context .with (Span .wrap (spanContext ));
248
257
}
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 );
251
263
}
264
+
252
265
return context ;
253
266
}
254
267
@@ -316,6 +329,31 @@ private static String parseSpanId(String xrayParentId) {
316
329
return xrayParentId ;
317
330
}
318
331
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
+
319
357
@ Nullable
320
358
private static Boolean parseTraceFlag (String xraySampledFlag ) {
321
359
if (xraySampledFlag .length () != SAMPLED_FLAG_LENGTH ) {
@@ -332,4 +370,12 @@ private static Boolean parseTraceFlag(String xraySampledFlag) {
332
370
return null ;
333
371
}
334
372
}
373
+
374
+ private static int parseIntOrReturnNegative (String num ) {
375
+ try {
376
+ return Integer .parseInt (num );
377
+ } catch (NumberFormatException e ) {
378
+ return -1 ;
379
+ }
380
+ }
335
381
}
0 commit comments