Let AWS Lambda SQS handlers report partial batch failures (#14468)

This commit is contained in:
Lauri Tulmin 2025-08-22 06:20:08 +03:00 committed by GitHub
parent c04b8fc492
commit fd74eb8a98
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 1934 additions and 58 deletions

View File

@ -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'

View File

@ -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),<br>[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),<br>[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,<br>2.2+ | [opentelemetry-aws-sdk-1.11](../instrumentation/aws-sdk/aws-sdk-1.11/library),<br>[opentelemetry-aws-sdk-1.11-autoconfigure](../instrumentation/aws-sdk/aws-sdk-1.11/library-autoconfigure),<br>[opentelemetry-aws-sdk-2.2](../instrumentation/aws-sdk/aws-sdk-2.2/library),<br>[opentelemetry-aws-sdk-2.2-autoconfigure](../instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure) | [Messaging Spans], [Database Client Spans], [Database Client Metrics]&nbsp;[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]&nbsp;[6] |

View File

@ -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")
}

View File

@ -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<SQSEvent, Void> MESSAGE_TRACER =
AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get());
AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME);
private static final Duration FLUSH_TIMEOUT =
Duration.ofMillis(
AgentInstrumentationConfig.get()

View File

@ -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

View File

@ -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<APIGatewayProxyRequestEvent, Object> {

View File

@ -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();

View File

@ -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<I, O> extends TracingRequestHandler<I, O> {
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<I, O> extends TracingRequestHandler<I,
openTelemetrySdk,
WrapperConfiguration.flushTimeout(),
AwsLambdaEventsInstrumenterFactory.createInstrumenter(
openTelemetrySdk, HttpConstants.KNOWN_METHODS));
openTelemetrySdk, INSTRUMENTATION_NAME, HttpConstants.KNOWN_METHODS));
this.wrappedLambda = wrappedLambda;
this.targetMethod = wrappedLambda.getRequestTargetMethod();
this.parameterMapper = parameterMapper;

View File

@ -10,11 +10,17 @@ 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.v2_2.internal.AwsLambdaSqsInstrumenterFactory;
import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import java.time.Duration;
/**
* @deprecated use {@link
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsEventHandler} instead.
*/
@Deprecated
public abstract class TracingSqsEventHandler extends TracingRequestHandler<SQSEvent, Void> {
static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2";
private final Instrumenter<SQSEvent, Void> instrumenter;
@ -33,7 +39,9 @@ public abstract class TracingSqsEventHandler extends TracingRequestHandler<SQSEv
*/
protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) {
this(
openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk));
openTelemetrySdk,
flushTimeout,
AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME));
}
/**

View File

@ -9,11 +9,17 @@ import com.amazonaws.services.lambda.runtime.Context;
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;
/**
* @deprecated use {@link
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsEventWrapper} instead.
*/
@Deprecated
public class TracingSqsEventWrapper extends TracingSqsEventHandler {
private final WrappedLambda wrappedLambda;

View File

@ -10,10 +10,15 @@ 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.v2_2.internal.AwsLambdaSqsInstrumenterFactory;
import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import java.time.Duration;
/**
* @deprecated use {@link
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsMessageHandler} instead.
*/
@Deprecated
public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler {
private final Instrumenter<SQSMessage, Void> 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));
}
/**

View File

@ -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 {

View File

@ -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

View File

@ -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,

View File

@ -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 {

View File

@ -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 {

View File

@ -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<Class<?>, EventInfo> EVENTS_JSON = buildEventExamples();

View File

@ -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<SQSEvent, Void> handler();
protected abstract RequestHandler<SQSEvent, ?> handler();
protected abstract InstrumentationExtension testing();

View File

@ -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<String, String> {
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
<dependencies>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-extension-trace-propagators</artifactId>
<version>0.8.0</version>
</dependency>
</dependencies>
```
## 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 <https://github.com/open-telemetry/opentelemetry-java/tree/main/extensions/trace-propagators>).
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).

View File

@ -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<Test>().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")
}

View File

@ -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<APIGatewayProxyRequestEvent, Object> {
public TracingRequestApiGatewayWrapper() {
super(TracingRequestApiGatewayWrapper::map);
}
// Visible for testing
TracingRequestApiGatewayWrapper(
OpenTelemetrySdk openTelemetrySdk,
WrappedLambda wrappedLambda,
BiFunction<APIGatewayProxyRequestEvent, Class<?>, Object> mapper) {
super(openTelemetrySdk, wrappedLambda, mapper);
}
// Visible for testing
static <T> T map(APIGatewayProxyRequestEvent event, Class<T> 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;
}
}

View File

@ -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<String, String> 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
<INPUT, OUTPUT> 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> T map(InputStream inputStream, Class<T> clazz) {
try {
return SerializationUtil.fromJson(inputStream, clazz);
} catch (IllegalArgumentException e) {
throw new IllegalStateException(
"Could not map input to requested parameter type: " + clazz, e);
}
}
}

View File

@ -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<I, O> extends TracingRequestHandler<I, O> {
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-3.11";
private final WrappedLambda wrappedLambda;
private final Method targetMethod;
private final BiFunction<I, Class<?>, Object> parameterMapper;
protected TracingRequestWrapperBase(BiFunction<I, Class<?>, Object> parameterMapper) {
this(
AutoConfiguredOpenTelemetrySdk.initialize().getOpenTelemetrySdk(),
WrappedLambda.fromConfiguration(),
parameterMapper);
}
// Visible for testing
TracingRequestWrapperBase(
OpenTelemetrySdk openTelemetrySdk,
WrappedLambda wrappedLambda,
BiFunction<I, Class<?>, 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<String, String> extractHttpHeaders(I input) {
if (input instanceof APIGatewayProxyRequestEvent) {
return MapUtils.emptyIfNull(((APIGatewayProxyRequestEvent) input).getHeaders());
}
return Collections.emptyMap();
}
}

View File

@ -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<SQSEvent, SQSBatchResponse> {
static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-3.11";
private final Instrumenter<SQSEvent, Void> 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<SQSEvent, Void> 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);
}

View File

@ -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()));
}
}
}

View File

@ -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<SQSMessage, Void> 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<SQSEvent,
* Void>}.
*/
protected TracingSqsMessageHandler(
OpenTelemetrySdk openTelemetrySdk,
Duration flushTimeout,
Instrumenter<SQSEvent, Void> 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<SQSEvent, Void>} and {@code
* Instrumenter<SQSMessage, Void>}.
*/
protected TracingSqsMessageHandler(
OpenTelemetrySdk openTelemetrySdk,
Duration flushTimeout,
Instrumenter<SQSEvent, Void> eventInstrumenter,
Instrumenter<SQSMessage, Void> messageInstrumenter) {
super(openTelemetrySdk, flushTimeout, eventInstrumenter);
this.messageInstrumenter = messageInstrumenter;
}
@Override
protected final SQSBatchResponse handleEvent(SQSEvent event, Context context) {
List<SQSBatchResponse.BatchItemFailure> 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<SQSBatchResponse.BatchItemFailure> 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);
}

View File

@ -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<String, String> 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<String, String> 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<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
@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<String, String> {
@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<Integer, String> {
@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<CustomType, String> {
@Override
public String handleRequest(CustomType input, Context context) {
if (input.key.equals("hello")) {
return input.value;
}
throw new IllegalArgumentException("bad argument");
}
}
}

View File

@ -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<SQSEvent, SQSBatchResponse> 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;
}
}
}

View File

@ -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<SQSEvent, SQSBatchResponse> {
@Override
public SQSBatchResponse handleRequest(SQSEvent input, Context context) {
return null;
}
}
// Constructor private in early versions.
private static SQSEvent.SQSMessage newMessage() {
try {
Constructor<SQSEvent.SQSMessage> ctor = SQSEvent.SQSMessage.class.getDeclaredConstructor();
return ctor.newInstance();
} catch (Throwable t) {
throw new AssertionError(t);
}
}
}

View File

@ -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<SQSEvent.SQSMessage> 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());
}
}
}

View File

@ -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<String, String> {
@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<Integer, String> {
@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<CustomType, String> {
@Override
public String handleRequest(CustomType input, Context context) {
if (input.key.equals("hello there")) {
return input.value;
}
throw new IllegalArgumentException("bad argument");
}
}
}

View File

@ -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<Class<?>, 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<Class<?>, EventInfo> buildEventExamples() {
Map<Class<?>, 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&amp;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<ScheduledEvent, ScheduledEvent> {
@Override
public ScheduledEvent handleRequest(ScheduledEvent i, Context cntxt) {
return i;
}
}
public static class KinesisEventRequestHandler
implements RequestHandler<KinesisEvent, KinesisEvent> {
@Override
public KinesisEvent handleRequest(KinesisEvent i, Context cntxt) {
return i;
}
}
public static class SqsEventRequestHandler implements RequestHandler<SQSEvent, SQSEvent> {
@Override
public SQSEvent handleRequest(SQSEvent i, Context cntxt) {
return i;
}
}
public static class S3EventRequestHandler implements RequestHandler<S3Event, S3Event> {
@Override
public S3Event handleRequest(S3Event i, Context cntxt) {
return i;
}
}
public static class SnsEventRequestHandler implements RequestHandler<SNSEvent, SNSEvent> {
@Override
public SNSEvent handleRequest(SNSEvent i, Context cntxt) {
return i;
}
}
}

View File

@ -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<Test>().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")
}

View File

@ -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;

View File

@ -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<String> knownMethods) {
OpenTelemetry openTelemetry, String instrumentationName, Set<String> 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()));

View File

@ -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<SQSEvent, Void> forEvent(OpenTelemetry openTelemetry) {
public static Instrumenter<SQSEvent, Void> forEvent(
OpenTelemetry openTelemetry, String instrumentationName) {
return Instrumenter.<SQSEvent, Void>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<SQSMessage, Void> forMessage(OpenTelemetry openTelemetry) {
public static Instrumenter<SQSMessage, Void> forMessage(
OpenTelemetry openTelemetry, String instrumentationName) {
return Instrumenter.<SQSMessage, Void>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());

View File

@ -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());
}

View File

@ -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 <T> Object[] toArray(
public static <T> Object[] toArray(
Method targetMethod, T input, Context context, BiFunction<T, Class<?>, Object> mapper) {
Class<?>[] parameterTypes = targetMethod.getParameterTypes();
Object[] parameters = new Object[parameterTypes.length];
@ -28,7 +32,7 @@ final class LambdaParameters {
return parameters;
}
static <T> Object[] toParameters(Method targetMethod, T input, Context context) {
public static <T> 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<InputStream, Class<?>, Object> mapper) {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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")