Let AWS Lambda SQS handlers report partial batch failures (#14468)
This commit is contained in:
parent
c04b8fc492
commit
fd74eb8a98
|
|
@ -346,6 +346,12 @@ targets:
|
||||||
- type: gradle
|
- type: gradle
|
||||||
path: ./
|
path: ./
|
||||||
target: ':instrumentation:aws-lambda:aws-lambda-events-2.2:library'
|
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
|
- type: gradle
|
||||||
path: ./
|
path: ./
|
||||||
target: ':instrumentation:aws-sdk:aws-sdk-1.11:javaagent'
|
target: ':instrumentation:aws-sdk:aws-sdk-1.11:javaagent'
|
||||||
|
|
|
||||||
|
|
@ -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] |
|
| [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] |
|
| [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] |
|
| [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] [6], [HTTP Client Spans], [GenAI Client Spans], [GenAI Client Metrics] |
|
| [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] [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 |
|
| [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] |
|
| [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] |
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ dependencies {
|
||||||
|
|
||||||
implementation(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library"))
|
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.
|
// Only needed by wrappers, not the javaagent. Muzzle will catch if we accidentally change this.
|
||||||
exclude("com.fasterxml.jackson.core", "jackson-databind")
|
exclude("com.fasterxml.jackson.core", "jackson-databind")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,21 @@ import io.opentelemetry.api.GlobalOpenTelemetry;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionInstrumenter;
|
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.AwsLambdaFunctionInstrumenter;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration;
|
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.v2_2.internal.AwsLambdaSqsInstrumenterFactory;
|
import io.opentelemetry.instrumentation.awslambdaevents.common.v2_2.internal.AwsLambdaSqsInstrumenterFactory;
|
||||||
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
|
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
|
||||||
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
|
import io.opentelemetry.javaagent.bootstrap.internal.AgentInstrumentationConfig;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
public final class AwsLambdaSingletons {
|
public final class AwsLambdaSingletons {
|
||||||
|
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.aws-lambda-events-2.2";
|
||||||
private static final AwsLambdaFunctionInstrumenter FUNCTION_INSTRUMENTER =
|
private static final AwsLambdaFunctionInstrumenter FUNCTION_INSTRUMENTER =
|
||||||
AwsLambdaEventsInstrumenterFactory.createInstrumenter(
|
AwsLambdaEventsInstrumenterFactory.createInstrumenter(
|
||||||
GlobalOpenTelemetry.get(), AgentCommonConfig.get().getKnownHttpRequestMethods());
|
GlobalOpenTelemetry.get(),
|
||||||
|
INSTRUMENTATION_NAME,
|
||||||
|
AgentCommonConfig.get().getKnownHttpRequestMethods());
|
||||||
private static final Instrumenter<SQSEvent, Void> MESSAGE_TRACER =
|
private static final Instrumenter<SQSEvent, Void> MESSAGE_TRACER =
|
||||||
AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get());
|
AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get(), INSTRUMENTATION_NAME);
|
||||||
private static final Duration FLUSH_TIMEOUT =
|
private static final Duration FLUSH_TIMEOUT =
|
||||||
Duration.ofMillis(
|
Duration.ofMillis(
|
||||||
AgentInstrumentationConfig.get()
|
AgentInstrumentationConfig.get()
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,12 @@ plugins {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api(project(":instrumentation:aws-lambda:aws-lambda-core-1.0:library"))
|
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")
|
||||||
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
|
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")
|
library("com.amazonaws:aws-lambda-java-core:1.0.0")
|
||||||
// First version to includes support for SQSEvent, currently the most popular message queue used
|
// First version to includes support for SQSEvent, currently the most popular message queue used
|
||||||
// with lambda.
|
// with lambda.
|
||||||
|
|
@ -25,13 +24,6 @@ dependencies {
|
||||||
// So that is the reason that why we add it as compile only dependency.
|
// So that is the reason that why we add it as compile only dependency.
|
||||||
compileOnly("com.amazonaws:aws-lambda-java-serialization:1.1.5")
|
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
|
// allows to get the function ARN
|
||||||
testLibrary("com.amazonaws:aws-lambda-java-core:1.2.1")
|
testLibrary("com.amazonaws:aws-lambda-java-core:1.2.1")
|
||||||
// allows to get the default events
|
// allows to get the default events
|
||||||
|
|
|
||||||
|
|
@ -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.APIGatewayProxyRequestEvent;
|
||||||
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
|
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda;
|
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 io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
|
|
||||||
|
|
@ -17,7 +17,12 @@ import java.util.function.BiFunction;
|
||||||
* Wrapper for {@link io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler}.
|
* 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
|
* Allows for wrapping a lambda proxied through API Gateway, enabling single span tracing and HTTP
|
||||||
* context propagation.
|
* context propagation.
|
||||||
|
*
|
||||||
|
* @deprecated use {@link
|
||||||
|
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingRequestApiGatewayWrapper}
|
||||||
|
* instead.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class TracingRequestApiGatewayWrapper
|
public class TracingRequestApiGatewayWrapper
|
||||||
extends TracingRequestWrapperBase<APIGatewayProxyRequestEvent, Object> {
|
extends TracingRequestWrapperBase<APIGatewayProxyRequestEvent, Object> {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.ApiGatewayProxyRequest;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.MapUtils;
|
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.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 io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
|
@ -26,7 +27,11 @@ import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper for {@link com.amazonaws.services.lambda.runtime.RequestHandler} based Lambda handlers.
|
* 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 class TracingRequestWrapper extends TracingRequestStreamWrapper {
|
||||||
public TracingRequestWrapper() {
|
public TracingRequestWrapper() {
|
||||||
super();
|
super();
|
||||||
|
|
|
||||||
|
|
@ -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.MapUtils;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda;
|
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration;
|
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.OpenTelemetrySdk;
|
||||||
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
|
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
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
|
* env property OTEL_INSTRUMENTATION_AWS_LAMBDA_HANDLER in package.ClassName::methodName format
|
||||||
*/
|
*/
|
||||||
abstract class TracingRequestWrapperBase<I, O> extends TracingRequestHandler<I, O> {
|
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 WrappedLambda wrappedLambda;
|
||||||
private final Method targetMethod;
|
private final Method targetMethod;
|
||||||
|
|
@ -47,7 +49,7 @@ abstract class TracingRequestWrapperBase<I, O> extends TracingRequestHandler<I,
|
||||||
openTelemetrySdk,
|
openTelemetrySdk,
|
||||||
WrapperConfiguration.flushTimeout(),
|
WrapperConfiguration.flushTimeout(),
|
||||||
AwsLambdaEventsInstrumenterFactory.createInstrumenter(
|
AwsLambdaEventsInstrumenterFactory.createInstrumenter(
|
||||||
openTelemetrySdk, HttpConstants.KNOWN_METHODS));
|
openTelemetrySdk, INSTRUMENTATION_NAME, HttpConstants.KNOWN_METHODS));
|
||||||
this.wrappedLambda = wrappedLambda;
|
this.wrappedLambda = wrappedLambda;
|
||||||
this.targetMethod = wrappedLambda.getRequestTargetMethod();
|
this.targetMethod = wrappedLambda.getRequestTargetMethod();
|
||||||
this.parameterMapper = parameterMapper;
|
this.parameterMapper = parameterMapper;
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,17 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.TracingRequestHandler;
|
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 io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link
|
||||||
|
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsEventHandler} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class TracingSqsEventHandler extends TracingRequestHandler<SQSEvent, Void> {
|
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;
|
private final Instrumenter<SQSEvent, Void> instrumenter;
|
||||||
|
|
||||||
|
|
@ -33,7 +39,9 @@ public abstract class TracingSqsEventHandler extends TracingRequestHandler<SQSEv
|
||||||
*/
|
*/
|
||||||
protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) {
|
protected TracingSqsEventHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) {
|
||||||
this(
|
this(
|
||||||
openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk));
|
openTelemetrySdk,
|
||||||
|
flushTimeout,
|
||||||
|
AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,17 @@ import com.amazonaws.services.lambda.runtime.Context;
|
||||||
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
|
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.WrappedLambda;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrapperConfiguration;
|
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.OpenTelemetrySdk;
|
||||||
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
|
import io.opentelemetry.sdk.autoconfigure.AutoConfiguredOpenTelemetrySdk;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link
|
||||||
|
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsEventWrapper} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public class TracingSqsEventWrapper extends TracingSqsEventHandler {
|
public class TracingSqsEventWrapper extends TracingSqsEventHandler {
|
||||||
|
|
||||||
private final WrappedLambda wrappedLambda;
|
private final WrappedLambda wrappedLambda;
|
||||||
|
|
|
||||||
|
|
@ -10,10 +10,15 @@ import com.amazonaws.services.lambda.runtime.events.SQSEvent;
|
||||||
import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
|
import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
|
||||||
import io.opentelemetry.context.Scope;
|
import io.opentelemetry.context.Scope;
|
||||||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
|
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 io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated use {@link
|
||||||
|
* io.opentelemetry.instrumentation.awslambdaevents.v3_11.TracingSqsMessageHandler} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler {
|
public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler {
|
||||||
|
|
||||||
private final Instrumenter<SQSMessage, Void> messageInstrumenter;
|
private final Instrumenter<SQSMessage, Void> messageInstrumenter;
|
||||||
|
|
@ -33,7 +38,9 @@ public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler {
|
||||||
*/
|
*/
|
||||||
protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) {
|
protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) {
|
||||||
this(
|
this(
|
||||||
openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk));
|
openTelemetrySdk,
|
||||||
|
flushTimeout,
|
||||||
|
AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk, INSTRUMENTATION_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -50,7 +57,7 @@ public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler {
|
||||||
openTelemetrySdk,
|
openTelemetrySdk,
|
||||||
flushTimeout,
|
flushTimeout,
|
||||||
eventInstrumenter,
|
eventInstrumenter,
|
||||||
AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk));
|
AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk, INSTRUMENTATION_NAME));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
import org.mockito.junit.jupiter.MockitoSettings;
|
import org.mockito.junit.jupiter.MockitoSettings;
|
||||||
import org.mockito.quality.Strictness;
|
import org.mockito.quality.Strictness;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // testing deprecated class
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||||
class AwsLambdaApiGatewayWrapperTest {
|
class AwsLambdaApiGatewayWrapperTest {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExte
|
||||||
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
import io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||||
import org.junit.jupiter.api.extension.RegisterExtension;
|
import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // testing deprecated class
|
||||||
class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest {
|
class AwsLambdaSqsEventHandlerTest extends AbstractAwsLambdaSqsEventHandlerTest {
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import org.junitpioneer.jupiter.SetEnvironmentVariable;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // testing deprecated class
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
@SetEnvironmentVariable(
|
@SetEnvironmentVariable(
|
||||||
key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY,
|
key = WrappedLambda.OTEL_LAMBDA_HANDLER_ENV_KEY,
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // testing deprecated class
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class AwsLambdaSqsMessageHandlerTest {
|
class AwsLambdaSqsMessageHandlerTest {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ import org.junitpioneer.jupiter.SetEnvironmentVariable;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.jupiter.MockitoExtension;
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // testing deprecated class
|
||||||
@ExtendWith(MockitoExtension.class)
|
@ExtendWith(MockitoExtension.class)
|
||||||
class AwsLambdaWrapperTest {
|
class AwsLambdaWrapperTest {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.SQSEvent;
|
||||||
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
|
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
|
||||||
import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.WrappedLambda;
|
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 io.opentelemetry.sdk.OpenTelemetrySdk;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
@ -24,6 +24,7 @@ import java.util.Map;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") // testing deprecated class
|
||||||
class TracingRequestWrapperStandardEventsTest {
|
class TracingRequestWrapperStandardEventsTest {
|
||||||
private static final Map<Class<?>, EventInfo> EVENTS_JSON = buildEventExamples();
|
private static final Map<Class<?>, EventInfo> EVENTS_JSON = buildEventExamples();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public abstract class AbstractAwsLambdaSqsEventHandlerTest {
|
||||||
private static final String AWS_TRACE_HEADER =
|
private static final String AWS_TRACE_HEADER =
|
||||||
"Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1";
|
"Root=1-5759e988-bd862e3fe1be46a994272793;Parent=53995c3f42cd8ad8;Sampled=1";
|
||||||
|
|
||||||
protected abstract RequestHandler<SQSEvent, Void> handler();
|
protected abstract RequestHandler<SQSEvent, ?> handler();
|
||||||
|
|
||||||
protected abstract InstrumentationExtension testing();
|
protected abstract InstrumentationExtension testing();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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).
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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&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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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")
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.AttributesExtractorUtil.internalSet;
|
||||||
import static io.opentelemetry.instrumentation.api.internal.HttpConstants._OTHER;
|
import static io.opentelemetry.instrumentation.api.internal.HttpConstants._OTHER;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
|
||||||
import io.opentelemetry.api.OpenTelemetry;
|
import io.opentelemetry.api.OpenTelemetry;
|
||||||
|
|
@ -21,13 +21,11 @@ import java.util.Set;
|
||||||
public final class AwsLambdaEventsInstrumenterFactory {
|
public final class AwsLambdaEventsInstrumenterFactory {
|
||||||
|
|
||||||
public static AwsLambdaFunctionInstrumenter createInstrumenter(
|
public static AwsLambdaFunctionInstrumenter createInstrumenter(
|
||||||
OpenTelemetry openTelemetry, Set<String> knownMethods) {
|
OpenTelemetry openTelemetry, String instrumentationName, Set<String> knownMethods) {
|
||||||
return new AwsLambdaFunctionInstrumenter(
|
return new AwsLambdaFunctionInstrumenter(
|
||||||
openTelemetry,
|
openTelemetry,
|
||||||
Instrumenter.builder(
|
Instrumenter.builder(
|
||||||
openTelemetry,
|
openTelemetry, instrumentationName, AwsLambdaEventsInstrumenterFactory::spanName)
|
||||||
"io.opentelemetry.aws-lambda-events-2.2",
|
|
||||||
AwsLambdaEventsInstrumenterFactory::spanName)
|
|
||||||
.addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor())
|
.addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor())
|
||||||
.addAttributesExtractor(new ApiGatewayProxyAttributesExtractor(knownMethods))
|
.addAttributesExtractor(new ApiGatewayProxyAttributesExtractor(knownMethods))
|
||||||
.buildInstrumenter(SpanKindExtractor.alwaysServer()));
|
.buildInstrumenter(SpanKindExtractor.alwaysServer()));
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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;
|
||||||
import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
|
import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
|
||||||
|
|
@ -18,21 +18,19 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public final class AwsLambdaSqsInstrumenterFactory {
|
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(
|
return Instrumenter.<SQSEvent, Void>builder(
|
||||||
openTelemetry,
|
openTelemetry, instrumentationName, AwsLambdaSqsInstrumenterFactory::spanName)
|
||||||
"io.opentelemetry.aws-lambda-events-2.2",
|
|
||||||
AwsLambdaSqsInstrumenterFactory::spanName)
|
|
||||||
.addAttributesExtractor(new SqsEventAttributesExtractor())
|
.addAttributesExtractor(new SqsEventAttributesExtractor())
|
||||||
.addSpanLinksExtractor(new SqsEventSpanLinksExtractor())
|
.addSpanLinksExtractor(new SqsEventSpanLinksExtractor())
|
||||||
.buildInstrumenter(SpanKindExtractor.alwaysConsumer());
|
.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(
|
return Instrumenter.<SQSMessage, Void>builder(
|
||||||
openTelemetry,
|
openTelemetry, instrumentationName, message -> message.getEventSource() + " process")
|
||||||
"io.opentelemetry.aws-lambda-events-2.2",
|
|
||||||
message -> message.getEventSource() + " process")
|
|
||||||
.addAttributesExtractor(new SqsMessageAttributesExtractor())
|
.addAttributesExtractor(new SqsMessageAttributesExtractor())
|
||||||
.addSpanLinksExtractor(new SqsMessageSpanLinksExtractor())
|
.addSpanLinksExtractor(new SqsMessageSpanLinksExtractor())
|
||||||
.buildInstrumenter(SpanKindExtractor.alwaysConsumer());
|
.buildInstrumenter(SpanKindExtractor.alwaysConsumer());
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.core.JsonParser;
|
||||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||||
|
|
@ -34,7 +34,6 @@ class CustomJodaModule extends SimpleModule {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public CustomJodaModule() {
|
public CustomJodaModule() {
|
||||||
super();
|
|
||||||
addDeserializer(DateTime.class, new DateTimeDeserialiser());
|
addDeserializer(DateTime.class, new DateTimeDeserialiser());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3,16 +3,20 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 com.amazonaws.services.lambda.runtime.Context;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.util.function.BiFunction;
|
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) {
|
Method targetMethod, T input, Context context, BiFunction<T, Class<?>, Object> mapper) {
|
||||||
Class<?>[] parameterTypes = targetMethod.getParameterTypes();
|
Class<?>[] parameterTypes = targetMethod.getParameterTypes();
|
||||||
Object[] parameters = new Object[parameterTypes.length];
|
Object[] parameters = new Object[parameterTypes.length];
|
||||||
|
|
@ -28,7 +32,7 @@ final class LambdaParameters {
|
||||||
return parameters;
|
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();
|
Class<?>[] parameterTypes = targetMethod.getParameterTypes();
|
||||||
Object[] parameters = new Object[parameterTypes.length];
|
Object[] parameters = new Object[parameterTypes.length];
|
||||||
for (int i = 0; i < parameterTypes.length; i++) {
|
for (int i = 0; i < parameterTypes.length; i++) {
|
||||||
|
|
@ -43,7 +47,7 @@ final class LambdaParameters {
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Object toInput(
|
public static Object toInput(
|
||||||
Method targetMethod,
|
Method targetMethod,
|
||||||
InputStream inputStream,
|
InputStream inputStream,
|
||||||
BiFunction<InputStream, Class<?>, Object> mapper) {
|
BiFunction<InputStream, Class<?>, Object> mapper) {
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.PojoSerializer;
|
||||||
import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
|
import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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;
|
||||||
import io.opentelemetry.api.common.AttributeKey;
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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;
|
||||||
import io.opentelemetry.context.Context;
|
import io.opentelemetry.context.Context;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
|
||||||
import io.opentelemetry.api.common.AttributeKey;
|
import io.opentelemetry.api.common.AttributeKey;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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 com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage;
|
||||||
import io.opentelemetry.api.trace.Span;
|
import io.opentelemetry.api.trace.Span;
|
||||||
|
|
@ -3,13 +3,12 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
|
||||||
import com.amazonaws.services.lambda.runtime.Context;
|
import com.amazonaws.services.lambda.runtime.Context;
|
||||||
import io.opentelemetry.instrumentation.awslambdaevents.v2_2.internal.SerializationUtil;
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* 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;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
|
@ -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:javaagent")
|
||||||
include(":instrumentation:aws-lambda:aws-lambda-events-2.2:library")
|
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-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:javaagent")
|
||||||
include(":instrumentation:aws-sdk:aws-sdk-1.11:library")
|
include(":instrumentation:aws-sdk:aws-sdk-1.11:library")
|
||||||
include(":instrumentation:aws-sdk:aws-sdk-1.11:library-autoconfigure")
|
include(":instrumentation:aws-sdk:aws-sdk-1.11:library-autoconfigure")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue