22// SPDX-License-Identifier: Apache-2.0
33
44import { AWSCloudWatchEMFExporter } from '../src/exporter/aws/metrics/aws-cloudwatch-emf-exporter' ;
5- import { Span , TraceFlags , Tracer } from '@opentelemetry/api' ;
5+ import { propagation , ROOT_CONTEXT , Span , TextMapGetter , trace , TraceFlags , Tracer } from '@opentelemetry/api' ;
66import { OTLPMetricExporter as OTLPGrpcOTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc' ;
77import { OTLPMetricExporter as OTLPHttpOTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http' ;
88import { OTLPTraceExporter as OTLPGrpcTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc' ;
@@ -63,6 +63,7 @@ import {
6363import { OTLPAwsLogExporter } from '../src/exporter/otlp/aws/logs/otlp-aws-log-exporter' ;
6464import { OTLPAwsSpanExporter } from '../src/exporter/otlp/aws/traces/otlp-aws-span-exporter' ;
6565import { AwsCloudWatchOtlpBatchLogRecordProcessor } from '../src/exporter/otlp/aws/logs/aws-cw-otlp-batch-log-record-processor' ;
66+ import { TRACE_PARENT_HEADER } from '@opentelemetry/core' ;
6667
6768// Tests AwsOpenTelemetryConfigurator after running Environment Variable setup in register.ts
6869describe ( 'AwsOpenTelemetryConfiguratorTest' , ( ) => {
@@ -90,6 +91,12 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {
9091 ( awsOtelConfigurator as any ) . spanProcessors . forEach ( ( spanProcessor : SpanProcessor ) => {
9192 spanProcessor . shutdown ( ) ;
9293 } ) ;
94+
95+ delete process . env . OTEL_TRACES_EXPORTER ;
96+ delete process . env . OTEL_METRICS_EXPORTER ;
97+ delete process . env . OTEL_LOGS_EXPORTER ;
98+ delete process . env . OTEL_TRACES_SAMPLER ;
99+ delete process . env . OTEL_TRACES_SAMPLER_ARG ;
93100 } ) ;
94101
95102 // The probability of this passing once without correct IDs is low, 20 times is inconceivable.
@@ -110,6 +117,152 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {
110117 }
111118 } ) ;
112119
120+ describe ( 'Propagator Extraction' , ( ) => {
121+ const envVarsToRestore : NodeJS . ProcessEnv = { } ;
122+ beforeEach ( ( ) => {
123+ for ( const [ key , value ] of Object . entries ( process . env ) ) {
124+ if ( key . startsWith ( 'OTEL_' ) ) {
125+ envVarsToRestore [ key ] = value ;
126+ delete process . env [ key ] ;
127+ }
128+ }
129+ } ) ;
130+
131+ afterEach ( ( ) => {
132+ // Clean-up
133+ for ( const [ key , value ] of Object . entries ( envVarsToRestore ) ) {
134+ process . env [ key ] = value ;
135+ }
136+ } ) ;
137+
138+ it ( 'Default Propagator extraction of Trace Context works if only X-Ray Trace Header is set' , ( ) => {
139+ const carrier : Record < string , string > = {
140+ 'X-Amzn-Trace-Id' :
141+ 'Root=1-5759e988-bd862e3fe1bf46a994270000;Parent=53945c3f42cd0000;Sampled=1;Lineage=a87bd80c:1|68fd508a:5|c512fbe3:2' ,
142+ } ;
143+ const textMapGetter : TextMapGetter = {
144+ keys : ( carrier : Record < string , string > ) : string [ ] => {
145+ return Object . keys ( carrier ) ;
146+ } ,
147+ get : ( carrier : Record < string , string > , key : string ) => {
148+ return carrier ?. [ key ] ;
149+ } ,
150+ } ;
151+
152+ // Create configurator with default settings and AgentObservability enabled for this test case
153+ setAwsDefaultEnvironmentVariables ( ) ;
154+ const customAwsOtelConfigurator = new AwsOpentelemetryConfigurator ( [ ] ) ;
155+ const customAwsOtelConfiguration = customAwsOtelConfigurator . configure ( ) ;
156+
157+ const tracerProvider : NodeTracerProvider = new NodeTracerProvider ( customAwsOtelConfiguration ) ;
158+ const tracer : Tracer = tracerProvider . getTracer ( 'test' ) ;
159+
160+ const contextAfterExtraction = customAwsOtelConfiguration . textMapPropagator ?. extract (
161+ ROOT_CONTEXT ,
162+ carrier ,
163+ textMapGetter
164+ ) ;
165+
166+ // Test Trace Context extraction directly
167+ const spanContextAfterExtraction = trace . getSpanContext ( contextAfterExtraction ! ) ;
168+ expect ( spanContextAfterExtraction ?. traceId ) . toEqual ( '5759e988bd862e3fe1bf46a994270000' ) ;
169+ expect ( spanContextAfterExtraction ?. spanId ) . toEqual ( '53945c3f42cd0000' ) ;
170+ expect ( spanContextAfterExtraction ?. traceFlags ) . toEqual ( 1 ) ;
171+
172+ // Test Trace Context extraction indirectly through span creation
173+ const span : Span = tracer . startSpan ( 'test' , undefined , contextAfterExtraction ) ;
174+ span . end ( ) ;
175+ const spanContext = span . spanContext ( ) ;
176+
177+ expect ( spanContext . traceId ) . toEqual ( '5759e988bd862e3fe1bf46a994270000' ) ;
178+ expect ( ( span as any ) . parentSpanId ) . toEqual ( '53945c3f42cd0000' ) ;
179+ expect ( spanContext . traceFlags ) . toEqual ( 1 ) ;
180+ } ) ;
181+
182+ it ( 'Default Propagator extraction of Trace Context prioritizes W3C Trace Header over X-Ray Trace Header for span context if they mismatch' , ( ) => {
183+ const carrier : Record < string , string > = {
184+ 'X-Amzn-Trace-Id' :
185+ 'Root=1-5759e988-bd862e3fe1bf46a994270000;Parent=53945c3f42cd0000;Sampled=0;Lineage=a87bd80c:1|68fd508a:5|c512fbe3:2' ,
186+ [ TRACE_PARENT_HEADER ] : '00-11111111222222223333333344444444-5555555566666666-01' ,
187+ } ;
188+ const textMapGetter : TextMapGetter = {
189+ keys : ( carrier : Record < string , string > ) : string [ ] => {
190+ return Object . keys ( carrier ) ;
191+ } ,
192+ get : ( carrier : Record < string , string > , key : string ) => {
193+ return carrier ?. [ key ] ;
194+ } ,
195+ } ;
196+
197+ // Create configurator with default settings and AgentObservability enabled for this test case
198+ setAwsDefaultEnvironmentVariables ( ) ;
199+ const customAwsOtelConfigurator = new AwsOpentelemetryConfigurator ( [ ] ) ;
200+ const customAwsOtelConfiguration = customAwsOtelConfigurator . configure ( ) ;
201+
202+ const tracerProvider : NodeTracerProvider = new NodeTracerProvider ( customAwsOtelConfiguration ) ;
203+ const tracer : Tracer = tracerProvider . getTracer ( 'test' ) ;
204+
205+ const contextAfterExtraction = customAwsOtelConfiguration . textMapPropagator ?. extract (
206+ ROOT_CONTEXT ,
207+ carrier ,
208+ textMapGetter
209+ ) ;
210+
211+ // Test Trace Context extraction directly
212+ const spanContextAfterExtraction = trace . getSpanContext ( contextAfterExtraction ! ) ;
213+ expect ( spanContextAfterExtraction ?. traceId ) . toEqual ( '11111111222222223333333344444444' ) ;
214+ expect ( spanContextAfterExtraction ?. spanId ) . toEqual ( '5555555566666666' ) ;
215+ expect ( spanContextAfterExtraction ?. traceFlags ) . toEqual ( 1 ) ;
216+
217+ // Test Trace Context extraction indirectly through span creation
218+ const span : Span = tracer . startSpan ( 'test' , undefined , contextAfterExtraction ) ;
219+ span . end ( ) ;
220+ const spanContext = span . spanContext ( ) ;
221+
222+ expect ( spanContext . traceId ) . toEqual ( '11111111222222223333333344444444' ) ;
223+ expect ( ( span as any ) . parentSpanId ) . toEqual ( '5555555566666666' ) ;
224+ expect ( spanContext . traceFlags ) . toEqual ( 1 ) ;
225+ } ) ;
226+
227+ it ( 'Propagator extracts session.id baggage header attribute into to span attributes when Agent Observability is enabled' , ( ) => {
228+ // Create configurator with default settings and AgentObservability enabled for this test case
229+ setAwsDefaultEnvironmentVariables ( ) ;
230+ process . env . AGENT_OBSERVABILITY_ENABLED = 'true' ;
231+ const customAwsOtelConfigurator = new AwsOpentelemetryConfigurator ( [ ] ) ;
232+ const customAwsOtelConfiguration = customAwsOtelConfigurator . configure ( ) ;
233+
234+ const tracerProvider : NodeTracerProvider = new NodeTracerProvider ( customAwsOtelConfiguration ) ;
235+ const tracer : Tracer = tracerProvider . getTracer ( 'test' ) ;
236+
237+ const carrier : Record < string , string > = {
238+ baggage : 'session.id=test-adot-js-dev' ,
239+ } ;
240+ const textMapGetter : TextMapGetter = {
241+ keys : ( carrier : Record < string , string > ) : string [ ] => {
242+ return Object . keys ( carrier ) ;
243+ } ,
244+ get : ( carrier : Record < string , string > , key : string ) => {
245+ return carrier ?. [ key ] ;
246+ } ,
247+ } ;
248+ const contextAfterExtraction = customAwsOtelConfiguration . textMapPropagator ?. extract (
249+ ROOT_CONTEXT ,
250+ carrier ,
251+ textMapGetter
252+ ) ;
253+
254+ // Test baggage is set directly
255+ const baggageAfterExtraction = propagation . getBaggage ( contextAfterExtraction ! ) ;
256+ expect ( baggageAfterExtraction ?. getAllEntries ( ) . length ) . toEqual ( 1 ) ;
257+ expect ( baggageAfterExtraction ?. getEntry ( 'session.id' ) ?. value ) . toEqual ( 'test-adot-js-dev' ) ;
258+
259+ // Test baggage is set indirectly through span creation
260+ const span : Span = tracer . startSpan ( 'test' , undefined , contextAfterExtraction ) ;
261+ span . end ( ) ;
262+ expect ( ( span as any ) . attributes [ 'session.id' ] ) . toEqual ( 'test-adot-js-dev' ) ;
263+ } ) ;
264+ } ) ;
265+
113266 // Sanity check that the trace ID ratio sampler works fine with the x-ray generator.
114267 it ( 'TraceIdRatioSamplerTest' , ( ) => {
115268 process . env . OTEL_AWS_APPLICATION_SIGNALS_ENABLED = 'True' ;
@@ -604,14 +757,14 @@ describe('AwsOpenTelemetryConfiguratorTest', () => {
604757
605758 function validateConfiguratorEnviron ( ) {
606759 // Set by register.ts
607- expect ( 'http/protobuf' ) . toEqual ( process . env . OTEL_EXPORTER_OTLP_PROTOCOL ) ;
608- expect ( 'xray,tracecontext' ) . toEqual ( process . env . OTEL_PROPAGATORS ) ;
760+ expect ( process . env ) . toHaveProperty ( ' OTEL_EXPORTER_OTLP_PROTOCOL' , 'http/protobuf' ) ;
761+ expect ( process . env ) . toHaveProperty ( ' OTEL_PROPAGATORS' , 'baggage,xray,tracecontext' ) ;
609762
610763 // Not set
611- expect ( undefined ) . toEqual ( process . env . OTEL_TRACES_SAMPLER ) ;
612- expect ( undefined ) . toEqual ( process . env . OTEL_TRACES_SAMPLER_ARG ) ;
613- expect ( undefined ) . toEqual ( process . env . OTEL_TRACES_EXPORTER ) ;
614- expect ( undefined ) . toEqual ( process . env . OTEL_METRICS_EXPORTER ) ;
764+ expect ( process . env ) . not . toHaveProperty ( ' OTEL_TRACES_SAMPLER' ) ;
765+ expect ( process . env ) . not . toHaveProperty ( ' OTEL_TRACES_SAMPLER_ARG' ) ;
766+ expect ( process . env ) . not . toHaveProperty ( ' OTEL_TRACES_EXPORTER' ) ;
767+ expect ( process . env ) . not . toHaveProperty ( ' OTEL_METRICS_EXPORTER' ) ;
615768 }
616769
617770 it ( 'OtelTracesSamplerInputValidationTest' , ( ) => {
0 commit comments