From 699a5adb0055d46e637bc59f863e28d69a5b4710 Mon Sep 17 00:00:00 2001 From: Nikita Salnikov-Tarnovski Date: Fri, 15 Oct 2021 22:31:08 +0300 Subject: [PATCH] Convert aws lambda integration to Instrumenter (#4362) * Convert AwsLambdaMessageTracer to Instrumenter * Convert AwsLambdaTracer to Instrumenter * Rename class * Polish * Polish * Move classes to internal package --- .../v1_0/AwsLambdaInstrumentationHelper.java | 21 +-- ...wsLambdaRequestHandlerInstrumentation.java | 41 ++++-- .../aws-lambda-1.0/library/build.gradle.kts | 3 + .../v1_0/AwsLambdaMessageTracer.java | 85 ----------- .../awslambda/v1_0/AwsLambdaRequest.java | 25 ++++ .../awslambda/v1_0/AwsLambdaTracer.java | 139 ------------------ .../awslambda/v1_0/HttpSpanAttributes.java | 82 ----------- .../awslambda/v1_0/TracingRequestHandler.java | 43 +++--- .../v1_0/TracingRequestStreamHandler.java | 39 +++-- .../v1_0/TracingSqsEventHandler.java | 45 +++--- .../v1_0/TracingSqsMessageHandler.java | 62 +++++--- .../ApiGatewayProxyAttributesExtractor.java | 93 ++++++++++++ .../ApiGatewayProxyRequest.java | 20 +-- .../AwsLambdaFunctionAttributesExtractor.java | 86 +++++++++++ .../AwsLambdaFunctionInstrumenter.java | 56 +++++++ .../AwsLambdaFunctionInstrumenterFactory.java | 36 +++++ .../AwsLambdaSqsInstrumenterFactory.java | 54 +++++++ .../v1_0/{ => internal}/HeadersFactory.java | 2 +- .../v1_0/{ => internal}/MapUtils.java | 2 +- .../ParentContextExtractor.java | 16 +- .../internal/SqsEventAttributesExtractor.java | 27 ++++ .../internal/SqsEventSpanLinksExtractor.java | 23 +++ .../SqsMessageAttributesExtractor.java | 29 ++++ .../SqsMessageSpanLinksExtractor.java | 29 ++++ .../ApiGatewayProxyRequestTest.java | 2 +- .../{ => internal}/HeadersFactoryTest.java | 2 +- .../ParentContextExtractorTest.java | 11 +- 27 files changed, 648 insertions(+), 425 deletions(-) delete mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaMessageTracer.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaRequest.java delete mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTracer.java delete mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HttpSpanAttributes.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyAttributesExtractor.java rename instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/ApiGatewayProxyRequest.java (80%) create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionAttributesExtractor.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenter.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaSqsInstrumenterFactory.java rename instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/HeadersFactory.java (94%) rename instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/MapUtils.java (90%) rename instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/ParentContextExtractor.java (78%) create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventAttributesExtractor.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventSpanLinksExtractor.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageAttributesExtractor.java create mode 100644 instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageSpanLinksExtractor.java rename instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/ApiGatewayProxyRequestTest.java (97%) rename instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/HeadersFactoryTest.java (95%) rename instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/{ => internal}/ParentContextExtractorTest.java (88%) diff --git a/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaInstrumentationHelper.java b/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaInstrumentationHelper.java index ce70be1fce..a6da8e96a2 100644 --- a/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaInstrumentationHelper.java +++ b/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaInstrumentationHelper.java @@ -5,23 +5,26 @@ package io.opentelemetry.javaagent.instrumentation.awslambda.v1_0; +import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.api.GlobalOpenTelemetry; -import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaMessageTracer; -import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaTracer; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenter; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenterFactory; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaSqsInstrumenterFactory; public final class AwsLambdaInstrumentationHelper { - private static final AwsLambdaTracer FUNCTION_TRACER = - new AwsLambdaTracer(GlobalOpenTelemetry.get()); + private static final AwsLambdaFunctionInstrumenter FUNCTION_INSTRUMENTER = + AwsLambdaFunctionInstrumenterFactory.createInstrumenter(GlobalOpenTelemetry.get()); - public static AwsLambdaTracer functionTracer() { - return FUNCTION_TRACER; + public static AwsLambdaFunctionInstrumenter functionInstrumenter() { + return FUNCTION_INSTRUMENTER; } - private static final AwsLambdaMessageTracer MESSAGE_TRACER = - new AwsLambdaMessageTracer(GlobalOpenTelemetry.get()); + private static final Instrumenter MESSAGE_TRACER = + AwsLambdaSqsInstrumenterFactory.forEvent(GlobalOpenTelemetry.get()); - public static AwsLambdaMessageTracer messageTracer() { + public static Instrumenter messageInstrumenter() { return MESSAGE_TRACER; } diff --git a/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaRequestHandlerInstrumentation.java b/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaRequestHandlerInstrumentation.java index 2467318eaa..b7096070a2 100644 --- a/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaRequestHandlerInstrumentation.java +++ b/instrumentation/aws-lambda-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awslambda/v1_0/AwsLambdaRequestHandlerInstrumentation.java @@ -7,8 +7,8 @@ package io.opentelemetry.javaagent.instrumentation.awslambda.v1_0; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface; -import static io.opentelemetry.javaagent.instrumentation.awslambda.v1_0.AwsLambdaInstrumentationHelper.functionTracer; -import static io.opentelemetry.javaagent.instrumentation.awslambda.v1_0.AwsLambdaInstrumentationHelper.messageTracer; +import static io.opentelemetry.javaagent.instrumentation.awslambda.v1_0.AwsLambdaInstrumentationHelper.functionInstrumenter; +import static io.opentelemetry.javaagent.instrumentation.awslambda.v1_0.AwsLambdaInstrumentationHelper.messageInstrumenter; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -16,11 +16,12 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaRequest; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; import io.opentelemetry.javaagent.instrumentation.api.OpenTelemetrySdkAccess; +import java.util.Collections; import java.util.concurrent.TimeUnit; import net.bytebuddy.asm.Advice; import net.bytebuddy.description.type.TypeDescription; @@ -56,21 +57,34 @@ public class AwsLambdaRequestHandlerInstrumentation implements TypeInstrumentati public static void onEnter( @Advice.Argument(value = 0, typing = Typing.DYNAMIC) Object arg, @Advice.Argument(1) Context context, + @Advice.Local("otelInput") AwsLambdaRequest input, @Advice.Local("otelFunctionContext") io.opentelemetry.context.Context functionContext, @Advice.Local("otelFunctionScope") Scope functionScope, @Advice.Local("otelMessageContext") io.opentelemetry.context.Context messageContext, @Advice.Local("otelMessageScope") Scope messageScope) { - functionContext = functionTracer().startSpan(context, SpanKind.SERVER, arg); + input = AwsLambdaRequest.create(context, arg, Collections.emptyMap()); + io.opentelemetry.context.Context parentContext = functionInstrumenter().extract(input); + + if (!functionInstrumenter().shouldStart(parentContext, input)) { + return; + } + + functionContext = functionInstrumenter().start(parentContext, input); functionScope = functionContext.makeCurrent(); + if (arg instanceof SQSEvent) { - messageContext = messageTracer().startSpan(functionContext, (SQSEvent) arg); - messageScope = messageContext.makeCurrent(); + if (messageInstrumenter().shouldStart(functionContext, (SQSEvent) arg)) { + messageContext = messageInstrumenter().start(functionContext, (SQSEvent) arg); + messageScope = messageContext.makeCurrent(); + } } } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) public static void stopSpan( + @Advice.Argument(value = 0, typing = Typing.DYNAMIC) Object arg, @Advice.Thrown Throwable throwable, + @Advice.Local("otelInput") AwsLambdaRequest input, @Advice.Local("otelFunctionContext") io.opentelemetry.context.Context functionContext, @Advice.Local("otelFunctionScope") Scope functionScope, @Advice.Local("otelMessageContext") io.opentelemetry.context.Context messageContext, @@ -78,19 +92,14 @@ public class AwsLambdaRequestHandlerInstrumentation implements TypeInstrumentati if (messageScope != null) { messageScope.close(); - if (throwable != null) { - messageTracer().endExceptionally(messageContext, throwable); - } else { - messageTracer().end(messageContext); - } + messageInstrumenter().end(messageContext, (SQSEvent) arg, null, throwable); } - functionScope.close(); - if (throwable != null) { - functionTracer().endExceptionally(functionContext, throwable); - } else { - functionTracer().end(functionContext); + if (functionScope != null) { + functionScope.close(); + functionInstrumenter().end(functionContext, input, null, throwable); } + OpenTelemetrySdkAccess.forceFlush(1, TimeUnit.SECONDS); } } diff --git a/instrumentation/aws-lambda-1.0/library/build.gradle.kts b/instrumentation/aws-lambda-1.0/library/build.gradle.kts index 5174ae7035..deeffe7fdc 100644 --- a/instrumentation/aws-lambda-1.0/library/build.gradle.kts +++ b/instrumentation/aws-lambda-1.0/library/build.gradle.kts @@ -6,6 +6,9 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-sdk") compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure") + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") + library("com.amazonaws:aws-lambda-java-core:1.0.0") // First version to includes support for SQSEvent, currently the most popular message queue used // with lambda. diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaMessageTracer.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaMessageTracer.java deleted file mode 100644 index 826714b963..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaMessageTracer.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0; - -import static io.opentelemetry.api.trace.SpanKind.CONSUMER; - -import com.amazonaws.services.lambda.runtime.events.SQSEvent; -import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; -import io.opentelemetry.api.OpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanBuilder; -import io.opentelemetry.api.trace.SpanContext; -import io.opentelemetry.context.Context; -import io.opentelemetry.instrumentation.api.tracer.BaseTracer; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; - -public class AwsLambdaMessageTracer extends BaseTracer { - - private static final String AWS_TRACE_HEADER_SQS_ATTRIBUTE_KEY = "AWSTraceHeader"; - - public AwsLambdaMessageTracer(OpenTelemetry openTelemetry) { - super(openTelemetry); - } - - public Context startSpan(Context parentContext, SQSEvent event) { - // Use event source in name if all messages have the same source, otherwise use placeholder. - String source = "multiple_sources"; - if (!event.getRecords().isEmpty()) { - String messageSource = event.getRecords().get(0).getEventSource(); - for (int i = 1; i < event.getRecords().size(); i++) { - SQSMessage message = event.getRecords().get(i); - if (!message.getEventSource().equals(messageSource)) { - messageSource = null; - break; - } - } - if (messageSource != null) { - source = messageSource; - } - } - - SpanBuilder span = spanBuilder(parentContext, source + " process", CONSUMER); - - span.setAttribute(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"); - span.setAttribute(SemanticAttributes.MESSAGING_OPERATION, "process"); - - for (SQSMessage message : event.getRecords()) { - addLinkToMessageParent(message, span); - } - - return parentContext.with(span.startSpan()); - } - - public Context startSpan(Context parentContext, SQSMessage message) { - SpanBuilder span = spanBuilder(parentContext, message.getEventSource() + " process", CONSUMER); - - span.setAttribute(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"); - span.setAttribute(SemanticAttributes.MESSAGING_OPERATION, "process"); - span.setAttribute(SemanticAttributes.MESSAGING_MESSAGE_ID, message.getMessageId()); - span.setAttribute(SemanticAttributes.MESSAGING_DESTINATION, message.getEventSource()); - - addLinkToMessageParent(message, span); - - return parentContext.with(span.startSpan()); - } - - private static void addLinkToMessageParent(SQSMessage message, SpanBuilder span) { - String parentHeader = message.getAttributes().get(AWS_TRACE_HEADER_SQS_ATTRIBUTE_KEY); - if (parentHeader != null) { - SpanContext parentCtx = - Span.fromContext(ParentContextExtractor.fromXrayHeader(parentHeader)).getSpanContext(); - if (parentCtx.isValid()) { - span.addLink(parentCtx); - } - } - } - - @Override - protected String getInstrumentationName() { - return "io.opentelemetry.aws-lambda-1.0"; - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaRequest.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaRequest.java new file mode 100644 index 0000000000..4519d690d4 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaRequest.java @@ -0,0 +1,25 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0; + +import com.amazonaws.services.lambda.runtime.Context; +import com.google.auto.value.AutoValue; +import java.util.Map; + +@AutoValue +public abstract class AwsLambdaRequest { + + public static AwsLambdaRequest create( + Context awsContext, Object input, Map headers) { + return new AutoValue_AwsLambdaRequest(awsContext, input, headers); + } + + public abstract Context getAwsContext(); + + public abstract Object getInput(); + + public abstract Map getHeaders(); +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTracer.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTracer.java deleted file mode 100644 index e85015a5f0..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/AwsLambdaTracer.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0; - -import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.CLOUD_ACCOUNT_ID; -import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.FAAS_ID; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_EXECUTION; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_TRIGGER; - -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.api.OpenTelemetry; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanBuilder; -import io.opentelemetry.api.trace.SpanKind; -import io.opentelemetry.instrumentation.api.tracer.BaseTracer; -import io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FaasTriggerValues; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.util.Collections; -import java.util.Map; -import org.checkerframework.checker.nullness.qual.Nullable; - -// Context is defined in both OTel and Lambda -@SuppressWarnings("ParameterPackage") -public class AwsLambdaTracer extends BaseTracer { - - @Nullable private static final MethodHandle GET_FUNCTION_ARN; - - static { - MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - MethodHandle getFunctionArn; - try { - getFunctionArn = - lookup.findVirtual( - Context.class, "getInvokedFunctionArn", MethodType.methodType(String.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - getFunctionArn = null; - } - GET_FUNCTION_ARN = getFunctionArn; - } - - // cached accountId value - private volatile String accountId; - - public AwsLambdaTracer(OpenTelemetry openTelemetry) { - super(openTelemetry); - } - - private void setAttributes(SpanBuilder span, Context context, Object input) { - setCommonAttributes(span, context); - if (input instanceof APIGatewayProxyRequestEvent) { - span.setAttribute(FAAS_TRIGGER, FaasTriggerValues.HTTP); - HttpSpanAttributes.onRequest(span, (APIGatewayProxyRequestEvent) input); - } - } - - private void setCommonAttributes(SpanBuilder span, Context context) { - span.setAttribute(FAAS_EXECUTION, context.getAwsRequestId()); - String arn = getFunctionArn(context); - if (arn != null) { - span.setAttribute(FAAS_ID, arn); - } - String accountId = getAccountId(arn); - if (accountId != null) { - span.setAttribute(CLOUD_ACCOUNT_ID, accountId); - } - } - - @Nullable - private static String getFunctionArn(Context context) { - if (GET_FUNCTION_ARN == null) { - return null; - } - try { - return (String) GET_FUNCTION_ARN.invoke(context); - } catch (Throwable throwable) { - return null; - } - } - - @Nullable - private String getAccountId(@Nullable String arn) { - if (arn == null) { - return null; - } - if (accountId == null) { - synchronized (this) { - if (accountId == null) { - String[] arnParts = arn.split(":"); - if (arnParts.length >= 5) { - accountId = arnParts[4]; - } - } - } - } - return accountId; - } - - private static String spanName(Context context, Object input) { - String name = null; - if (input instanceof APIGatewayProxyRequestEvent) { - name = ((APIGatewayProxyRequestEvent) input).getResource(); - } - return name == null ? context.getFunctionName() : name; - } - - public io.opentelemetry.context.Context startSpan( - Context awsContext, SpanKind kind, Object input) { - return startSpan(awsContext, kind, input, Collections.emptyMap()); - } - - public io.opentelemetry.context.Context startSpan( - Context awsContext, SpanKind kind, Object input, Map headers) { - io.opentelemetry.context.Context parentContext = ParentContextExtractor.extract(headers, this); - - SpanBuilder spanBuilder = spanBuilder(parentContext, spanName(awsContext, input), kind); - setAttributes(spanBuilder, awsContext, input); - - return withServerSpan(parentContext, spanBuilder.startSpan()); - } - - public void onOutput(io.opentelemetry.context.Context context, Object output) { - if (output instanceof APIGatewayProxyResponseEvent) { - HttpSpanAttributes.onResponse( - Span.fromContext(context), (APIGatewayProxyResponseEvent) output); - } - } - - @Override - protected String getInstrumentationName() { - return "io.opentelemetry.aws-lambda-1.0"; - } -} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HttpSpanAttributes.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HttpSpanAttributes.java deleted file mode 100644 index 165ab5daa7..0000000000 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HttpSpanAttributes.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awslambda.v1_0; - -import static io.opentelemetry.instrumentation.awslambda.v1_0.MapUtils.emptyIfNull; -import static io.opentelemetry.instrumentation.awslambda.v1_0.MapUtils.lowercaseMap; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_METHOD; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_STATUS_CODE; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL; -import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_USER_AGENT; - -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.api.trace.SpanBuilder; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Map; - -final class HttpSpanAttributes { - static void onRequest(SpanBuilder span, APIGatewayProxyRequestEvent request) { - String httpMethod = request.getHttpMethod(); - if (httpMethod != null) { - span.setAttribute(HTTP_METHOD, httpMethod); - } - - Map headers = lowercaseMap(request.getHeaders()); - String userAgent = headers.get("user-agent"); - if (userAgent != null) { - span.setAttribute(HTTP_USER_AGENT, userAgent); - } - String url = getHttpUrl(request, headers); - if (!url.isEmpty()) { - span.setAttribute(HTTP_URL, url); - } - } - - private static String getHttpUrl( - APIGatewayProxyRequestEvent request, Map headers) { - StringBuilder str = new StringBuilder(); - - String scheme = headers.get("x-forwarded-proto"); - if (scheme != null) { - str.append(scheme).append("://"); - } - String host = headers.get("host"); - if (host != null) { - str.append(host); - } - String path = request.getPath(); - if (path != null) { - str.append(path); - } - - try { - boolean first = true; - for (Map.Entry entry : - emptyIfNull(request.getQueryStringParameters()).entrySet()) { - String key = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()); - String value = URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()); - str.append(first ? '?' : '&').append(key).append('=').append(value); - first = false; - } - } catch (UnsupportedEncodingException ignored) { - // Ignore - } - return str.toString(); - } - - static void onResponse(Span span, APIGatewayProxyResponseEvent response) { - Integer statusCode = response.getStatusCode(); - if (statusCode != null) { - span.setAttribute(HTTP_STATUS_CODE, statusCode); - } - } - - private HttpSpanAttributes() {} -} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestHandler.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestHandler.java index 438b8b44e2..ece1709d1d 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestHandler.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestHandler.java @@ -8,8 +8,9 @@ package io.opentelemetry.instrumentation.awslambda.v1_0; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestHandler; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; -import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenter; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenterFactory; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.time.Duration; import java.util.Collections; @@ -25,7 +26,7 @@ public abstract class TracingRequestHandler implements RequestHandler implements RequestHandler getHeaders(I input) { + Map result = null; if (input instanceof APIGatewayProxyRequestEvent) { APIGatewayProxyRequestEvent event = (APIGatewayProxyRequestEvent) input; - return event.getHeaders(); + result = event.getHeaders(); } - return Collections.emptyMap(); + return result == null ? Collections.emptyMap() : result; } @Override public final O handleRequest(I input, Context context) { - io.opentelemetry.context.Context otelContext = - tracer.startSpan(context, SpanKind.SERVER, input, getHeaders(input)); + AwsLambdaRequest request = AwsLambdaRequest.create(context, input, getHeaders(input)); + io.opentelemetry.context.Context parentContext = instrumenter.extract(request); + + if (!instrumenter.shouldStart(parentContext, request)) { + return doHandleRequest(input, context); + } + + io.opentelemetry.context.Context otelContext = instrumenter.start(parentContext, request); Throwable error = null; + O output = null; try (Scope ignored = otelContext.makeCurrent()) { - O output = doHandleRequest(input, context); - tracer.onOutput(otelContext, output); + output = doHandleRequest(input, context); return output; } catch (Throwable t) { error = t; throw t; } finally { - if (error != null) { - tracer.endExceptionally(otelContext, error); - } else { - tracer.end(otelContext); - } + instrumenter.end(otelContext, request, output, error); LambdaUtils.forceFlush(openTelemetrySdk, flushTimeoutNanos, TimeUnit.NANOSECONDS); } } diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamHandler.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamHandler.java index 95a327902c..e3fbd9897c 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamHandler.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingRequestStreamHandler.java @@ -7,8 +7,10 @@ package io.opentelemetry.instrumentation.awslambda.v1_0; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.RequestStreamHandler; -import io.opentelemetry.api.trace.SpanKind; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.ApiGatewayProxyRequest; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenter; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenterFactory; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.io.IOException; import java.io.InputStream; @@ -26,7 +28,7 @@ public abstract class TracingRequestStreamHandler implements RequestStreamHandle private final OpenTelemetrySdk openTelemetrySdk; private final long flushTimeoutNanos; - private final AwsLambdaTracer tracer; + private final AwsLambdaFunctionInstrumenter instrumenter; /** * Creates a new {@link TracingRequestStreamHandler} which traces using the provided {@link @@ -42,19 +44,24 @@ public abstract class TracingRequestStreamHandler implements RequestStreamHandle * invocation. */ protected TracingRequestStreamHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { - this(openTelemetrySdk, flushTimeout, new AwsLambdaTracer(openTelemetrySdk)); + this( + openTelemetrySdk, + flushTimeout, + AwsLambdaFunctionInstrumenterFactory.createInstrumenter(openTelemetrySdk)); } /** * Creates a new {@link TracingRequestStreamHandler} 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 AwsLambdaTracer}. + * invocation, and traces using the provided {@link AwsLambdaFunctionInstrumenter}. */ protected TracingRequestStreamHandler( - OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout, AwsLambdaTracer tracer) { + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + AwsLambdaFunctionInstrumenter instrumenter) { this.openTelemetrySdk = openTelemetrySdk; this.flushTimeoutNanos = flushTimeout.toNanos(); - this.tracer = tracer; + this.instrumenter = instrumenter; } @Override @@ -62,16 +69,23 @@ public abstract class TracingRequestStreamHandler implements RequestStreamHandle throws IOException { ApiGatewayProxyRequest proxyRequest = ApiGatewayProxyRequest.forStream(input); - io.opentelemetry.context.Context otelContext = - tracer.startSpan(context, SpanKind.SERVER, input, proxyRequest.getHeaders()); + AwsLambdaRequest request = + AwsLambdaRequest.create(context, proxyRequest, proxyRequest.getHeaders()); + io.opentelemetry.context.Context parentContext = instrumenter.extract(request); + if (!instrumenter.shouldStart(parentContext, request)) { + doHandleRequest(proxyRequest.freshStream(), output, context); + return; + } + + io.opentelemetry.context.Context otelContext = instrumenter.start(parentContext, request); try (Scope ignored = otelContext.makeCurrent()) { doHandleRequest( proxyRequest.freshStream(), - new OutputStreamWrapper(output, otelContext, openTelemetrySdk), + new OutputStreamWrapper(output, otelContext, request, openTelemetrySdk), context); } catch (Throwable t) { - tracer.endExceptionally(otelContext, t); + instrumenter.end(otelContext, request, null, t); LambdaUtils.forceFlush(openTelemetrySdk, flushTimeoutNanos, TimeUnit.NANOSECONDS); throw t; } @@ -84,14 +98,17 @@ public abstract class TracingRequestStreamHandler implements RequestStreamHandle private final OutputStream delegate; private final io.opentelemetry.context.Context otelContext; + private final AwsLambdaRequest request; private final OpenTelemetrySdk openTelemetrySdk; private OutputStreamWrapper( OutputStream delegate, io.opentelemetry.context.Context otelContext, + AwsLambdaRequest request, OpenTelemetrySdk openTelemetrySdk) { this.delegate = delegate; this.otelContext = otelContext; + this.request = request; this.openTelemetrySdk = openTelemetrySdk; } @@ -118,7 +135,7 @@ public abstract class TracingRequestStreamHandler implements RequestStreamHandle @Override public void close() throws IOException { delegate.close(); - tracer.end(otelContext); + instrumenter.end(otelContext, request, null, null); LambdaUtils.forceFlush(openTelemetrySdk, flushTimeoutNanos, TimeUnit.NANOSECONDS); } } diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventHandler.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventHandler.java index 7d85ff56e0..dc14b66f45 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventHandler.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/TracingSqsEventHandler.java @@ -8,12 +8,15 @@ package io.opentelemetry.instrumentation.awslambda.v1_0; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaFunctionInstrumenter; +import io.opentelemetry.instrumentation.awslambda.v1_0.internal.AwsLambdaSqsInstrumenterFactory; import io.opentelemetry.sdk.OpenTelemetrySdk; import java.time.Duration; public abstract class TracingSqsEventHandler extends TracingRequestHandler { - private final AwsLambdaMessageTracer tracer; + private final Instrumenter instrumenter; /** * Creates a new {@link TracingSqsEventHandler} which traces using the provided {@link @@ -29,36 +32,39 @@ public abstract class TracingSqsEventHandler extends TracingRequestHandler instrumenter) { super(openTelemetrySdk, flushTimeout); - this.tracer = tracer; + this.instrumenter = instrumenter; } @Override public Void doHandleRequest(SQSEvent event, Context context) { io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current(); - io.opentelemetry.context.Context otelContext = tracer.startSpan(parentContext, event); - Throwable error = null; - try (Scope ignored = otelContext.makeCurrent()) { - handleEvent(event, context); - } catch (Throwable t) { - error = t; - throw t; - } finally { - if (error != null) { - tracer.endExceptionally(otelContext, error); - } else { - tracer.end(otelContext); + if (instrumenter.shouldStart(parentContext, event)) { + io.opentelemetry.context.Context otelContext = instrumenter.start(parentContext, event); + Throwable error = null; + try (Scope ignored = otelContext.makeCurrent()) { + handleEvent(event, context); + } catch (Throwable t) { + error = t; + throw t; + } finally { + instrumenter.end(otelContext, event, null, error); } + } else { + handleEvent(event, context); } return null; } @@ -68,9 +74,4 @@ public abstract class TracingSqsEventHandler extends TracingRequestHandler 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) { - super(openTelemetrySdk); + this(openTelemetrySdk, DEFAULT_FLUSH_TIMEOUT); } /** @@ -28,36 +32,60 @@ public abstract class TracingSqsMessageHandler extends TracingSqsEventHandler { * invocation. */ protected TracingSqsMessageHandler(OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout) { - super(openTelemetrySdk, flushTimeout); + this( + openTelemetrySdk, flushTimeout, AwsLambdaSqsInstrumenterFactory.forEvent(openTelemetrySdk)); } /** * 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 {@link AwsLambdaTracer}. + * invocation, and instruments {@link SQSEvent} using the provided {@code Instrumenter}. */ protected TracingSqsMessageHandler( - OpenTelemetrySdk openTelemetrySdk, Duration flushTimeout, AwsLambdaMessageTracer tracer) { - super(openTelemetrySdk, flushTimeout, tracer); + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter eventInstrumenter) { + this( + openTelemetrySdk, + flushTimeout, + eventInstrumenter, + AwsLambdaSqsInstrumenterFactory.forMessage(openTelemetrySdk)); + } + + /** + * Creates a new {@link TracingSqsMessageHandler} which flushes the provided {@link + * OpenTelemetrySdk}, has a timeout of {@code flushTimeout} when flushing at the end of an + * invocation, and traces using the provided {@code Instrumenter} and {@code + * Instrumenter}. + */ + protected TracingSqsMessageHandler( + OpenTelemetrySdk openTelemetrySdk, + Duration flushTimeout, + Instrumenter eventInstrumenter, + Instrumenter messageInstrumenter) { + super(openTelemetrySdk, flushTimeout, eventInstrumenter); + this.messageInstrumenter = messageInstrumenter; } @Override protected final void handleEvent(SQSEvent event, Context context) { io.opentelemetry.context.Context parentContext = io.opentelemetry.context.Context.current(); for (SQSMessage message : event.getRecords()) { - io.opentelemetry.context.Context otelContext = getTracer().startSpan(parentContext, message); - Throwable error = null; - try (Scope ignored = otelContext.makeCurrent()) { - handleMessage(message, context); - } catch (Throwable t) { - error = t; - throw t; - } finally { - if (error != null) { - getTracer().endExceptionally(otelContext, error); - } else { - getTracer().end(otelContext); + 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); + } catch (Throwable t) { + error = t; + throw t; + } finally { + messageInstrumenter.end(otelContext, message, null, error); } + } else { + handleMessage(message, context); } } } diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyAttributesExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyAttributesExtractor.java new file mode 100644 index 0000000000..0aa84ab68b --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyAttributesExtractor.java @@ -0,0 +1,93 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import static io.opentelemetry.instrumentation.awslambda.v1_0.internal.MapUtils.emptyIfNull; +import static io.opentelemetry.instrumentation.awslambda.v1_0.internal.MapUtils.lowercaseMap; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_TRIGGER; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_METHOD; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_STATUS_CODE; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_USER_AGENT; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaRequest; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; + +final class ApiGatewayProxyAttributesExtractor + implements AttributesExtractor { + @Override + public void onStart(AttributesBuilder attributes, AwsLambdaRequest request) { + if (request.getInput() instanceof APIGatewayProxyRequestEvent) { + set(attributes, FAAS_TRIGGER, SemanticAttributes.FaasTriggerValues.HTTP); + onRequest(attributes, (APIGatewayProxyRequestEvent) request.getInput()); + } + } + + void onRequest(AttributesBuilder attributes, APIGatewayProxyRequestEvent request) { + set(attributes, HTTP_METHOD, request.getHttpMethod()); + + Map headers = lowercaseMap(request.getHeaders()); + set(attributes, HTTP_USER_AGENT, headers.get("user-agent")); + set(attributes, HTTP_URL, getHttpUrl(request, headers)); + } + + private static String getHttpUrl( + APIGatewayProxyRequestEvent request, Map headers) { + StringBuilder str = new StringBuilder(); + + String scheme = headers.get("x-forwarded-proto"); + if (scheme != null) { + str.append(scheme).append("://"); + } + String host = headers.get("host"); + if (host != null) { + str.append(host); + } + String path = request.getPath(); + if (path != null) { + str.append(path); + } + + try { + boolean first = true; + for (Map.Entry entry : + emptyIfNull(request.getQueryStringParameters()).entrySet()) { + String key = URLEncoder.encode(entry.getKey(), StandardCharsets.UTF_8.name()); + String value = URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8.name()); + str.append(first ? '?' : '&').append(key).append('=').append(value); + first = false; + } + } catch (UnsupportedEncodingException ignored) { + // Ignore + } + return str.length() == 0 ? null : str.toString(); + } + + @Override + public void onEnd( + AttributesBuilder attributes, + AwsLambdaRequest request, + @Nullable Object response, + @Nullable Throwable error) { + if (response instanceof APIGatewayProxyResponseEvent) { + Integer statusCode = ((APIGatewayProxyResponseEvent) response).getStatusCode(); + if (statusCode != null) { + attributes.put(HTTP_STATUS_CODE, statusCode); + } + } + } + + ApiGatewayProxyAttributesExtractor() {} +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/ApiGatewayProxyRequest.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyRequest.java similarity index 80% rename from instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/ApiGatewayProxyRequest.java rename to instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyRequest.java index cb54a38104..c2cb1df317 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/ApiGatewayProxyRequest.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyRequest.java @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; -import static io.opentelemetry.instrumentation.awslambda.v1_0.HeadersFactory.ofStream; +import static io.opentelemetry.instrumentation.awslambda.v1_0.internal.HeadersFactory.ofStream; import io.opentelemetry.api.GlobalOpenTelemetry; import java.io.ByteArrayInputStream; @@ -17,7 +17,7 @@ import java.util.Map; import org.apache.commons.io.IOUtils; import org.checkerframework.checker.nullness.qual.Nullable; -abstract class ApiGatewayProxyRequest { +public abstract class ApiGatewayProxyRequest { // TODO(anuraaga): We should create a RequestFactory type of class instead of evaluating this // for every request. @@ -34,7 +34,7 @@ abstract class ApiGatewayProxyRequest { fields.iterator().next()); } - static ApiGatewayProxyRequest forStream(InputStream source) throws IOException { + public static ApiGatewayProxyRequest forStream(InputStream source) throws IOException { if (noHttpPropagationNeeded()) { return new NoopRequest(source); @@ -48,12 +48,12 @@ abstract class ApiGatewayProxyRequest { } @Nullable - Map getHeaders() throws IOException { + public Map getHeaders() throws IOException { Map headers = ofStream(freshStream()); return (headers == null ? Collections.emptyMap() : headers); } - abstract InputStream freshStream() throws IOException; + public abstract InputStream freshStream() throws IOException; private static class NoopRequest extends ApiGatewayProxyRequest { @@ -64,12 +64,12 @@ abstract class ApiGatewayProxyRequest { } @Override - InputStream freshStream() { + public InputStream freshStream() { return stream; } @Override - Map getHeaders() { + public Map getHeaders() { return Collections.emptyMap(); } } @@ -84,7 +84,7 @@ abstract class ApiGatewayProxyRequest { } @Override - InputStream freshStream() throws IOException { + public InputStream freshStream() throws IOException { inputStream.reset(); inputStream.mark(Integer.MAX_VALUE); @@ -101,7 +101,7 @@ abstract class ApiGatewayProxyRequest { } @Override - InputStream freshStream() { + public InputStream freshStream() { return new ByteArrayInputStream(data); } } diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionAttributesExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionAttributesExtractor.java new file mode 100644 index 0000000000..8f0ae2290e --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionAttributesExtractor.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.CLOUD_ACCOUNT_ID; +import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.FAAS_ID; +import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.FAAS_EXECUTION; + +import com.amazonaws.services.lambda.runtime.Context; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaRequest; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; +import org.checkerframework.checker.nullness.qual.Nullable; + +class AwsLambdaFunctionAttributesExtractor + implements AttributesExtractor { + + @Nullable private static final MethodHandle GET_FUNCTION_ARN; + + static { + MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + MethodHandle getFunctionArn; + try { + getFunctionArn = + lookup.findVirtual( + Context.class, "getInvokedFunctionArn", MethodType.methodType(String.class)); + } catch (NoSuchMethodException | IllegalAccessException e) { + getFunctionArn = null; + } + GET_FUNCTION_ARN = getFunctionArn; + } + + // cached accountId value + private volatile String accountId; + + @Override + public void onStart(AttributesBuilder attributes, AwsLambdaRequest request) { + Context context = request.getAwsContext(); + set(attributes, FAAS_EXECUTION, context.getAwsRequestId()); + set(attributes, FAAS_ID, getFunctionArn(context)); + set(attributes, CLOUD_ACCOUNT_ID, getAccountId(getFunctionArn(context))); + } + + @Override + public void onEnd( + AttributesBuilder attributes, + AwsLambdaRequest request, + @Nullable Object response, + @Nullable Throwable error) {} + + @Nullable + private static String getFunctionArn(Context context) { + if (GET_FUNCTION_ARN == null) { + return null; + } + try { + return (String) GET_FUNCTION_ARN.invoke(context); + } catch (Throwable throwable) { + return null; + } + } + + @Nullable + private String getAccountId(@Nullable String arn) { + if (arn == null) { + return null; + } + if (accountId == null) { + synchronized (this) { + if (accountId == null) { + String[] arnParts = arn.split(":"); + if (arnParts.length >= 5) { + accountId = arnParts[4]; + } + } + } + } + return accountId; + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenter.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenter.java new file mode 100644 index 0000000000..81e4dd0c80 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenter.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.propagation.TextMapGetter; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.internal.ContextPropagationDebug; +import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaRequest; +import java.util.Map; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class AwsLambdaFunctionInstrumenter { + + private final OpenTelemetry openTelemetry; + final Instrumenter instrumenter; + + AwsLambdaFunctionInstrumenter( + OpenTelemetry openTelemetry, Instrumenter instrumenter) { + this.openTelemetry = openTelemetry; + this.instrumenter = instrumenter; + } + + public boolean shouldStart(Context parentContext, AwsLambdaRequest input) { + return instrumenter.shouldStart(parentContext, input); + } + + public Context start(Context parentContext, AwsLambdaRequest input) { + return instrumenter.start(parentContext, input); + } + + public void end( + Context context, + AwsLambdaRequest input, + @Nullable Object response, + @Nullable Throwable error) { + instrumenter.end(context, input, response, error); + } + + public Context extract(AwsLambdaRequest input) { + return ParentContextExtractor.extract(input.getHeaders(), this); + } + + public Context extract(Map headers, TextMapGetter> getter) { + ContextPropagationDebug.debugContextLeakIfEnabled(); + + return openTelemetry + .getPropagators() + .getTextMapPropagator() + .extract(Context.root(), headers, getter); + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java new file mode 100644 index 0000000000..dcb82f934b --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java @@ -0,0 +1,36 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; +import io.opentelemetry.instrumentation.awslambda.v1_0.AwsLambdaRequest; + +public class AwsLambdaFunctionInstrumenterFactory { + + public static AwsLambdaFunctionInstrumenter createInstrumenter(OpenTelemetry openTelemetry) { + return new AwsLambdaFunctionInstrumenter( + openTelemetry, + Instrumenter.newBuilder( + openTelemetry, + "io.opentelemetry.aws-lambda-1.0", + AwsLambdaFunctionInstrumenterFactory::spanName) + .addAttributesExtractors( + new AwsLambdaFunctionAttributesExtractor(), + new ApiGatewayProxyAttributesExtractor()) + .newInstrumenter(SpanKindExtractor.alwaysServer())); + } + + private static String spanName(AwsLambdaRequest input) { + String name = null; + if (input.getInput() instanceof APIGatewayProxyRequestEvent) { + name = ((APIGatewayProxyRequestEvent) input.getInput()).getResource(); + } + return name == null ? input.getAwsContext().getFunctionName() : name; + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaSqsInstrumenterFactory.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaSqsInstrumenterFactory.java new file mode 100644 index 0000000000..3d933b22f2 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/AwsLambdaSqsInstrumenterFactory.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import io.opentelemetry.api.OpenTelemetry; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor; + +public class AwsLambdaSqsInstrumenterFactory { + + public static Instrumenter forEvent(OpenTelemetry openTelemetry) { + return Instrumenter.newBuilder( + openTelemetry, + "io.opentelemetry.aws-lambda-1.0", + AwsLambdaSqsInstrumenterFactory::spanName) + .addAttributesExtractors(new SqsEventAttributesExtractor()) + .addSpanLinksExtractor(new SqsEventSpanLinksExtractor()) + .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + } + + public static Instrumenter forMessage(OpenTelemetry openTelemetry) { + return Instrumenter.newBuilder( + openTelemetry, + "io.opentelemetry.aws-lambda-1.0", + message -> message.getEventSource() + " process") + .addAttributesExtractors(new SqsMessageAttributesExtractor()) + .addSpanLinksExtractor(new SqsMessageSpanLinksExtractor()) + .newInstrumenter(SpanKindExtractor.alwaysConsumer()); + } + + private static String spanName(SQSEvent event) { + String source = "multiple_sources"; + if (!event.getRecords().isEmpty()) { + String messageSource = event.getRecords().get(0).getEventSource(); + for (int i = 1; i < event.getRecords().size(); i++) { + SQSMessage message = event.getRecords().get(i); + if (!message.getEventSource().equals(messageSource)) { + messageSource = null; + break; + } + } + if (messageSource != null) { + source = messageSource; + } + } + + return source + " process"; + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HeadersFactory.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/HeadersFactory.java similarity index 94% rename from instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HeadersFactory.java rename to instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/HeadersFactory.java index df1144b10b..56edecac42 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/HeadersFactory.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/HeadersFactory.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/MapUtils.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/MapUtils.java similarity index 90% rename from instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/MapUtils.java rename to instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/MapUtils.java index 190746d5e0..3258d92142 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/MapUtils.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/MapUtils.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; import java.util.Collections; import java.util.Locale; diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/ParentContextExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ParentContextExtractor.java similarity index 78% rename from instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/ParentContextExtractor.java rename to instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ParentContextExtractor.java index 9e9f18058b..d040dfd058 100644 --- a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/ParentContextExtractor.java +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ParentContextExtractor.java @@ -3,25 +3,24 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; -import static io.opentelemetry.instrumentation.awslambda.v1_0.MapUtils.lowercaseMap; +import static io.opentelemetry.instrumentation.awslambda.v1_0.internal.MapUtils.lowercaseMap; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapGetter; import io.opentelemetry.extension.aws.AwsXrayPropagator; -import io.opentelemetry.instrumentation.api.tracer.BaseTracer; import java.util.Collections; import java.util.Locale; import java.util.Map; -public final class ParentContextExtractor { +final class ParentContextExtractor { private static final String AWS_TRACE_HEADER_ENV_KEY = "_X_AMZN_TRACE_ID"; - static Context extract(Map headers, BaseTracer tracer) { + static Context extract(Map headers, AwsLambdaFunctionInstrumenter instrumenter) { Context parentContext = null; String parentTraceHeader = System.getenv(AWS_TRACE_HEADER_ENV_KEY); if (parentTraceHeader != null) { @@ -29,7 +28,7 @@ public final class ParentContextExtractor { } if (!isValidAndSampled(parentContext)) { // try http - parentContext = fromHttpHeaders(headers, tracer); + parentContext = fromHttpHeaders(headers, instrumenter); } return parentContext; } @@ -43,8 +42,9 @@ public final class ParentContextExtractor { return (parentSpanContext.isValid() && parentSpanContext.isSampled()); } - private static Context fromHttpHeaders(Map headers, BaseTracer tracer) { - return tracer.extract(lowercaseMap(headers), MapGetter.INSTANCE); + private static Context fromHttpHeaders( + Map headers, AwsLambdaFunctionInstrumenter instrumenter) { + return instrumenter.extract(lowercaseMap(headers), MapGetter.INSTANCE); } // lower-case map getter used for extraction diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventAttributesExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventAttributesExtractor.java new file mode 100644 index 0000000000..6a35989b8e --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventAttributesExtractor.java @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import org.checkerframework.checker.nullness.qual.Nullable; + +class SqsEventAttributesExtractor implements AttributesExtractor { + @Override + public void onStart(AttributesBuilder attributes, SQSEvent event) { + attributes.put(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"); + attributes.put(SemanticAttributes.MESSAGING_OPERATION, "process"); + } + + @Override + public void onEnd( + AttributesBuilder attributes, + SQSEvent event, + @Nullable Void unused, + @Nullable Throwable error) {} +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventSpanLinksExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventSpanLinksExtractor.java new file mode 100644 index 0000000000..c6611cc243 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsEventSpanLinksExtractor.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; + +class SqsEventSpanLinksExtractor implements SpanLinksExtractor { + private static final SqsMessageSpanLinksExtractor messageSpanLinksExtractor = + new SqsMessageSpanLinksExtractor(); + + @Override + public void extract(SpanLinksBuilder spanLinks, Context parentContext, SQSEvent event) { + for (SQSEvent.SQSMessage message : event.getRecords()) { + messageSpanLinksExtractor.extract(spanLinks, parentContext, message); + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageAttributesExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageAttributesExtractor.java new file mode 100644 index 0000000000..40feb1ba36 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageAttributesExtractor.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import io.opentelemetry.api.common.AttributesBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; +import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; +import org.checkerframework.checker.nullness.qual.Nullable; + +class SqsMessageAttributesExtractor implements AttributesExtractor { + @Override + public void onStart(AttributesBuilder attributes, SQSMessage message) { + attributes.put(SemanticAttributes.MESSAGING_SYSTEM, "AmazonSQS"); + attributes.put(SemanticAttributes.MESSAGING_OPERATION, "process"); + attributes.put(SemanticAttributes.MESSAGING_MESSAGE_ID, message.getMessageId()); + attributes.put(SemanticAttributes.MESSAGING_DESTINATION, message.getEventSource()); + } + + @Override + public void onEnd( + AttributesBuilder attributes, + SQSMessage message, + @Nullable Void unused, + @Nullable Throwable error) {} +} diff --git a/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageSpanLinksExtractor.java b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageSpanLinksExtractor.java new file mode 100644 index 0000000000..21202c8989 --- /dev/null +++ b/instrumentation/aws-lambda-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/SqsMessageSpanLinksExtractor.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; + +import com.amazonaws.services.lambda.runtime.events.SQSEvent.SQSMessage; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; + +class SqsMessageSpanLinksExtractor implements SpanLinksExtractor { + private static final String AWS_TRACE_HEADER_SQS_ATTRIBUTE_KEY = "AWSTraceHeader"; + + @Override + public void extract(SpanLinksBuilder spanLinks, Context parentContext, SQSMessage message) { + String parentHeader = message.getAttributes().get(AWS_TRACE_HEADER_SQS_ATTRIBUTE_KEY); + if (parentHeader != null) { + SpanContext parentCtx = + Span.fromContext(ParentContextExtractor.fromXrayHeader(parentHeader)).getSpanContext(); + if (parentCtx.isValid()) { + spanLinks.addLink(parentCtx); + } + } + } +} diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/ApiGatewayProxyRequestTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyRequestTest.java similarity index 97% rename from instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/ApiGatewayProxyRequestTest.java rename to instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyRequestTest.java index ceb0afe876..ddb403dce4 100644 --- a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/ApiGatewayProxyRequestTest.java +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ApiGatewayProxyRequestTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/HeadersFactoryTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/HeadersFactoryTest.java similarity index 95% rename from instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/HeadersFactoryTest.java rename to instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/HeadersFactoryTest.java index a003177632..28a66357ac 100644 --- a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/HeadersFactoryTest.java +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/HeadersFactoryTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; diff --git a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/ParentContextExtractorTest.java b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ParentContextExtractorTest.java similarity index 88% rename from instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/ParentContextExtractorTest.java rename to instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ParentContextExtractorTest.java index 32663fecc0..1340c45378 100644 --- a/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/ParentContextExtractorTest.java +++ b/instrumentation/aws-lambda-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambda/v1_0/internal/ParentContextExtractorTest.java @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package io.opentelemetry.instrumentation.awslambda.v1_0; +package io.opentelemetry.instrumentation.awslambda.v1_0.internal; import static org.assertj.core.api.Assertions.assertThat; @@ -30,7 +30,8 @@ public class ParentContextExtractorTest { private static final OpenTelemetry OTEL = OpenTelemetry.propagating(ContextPropagators.create(B3Propagator.injectingSingleHeader())); - private static final AwsLambdaTracer TRACER = new AwsLambdaTracer(OTEL); + private static final AwsLambdaFunctionInstrumenter INSTRUMENTER = + AwsLambdaFunctionInstrumenterFactory.createInstrumenter(OTEL); @Test public void shouldUseHttpIfAwsParentNotSampled() { @@ -48,7 +49,7 @@ public class ParentContextExtractorTest { "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=0"); // when - Context context = ParentContextExtractor.extract(headers, TRACER); + Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); // then Span span = Span.fromContext(context); SpanContext spanContext = span.getSpanContext(); @@ -74,7 +75,7 @@ public class ParentContextExtractorTest { "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=1"); // when - Context context = ParentContextExtractor.extract(headers, TRACER); + Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); // then Span span = Span.fromContext(context); SpanContext spanContext = span.getSpanContext(); @@ -97,7 +98,7 @@ public class ParentContextExtractorTest { "true"); // when - Context context = ParentContextExtractor.extract(headers, TRACER); + Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); // then Span span = Span.fromContext(context); SpanContext spanContext = span.getSpanContext();