@@ -147,10 +147,10 @@ index 9341bf6f79..f719c1ea93 100644
147147 }
148148diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractor.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractor.java
149149new file mode 100644
150- index 0000000000..9bea9808a5
150+ index 0000000000..921a7b6b7f
151151--- /dev/null
152152+++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractor.java
153- @@ -0,0 +1,76 @@
153+ @@ -0,0 +1,89 @@
154154+ /*
155155+ * Copyright The OpenTelemetry Authors
156156+ * SPDX-License-Identifier: Apache-2.0
@@ -162,6 +162,9 @@ index 0000000000..9bea9808a5
162162+
163163+ import io.opentelemetry.context.Context;
164164+ import io.opentelemetry.context.propagation.TextMapGetter;
165+ + import java.lang.invoke.MethodHandle;
166+ + import java.lang.invoke.MethodHandles;
167+ + import java.lang.invoke.MethodType;
165168+ import java.util.Locale;
166169+ import java.util.Map;
167170+ import java.util.logging.Logger;
@@ -177,6 +180,7 @@ index 0000000000..9bea9808a5
177180+ private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader";
178181+ // lower-case map getter used for extraction
179182+ static final String AWS_TRACE_HEADER_PROPAGATOR_KEY = "x-amzn-trace-id";
183+ + static volatile MethodHandle getXrayTraceIdHandle;
180184+
181185+ static Context extract(
182186+ Map<String, String> headers,
@@ -194,10 +198,19 @@ index 0000000000..9bea9808a5
194198+ com.amazonaws.services.lambda.runtime.Context lambdaContext) {
195199+ String traceHeader = null;
196200+ try {
197- + // Use reflection to avoid muzzle issues with getXrayTraceId method
198- + traceHeader = (String) lambdaContext.getClass().getMethod("getXrayTraceId").invoke(lambdaContext);
199- + } catch (Exception e) {
200- + logger.fine("Failed to get X-Ray trace ID via reflection: " + e.getMessage());
201+ + MethodHandle handle = getXrayTraceIdHandle;
202+ + if (handle == null) {
203+ + handle =
204+ + MethodHandles.lookup()
205+ + .findVirtual(
206+ + lambdaContext.getClass(),
207+ + "getXrayTraceId",
208+ + MethodType.methodType(String.class));
209+ + getXrayTraceIdHandle = handle;
210+ + }
211+ + traceHeader = (String) handle.invoke(lambdaContext);
212+ + } catch (Throwable t) {
213+ + logger.fine("Failed to get X-Ray trace ID via MethodHandle: " + t.getMessage());
201214+ }
202215+ if (traceHeader != null && !traceHeader.isEmpty()) {
203216+ return traceHeader;
@@ -242,10 +255,10 @@ index cb19d1e568..12ed174bb2 100644
242255 assertThat(spanContext.getSpanId()).isEqualTo("00f067aa0ba902b7");
243256diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java
244257new file mode 100644
245- index 0000000000..edca3ce68c
258+ index 0000000000..e2a69ee6d8
246259--- /dev/null
247260+++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java
248- @@ -0,0 +1,359 @@
261+ @@ -0,0 +1,418 @@
249262+ /*
250263+ * Copyright The OpenTelemetry Authors
251264+ * SPDX-License-Identifier: Apache-2.0
@@ -255,6 +268,8 @@ index 0000000000..edca3ce68c
255268+
256269+ import static org.assertj.core.api.Assertions.assertThat;
257270+ import static org.mockito.Mockito.mock;
271+ + import static org.mockito.Mockito.times;
272+ + import static org.mockito.Mockito.verify;
258273+ import static org.mockito.Mockito.when;
259274+
260275+ import com.amazonaws.services.lambda.runtime.Context;
@@ -266,6 +281,7 @@ index 0000000000..edca3ce68c
266281+ import io.opentelemetry.context.propagation.TextMapPropagator;
267282+ import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator;
268283+ import io.opentelemetry.extension.trace.propagation.B3Propagator;
284+ + import java.lang.invoke.MethodHandle;
269285+ import java.util.Map;
270286+ import org.junit.jupiter.api.Test;
271287+ import org.junit.jupiter.api.extension.ExtendWith;
@@ -587,7 +603,8 @@ index 0000000000..edca3ce68c
587603+ void shouldFallbackToSystemPropertyWhenReflectionFails() {
588604+ // given
589605+ Map<String, String> headers = ImmutableMap.of();
590- + // Create a mock context that doesn't have getXrayTraceId method (simulates older Lambda runtime)
606+ + // Create a mock context that doesn't have getXrayTraceId method (simulates older Lambda
607+ + // runtime)
591608+ Context mockLambdaContextWithoutXrayMethod = mock(Context.class);
592609+ systemProperties.set(
593610+ "com.amazonaws.xray.traceHeader",
@@ -604,6 +621,61 @@ index 0000000000..edca3ce68c
604621+ assertThat(spanContext.getSpanId()).isEqualTo("0000000000000789");
605622+ assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa7");
606623+ }
624+ +
625+ + @Test
626+ + void shouldFallbackToSystemPropertyWhenIllegalAccessException() {
627+ + // given
628+ + Map<String, String> headers = ImmutableMap.of();
629+ + Context mockLambdaContextWithAccessException = mock(Context.class);
630+ + when(mockLambdaContextWithAccessException.getXrayTraceId())
631+ + .thenThrow(new RuntimeException("Simulated IllegalAccessException"));
632+ + systemProperties.set(
633+ + "com.amazonaws.xray.traceHeader",
634+ + "Root=1-8a3c60f7-d188f8fa79d48a391a778fa7;Parent=0000000000000789;Sampled=1");
635+ +
636+ + // when
637+ + io.opentelemetry.context.Context context =
638+ + ParentContextExtractor.extract(
639+ + headers, INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContextWithAccessException);
640+ + // then
641+ + Span span = Span.fromContext(context);
642+ + SpanContext spanContext = span.getSpanContext();
643+ + assertThat(spanContext.isValid()).isTrue();
644+ + assertThat(spanContext.getSpanId()).isEqualTo("0000000000000789");
645+ + assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa7");
646+ + }
647+ +
648+ + @Test
649+ + void shouldCacheMethodHandleAcrossMultipleInvocations() {
650+ + // given
651+ + Context mockLambdaContext = mock(Context.class);
652+ + when(mockLambdaContext.getXrayTraceId())
653+ + .thenReturn("Root=1-4fd0b613-1f19f39af59518d127b0cafe;Parent=0000000000000123;Sampled=1");
654+ +
655+ + // Clear any previously cached MethodHandle
656+ + ParentContextExtractor.getXrayTraceIdHandle = null;
657+ +
658+ + // when - call extract multiple times (which internally calls getTraceHeader)
659+ + assertThat(ParentContextExtractor.getXrayTraceIdHandle).isNull(); // Initially null
660+ +
661+ + ParentContextExtractor.extract(
662+ + ImmutableMap.of(), INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContext);
663+ + assertThat(ParentContextExtractor.getXrayTraceIdHandle).isNotNull(); // Cached after first call
664+ +
665+ + MethodHandle cachedHandle = ParentContextExtractor.getXrayTraceIdHandle;
666+ +
667+ + ParentContextExtractor.extract(
668+ + ImmutableMap.of(), INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContext);
669+ + ParentContextExtractor.extract(
670+ + ImmutableMap.of(), INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContext);
671+ +
672+ + // then - MethodHandle should remain the same (cached)
673+ + assertThat(ParentContextExtractor.getXrayTraceIdHandle).isSameAs(cachedHandle);
674+ +
675+ + // verify getXrayTraceId was called for each invocation (MethodHandle caching doesn't affect
676+ + // this)
677+ + verify(mockLambdaContext, times(3)).getXrayTraceId();
678+ + }
607679+ }
608680diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaRequestHandlerInstrumentation.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaRequestHandlerInstrumentation.java
609681index e059250807..1fa80c3735 100644
0 commit comments