From fd74eb8a983e6f805c8eca30a43dbc3e1dc2aae7 Mon Sep 17 00:00:00 2001 From: Lauri Tulmin Date: Fri, 22 Aug 2025 06:20:08 +0300 Subject: [PATCH] Let AWS Lambda SQS handlers report partial batch failures (#14468) --- .fossa.yml | 6 + docs/supported-libraries.md | 2 +- .../javaagent/build.gradle.kts | 2 +- .../v2_2/AwsLambdaSingletons.java | 12 +- .../library/build.gradle.kts | 12 +- .../v2_2/TracingRequestApiGatewayWrapper.java | 7 +- .../v2_2/TracingRequestWrapper.java | 7 +- .../v2_2/TracingRequestWrapperBase.java | 6 +- .../v2_2/TracingSqsEventHandler.java | 12 +- .../v2_2/TracingSqsEventWrapper.java | 6 + .../v2_2/TracingSqsMessageHandler.java | 13 +- .../v2_2/AwsLambdaApiGatewayWrapperTest.java | 1 + .../v2_2/AwsLambdaSqsEventHandlerTest.java | 1 + .../v2_2/AwsLambdaSqsEventWrapperTest.java | 1 + .../v2_2/AwsLambdaSqsMessageHandlerTest.java | 1 + .../v2_2/AwsLambdaWrapperTest.java | 1 + ...acingRequestWrapperStandardEventsTest.java | 3 +- .../AbstractAwsLambdaSqsEventHandlerTest.java | 2 +- .../aws-lambda-events-3.11/library/README.md | 136 ++++++++ .../library/build.gradle.kts | 30 ++ .../TracingRequestApiGatewayWrapper.java | 55 ++++ .../v3_11/TracingRequestWrapper.java | 98 ++++++ .../v3_11/TracingRequestWrapperBase.java | 82 +++++ .../v3_11/TracingSqsEventHandler.java | 85 +++++ .../v3_11/TracingSqsEventWrapper.java | 52 +++ .../v3_11/TracingSqsMessageHandler.java | 118 +++++++ .../v3_11/AwsLambdaApiGatewayWrapperTest.java | 295 ++++++++++++++++++ .../v3_11/AwsLambdaSqsEventHandlerTest.java | 44 +++ .../v3_11/AwsLambdaSqsEventWrapperTest.java | 115 +++++++ .../v3_11/AwsLambdaSqsMessageHandlerTest.java | 177 +++++++++++ .../v3_11/AwsLambdaWrapperTest.java | 214 +++++++++++++ ...acingRequestWrapperStandardEventsTest.java | 295 ++++++++++++++++++ .../library/build.gradle.kts | 41 +++ .../ApiGatewayProxyAttributesExtractor.java | 2 +- .../AwsLambdaEventsInstrumenterFactory.java | 8 +- .../AwsLambdaSqsInstrumenterFactory.java | 16 +- .../v2_2/internal/CustomJodaModule.java | 3 +- .../v2_2/internal}/LambdaParameters.java | 14 +- .../v2_2/internal/SerializationUtil.java | 2 +- .../internal/SqsEventAttributesExtractor.java | 2 +- .../internal/SqsEventSpanLinksExtractor.java | 2 +- .../SqsMessageAttributesExtractor.java | 2 +- .../SqsMessageSpanLinksExtractor.java | 2 +- .../v2_2/internal}/LambdaParametersTest.java | 3 +- .../v2_2/internal/SerializationUtilTest.java | 2 +- settings.gradle.kts | 2 + 46 files changed, 1934 insertions(+), 58 deletions(-) create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java create mode 100644 instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/ApiGatewayProxyAttributesExtractor.java (98%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java (86%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java (77%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/CustomJodaModule.java (96%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2 => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal}/LambdaParameters.java (81%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/SerializationUtil.java (98%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/SqsEventAttributesExtractor.java (94%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/SqsEventSpanLinksExtractor.java (90%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/SqsMessageAttributesExtractor.java (95%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/SqsMessageSpanLinksExtractor.java (96%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2 => aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal}/LambdaParametersTest.java (97%) rename instrumentation/aws-lambda/{aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents => aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common}/v2_2/internal/SerializationUtilTest.java (99%) diff --git a/.fossa.yml b/.fossa.yml index 69f5797172..7d094439bd 100644 --- a/.fossa.yml +++ b/.fossa.yml @@ -346,6 +346,12 @@ targets: - type: gradle path: ./ target: ':instrumentation:aws-lambda:aws-lambda-events-2.2:library' + - type: gradle + path: ./ + target: ':instrumentation:aws-lambda:aws-lambda-events-3.11:library' + - type: gradle + path: ./ + target: ':instrumentation:aws-lambda:aws-lambda-events-common-2.2:library' - type: gradle path: ./ target: ':instrumentation:aws-sdk:aws-sdk-1.11:javaagent' diff --git a/docs/supported-libraries.md b/docs/supported-libraries.md index cc87ac34c0..a46bc7da28 100644 --- a/docs/supported-libraries.md +++ b/docs/supported-libraries.md @@ -47,7 +47,7 @@ These are the supported libraries and frameworks: | [Armeria gRPC](https://armeria.dev) | 1.14+ | | [RPC Client Spans], [RPC Client Metrics], [RPC Server Spans], [RPC Server Metrics] | | [AsyncHttpClient](https://github.com/AsyncHttpClient/async-http-client) | 1.9+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] | | [Avaje Jex](https://avaje.io/jex/) | 3.0+ | N/A | Provides `http.route` [2] | -| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | [opentelemetry-aws-lambda-core-1.0](../instrumentation/aws-lambda/aws-lambda-core-1.0/library),
[opentelemetry-aws-lambda-events-2.2](../instrumentation/aws-lambda/aws-lambda-events-2.2/library) | [FaaS Server Spans] | +| [AWS Lambda](https://docs.aws.amazon.com/lambda/latest/dg/java-handler.html) | 1.0+ | [opentelemetry-aws-lambda-core-1.0](../instrumentation/aws-lambda/aws-lambda-core-1.0/library),
[opentelemetry-aws-lambda-events-3.11](../instrumentation/aws-lambda/aws-lambda-events-3.11/library) | [FaaS Server Spans] | | [AWS SDK](https://aws.amazon.com/sdk-for-java/) | 1.11 - 1.12.583,
2.2+ | [opentelemetry-aws-sdk-1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library),
[opentelemetry-aws-sdk-1.11-autoconfigure](../instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure),
[opentelemetry-aws-sdk-2.2](../instrumentation/aws-sdk/aws-sdk-2.2/library),
[opentelemetry-aws-sdk-2.2-autoconfigure](../instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure) | [Messaging Spans], [Database Client Spans], [Database Client Metrics] [6], [HTTP Client Spans], [GenAI Client Spans], [GenAI Client Metrics] | | [Azure Core](https://docs.microsoft.com/en-us/java/api/overview/azure/core-readme) | 1.14+ | N/A | Context propagation | | [Cassandra Driver](https://github.com/datastax/java-driver) | 3.0+ | [opentelemetry-cassandra-4.4](../instrumentation/cassandra/cassandra-4.4/library) | [Database Client Spans], [Database Client Metrics] [6] | diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts index acfad8626d..dba82d6f6c 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/build.gradle.kts @@ -17,7 +17,7 @@ dependencies { implementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library")) - implementation(project(":instrumentation:aws-lambda:aws-lambda-events-2.2:library")) { + implementation(project(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library")) { // Only needed by wrappers, not the javaagent. Muzzle will catch if we accidentally change this. exclude("com.fasterxml.jackson.core", "jackson-databind") } diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java index 1ecafdeb19..8ffd8f7a10 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambdaevents/v2_2/AwsLambdaSingletons.java @@ -10,19 +10,21 @@ import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionInstrumenter; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaEventsInstrumenterFactory; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig; import java.time.Duration; public final class AwsLambdaSingletons { - + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2"; private static final AwsLambdaFunctionInstrumenter FUNCTION_INSTRUMENTER = AwsLambdaEventsInstrumenterFactory.createInstrumenter( - GlobalOpenTelemetry.get(), AgentCommonConfig.get().getKnownHttpRequestMethods()); + GlobalOpenTelemetry.get(), + INSTRUMENTATION_NAME, + AgentCommonConfig.get().getKnownHttpRequestMethods()); private static final Instrumenter MESSAGE_TRACER = - AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get()); + AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME); private static final Duration FLUSH_TIMEOUT = Duration.ofMillis( AgentInstrumentationConfig.get() diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts index baae82471b..b57f150698 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/build.gradle.kts @@ -4,13 +4,12 @@ plugins { dependencies { api(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library")) + implementation(project(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library")) + compileOnly(project(":instrumentation:aws-lambda:aws-lambda-events-3.11:library")) compileOnly("io.opentelemetry:opentelemetry-sdk") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") - compileOnly("com.google.auto.value:auto-value-annotations") - annotationProcessor("com.google.auto.value:auto-value") - library("com.amazonaws:aws-lambda-java-core:1.0.0") // First version to includes support for SQSEvent, currently the most popular message queue used // with lambda. @@ -25,13 +24,6 @@ dependencies { // So that is the reason that why we add it as compile only dependency. compileOnly("com.amazonaws:aws-lambda-java-serialization:1.1.5") - // We need Jackson for wrappers to reproduce the serialization does when Lambda invokes a RequestHandler with event - // since Lambda will only be able to invoke the wrapper itself with a generic Object. - // Note that Lambda itself uses Jackson, but does not expose it to the function so we need to include it here. - // TODO: Switch to aws-lambda-java-serialization to more robustly follow Lambda's serialization logic. - implementation("com.fasterxml.jackson.core:jackson-databind") - implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") - // allows to get the function ARN testLibrary("com.amazonaws:aws-lambda-java-core:1.2.1") // allows to get the default events diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java index 711b7e3079..717cf7d30d 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestApiGatewayWrapper.java @@ -9,7 +9,7 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.util.function.BiFunction; @@ -17,7 +17,12 @@ import java.util.function.BiFunction; * Wrapper for {@link io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler}. * Allows for wrapping a lambda proxied through API Gateway, enabling single span tracing and HTTP * context propagation. + * + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestApiGatewayWrapper} + * instead. */ +@Deprecated public class TracingRequestApiGatewayWrapper extends TracingRequestWrapperBase { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java index 484ee169d6..a062beea24 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapper.java @@ -12,7 +12,8 @@ import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestStreamW import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.ApiGatewayProxyRequest; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -26,7 +27,11 @@ import java.util.Map; /** * Wrapper for {@link com.amazonaws.services.lambda.runtime.RequestHandler} based Lambda handlers. + * + * @deprecated use {@link + * io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestWrapper} instead. */ +@Deprecated public class TracingRequestWrapper extends TracingRequestStreamWrapper { public TracingRequestWrapper() { super(); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java index 5169c8f121..239cf07620 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperBase.java @@ -12,7 +12,8 @@ import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; import io.opentelemetry.sdk.OpenTelemetrySdk; import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; import java.lang.reflect.InvocationTargetException; @@ -26,6 +27,7 @@ import java.util.function.BiFunction; * env property OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER in package.ClassName::methodName format */ abstract class TracingRequestWrapperBase extends TracingRequestHandler { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2"; private final WrappedLambda wrappedLambda; private final Method targetMethod; @@ -47,7 +49,7 @@ abstract class TracingRequestWrapperBase extends TracingRequestHandler { + static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2"; private final Instrumenter instrumenter; @@ -33,7 +39,9 @@ public abstract class TracingSqsEventHandler extends TracingRequestHandler messageInstrumenter; @@ -33,7 +38,9 @@ public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler { */ protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { this( - openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk)); + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); } /** @@ -50,7 +57,7 @@ public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler { openTelemetrySdk, flushTimeout, eventInstrumenter, - AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk)); + AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk, INSTRUMENTATION_NAME)); } /** diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java index 4c37d92a5b..f19e3b8488 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaApiGatewayWrapperTest.java @@ -36,6 +36,7 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class AwsLambdaApiGatewayWrapperTest { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java index f46fa01eb4..e9486d53f2 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventHandlerTest.java @@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExte import io.opentelemetry.sdk.OpenTelemetrySdk; import org.junit.jupiter.api.extension.RegisterExtension; +@SuppressWarnings("deprecation") // testing deprecated class class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest { @RegisterExtension diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java index 18344969fd..54e5e83055 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsEventWrapperTest.java @@ -32,6 +32,7 @@ import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) @SetEnvironmentVariable( key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java index 04c6538bbe..13deea3b52 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaSqsMessageHandlerTest.java @@ -36,6 +36,7 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) class AwsLambdaSqsMessageHandlerTest { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java index f72fe08f5d..d9af41616f 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AwsLambdaWrapperTest.java @@ -29,6 +29,7 @@ import org.junitpioneer.jupiter.SetEnvironmentVariable; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +@SuppressWarnings("deprecation") // testing deprecated class @ExtendWith(MockitoExtension.class) class AwsLambdaWrapperTest { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java index c25d911ed1..1c9d773668 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/TracingRequestWrapperStandardEventsTest.java @@ -16,7 +16,7 @@ import com.amazonaws.services.lambda.runtime.events.SNSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.IOException; import java.util.HashMap; @@ -24,6 +24,7 @@ import java.util.Map; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +@SuppressWarnings("deprecation") // testing deprecated class class TracingRequestWrapperStandardEventsTest { private static final Map, EventInfo> EVENTS_JSON = buildEventExamples(); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java index 4ee2278873..7c69c27e38 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/testing/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/AbstractAwsLambdaSqsEventHandlerTest.java @@ -35,7 +35,7 @@ public abstract class AbstractAwsLambdaSqsEventHandlerTest { private static final String AWS_TRACE_HEADER = "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"; - protected abstract RequestHandler handler(); + protected abstract RequestHandler handler(); protected abstract InstrumentationExtension testing(); diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md new file mode 100644 index 0000000000..cbde50929e --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/README.md @@ -0,0 +1,136 @@ +# AWS Lambda Instrumentation + +This package contains libraries to help instrument AWS lambda functions in your code. + +## Using wrappers + +To use the instrumentation, configure `OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER` env property to your lambda handler method in following format `package.ClassName::methodName` +and use one of wrappers as your lambda `Handler`. + +In order to configure a span flush timeout (default is set to 10 seconds), please configure `OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT` env property. The value is in milliseconds. + +Available wrappers: + +- `io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestWrapper` - for wrapping regular handlers (implementing `RequestHandler`) +- `io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestApiGatewayWrapper` - for wrapping regular handlers (implementing `RequestHandler`) proxied through API Gateway, enabling HTTP context propagation +- `io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestStreamWrapper` - for wrapping streaming handlers (implementing `RequestStreamHandler`), enabling HTTP context propagation for HTTP requests + +If you are only using `TracingRequestStreamWrapper`, consider using [aws-lambda-core-1.0](../../aws-lambda-core-1.0/library) instead to reduce the size of +your compiled function. + +## Using handlers + +To use the instrumentation, replace your function classes that implement `RequestHandler` (or `RequestStreamHandler`) with those +that extend `TracingRequestHandler` (or `TracingRequestStreamHandler`). You will need to change the method name to `doHandleRequest` +and pass an initialized `OpenTelemetrySdk` to the base class. + +```java +public class MyRequestHandler extends TracingRequestHandler { + + private static final OpenTelemetrySdk SDK = OpenTelemetrySdk.builder() + .addSpanProcessor(spanProcessor) + .buildAndRegisterGlobal(); + + public MyRequestHandler() { + super(SDK); + } + + // Note the method is named doHandleRequest instead of handleRequest. + @Override + protected String doHandleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + return "goodbye"; + } +} +``` + +A `SERVER` span will be created with the name you specify for the function when deploying it. + +In addition, it is recommended to set up X-Ray trace propagation to be able to +link to tracing information provided by Lambda itself. To do so, add a dependency on +`opentelemetry-extension-tracepropagators`. Make sure the version matches the version of the SDK +you use. + +Gradle: + +```kotlin +dependencies { + implementation("io.opentelemetry:opentelemetry-extension-trace-propagators:0.8.0") +} +``` + +Maven: + +```xml + + + io.opentelemetry + opentelemetry-extension-trace-propagators + 0.8.0 + + +``` + +## SQS Handler + +This package provides a special handler for SQS-triggered functions to include messaging data. +If using SQS, it is recommended to use them instead of `TracingRequestHandler`. + +If your application processes one message at a time, each independently, it is recommended to extend +`TracingSQSMessageHandler`. This will create a single span corresponding to a received batch of +messages along with one span for each of the messages as you process them. + +```java +public class MyMessageHandler extends TracingSQSMessageHandler { + @Override + protected boolean handleMessage(SQSMessage message, Context context) { + System.out.println(message.getBody()); + return true; + } +} +``` + +If you handle a batch of messages together, for example by aggregating them into a single unit, +extend `TracingSQSEventHandler` to process a batch at a time. + +```java +public class MyBatchHandler extends TracingSQSEventHandler { + @Override + protected SQSBatchResponse handleEvent(SQSEvent event, Context context) { + System.out.println(event.getRecords().size()); + return null; + } +} +``` + +## Trace propagation + +Context propagation for this instrumentation can be done either with X-Ray propagation or regular HTTP propagation. If X-Ray is enabled for instrumented lambda, it will be preferred. If X-Ray is disabled, HTTP propagation will be tried (that is HTTP headers will be read to check for a valid trace context). + +### X-Ray propagation + +This instrumentation supports propagating traces using the `X-Amzn-Trace-Id` format for both normal +requests and SQS requests. X-Ray propagation is always enabled, there is no need to configure it explicitly. + +### HTTP headers based propagation + +For API Gateway (HTTP) requests instrumented by using one of following methods: + +- extending `TracingRequestStreamHandler` or `TracingRequestHandler` +- wrapping with `TracingRequestStreamWrapper` or `TracingRequestApiGatewayWrapper` + traces can be propagated with supported HTTP headers (see ). + +In order to enable requested propagation for a handler, configure it on the SDK you build. + +```java + static { + OpenTelemetrySdk.builder() + ... + .setPropagators(ContextPropagators.create(B3Propagator.injectingSingleHeader())) + .buildAndRegisterGlobal(); + } +``` + +If using the wrappers, set the `OTEL_PROPAGATORS` environment variable as described [here](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/autoconfigure/README.md#propagator). diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts new file mode 100644 index 0000000000..7e63bb6e53 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/build.gradle.kts @@ -0,0 +1,30 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + implementation(project(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library")) + + compileOnly("io.opentelemetry:opentelemetry-sdk") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + + library("com.amazonaws:aws-lambda-java-core:1.0.0") + library("com.amazonaws:aws-lambda-java-events:3.11.0") + + // allows to get the function ARN + testLibrary("com.amazonaws:aws-lambda-java-core:1.2.1") + + testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + testImplementation("io.opentelemetry:opentelemetry-extension-trace-propagators") + testImplementation("com.amazonaws:aws-lambda-java-serialization:1.1.5") + + testImplementation(project(":instrumentation:aws-lambda:aws-lambda-events-2.2:testing")) + testImplementation("uk.org.webcompere:system-stubs-jupiter") +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java new file mode 100644 index 0000000000..717458b082 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestApiGatewayWrapper.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.util.function.BiFunction; + +/** + * Wrapper for {@link io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler}. + * Allows for wrapping a lambda proxied through API Gateway, enabling single span tracing and HTTP + * context propagation. + */ +public class TracingRequestApiGatewayWrapper + extends TracingRequestWrapperBase { + + public TracingRequestApiGatewayWrapper() { + super(TracingRequestApiGatewayWrapper::map); + } + + // Visible for testing + TracingRequestApiGatewayWrapper( + OpenTelemetrySdk openTelemetrySdk, + WrappedLambda wrappedLambda, + BiFunction, Object> mapper) { + super(openTelemetrySdk, wrappedLambda, mapper); + } + + // Visible for testing + static T map(APIGatewayProxyRequestEvent event, Class clazz) { + return SerializationUtil.fromJson(event.getBody(), clazz); + } + + @Override + protected APIGatewayProxyResponseEvent doHandleRequest( + APIGatewayProxyRequestEvent input, Context context) { + Object result = super.doHandleRequest(input, context); + APIGatewayProxyResponseEvent event; + // map to response event if needed + if (result instanceof APIGatewayProxyResponseEvent) { + event = (APIGatewayProxyResponseEvent) result; + } else { + event = new APIGatewayProxyResponseEvent(); + event.setBody(SerializationUtil.toJson(result)); + } + return event; + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java new file mode 100644 index 0000000000..c3627ea65a --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapper.java @@ -0,0 +1,98 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.AwsLambdaRequest; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestStreamWrapper; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.ApiGatewayProxyRequest; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; + +/** + * Wrapper for {@link com.amazonaws.services.lambda.runtime.RequestHandler} based Lambda handlers. + */ +public class TracingRequestWrapper extends TracingRequestStreamWrapper { + public TracingRequestWrapper() { + super(); + } + + // Visible for testing + TracingRequestWrapper(OpenTelemetrySdk openTelemetrySdk, WrappedLambda wrappedLambda) { + super(openTelemetrySdk, wrappedLambda); + } + + @Override + protected final AwsLambdaRequest createRequest( + InputStream inputStream, Context context, ApiGatewayProxyRequest proxyRequest) { + Method targetMethod = wrappedLambda.getRequestTargetMethod(); + Object input = LambdaParameters.toInput(targetMethod, inputStream, TracingRequestWrapper::map); + return AwsLambdaRequest.create(context, input, extractHeaders(input)); + } + + protected Map extractHeaders(Object input) { + if (input instanceof APIGatewayProxyRequestEvent) { + return MapUtils.emptyIfNull(((APIGatewayProxyRequestEvent) input).getHeaders()); + } + return Collections.emptyMap(); + } + + @Override + protected final void doHandleRequest( + InputStream input, OutputStream output, Context context, AwsLambdaRequest request) { + Method targetMethod = wrappedLambda.getRequestTargetMethod(); + Object[] parameters = LambdaParameters.toParameters(targetMethod, request.getInput(), context); + try { + Object result = targetMethod.invoke(wrappedLambda.getTargetObject(), parameters); + SerializationUtil.toJson(output, result); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Method is inaccessible", e); + } catch (InvocationTargetException e) { + throw (e.getCause() instanceof RuntimeException + ? (RuntimeException) e.getCause() + : new IllegalStateException(e.getTargetException())); + } + } + + @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"}) + // Used for testing + OUTPUT handleRequest(INPUT input, Context context) throws IOException { + byte[] inputJsonData = SerializationUtil.toJsonData(input); + ByteArrayInputStream inputStream = new ByteArrayInputStream(inputJsonData); + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + + super.handleRequest(inputStream, outputStream, context); + + byte[] outputJsonData = outputStream.toByteArray(); + return (OUTPUT) + SerializationUtil.fromJson( + new ByteArrayInputStream(outputJsonData), + wrappedLambda.getRequestTargetMethod().getReturnType()); + } + + // Visible for testing + static T map(InputStream inputStream, Class clazz) { + try { + return SerializationUtil.fromJson(inputStream, clazz); + } catch (IllegalArgumentException e) { + throw new IllegalStateException( + "Could not map input to requested parameter type: " + clazz, e); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java new file mode 100644 index 0000000000..df6e6a6504 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperBase.java @@ -0,0 +1,82 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import io.opentelemetry.instrumentation.api.internal.HttpConstants; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaEventsInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.function.BiFunction; + +/** + * Base abstract wrapper for {@link TracingRequestHandler}. Provides: - delegation to a lambda via + * env property OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER in package.ClassName::methodName format + */ +abstract class TracingRequestWrapperBase extends TracingRequestHandler { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-3.11"; + + private final WrappedLambda wrappedLambda; + private final Method targetMethod; + private final BiFunction, Object> parameterMapper; + + protected TracingRequestWrapperBase(BiFunction, Object> parameterMapper) { + this( + AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + parameterMapper); + } + + // Visible for testing + TracingRequestWrapperBase( + OpenTelemetrySdk openTelemetrySdk, + WrappedLambda wrappedLambda, + BiFunction, Object> parameterMapper) { + super( + openTelemetrySdk, + WrapperConfiguration.flushTimeout(), + AwsLambdaEventsInstrumenterFactory.createInstrumenter( + openTelemetrySdk, INSTRUMENTATION_NAME, HttpConstants.KNOWN_METHODS)); + this.wrappedLambda = wrappedLambda; + this.targetMethod = wrappedLambda.getRequestTargetMethod(); + this.parameterMapper = parameterMapper; + } + + @Override + @SuppressWarnings("unchecked") + protected O doHandleRequest(I input, Context context) { + Object[] parameters = LambdaParameters.toArray(targetMethod, input, context, parameterMapper); + O result; + try { + result = (O) targetMethod.invoke(wrappedLambda.getTargetObject(), parameters); + } catch (IllegalAccessException e) { + throw new IllegalStateException("Method is inaccessible", e); + } catch (InvocationTargetException e) { + throw (e.getCause() instanceof RuntimeException + ? (RuntimeException) e.getCause() + : new IllegalStateException(e.getTargetException())); + } + return result; + } + + @Override + protected final Map extractHttpHeaders(I input) { + if (input instanceof APIGatewayProxyRequestEvent) { + return MapUtils.emptyIfNull(((APIGatewayProxyRequestEvent) input).getHeaders()); + } + return Collections.emptyMap(); + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java new file mode 100644 index 0000000000..b0e7727eec --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventHandler.java @@ -0,0 +1,85 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.time.Duration; +import javax.annotation.Nullable; + +public abstract class TracingSqsEventHandler + extends TracingRequestHandler { + static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-3.11"; + + private final Instrumenter instrumenter; + + /** + * Creates a new {@link TracingSqsEventHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of 1s when flushing at the end of an invocation. + */ + protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk) { + this(openTelemetrySdk, DEFAULT_FLUSH_TIMEOUT); + } + + /** + * Creates a new {@link TracingSqsEventHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation. + */ + protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { + this( + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); + } + + /** + * Creates a new {@link TracingSqsEventHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and traces using the provided {@link + * io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionInstrumenter}. + */ + protected TracingSqsEventHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter instrumenter) { + super(openTelemetrySdk, flushTimeout); + this.instrumenter = instrumenter; + } + + @Nullable + @Override + public SQSBatchResponse doHandleRequest(SQSEvent event, Context context) { + io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current(); + if (instrumenter.shouldStart(parentContext, event)) { + io.opentelemetry.context.Context otelContext = instrumenter.start(parentContext, event); + Throwable error = null; + try (Scope ignored = otelContext.makeCurrent()) { + return handleEvent(event, context); + } catch (Throwable t) { + error = t; + throw t; + } finally { + instrumenter.end(otelContext, event, null, error); + } + } else { + return handleEvent(event, context); + } + } + + /** + * Handles a {@linkplain SQSEvent batch of messages}. Implement this class to do the actual + * processing of incoming SQS messages. + */ + @Nullable + protected abstract SQSBatchResponse handleEvent(SQSEvent event, Context context); +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java new file mode 100644 index 0000000000..7163d37473 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsEventWrapper.java @@ -0,0 +1,52 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.LambdaParameters; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class TracingSqsEventWrapper extends TracingSqsEventHandler { + + private final WrappedLambda wrappedLambda; + private final Method targetMethod; + + public TracingSqsEventWrapper() { + this( + AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration()); + } + + // Visible for testing + TracingSqsEventWrapper(OpenTelemetrySdk openTelemetrySdk, WrappedLambda wrappedLambda) { + super(openTelemetrySdk, WrapperConfiguration.flushTimeout()); + this.wrappedLambda = wrappedLambda; + this.targetMethod = wrappedLambda.getRequestTargetMethod(); + } + + @Override + protected SQSBatchResponse handleEvent(SQSEvent sqsEvent, Context context) { + Object[] parameters = + LambdaParameters.toArray(targetMethod, sqsEvent, context, (event, clazz) -> event); + try { + Object result = targetMethod.invoke(wrappedLambda.getTargetObject(), parameters); + return result instanceof SQSBatchResponse ? (SQSBatchResponse) result : null; + } catch (IllegalAccessException e) { + throw new IllegalStateException("Method is inaccessible", e); + } catch (InvocationTargetException e) { + throw (e.getCause() instanceof RuntimeException + ? (RuntimeException) e.getCause() + : new IllegalStateException(e.getTargetException())); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java new file mode 100644 index 0000000000..d6d315a46d --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingSqsMessageHandler.java @@ -0,0 +1,118 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler { + + private final Instrumenter messageInstrumenter; + + /** + * Creates a new {@link TracingSqsMessageHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of 1s when flushing at the end of an invocation. + */ + protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk) { + this(openTelemetrySdk, DEFAULT_FLUSH_TIMEOUT); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which traces using the provided {@link + * OpenTelemetrySdk} and has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation. + */ + protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { + this( + openTelemetrySdk, + flushTimeout, + AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME)); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and instruments {@link SQSEvent} using the provided {@code Instrumenter}. + */ + protected TracingSqsMessageHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter eventInstrumenter) { + this( + openTelemetrySdk, + flushTimeout, + eventInstrumenter, + AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk, INSTRUMENTATION_NAME)); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and traces using the provided {@code Instrumenter} and {@code + * Instrumenter}. + */ + protected TracingSqsMessageHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter eventInstrumenter, + Instrumenter messageInstrumenter) { + super(openTelemetrySdk, flushTimeout, eventInstrumenter); + this.messageInstrumenter = messageInstrumenter; + } + + @Override + protected final SQSBatchResponse handleEvent(SQSEvent event, Context context) { + List batchItemFailures = new ArrayList<>(); + io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current(); + for (SQSMessage message : event.getRecords()) { + if (messageInstrumenter.shouldStart(parentContext, message)) { + io.opentelemetry.context.Context otelContext = + messageInstrumenter.start(parentContext, message); + Throwable error = null; + try (Scope ignored = otelContext.makeCurrent()) { + handleMessage(message, context, batchItemFailures); + } catch (Throwable t) { + error = t; + throw t; + } finally { + messageInstrumenter.end(otelContext, message, null, error); + } + } else { + handleMessage(message, context, batchItemFailures); + } + } + + return new SQSBatchResponse(batchItemFailures); + } + + private void handleMessage( + SQSMessage message, + Context context, + List batchItemFailures) { + if (!handleMessage(message, context)) { + batchItemFailures.add(new SQSBatchResponse.BatchItemFailure(message.getMessageId())); + } + } + + /** + * Handles a {@linkplain SQSMessage message}. Implement this class to do the actual processing of + * incoming SQS messages. + * + * @return {@code true} when message was processed successfully, {@code false} when it should be + * reported as a failed batch item. + */ + protected abstract boolean handleMessage(SQSMessage message, Context context); +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java new file mode 100644 index 0000000000..d0bf6a93e5 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaApiGatewayWrapperTest.java @@ -0,0 +1,295 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD; +import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE; +import static io.opentelemetry.semconv.UrlAttributes.URL_FULL; +import static io.opentelemetry.semconv.UserAgentAttributes.USER_AGENT_ORIGINAL; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +class AwsLambdaApiGatewayWrapperTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerApiGateway::handleRequest") + void tracedWithHttpPropagation() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + + Map headers = new HashMap<>(); + headers.put("traceparent", "00-4fd0b6131f19f39af59518d127b0cafe-0000000000000456-01"); + headers.put("User-Agent", "Test Client"); + headers.put("host", "localhost:123"); + headers.put("X-FORWARDED-PROTO", "http"); + Map query = new HashMap<>(); + query.put("a", "b"); + query.put("c", "d"); + APIGatewayProxyRequestEvent input = + new APIGatewayProxyRequestEvent() + .withHttpMethod("GET") + .withResource("/hello/{param}") + .withPath("/hello/world") + .withBody("hello") + .withQueryStringParameters(query) + .withHeaders(headers); + + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) wrapper.handleRequest(input, context); + + assertThat(result.getBody()).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("GET /hello/{param}") + .hasKind(SpanKind.SERVER) + .hasTraceId("4fd0b6131f19f39af59518d127b0cafe") + .hasParentSpanId("0000000000000456") + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http"), + equalTo(HTTP_REQUEST_METHOD, "GET"), + equalTo(USER_AGENT_ORIGINAL, "Test Client"), + equalTo(URL_FULL, "http://localhost:123/hello/world?a=b&c=d"), + equalTo(HTTP_RESPONSE_STATUS_CODE, 200L)))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerApiGateway::handleRequest") + void handlerTraced_empty() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("empty"), context); + + assertThat(result.getBody()).isNull(); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTraced_string() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("\"hello\""), context); + + assertThat(result.getBody()).isEqualTo("\"world\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerInteger::handleRequest") + void handlerTraced_integer() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest(new APIGatewayProxyRequestEvent().withBody("1"), context); + + assertThat(result.getBody()).isEqualTo("\"world\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaApiGatewayWrapperTest$TestRequestHandlerCustomType::handleRequest") + void handlerTraced_customType() { + TracingRequestApiGatewayWrapper wrapper = + new TracingRequestApiGatewayWrapper( + testing.getOpenTelemetrySdk(), + WrappedLambda.fromConfiguration(), + TracingRequestApiGatewayWrapper::map); + APIGatewayProxyResponseEvent result = + (APIGatewayProxyResponseEvent) + wrapper.handleRequest( + new APIGatewayProxyRequestEvent() + .withBody("{\"key\":\"hello\", \"value\":\"General Kenobi\"}"), + context); + + assertThat(result.getBody()).isEqualTo("\"General Kenobi\""); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333"), + equalTo(FaasIncubatingAttributes.FAAS_TRIGGER, "http")))); + } + + public static class TestRequestHandlerApiGateway + implements RequestHandler { + + @Override + public APIGatewayProxyResponseEvent handleRequest( + APIGatewayProxyRequestEvent input, Context context) { + if (input.getBody().equals("hello")) { + return new APIGatewayProxyResponseEvent().withStatusCode(200).withBody("world"); + } else if (input.getBody().equals("empty")) { + return new APIGatewayProxyResponseEvent(); + } + throw new IllegalStateException("bad request"); + } + } + + public static final class TestRequestHandlerString implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static final class TestRequestHandlerInteger implements RequestHandler { + + @Override + public String handleRequest(Integer input, Context context) { + if (input == 1) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static class CustomType { + public String key; + public String value; + } + + public static final class TestRequestHandlerCustomType + implements RequestHandler { + + @Override + public String handleRequest(CustomType input, Context context) { + if (input.key.equals("hello")) { + return input.value; + } + throw new IllegalArgumentException("bad argument"); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java new file mode 100644 index 0000000000..50efefcd4a --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventHandlerTest.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.instrumentation.awslambdaevents.v2_2.AbstractAwsLambdaSqsEventHandlerTest; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import org.junit.jupiter.api.extension.RegisterExtension; + +class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Override + protected RequestHandler handler() { + return new TestHandler(testing.getOpenTelemetrySdk()); + } + + @Override + protected InstrumentationExtension testing() { + return testing; + } + + private static final class TestHandler extends TracingSqsEventHandler { + + TestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected SQSBatchResponse handleEvent(SQSEvent event, Context context) { + return null; + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java new file mode 100644 index 0000000000..e2216828bf --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsEventWrapperTest.java @@ -0,0 +1,115 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import java.lang.reflect.Constructor; +import java.util.Collections; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +@SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaSqsEventWrapperTest$TestRequestHandler::handleRequest") +class AwsLambdaSqsEventWrapperTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + @Test + void eventTraced() { + SQSEvent event = new SQSEvent(); + SQSEvent.SQSMessage record = newMessage(); + record.setEventSource("otel"); + record.setAttributes(Collections.emptyMap()); + event.setRecords(Collections.singletonList(record)); + + TracingSqsEventWrapper wrapper = + new TracingSqsEventWrapper( + testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + wrapper.handleRequest(event, context); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")), + span -> + span.hasName("otel process") + .hasKind(SpanKind.CONSUMER) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process")))); + } + + public static final class TestRequestHandler + implements RequestHandler { + @Override + public SQSBatchResponse handleRequest(SQSEvent input, Context context) { + return null; + } + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java new file mode 100644 index 0000000000..26863fbf08 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaSqsMessageHandlerTest.java @@ -0,0 +1,177 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_DESTINATION_NAME; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_MESSAGE_ID; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_OPERATION; +import static io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes.MESSAGING_SYSTEM; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.events.SQSBatchResponse; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.TraceFlags; +import io.opentelemetry.api.trace.TraceState; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.trace.data.LinkData; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import io.opentelemetry.semconv.incubating.MessagingIncubatingAttributes; +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AwsLambdaSqsMessageHandlerTest { + + private static final String AWS_TRACE_HEADER1 = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1"; + private static final String AWS_TRACE_HEADER2 = + "Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad9;Sampled=1"; + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @SuppressWarnings("deprecation") // using deprecated semconv + @Test + void processSpans() { + SQSEvent.SQSMessage message1 = newMessage(); + message1.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER1)); + message1.setMessageId("message1"); + message1.setEventSource("queue1"); + + SQSEvent.SQSMessage message2 = newMessage(); + message2.setAttributes(Collections.singletonMap("AWSTraceHeader", AWS_TRACE_HEADER2)); + message2.setMessageId("message2"); + message2.setEventSource("queue1"); + + SQSEvent event = new SQSEvent(); + event.setRecords(Arrays.asList(message1, message2)); + + SQSBatchResponse response = + new TestHandler(testing.getOpenTelemetrySdk()).handleRequest(event, context); + assertThat(response.getBatchItemFailures()) + .satisfiesExactly(item -> assertThat(item.getItemIdentifier()).isEqualTo("message2")); + + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(0).getSpanId()) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process")) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad8", + TraceFlags.getSampled(), + TraceState.getDefault())), + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad9", + TraceFlags.getSampled(), + TraceState.getDefault()))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(1).getSpanId()) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process"), + equalTo(MESSAGING_MESSAGE_ID, "message1"), + equalTo(MESSAGING_DESTINATION_NAME, "queue1")) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad8", + TraceFlags.getSampled(), + TraceState.getDefault()))), + span -> + span.hasName("queue1 process") + .hasKind(SpanKind.CONSUMER) + .hasParentSpanId(trace.getSpan(1).getSpanId()) + .hasAttributesSatisfyingExactly( + equalTo( + MESSAGING_SYSTEM, + MessagingIncubatingAttributes.MessagingSystemIncubatingValues + .AWS_SQS), + equalTo(MESSAGING_OPERATION, "process"), + equalTo(MESSAGING_MESSAGE_ID, "message2"), + equalTo(MESSAGING_DESTINATION_NAME, "queue1")) + .hasLinks( + LinkData.create( + SpanContext.createFromRemoteParent( + "5759e988bd862e3fe1be46a994272793", + "53995c3f42cd8ad9", + TraceFlags.getSampled(), + TraceState.getDefault()))))); + } + + // Constructor private in early versions. + private static SQSEvent.SQSMessage newMessage() { + try { + Constructor ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor(); + return ctor.newInstance(); + } catch (Throwable t) { + throw new AssertionError(t); + } + } + + private static final class TestHandler extends TracingSqsMessageHandler { + + TestHandler(OpenTelemetrySdk openTelemetrySdk) { + super(openTelemetrySdk); + } + + @Override + protected boolean handleMessage(SQSEvent.SQSMessage message, Context context) { + return "message1".equals(message.getMessageId()); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java new file mode 100644 index 0000000000..ad32986d44 --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/AwsLambdaWrapperTest.java @@ -0,0 +1,214 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; +import static org.mockito.Mockito.when; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension; +import io.opentelemetry.sdk.trace.data.StatusData; +import io.opentelemetry.semconv.incubating.CloudIncubatingAttributes; +import io.opentelemetry.semconv.incubating.FaasIncubatingAttributes; +import java.io.IOException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junitpioneer.jupiter.SetEnvironmentVariable; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class AwsLambdaWrapperTest { + + @RegisterExtension + static final InstrumentationExtension testing = LibraryInstrumentationExtension.create(); + + @Mock private Context context; + + @BeforeEach + void setUp() { + when(context.getFunctionName()).thenReturn("my_function"); + when(context.getAwsRequestId()).thenReturn("1-22-333"); + when(context.getInvokedFunctionArn()) + .thenReturn("arn:aws:lambda:us-east-1:123456789:function:test"); + } + + @AfterEach + void tearDown() { + assertThat(testing.forceFlushCalled()).isTrue(); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTraced() throws IOException { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + Object result = wrapper.handleRequest("hello", context); + + assertThat(result).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerString::handleRequest") + void handlerTracedWithException() { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + Throwable thrown = catchThrowable(() -> wrapper.handleRequest("goodbye", context)); + + assertThat(thrown).isInstanceOf(IllegalArgumentException.class); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasStatus(StatusData.error()) + .hasException(thrown) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerInteger::handleRequest") + void handlerTraced_integer() throws IOException { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + Object result = wrapper.handleRequest(1, context); + + assertThat(result).isEqualTo("world"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + @Test + @SetEnvironmentVariable( + key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY, + value = + "io.opentelemetry.instrumentation.awslambdaevents.v3_11.AwsLambdaWrapperTest$TestRequestHandlerCustomType::handleRequest") + void handlerTraced_custom() throws IOException { + TracingRequestWrapper wrapper = + new TracingRequestWrapper(testing.getOpenTelemetrySdk(), WrappedLambda.fromConfiguration()); + CustomType ct = new CustomType(); + ct.key = "hello there"; + ct.value = "General Kenobi"; + Object result = wrapper.handleRequest(ct, context); + + assertThat(result).isEqualTo("General Kenobi"); + testing.waitAndAssertTraces( + trace -> + trace.hasSpansSatisfyingExactly( + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) + .hasAttributesSatisfyingExactly( + equalTo( + CloudIncubatingAttributes.CLOUD_RESOURCE_ID, + "arn:aws:lambda:us-east-1:123456789:function:test"), + equalTo(CloudIncubatingAttributes.CLOUD_ACCOUNT_ID, "123456789"), + equalTo(FaasIncubatingAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } + + public static final class TestRequestHandlerString implements RequestHandler { + + @Override + public String handleRequest(String input, Context context) { + if (input.equals("hello")) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + public static final class TestRequestHandlerInteger implements RequestHandler { + + @Override + public String handleRequest(Integer input, Context context) { + if (input == 1) { + return "world"; + } + throw new IllegalArgumentException("bad argument"); + } + } + + static class CustomType { + String key; + String value; + + // Need getter/setter of all the attributes for serialization/deserialization + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public static final class TestRequestHandlerCustomType + implements RequestHandler { + + @Override + public String handleRequest(CustomType input, Context context) { + if (input.key.equals("hello there")) { + return input.value; + } + throw new IllegalArgumentException("bad argument"); + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java new file mode 100644 index 0000000000..917014df1c --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-3.11/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v3_11/TracingRequestWrapperStandardEventsTest.java @@ -0,0 +1,295 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambdaevents.v3_11; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.RequestHandler; +import com.amazonaws.services.lambda.runtime.events.KinesisEvent; +import com.amazonaws.services.lambda.runtime.events.S3Event; +import com.amazonaws.services.lambda.runtime.events.SNSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.ScheduledEvent; +import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda; +import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.SerializationUtil; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +class TracingRequestWrapperStandardEventsTest { + private static final Map, EventInfo> EVENTS_JSON = buildEventExamples(); + + private final OpenTelemetrySdk sdk = OpenTelemetrySdk.builder().build(); + private final Context context = mock(Context.class); + private TracingRequestWrapper wrapper; + + static final class EventInfo { + final Class eventType; + final String eventBody; + + EventInfo(Class eventType, String eventBody) { + this.eventType = eventType; + this.eventBody = eventBody; + } + } + + private static Map, EventInfo> buildEventExamples() { + Map, EventInfo> events = new HashMap<>(); + events.put( + ScheduledEventRequestHandler.class, + new EventInfo( + ScheduledEvent.class, + "{\n" + + " \"version\": \"0\",\n" + + " \"id\": \"53dc4d37-cffa-4f76-80c9-8b7d4a4d2eaa\",\n" + + " \"detail-type\": \"Scheduled Event\",\n" + + " \"source\": \"aws.events\",\n" + + " \"account\": \"123456789012\",\n" + + " \"time\": \"2015-10-08T16:53:06Z\",\n" + + " \"region\": \"us-east-1\",\n" + + " \"resources\": [\n" + + " \"arn:aws:events:us-east-1:123456789012:rule/my-scheduled-rule\"\n" + + " ],\n" + + " \"detail\": {}\n" + + "}")); + events.put( + KinesisEventRequestHandler.class, + new EventInfo( + KinesisEvent.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"kinesis\": {\n" + + " \"kinesisSchemaVersion\": \"1.0\",\n" + + " \"partitionKey\": \"1\",\n" + + " \"sequenceNumber\": \"49590338271490256608559692538361571095921575989136588898\",\n" + + " \"data\": \"SGVsbG8sIHRoaXMgaXMgYSB0ZXN0Lg==\",\n" + + " \"approximateArrivalTimestamp\": 1545084650.987\n" + + " },\n" + + " \"eventSource\": \"aws:kinesis\",\n" + + " \"eventVersion\": \"1.0\",\n" + + " \"eventID\": \"shardId-000000000006:49590338271490256608559692538361571095921575989136588898\",\n" + + " \"eventName\": \"aws:kinesis:record\",\n" + + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\",\n" + + " \"awsRegion\": \"us-east-2\",\n" + + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"\n" + + " },\n" + + " {\n" + + " \"kinesis\": {\n" + + " \"kinesisSchemaVersion\": \"1.0\",\n" + + " \"partitionKey\": \"1\",\n" + + " \"sequenceNumber\": \"49590338271490256608559692540925702759324208523137515618\",\n" + + " \"data\": \"VGhpcyBpcyBvbmx5IGEgdGVzdC4=\",\n" + + " \"approximateArrivalTimestamp\": 1545084711.166\n" + + " },\n" + + " \"eventSource\": \"aws:kinesis\",\n" + + " \"eventVersion\": \"1.0\",\n" + + " \"eventID\": \"shardId-000000000006:49590338271490256608559692540925702759324208523137515618\",\n" + + " \"eventName\": \"aws:kinesis:record\",\n" + + " \"invokeIdentityArn\": \"arn:aws:iam::123456789012:role/lambda-role\",\n" + + " \"awsRegion\": \"us-east-2\",\n" + + " \"eventSourceARN\": \"arn:aws:kinesis:us-east-2:123456789012:stream/lambda-stream\"\n" + + " }\n" + + " ]\n" + + "}")); + events.put( + SqsEventRequestHandler.class, + new EventInfo( + SQSEvent.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"messageId\": \"059f36b4-87a3-44ab-83d2-661975830a7d\",\n" + + " \"receiptHandle\": \"AQEBwJnKyrHigUMZj6rYigCgxlaS3SLy0a...\",\n" + + " \"body\": \"Test message.\",\n" + + " \"attributes\": {\n" + + " \"ApproximateReceiveCount\": \"1\",\n" + + " \"SentTimestamp\": \"1545082649183\",\n" + + " \"SenderId\": \"AIDAIENQZJOLO23YVJ4VO\",\n" + + " \"ApproximateFirstReceiveTimestamp\": \"1545082649185\"\n" + + " },\n" + + " \"messageAttributes\": {},\n" + + " \"md5OfBody\": \"e4e68fb7bd0e697a0ae8f1bb342846b3\",\n" + + " \"eventSource\": \"aws:sqs\",\n" + + " \"eventSourceARN\": \"arn:aws:sqs:us-east-2:123456789012:my-queue\",\n" + + " \"awsRegion\": \"us-east-2\"\n" + + " },\n" + + " {\n" + + " \"messageId\": \"2e1424d4-f796-459a-8184-9c92662be6da\",\n" + + " \"receiptHandle\": \"AQEBzWwaftRI0KuVm4tP+/7q1rGgNqicHq...\",\n" + + " \"body\": \"Test message.\",\n" + + " \"attributes\": {\n" + + " \"ApproximateReceiveCount\": \"1\",\n" + + " \"SentTimestamp\": \"1545082650636\",\n" + + " \"SenderId\": \"AIDAIENQZJOLO23YVJ4VO\",\n" + + " \"ApproximateFirstReceiveTimestamp\": \"1545082650649\"\n" + + " },\n" + + " \"messageAttributes\": {},\n" + + " \"md5OfBody\": \"e4e68fb7bd0e697a0ae8f1bb342846b3\",\n" + + " \"eventSource\": \"aws:sqs\",\n" + + " \"eventSourceARN\": \"arn:aws:sqs:us-east-2:123456789012:my-queue\",\n" + + " \"awsRegion\": \"us-east-2\"\n" + + " }\n" + + " ]\n" + + "}")); + events.put( + S3EventRequestHandler.class, + new EventInfo( + S3Event.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"eventVersion\": \"2.1\",\n" + + " \"eventSource\": \"aws:s3\",\n" + + " \"awsRegion\": \"us-east-2\",\n" + + " \"eventTime\": \"2019-09-03T19:37:27.192Z\",\n" + + " \"eventName\": \"ObjectCreated:Put\",\n" + + " \"userIdentity\": {\n" + + " \"principalId\": \"AWS:AIDAINPONIXQXHT3IKHL2\"\n" + + " },\n" + + " \"requestParameters\": {\n" + + " \"sourceIPAddress\": \"205.255.255.255\"\n" + + " },\n" + + " \"responseElements\": {\n" + + " \"x-amz-request-id\": \"D82B88E5F771F645\",\n" + + " \"x-amz-id-2\": \"vlR7PnpV2Ce81l0PRw6jlUpck7Jo5ZsQjryTjKlc5aLWGVHPZLj5NeC6qMa0emYBDXOo6QBU0Wo=\"\n" + + " },\n" + + " \"s3\": {\n" + + " \"s3SchemaVersion\": \"1.0\",\n" + + " \"configurationId\": \"828aa6fc-f7b5-4305-8584-487c791949c1\",\n" + + " \"bucket\": {\n" + + " \"name\": \"DOC-EXAMPLE-BUCKET\",\n" + + " \"ownerIdentity\": {\n" + + " \"principalId\": \"A3I5XTEXAMAI3E\"\n" + + " },\n" + + " \"arn\": \"arn:aws:s3:::lambda-artifacts-deafc19498e3f2df\"\n" + + " },\n" + + " \"object\": {\n" + + " \"key\": \"b21b84d653bb07b05b1e6b33684dc11b\",\n" + + " \"size\": 1305107,\n" + + " \"eTag\": \"b21b84d653bb07b05b1e6b33684dc11b\",\n" + + " \"sequencer\": \"0C0F6F405D6ED209E1\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + "}")); + events.put( + SnsEventRequestHandler.class, + new EventInfo( + SNSEvent.class, + "{\n" + + " \"Records\": [\n" + + " {\n" + + " \"EventVersion\": \"1.0\",\n" + + " \"EventSubscriptionArn\": \"arn:aws:sns:us-east-2:123456789012:sns-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + + " \"EventSource\": \"aws:sns\",\n" + + " \"Sns\": {\n" + + " \"SignatureVersion\": \"1\",\n" + + " \"Timestamp\": \"2019-01-02T12:45:07.000Z\",\n" + + " \"Signature\": \"tcc6faL2yUC6dgZdmrwh1Y4cGa/ebXEkAi6RibDsvpi+tE/1+82j...65r==\",\n" + + " \"SigningCertUrl\": \"https://sns.us-east-2.amazonaws.com/SimpleNotificationService-ac565b8b1a6c5d002d285f9598aa1d9b.pem\",\n" + + " \"MessageId\": \"95df01b4-ee98-5cb9-9903-4c221d41eb5e\",\n" + + " \"Message\": \"Hello from SNS!\",\n" + + " \"MessageAttributes\": {\n" + + " \"Test\": {\n" + + " \"Type\": \"String\",\n" + + " \"Value\": \"TestString\"\n" + + " },\n" + + " \"TestBinary\": {\n" + + " \"Type\": \"Binary\",\n" + + " \"Value\": \"TestBinary\"\n" + + " }\n" + + " },\n" + + " \"Type\": \"Notification\",\n" + + " \"UnsubscribeUrl\": \"https://sns.us-east-2.amazonaws.com/?Action=Unsubscribe&SubscriptionArn=arn:aws:sns:us-east-2:123456789012:test-lambda:21be56ed-a058-49f5-8c98-aedd2564c486\",\n" + + " \"TopicArn\":\"arn:aws:sns:us-east-2:123456789012:sns-lambda\",\n" + + " \"Subject\": \"TestInvoke\"\n" + + " }\n" + + " }\n" + + " ]\n" + + "}")); + return events; + } + + private TracingRequestWrapper buildWrapper(Class targetClass) { + WrappedLambda wrappedLambda = new WrappedLambda(targetClass, "handleRequest"); + + return new TracingRequestWrapper(sdk, wrappedLambda); + } + + @ParameterizedTest + @ValueSource( + classes = { + ScheduledEventRequestHandler.class, + KinesisEventRequestHandler.class, + SqsEventRequestHandler.class, + S3EventRequestHandler.class, + SnsEventRequestHandler.class + }) + void handleLambdaEvent(Class targetClass) throws IOException { + wrapper = buildWrapper(targetClass); + EventInfo eventInfo = EVENTS_JSON.get(targetClass); + + Object input = SerializationUtil.fromJson(eventInfo.eventBody, eventInfo.eventType); + + // Call to object based "O handleRequest(I input, Context context)" method + // delegates to the stream based + // "handleRequest(InputStream input, OutputStream output, Context context)" method. + // So serialization/deserialization of both input and outputs are triggered to be verified here. + Object output = wrapper.handleRequest(input, context); + + assertThat(input.getClass()).isEqualTo(output.getClass()); + + // "equals" methods are not properly implemented of Lambda events, + // so we are comparing them over their serialized json data + String inputJson = SerializationUtil.toJson(input); + String outputJson = SerializationUtil.toJson(output); + assertThat(inputJson).isEqualTo(outputJson); + } + + public static class ScheduledEventRequestHandler + implements RequestHandler { + @Override + public ScheduledEvent handleRequest(ScheduledEvent i, Context cntxt) { + return i; + } + } + + public static class KinesisEventRequestHandler + implements RequestHandler { + @Override + public KinesisEvent handleRequest(KinesisEvent i, Context cntxt) { + return i; + } + } + + public static class SqsEventRequestHandler implements RequestHandler { + @Override + public SQSEvent handleRequest(SQSEvent i, Context cntxt) { + return i; + } + } + + public static class S3EventRequestHandler implements RequestHandler { + @Override + public S3Event handleRequest(S3Event i, Context cntxt) { + return i; + } + } + + public static class SnsEventRequestHandler implements RequestHandler { + @Override + public SNSEvent handleRequest(SNSEvent i, Context cntxt) { + return i; + } + } +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts new file mode 100644 index 0000000000..0e52a2231c --- /dev/null +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/build.gradle.kts @@ -0,0 +1,41 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + api(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library")) + + compileOnly("io.opentelemetry:opentelemetry-sdk") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + + library("com.amazonaws:aws-lambda-java-core:1.0.0") + // First version to includes support for SQSEvent, currently the most popular message queue used + // with lambda. + // NB: 2.2.0 includes a class called SQSEvent but isn't usable due to it returning private classes + // in public API. + library("com.amazonaws:aws-lambda-java-events:2.2.1") + + // By default, "aws-lambda-java-serialization" library is enabled in the classpath + // at the AWS Lambda environment except "java8" runtime which is deprecated. + // But it is available at "java8.al2" runtime, so it is still can be used + // by Java 8 based Lambda functions. + // So that is the reason that why we add it as compile only dependency. + library("com.amazonaws:aws-lambda-java-serialization:1.1.5") + + // We need Jackson for wrappers to reproduce the serialization does when Lambda invokes a RequestHandler with event + // since Lambda will only be able to invoke the wrapper itself with a generic Object. + // Note that Lambda itself uses Jackson, but does not expose it to the function so we need to include it here. + // TODO: Switch to aws-lambda-java-serialization to more robustly follow Lambda's serialization logic. + implementation("com.fasterxml.jackson.core:jackson-databind") + implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") + + // allows to get the default events + testLibrary("com.amazonaws:aws-lambda-java-events:3.10.0") +} + +tasks.withType().configureEach { + // required on jdk17 + jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED") + jvmArgs("--add-opens=java.base/java.util=ALL-UNNAMED") + jvmArgs("-XX:+IgnoreUnrecognizedVMOptions") +} diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/ApiGatewayProxyAttributesExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/ApiGatewayProxyAttributesExtractor.java similarity index 98% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/ApiGatewayProxyAttributesExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/ApiGatewayProxyAttributesExtractor.java index 36f8dcd0b5..5413f1b09b 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/ApiGatewayProxyAttributesExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/ApiGatewayProxyAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import static io.opentelemetry.instrumentation.api.internal.AttributesExtractorUtil.internalSet; import static io.opentelemetry.instrumentation.api.internal.HttpConstants._OTHER; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java similarity index 86% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java index 502a8e016d..e5d6151132 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaEventsInstrumenterFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import io.opentelemetry.api.OpenTelemetry; @@ -21,13 +21,11 @@ import java.util.Set; public final class AwsLambdaEventsInstrumenterFactory { public static AwsLambdaFunctionInstrumenter createInstrumenter( - OpenTelemetry openTelemetry, Set knownMethods) { + OpenTelemetry openTelemetry, String instrumentationName, Set knownMethods) { return new AwsLambdaFunctionInstrumenter( openTelemetry, Instrumenter.builder( - openTelemetry, - "io.opentelemetry.aws-lambda-events-2.2", - AwsLambdaEventsInstrumenterFactory::spanName) + openTelemetry, instrumentationName, AwsLambdaEventsInstrumenterFactory::spanName) .addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor()) .addAttributesExtractor(new ApiGatewayProxyAttributesExtractor(knownMethods)) .buildInstrumenter(SpanKindExtractor.alwaysServer())); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java similarity index 77% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java index 4cd11fc0c4..e4ce0be1a9 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/AwsLambdaSqsInstrumenterFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; @@ -18,21 +18,19 @@ import java.util.List; */ public final class AwsLambdaSqsInstrumenterFactory { - public static Instrumenter forEvent(OpenTelemetry openTelemetry) { + public static Instrumenter forEvent( + OpenTelemetry openTelemetry, String instrumentationName) { return Instrumenter.builder( - openTelemetry, - "io.opentelemetry.aws-lambda-events-2.2", - AwsLambdaSqsInstrumenterFactory::spanName) + openTelemetry, instrumentationName, AwsLambdaSqsInstrumenterFactory::spanName) .addAttributesExtractor(new SqsEventAttributesExtractor()) .addSpanLinksExtractor(new SqsEventSpanLinksExtractor()) .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); } - public static Instrumenter forMessage(OpenTelemetry openTelemetry) { + public static Instrumenter forMessage( + OpenTelemetry openTelemetry, String instrumentationName) { return Instrumenter.builder( - openTelemetry, - "io.opentelemetry.aws-lambda-events-2.2", - message -> message.getEventSource() + " process") + openTelemetry, instrumentationName, message -> message.getEventSource() + " process") .addAttributesExtractor(new SqsMessageAttributesExtractor()) .addSpanLinksExtractor(new SqsMessageSpanLinksExtractor()) .buildInstrumenter(SpanKindExtractor.alwaysConsumer()); diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/CustomJodaModule.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/CustomJodaModule.java similarity index 96% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/CustomJodaModule.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/CustomJodaModule.java index 1be598ae8f..5be884879c 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/CustomJodaModule.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/CustomJodaModule.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.DeserializationContext; @@ -34,7 +34,6 @@ class CustomJodaModule extends SimpleModule { private static final long serialVersionUID = 1L; public CustomJodaModule() { - super(); addDeserializer(DateTime.class, new DateTimeDeserialiser()); } diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParameters.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParameters.java similarity index 81% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParameters.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParameters.java index 53e68280ba..7e27bed622 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParameters.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParameters.java @@ -3,16 +3,20 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.Context; import java.io.InputStream; import java.lang.reflect.Method; import java.util.function.BiFunction; -final class LambdaParameters { +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class LambdaParameters { - static Object[] toArray( + public static Object[] toArray( Method targetMethod, T input, Context context, BiFunction, Object> mapper) { Class[] parameterTypes = targetMethod.getParameterTypes(); Object[] parameters = new Object[parameterTypes.length]; @@ -28,7 +32,7 @@ final class LambdaParameters { return parameters; } - static Object[] toParameters(Method targetMethod, T input, Context context) { + public static Object[] toParameters(Method targetMethod, T input, Context context) { Class[] parameterTypes = targetMethod.getParameterTypes(); Object[] parameters = new Object[parameterTypes.length]; for (int i = 0; i < parameterTypes.length; i++) { @@ -43,7 +47,7 @@ final class LambdaParameters { return parameters; } - static Object toInput( + public static Object toInput( Method targetMethod, InputStream inputStream, BiFunction, Object> mapper) { diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtil.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtil.java similarity index 98% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtil.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtil.java index 81649340e6..e8c1e88fb7 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtil.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtil.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventAttributesExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventAttributesExtractor.java similarity index 94% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventAttributesExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventAttributesExtractor.java index a29b6a7c4c..debabc8c76 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventAttributesExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.api.common.AttributeKey; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventSpanLinksExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventSpanLinksExtractor.java similarity index 90% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventSpanLinksExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventSpanLinksExtractor.java index afb01ec984..131f1c15ca 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsEventSpanLinksExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsEventSpanLinksExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.context.Context; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageAttributesExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageAttributesExtractor.java similarity index 95% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageAttributesExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageAttributesExtractor.java index 8962dfe08f..975f423787 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageAttributesExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageAttributesExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import io.opentelemetry.api.common.AttributeKey; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageSpanLinksExtractor.java similarity index 96% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageSpanLinksExtractor.java index 305e3e62a0..9c628fe89b 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SqsMessageSpanLinksExtractor.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; import io.opentelemetry.api.trace.Span; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParametersTest.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParametersTest.java similarity index 97% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParametersTest.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParametersTest.java index f738b5a349..efa49d7665 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/LambdaParametersTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/LambdaParametersTest.java @@ -3,13 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import com.amazonaws.services.lambda.runtime.Context; -import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil; import java.io.ByteArrayInputStream; import java.lang.reflect.Method; import org.junit.jupiter.api.Test; diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtilTest.java b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtilTest.java similarity index 99% rename from instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtilTest.java rename to instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtilTest.java index 7711d15779..fe2df60c90 100644 --- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SerializationUtilTest.java +++ b/instrumentation/aws-lambda/aws-lambda-events-common-2.2/library/src/test/java/io/opentelemetry/instrumentation/awslambdaevents/common/v2_2/internal/SerializationUtilTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal; +package io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal; import static org.assertj.core.api.Assertions.assertThat; diff --git a/settings.gradle.kts b/settings.gradle.kts index 163720f93e..52d6bafdd8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -164,6 +164,8 @@ include(":instrumentation:aws-lambda:aws-lambda-core-1.0:testing") include(":instrumentation:aws-lambda:aws-lambda-events-2.2:javaagent") include(":instrumentation:aws-lambda:aws-lambda-events-2.2:library") include(":instrumentation:aws-lambda:aws-lambda-events-2.2:testing") +include(":instrumentation:aws-lambda:aws-lambda-events-3.11:library") +include(":instrumentation:aws-lambda:aws-lambda-events-common-2.2:library") include(":instrumentation:aws-sdk:aws-sdk-1.11:javaagent") include(":instrumentation:aws-sdk:aws-sdk-1.11:library") include(":instrumentation:aws-sdk:aws-sdk-1.11:library-autoconfigure")