@@ -25,10 +25,15 @@ index a6b89d253d..e62d30eddb 100644
2525 if (!functionInstrumenter().shouldStart(parentContext, otelInput)) {
2626 return;
2727diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts
28- index df605add2f..b2f01d9d4d 100644
28+ index df605add2f..e16c736990 100644
2929--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts
3030+++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/build.gradle.kts
31- @@ -9,7 +9,7 @@ dependencies {
31+ @@ -5,11 +5,12 @@ plugins {
32+ dependencies {
33+ compileOnly("io.opentelemetry:opentelemetry-sdk")
34+ compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
35+ + compileOnly(project(":muzzle"))
36+
3237 compileOnly("com.google.auto.value:auto-value-annotations")
3338 annotationProcessor("com.google.auto.value:auto-value")
3439
@@ -37,7 +42,7 @@ index df605add2f..b2f01d9d4d 100644
3742
3843 // We do lightweight parsing of JSON to extract HTTP headers from requests for propagation.
3944 // This will be commonly needed even for users that don't use events, but luckily it's not too big.
40- @@ -26,6 +26 ,7 @@ dependencies {
45+ @@ -26,6 +27 ,7 @@ dependencies {
4146
4247 testImplementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:testing"))
4348 testImplementation("uk.org.webcompere:system-stubs-jupiter")
@@ -147,10 +152,10 @@ index 9341bf6f79..f719c1ea93 100644
147152 }
148153diff --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
149154new file mode 100644
150- index 0000000000..921a7b6b7f
155+ index 0000000000..6349d1bb29
151156--- /dev/null
152157+++ 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,89 @@
158+ @@ -0,0 +1,85 @@
154159+ /*
155160+ * Copyright The OpenTelemetry Authors
156161+ * SPDX-License-Identifier: Apache-2.0
@@ -162,9 +167,7 @@ index 0000000000..921a7b6b7f
162167+
163168+ import io.opentelemetry.context.Context;
164169+ import io.opentelemetry.context.propagation.TextMapGetter;
165- + import java.lang.invoke.MethodHandle;
166- + import java.lang.invoke.MethodHandles;
167- + import java.lang.invoke.MethodType;
170+ + import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle;
168171+ import java.util.Locale;
169172+ import java.util.Map;
170173+ import java.util.logging.Logger;
@@ -180,7 +183,7 @@ index 0000000000..921a7b6b7f
180183+ private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader";
181184+ // lower-case map getter used for extraction
182185+ static final String AWS_TRACE_HEADER_PROPAGATOR_KEY = "x-amzn-trace-id";
183- + static volatile MethodHandle getXrayTraceIdHandle ;
186+ + static boolean getXrayTraceIdMethodExists = true ;
184187+
185188+ static Context extract(
186189+ Map<String, String> headers,
@@ -194,23 +197,21 @@ index 0000000000..921a7b6b7f
194197+ return instrumenter.extract(mergedHeaders, MapGetter.INSTANCE);
195198+ }
196199+
200+ + @NoMuzzle
197201+ private static String getTraceHeader(
198202+ com.amazonaws.services.lambda.runtime.Context lambdaContext) {
199203+ String traceHeader = null;
200- + try {
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 ;
204+ +
205+ + // Lambda Core dependency that is actually used by Lambda Runtime may be on an older version
206+ + // that does not have the `getXrayTraceId` method. If `NoSuchMethodError` occurs, we do not
207+ + // attempt invoking `getXrayTraceId` again.
208+ + if (getXrayTraceIdMethodExists) {
209+ + try {
210+ + traceHeader = lambdaContext.getXrayTraceId();
211+ + } catch (NoSuchMethodError e) {
212+ + logger.fine("Failed to get X-Ray trace ID from lambdaContext: " + e );
213+ + getXrayTraceIdMethodExists = false ;
210214+ }
211- + traceHeader = (String) handle.invoke(lambdaContext);
212- + } catch (Throwable t) {
213- + logger.fine("Failed to get X-Ray trace ID via MethodHandle: " + t.getMessage());
214215+ }
215216+ if (traceHeader != null && !traceHeader.isEmpty()) {
216217+ return traceHeader;
@@ -255,10 +256,10 @@ index cb19d1e568..12ed174bb2 100644
255256 assertThat(spanContext.getSpanId()).isEqualTo("00f067aa0ba902b7");
256257diff --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
257258new file mode 100644
258- index 0000000000..e2a69ee6d8
259+ index 0000000000..4b0f354769
259260--- /dev/null
260261+++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java
261- @@ -0,0 +1,418 @@
262+ @@ -0,0 +1,375 @@
262263+ /*
263264+ * Copyright The OpenTelemetry Authors
264265+ * SPDX-License-Identifier: Apache-2.0
@@ -281,7 +282,6 @@ index 0000000000..e2a69ee6d8
281282+ import io.opentelemetry.context.propagation.TextMapPropagator;
282283+ import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator;
283284+ import io.opentelemetry.extension.trace.propagation.B3Propagator;
284- + import java.lang.invoke.MethodHandle;
285285+ import java.util.Map;
286286+ import org.junit.jupiter.api.Test;
287287+ import org.junit.jupiter.api.extension.ExtendWith;
@@ -600,81 +600,39 @@ index 0000000000..e2a69ee6d8
600600+ }
601601+
602602+ @Test
603- + void shouldFallbackToSystemPropertyWhenReflectionFails () {
603+ + void shouldFallbackToSystemPropertyWhenNoSuchMethodErrorThrown () {
604604+ // given
605605+ Map<String, String> headers = ImmutableMap.of();
606- + // Create a mock context that doesn't have getXrayTraceId method (simulates older Lambda
607- + // runtime )
608- + Context mockLambdaContextWithoutXrayMethod = mock(Context.class );
606+ + Context mockLambdaContextWithNoSuchMethodError = mock(Context.class);
607+ + when(mockLambdaContextWithNoSuchMethodError.getXrayTraceId() )
608+ + .thenThrow(new NoSuchMethodError("getXrayTraceId method not found") );
609609+ systemProperties.set(
610610+ "com.amazonaws.xray.traceHeader",
611611+ "Root=1-8a3c60f7-d188f8fa79d48a391a778fa7;Parent=0000000000000789;Sampled=1");
612612+
613- + // when
614- + io.opentelemetry.context.Context context =
615- + ParentContextExtractor.extract(
616- + headers, INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContextWithoutXrayMethod);
617- + // then
618- + Span span = Span.fromContext(context);
619- + SpanContext spanContext = span.getSpanContext();
620- + assertThat(spanContext.isValid()).isTrue();
621- + assertThat(spanContext.getSpanId()).isEqualTo("0000000000000789");
622- + assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa7");
623- + }
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");
613+ + // Reset the static flag to ensure the method is attempted
614+ + ParentContextExtractor.getXrayTraceIdMethodExists = true;
635615+
636- + // when
616+ + // when - call extract
637617+ io.opentelemetry.context.Context context =
638618+ ParentContextExtractor.extract(
639- + headers, INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContextWithAccessException);
619+ + headers, INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContextWithNoSuchMethodError);
620+ +
640621+ // then
641622+ Span span = Span.fromContext(context);
642623+ SpanContext spanContext = span.getSpanContext();
643624+ assertThat(spanContext.isValid()).isTrue();
644625+ assertThat(spanContext.getSpanId()).isEqualTo("0000000000000789");
645626+ 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");
627+ + // Verify getXrayTraceId was called only once
628+ + assertThat(ParentContextExtractor.getXrayTraceIdMethodExists).isFalse();
629+ + verify(mockLambdaContextWithNoSuchMethodError, times(1)).getXrayTraceId();
654630+
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);
631+ + // when - call extract again
669632+ 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();
633+ + headers, INSTRUMENTER_WITH_B3_XRAY_PROPAGATORS, mockLambdaContextWithNoSuchMethodError);
634+ + // Verify the call count of getXrayTraceId is still 1
635+ + verify(mockLambdaContextWithNoSuchMethodError, times(1)).getXrayTraceId();
678636+ }
679637+ }
680638diff --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
0 commit comments