@@ -27,7 +27,8 @@ import {
27
27
isValidTraceId ,
28
28
INVALID_TRACEID ,
29
29
INVALID_SPANID ,
30
- INVALID_SPAN_CONTEXT ,
30
+ propagation ,
31
+ Baggage ,
31
32
} from '@opentelemetry/api' ;
32
33
33
34
export const AWSXRAY_TRACE_ID_HEADER = 'x-amzn-trace-id' ;
@@ -49,6 +50,8 @@ const SAMPLED_FLAG_KEY = 'Sampled';
49
50
const IS_SAMPLED = '1' ;
50
51
const NOT_SAMPLED = '0' ;
51
52
53
+ const LINEAGE_KEY = "Lineage" ;
54
+
52
55
/**
53
56
* Implementation of the AWS X-Ray Trace Header propagation protocol. See <a href=
54
57
* https://https://docs.aws.amazon.com/xray/latest/devguide/xray-concepts.html#xray-concepts-tracingheader>AWS
@@ -66,48 +69,73 @@ export class AWSXRayPropagator implements TextMapPropagator {
66
69
const timestamp = otTraceId . substring ( 0 , TRACE_ID_FIRST_PART_LENGTH ) ;
67
70
const randomNumber = otTraceId . substring ( TRACE_ID_FIRST_PART_LENGTH ) ;
68
71
72
+ const xrayTraceId = `${ TRACE_ID_VERSION } ${ TRACE_ID_DELIMITER } ${ timestamp } ${ TRACE_ID_DELIMITER } ${ randomNumber } ` ;
73
+
69
74
const parentId = spanContext . spanId ;
70
75
const samplingFlag =
71
76
( TraceFlags . SAMPLED & spanContext . traceFlags ) === TraceFlags . SAMPLED
72
77
? IS_SAMPLED
73
78
: NOT_SAMPLED ;
74
79
// TODO: Add OT trace state to the X-Ray trace header
75
80
76
- const traceHeader = `Root=1-${ timestamp } -${ randomNumber } ;Parent=${ parentId } ;Sampled=${ samplingFlag } ` ;
81
+ let traceHeader =
82
+ `${ TRACE_ID_KEY } ` +
83
+ `${ KV_DELIMITER } ` +
84
+ `${ xrayTraceId } ` +
85
+ `${ TRACE_HEADER_DELIMITER } ` +
86
+ `${ PARENT_ID_KEY } ` +
87
+ `${ KV_DELIMITER } ` +
88
+ `${ parentId } ` +
89
+ `${ TRACE_HEADER_DELIMITER } ` +
90
+ `${ SAMPLED_FLAG_KEY } ` +
91
+ `${ KV_DELIMITER } ` +
92
+ `${ samplingFlag } ` ;
93
+
94
+ const baggage = propagation . getBaggage ( context ) ;
95
+ const lineageV2Header = baggage ?. getEntry ( LINEAGE_KEY ) ?. value ;
96
+
97
+ if ( lineageV2Header ) {
98
+ traceHeader +=
99
+ `${ TRACE_HEADER_DELIMITER } ` +
100
+ `${ LINEAGE_KEY } ` +
101
+ `${ KV_DELIMITER } ` +
102
+ `${ lineageV2Header } ` ;
103
+ }
104
+
77
105
setter . set ( carrier , AWSXRAY_TRACE_ID_HEADER , traceHeader ) ;
78
106
}
79
107
80
108
extract ( context : Context , carrier : unknown , getter : TextMapGetter ) : Context {
81
- const spanContext = this . getSpanContextFromHeader ( carrier , getter ) ;
82
- if ( ! isSpanContextValid ( spanContext ) ) return context ;
83
-
84
- return trace . setSpan ( context , trace . wrapSpanContext ( spanContext ) ) ;
109
+ return this . getContextFromHeader ( context , carrier , getter ) ;
85
110
}
86
111
87
112
fields ( ) : string [ ] {
88
113
return [ AWSXRAY_TRACE_ID_HEADER ] ;
89
114
}
90
115
91
- private getSpanContextFromHeader (
116
+ private getContextFromHeader (
117
+ context : Context ,
92
118
carrier : unknown ,
93
119
getter : TextMapGetter
94
- ) : SpanContext {
120
+ ) : Context {
95
121
const headerKeys = getter . keys ( carrier ) ;
96
122
const relevantHeaderKey = headerKeys . find ( e => {
97
123
return e . toLowerCase ( ) === AWSXRAY_TRACE_ID_HEADER ;
98
124
} ) ;
99
125
if ( ! relevantHeaderKey ) {
100
- return INVALID_SPAN_CONTEXT ;
126
+ return context ;
101
127
}
102
128
const rawTraceHeader = getter . get ( carrier , relevantHeaderKey ) ;
103
129
const traceHeader = Array . isArray ( rawTraceHeader )
104
130
? rawTraceHeader [ 0 ]
105
131
: rawTraceHeader ;
106
132
107
133
if ( ! traceHeader || typeof traceHeader !== 'string' ) {
108
- return INVALID_SPAN_CONTEXT ;
134
+ return context ;
109
135
}
110
136
137
+ let baggage : Baggage = propagation . getBaggage ( context ) || propagation . createBaggage ( ) ;
138
+
111
139
let pos = 0 ;
112
140
let trimmedPart : string ;
113
141
let parsedTraceId = INVALID_TRACEID ;
@@ -133,21 +161,27 @@ export class AWSXRayPropagator implements TextMapPropagator {
133
161
parsedSpanId = AWSXRayPropagator . _parseSpanId ( value ) ;
134
162
} else if ( trimmedPart . startsWith ( SAMPLED_FLAG_KEY ) ) {
135
163
parsedTraceFlags = AWSXRayPropagator . _parseTraceFlag ( value ) ;
164
+ } else if ( trimmedPart . startsWith ( LINEAGE_KEY ) ) {
165
+ baggage = baggage . setEntry ( LINEAGE_KEY , { value} ) ;
136
166
}
137
167
}
138
168
if ( parsedTraceFlags === null ) {
139
- return INVALID_SPAN_CONTEXT ;
169
+ return context ;
140
170
}
141
171
const resultSpanContext : SpanContext = {
142
172
traceId : parsedTraceId ,
143
173
spanId : parsedSpanId ,
144
174
traceFlags : parsedTraceFlags ,
145
175
isRemote : true ,
146
176
} ;
147
- if ( ! isSpanContextValid ( resultSpanContext ) ) {
148
- return INVALID_SPAN_CONTEXT ;
177
+ if ( isSpanContextValid ( resultSpanContext ) ) {
178
+ context = trace . setSpan ( context , trace . wrapSpanContext ( resultSpanContext ) ) ;
149
179
}
150
- return resultSpanContext ;
180
+ if ( baggage . getAllEntries ( ) . length > 0 ) {
181
+ context = propagation . setBaggage ( context , baggage ) ;
182
+ }
183
+
184
+ return context ;
151
185
}
152
186
153
187
private static _parseTraceId ( xrayTraceId : string ) : string {
0 commit comments