diff --git a/lambda-layer/.gitignore b/lambda-layer/.gitignore new file mode 100644 index 0000000000..1b6985c009 --- /dev/null +++ b/lambda-layer/.gitignore @@ -0,0 +1,5 @@ +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build diff --git a/lambda-layer/build-layer.sh b/lambda-layer/build-layer.sh new file mode 100755 index 0000000000..b99fce80c5 --- /dev/null +++ b/lambda-layer/build-layer.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +SOURCEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" + + +## Get OTel version +file="$SOURCEDIR/../.github/patches/versions" +version=$(awk -F'=v' '/OTEL_JAVA_INSTRUMENTATION_VERSION/ {print $2}' "$file") +echo "Found OTEL Version: ${version}" +# Exit if the version is empty or null +if [[ -z "$version" ]]; then + echo "Error: Version could not be found in ${file}." + exit 1 +fi + + +## Clone and Patch the OpenTelemetry Java Instrumentation Repository +git clone https://github.com/open-telemetry/opentelemetry-java-instrumentation.git +pushd opentelemetry-java-instrumentation +git checkout v${version} -b tag-v${version} + +# This patch is for Lambda related context propagation +patch -p1 < "$SOURCEDIR"/patches/opentelemetry-java-instrumentation.patch + +# There is another patch in the .github/patches directory for other changes. We should apply them too for consistency. +patch -p1 < "$SOURCEDIR"/../.github/patches/opentelemetry-java-instrumentation.patch + +git add -A +git commit -m "Create patch version" +./gradlew publishToMavenLocal +popd +rm -rf opentelemetry-java-instrumentation + + +## Build the ADOT Java from current source +pushd "$SOURCEDIR"/.. +patch -p1 < "${SOURCEDIR}"/patches/aws-otel-java-instrumentation.patch +CI=false ./gradlew publishToMavenLocal -Prelease.version=${version}-adot-lambda1 +popd + + +## Build ADOT Lambda Java SDK Layer Code +./gradlew build + + +## Copy ADOT Java Agent downloaded using Gradle task and bundle it with the Lambda handler script +cp "$SOURCEDIR"/build/javaagent/aws-opentelemetry-agent*.jar ./opentelemetry-javaagent.jar +zip -qr opentelemetry-javaagent-layer.zip opentelemetry-javaagent.jar otel-handler diff --git a/lambda-layer/build.gradle.kts b/lambda-layer/build.gradle.kts new file mode 100644 index 0000000000..92da026df2 --- /dev/null +++ b/lambda-layer/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + java + + id("com.diffplug.spotless") +} + +base.archivesBaseName = "aws-otel-lambda-java-extensions" +group = "software.amazon.opentelemetry.lambda" + +repositories { + mavenCentral() + mavenLocal() +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +spotless { + java { + googleJavaFormat("1.15.0") + } +} + +val javaagentDependency by configurations.creating { + extendsFrom() +} + +dependencies { + compileOnly(platform("io.opentelemetry:opentelemetry-bom:1.32.1")) + compileOnly(platform("io.opentelemetry:opentelemetry-bom-alpha:1.32.1-alpha")) + // Already included in wrapper so compileOnly + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi") + compileOnly("io.opentelemetry:opentelemetry-sdk-extension-aws") + javaagentDependency("software.amazon.opentelemetry:aws-opentelemetry-agent:1.32.1-adot-lambda1") +} + +tasks.register("download") { + from(javaagentDependency) + into("$buildDir/javaagent") +} + +tasks.named("build") { + dependsOn("download") +} diff --git a/lambda-layer/gradle/wrapper/gradle-wrapper.jar b/lambda-layer/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..943f0cbfa7 Binary files /dev/null and b/lambda-layer/gradle/wrapper/gradle-wrapper.jar differ diff --git a/lambda-layer/gradle/wrapper/gradle-wrapper.properties b/lambda-layer/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..37aef8d3f0 --- /dev/null +++ b/lambda-layer/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +networkTimeout=10000 +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/lambda-layer/gradlew b/lambda-layer/gradlew new file mode 100755 index 0000000000..5bba57aa19 --- /dev/null +++ b/lambda-layer/gradlew @@ -0,0 +1,245 @@ + +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/lambda-layer/gradlew.bat b/lambda-layer/gradlew.bat new file mode 100644 index 0000000000..084f02df4a --- /dev/null +++ b/lambda-layer/gradlew.bat @@ -0,0 +1,93 @@ + +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lambda-layer/otel-handler b/lambda-layer/otel-handler new file mode 100644 index 0000000000..9104e2a7b2 --- /dev/null +++ b/lambda-layer/otel-handler @@ -0,0 +1,24 @@ +#!/bin/bash + +export OTEL_INSTRUMENTATION_AWS_SDK_EXPERIMENTAL_SPAN_ATTRIBUTES=true + +export OTEL_PROPAGATORS="${OTEL_PROPAGATORS:-xray,tracecontext,b3,b3multi}" + +# Temporarily set OTEL_SERVICE_NAME variable to work around but in javaagent not handling +# OTEL_RESOURCE_ATTRIBUTES as set in otel-handler-upstream. It doesn't hurt to apply this +# to wrapper as well. +# TODO(anuraaga): Move to opentelemetry-lambda +export OTEL_SERVICE_NAME=${OTEL_SERVICE_NAME:-${AWS_LAMBDA_FUNCTION_NAME}} + +export JAVA_TOOL_OPTIONS="-javaagent:/opt/opentelemetry-javaagent.jar ${JAVA_TOOL_OPTIONS}" + +if [[ $OTEL_RESOURCE_ATTRIBUTES != *"service.name="* ]]; then + export OTEL_RESOURCE_ATTRIBUTES="service.name=${AWS_LAMBDA_FUNCTION_NAME},${OTEL_RESOURCE_ATTRIBUTES}" +fi + +export OTEL_INSTRUMENTATION_AWS_LAMBDA_FLUSH_TIMEOUT=10000 + +# Disable the Application Signals runtime metrics since we are on Lambda +export OTEL_AWS_APPLICATION_SIGNALS_RUNTIME_ENABLED=false + +exec "$@" diff --git a/lambda-layer/patches/aws-otel-java-instrumentation.patch b/lambda-layer/patches/aws-otel-java-instrumentation.patch new file mode 100644 index 0000000000..0a65783bfb --- /dev/null +++ b/lambda-layer/patches/aws-otel-java-instrumentation.patch @@ -0,0 +1,13 @@ +diff --git a/dependencyManagement/build.gradle.kts b/dependencyManagement/build.gradle.kts +index 9493189..6090207 100644 +--- a/dependencyManagement/build.gradle.kts ++++ b/dependencyManagement/build.gradle.kts +@@ -27,7 +27,7 @@ data class DependencySet(val group: String, val version: String, val modules: Li + val TEST_SNAPSHOTS = rootProject.findProperty("testUpstreamSnapshots") == "true" + + // This is the version of the upstream instrumentation BOM +-val otelVersion = "1.32.1-adot2" ++val otelVersion = "1.32.1-adot-lambda1" + val otelSnapshotVersion = "1.33.0" + val otelAlphaVersion = if (!TEST_SNAPSHOTS) "$otelVersion-alpha" else "$otelSnapshotVersion-alpha-SNAPSHOT" + val otelJavaAgentVersion = if (!TEST_SNAPSHOTS) otelVersion else "$otelSnapshotVersion-SNAPSHOT" \ No newline at end of file diff --git a/lambda-layer/patches/opentelemetry-java-instrumentation.patch b/lambda-layer/patches/opentelemetry-java-instrumentation.patch new file mode 100644 index 0000000000..4ccb33e9a2 --- /dev/null +++ b/lambda-layer/patches/opentelemetry-java-instrumentation.patch @@ -0,0 +1,645 @@ +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequest.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequest.java +index a96fa5e3f9..df5bcec438 100644 +--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequest.java ++++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ApiGatewayProxyRequest.java +@@ -30,7 +30,14 @@ public abstract class ApiGatewayProxyRequest { + private static boolean noHttpPropagationNeeded() { + Collection fields = + GlobalOpenTelemetry.getPropagators().getTextMapPropagator().fields(); +- return fields.isEmpty(); ++ return fields.isEmpty() || xrayPropagationFieldsOnly(fields); ++ } ++ ++ private static boolean xrayPropagationFieldsOnly(Collection fields) { ++ // ugly but faster than typical convert-to-set-and-check-contains-only ++ return (fields.size() == 1) ++ && ParentContextExtractor.AWS_TRACE_HEADER_PROPAGATOR_KEY.equalsIgnoreCase( ++ fields.iterator().next()); + } + + public static ApiGatewayProxyRequest forStream(InputStream source) { +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenter.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenter.java +index 4136e7bed9..dbbcb1c99d 100644 +--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenter.java ++++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenter.java +@@ -11,7 +11,6 @@ import io.opentelemetry.context.propagation.TextMapGetter; + import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; + import io.opentelemetry.instrumentation.api.internal.ContextPropagationDebug; + import io.opentelemetry.instrumentation.awslambdacore.v1_0.AwsLambdaRequest; +-import java.util.Locale; + import java.util.Map; + import javax.annotation.Nullable; + +@@ -47,25 +46,15 @@ public class AwsLambdaFunctionInstrumenter { + } + + 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(), input.getHeaders(), MapGetter.INSTANCE); +- } +- +- private enum MapGetter implements TextMapGetter> { +- INSTANCE; +- +- @Override +- public Iterable keys(Map map) { +- return map.keySet(); +- } +- +- @Override +- public String get(Map map, String s) { +- return map.get(s.toLowerCase(Locale.ROOT)); +- } ++ .extract(Context.root(), headers, getter); + } + } +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java +index aeb828b8e7..277c358ca8 100644 +--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java ++++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsLambdaFunctionInstrumenterFactory.java +@@ -23,7 +23,6 @@ public final class AwsLambdaFunctionInstrumenterFactory { + openTelemetry, + "io.opentelemetry.aws-lambda-core-1.0", + AwsLambdaFunctionInstrumenterFactory::spanName) +- .addSpanLinksExtractor(new AwsXrayEnvSpanLinksExtractor()) + .addAttributesExtractor(new AwsLambdaFunctionAttributesExtractor()) + .buildInstrumenter(SpanKindExtractor.alwaysServer())); + } +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsXrayEnvSpanLinksExtractor.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsXrayEnvSpanLinksExtractor.java +deleted file mode 100644 +index c88cf20c91..0000000000 +--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsXrayEnvSpanLinksExtractor.java ++++ /dev/null +@@ -1,84 +0,0 @@ +-/* +- * Copyright The OpenTelemetry Authors +- * SPDX-License-Identifier: Apache-2.0 +- */ +- +-package io.opentelemetry.instrumentation.awslambdacore.v1_0.internal; +- +-import io.opentelemetry.api.common.AttributeKey; +-import io.opentelemetry.api.common.Attributes; +-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.contrib.awsxray.propagator.AwsXrayPropagator; +-import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; +-import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; +-import io.opentelemetry.instrumentation.awslambdacore.v1_0.AwsLambdaRequest; +-import java.util.Collections; +-import java.util.Locale; +-import java.util.Map; +- +-/** +- * This class is internal and is hence not for public use. Its APIs are unstable and can change at +- * any time. +- */ +-final class AwsXrayEnvSpanLinksExtractor implements SpanLinksExtractor { +- +- private static final String AWS_TRACE_HEADER_ENV_KEY = "_X_AMZN_TRACE_ID"; +- private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader"; +- // lower-case map getter used for extraction +- private static final String AWS_TRACE_HEADER_PROPAGATOR_KEY = "x-amzn-trace-id"; +- +- private static final Attributes LINK_ATTRIBUTES = +- Attributes.of(AttributeKey.stringKey("source"), "x-ray-env"); +- +- @Override +- public void extract( +- SpanLinksBuilder spanLinks, +- io.opentelemetry.context.Context parentContext, +- AwsLambdaRequest awsLambdaRequest) { +- extract(spanLinks); +- } +- +- public static void extract(SpanLinksBuilder spanLinks) { +- Map contextMap = getTraceHeaderMap(); +- if (contextMap.isEmpty()) { +- return; +- } +- Context xrayContext = +- AwsXrayPropagator.getInstance().extract(Context.root(), contextMap, MapGetter.INSTANCE); +- SpanContext envVarSpanCtx = Span.fromContext(xrayContext).getSpanContext(); +- if (envVarSpanCtx.isValid()) { +- spanLinks.addLink(envVarSpanCtx, LINK_ATTRIBUTES); +- } +- } +- +- private static Map getTraceHeaderMap() { +- String traceHeader = System.getProperty(AWS_TRACE_HEADER_PROP); +- if (isEmptyOrNull(traceHeader)) { +- traceHeader = System.getenv(AWS_TRACE_HEADER_ENV_KEY); +- } +- return isEmptyOrNull(traceHeader) +- ? Collections.emptyMap() +- : Collections.singletonMap(AWS_TRACE_HEADER_PROPAGATOR_KEY, traceHeader); +- } +- +- private static boolean isEmptyOrNull(String value) { +- return value == null || value.isEmpty(); +- } +- +- private enum MapGetter implements TextMapGetter> { +- INSTANCE; +- +- @Override +- public Iterable keys(Map map) { +- return map.keySet(); +- } +- +- @Override +- public String get(Map map, String s) { +- return map.get(s.toLowerCase(Locale.ROOT)); +- } +- } +-} +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractor.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractor.java +new file mode 100644 +index 0000000000..72d4f9253b +--- /dev/null ++++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractor.java +@@ -0,0 +1,91 @@ ++/* ++ * Copyright The OpenTelemetry Authors ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++package io.opentelemetry.instrumentation.awslambdacore.v1_0.internal; ++ ++import static io.opentelemetry.instrumentation.awslambdacore.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.contrib.awsxray.propagator.AwsXrayPropagator; ++import java.util.Collections; ++import java.util.Locale; ++import java.util.Map; ++ ++/** ++ * This class is internal and is hence not for public use. Its APIs are unstable and can change at ++ * any time. ++ */ ++public final class ParentContextExtractor { ++ ++ private static final String AWS_TRACE_HEADER_ENV_KEY = "_X_AMZN_TRACE_ID"; ++ private static final String AWS_TRACE_HEADER_PROP = "com.amazonaws.xray.traceHeader"; ++ ++ static Context extract(Map headers, AwsLambdaFunctionInstrumenter instrumenter) { ++ Context parentContext = null; ++ String parentTraceHeader = getTraceHeader(); ++ if (parentTraceHeader != null) { ++ parentContext = fromXrayHeader(parentTraceHeader); ++ } ++ if (!isValidAndSampled(parentContext)) { ++ // try http ++ parentContext = fromHttpHeaders(headers, instrumenter); ++ } ++ return parentContext; ++ } ++ ++ private static String getTraceHeader() { ++ // Lambda propagates trace header by system property instead of environment variable from java17 ++ String traceHeader = System.getProperty(AWS_TRACE_HEADER_PROP); ++ if (traceHeader == null || traceHeader.isEmpty()) { ++ return System.getenv(AWS_TRACE_HEADER_ENV_KEY); ++ } ++ return traceHeader; ++ } ++ ++ private static boolean isValidAndSampled(Context context) { ++ if (context == null) { ++ return false; ++ } ++ Span parentSpan = Span.fromContext(context); ++ SpanContext parentSpanContext = parentSpan.getSpanContext(); ++ return (parentSpanContext.isValid() && parentSpanContext.isSampled()); ++ } ++ ++ private static Context fromHttpHeaders( ++ Map headers, AwsLambdaFunctionInstrumenter instrumenter) { ++ return instrumenter.extract(lowercaseMap(headers), MapGetter.INSTANCE); ++ } ++ ++ // lower-case map getter used for extraction ++ static final String AWS_TRACE_HEADER_PROPAGATOR_KEY = "x-amzn-trace-id"; ++ ++ public static Context fromXrayHeader(String parentHeader) { ++ return AwsXrayPropagator.getInstance() ++ .extract( ++ // see BaseTracer#extract() on why we're using root() here ++ Context.root(), ++ Collections.singletonMap(AWS_TRACE_HEADER_PROPAGATOR_KEY, parentHeader), ++ MapGetter.INSTANCE); ++ } ++ ++ private enum MapGetter implements TextMapGetter> { ++ INSTANCE; ++ ++ @Override ++ public Iterable keys(Map map) { ++ return map.keySet(); ++ } ++ ++ @Override ++ public String get(Map map, String s) { ++ return map.get(s.toLowerCase(Locale.ROOT)); ++ } ++ } ++ ++ private ParentContextExtractor() {} ++} +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsXrayEnvSpanLinksExtractorTest.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsXrayEnvSpanLinksExtractorTest.java +deleted file mode 100644 +index 509bfbd05e..0000000000 +--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/AwsXrayEnvSpanLinksExtractorTest.java ++++ /dev/null +@@ -1,128 +0,0 @@ +-/* +- * Copyright The OpenTelemetry Authors +- * SPDX-License-Identifier: Apache-2.0 +- */ +- +-package io.opentelemetry.instrumentation.awslambdacore.v1_0.internal; +- +-import static org.assertj.core.api.Assertions.assertThat; +-import static org.mockito.ArgumentMatchers.eq; +-import static org.mockito.Mockito.mock; +-import static org.mockito.Mockito.verify; +-import static org.mockito.Mockito.verifyNoInteractions; +- +-import io.opentelemetry.api.common.AttributeKey; +-import io.opentelemetry.api.common.Attributes; +-import io.opentelemetry.api.trace.SpanContext; +-import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; +-import org.junit.jupiter.api.Test; +-import org.junit.jupiter.api.extension.ExtendWith; +-import org.mockito.ArgumentCaptor; +-import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; +-import uk.org.webcompere.systemstubs.jupiter.SystemStub; +-import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; +-import uk.org.webcompere.systemstubs.properties.SystemProperties; +- +-/** +- * This class is internal and is hence not for public use. Its APIs are unstable and can change at +- * any time. +- */ +-@ExtendWith(SystemStubsExtension.class) +-class AwsXrayEnvSpanLinksExtractorTest { +- private static final Attributes EXPECTED_LINK_ATTRIBUTES = +- Attributes.of(AttributeKey.stringKey("source"), "x-ray-env"); +- +- @SystemStub final EnvironmentVariables environmentVariables = new EnvironmentVariables(); +- @SystemStub final SystemProperties systemProperties = new SystemProperties(); +- +- @Test +- void shouldIgnoreIfEnvVarAndSystemPropertyEmpty() { +- // given +- SpanLinksBuilder spanLinksBuilder = mock(SpanLinksBuilder.class); +- environmentVariables.set("_X_AMZN_TRACE_ID", ""); +- systemProperties.set("com.amazonaws.xray.traceHeader", ""); +- // when +- AwsXrayEnvSpanLinksExtractor.extract(spanLinksBuilder); +- // then +- verifyNoInteractions(spanLinksBuilder); +- } +- +- @Test +- void shouldLinkAwsParentHeaderAndChooseSystemPropertyIfValidAndNotSampled() { +- // given +- SpanLinksBuilder spanLinksBuilder = mock(SpanLinksBuilder.class); +- environmentVariables.set( +- "_X_AMZN_TRACE_ID", +- "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=0"); +- systemProperties.set( +- "com.amazonaws.xray.traceHeader", +- "Root=1-8a3c60f7-d188f8fa79d48a391a778fa7;Parent=0000000000000789;Sampled=0"); +- // when +- AwsXrayEnvSpanLinksExtractor.extract(spanLinksBuilder); +- // then +- ArgumentCaptor captor = ArgumentCaptor.forClass(SpanContext.class); +- verify(spanLinksBuilder).addLink(captor.capture(), eq(EXPECTED_LINK_ATTRIBUTES)); +- SpanContext spanContext = captor.getValue(); +- assertThat(spanContext.isValid()).isTrue(); +- assertThat(spanContext.isSampled()).isFalse(); +- assertThat(spanContext.getSpanId()).isEqualTo("0000000000000789"); +- assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa7"); +- } +- +- @Test +- void shouldLinkAwsParentHeaderIfValidAndNotSampled() { +- // given +- SpanLinksBuilder spanLinksBuilder = mock(SpanLinksBuilder.class); +- environmentVariables.set( +- "_X_AMZN_TRACE_ID", +- "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=0"); +- // when +- AwsXrayEnvSpanLinksExtractor.extract(spanLinksBuilder); +- // then +- ArgumentCaptor captor = ArgumentCaptor.forClass(SpanContext.class); +- verify(spanLinksBuilder).addLink(captor.capture(), eq(EXPECTED_LINK_ATTRIBUTES)); +- SpanContext spanContext = captor.getValue(); +- assertThat(spanContext.isValid()).isTrue(); +- assertThat(spanContext.isSampled()).isFalse(); +- assertThat(spanContext.getSpanId()).isEqualTo("0000000000000456"); +- assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa6"); +- } +- +- @Test +- void shouldLinkAwsParentHeaderIfValidAndNotSampledSystemProperty() { +- // given +- SpanLinksBuilder spanLinksBuilder = mock(SpanLinksBuilder.class); +- systemProperties.set( +- "com.amazonaws.xray.traceHeader", +- "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=0"); +- // when +- AwsXrayEnvSpanLinksExtractor.extract(spanLinksBuilder); +- // then +- ArgumentCaptor captor = ArgumentCaptor.forClass(SpanContext.class); +- verify(spanLinksBuilder).addLink(captor.capture(), eq(EXPECTED_LINK_ATTRIBUTES)); +- SpanContext spanContext = captor.getValue(); +- assertThat(spanContext.isValid()).isTrue(); +- assertThat(spanContext.isSampled()).isFalse(); +- assertThat(spanContext.getSpanId()).isEqualTo("0000000000000456"); +- assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa6"); +- } +- +- @Test +- void shouldLinkAwsParentHeaderIfValidAndSampledSystemProperty() { +- // given +- SpanLinksBuilder spanLinksBuilder = mock(SpanLinksBuilder.class); +- systemProperties.set( +- "com.amazonaws.xray.traceHeader", +- "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=1"); +- // when +- AwsXrayEnvSpanLinksExtractor.extract(spanLinksBuilder); +- // then +- ArgumentCaptor captor = ArgumentCaptor.forClass(SpanContext.class); +- verify(spanLinksBuilder).addLink(captor.capture(), eq(EXPECTED_LINK_ATTRIBUTES)); +- SpanContext spanContext = captor.getValue(); +- assertThat(spanContext.isValid()).isTrue(); +- assertThat(spanContext.isSampled()).isTrue(); +- assertThat(spanContext.getSpanId()).isEqualTo("0000000000000456"); +- assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa6"); +- } +-} +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java +new file mode 100644 +index 0000000000..1fa0b6e536 +--- /dev/null ++++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/library/src/test/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/internal/ParentContextExtractorTest.java +@@ -0,0 +1,135 @@ ++/* ++ * Copyright The OpenTelemetry Authors ++ * SPDX-License-Identifier: Apache-2.0 ++ */ ++ ++package io.opentelemetry.instrumentation.awslambdacore.v1_0.internal; ++ ++import static org.assertj.core.api.Assertions.assertThat; ++ ++import com.google.common.collect.ImmutableMap; ++import io.opentelemetry.api.OpenTelemetry; ++import io.opentelemetry.api.trace.Span; ++import io.opentelemetry.api.trace.SpanContext; ++import io.opentelemetry.context.Context; ++import io.opentelemetry.context.propagation.ContextPropagators; ++import io.opentelemetry.extension.trace.propagation.B3Propagator; ++import java.util.Map; ++import org.junit.jupiter.api.Test; ++import org.junit.jupiter.api.extension.ExtendWith; ++import uk.org.webcompere.systemstubs.environment.EnvironmentVariables; ++import uk.org.webcompere.systemstubs.jupiter.SystemStub; ++import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension; ++import uk.org.webcompere.systemstubs.properties.SystemProperties; ++ ++/** ++ * This class is internal and is hence not for public use. Its APIs are unstable and can change at ++ * any time. ++ */ ++@ExtendWith(SystemStubsExtension.class) ++class ParentContextExtractorTest { ++ ++ @SystemStub final EnvironmentVariables environmentVariables = new EnvironmentVariables(); ++ ++ private static final OpenTelemetry OTEL = ++ OpenTelemetry.propagating(ContextPropagators.create(B3Propagator.injectingSingleHeader())); ++ ++ private static final AwsLambdaFunctionInstrumenter INSTRUMENTER = ++ AwsLambdaFunctionInstrumenterFactory.createInstrumenter(OTEL); ++ ++ @Test ++ void shouldUseHttpIfAwsParentNotSampled() { ++ // given ++ Map headers = ++ ImmutableMap.of( ++ "X-b3-traceId", ++ "4fd0b6131f19f39af59518d127b0cafe", ++ "x-b3-spanid", ++ "0000000000000123", ++ "X-B3-Sampled", ++ "true"); ++ environmentVariables.set( ++ "_X_AMZN_TRACE_ID", ++ "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=0"); ++ ++ // when ++ Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); ++ // then ++ Span span = Span.fromContext(context); ++ SpanContext spanContext = span.getSpanContext(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.getSpanId()).isEqualTo("0000000000000123"); ++ assertThat(spanContext.getTraceId()).isEqualTo("4fd0b6131f19f39af59518d127b0cafe"); ++ } ++ ++ @Test ++ void shouldPreferAwsParentHeaderIfValidAndSampled() { ++ // given ++ Map headers = ++ ImmutableMap.of( ++ "X-b3-traceId", ++ "4fd0b6131f19f39af59518d127b0cafe", ++ "x-b3-spanid", ++ "0000000000000456", ++ "X-B3-Sampled", ++ "true"); ++ environmentVariables.set( ++ "_X_AMZN_TRACE_ID", ++ "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=1"); ++ ++ // when ++ Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); ++ // then ++ Span span = Span.fromContext(context); ++ SpanContext spanContext = span.getSpanContext(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.getSpanId()).isEqualTo("0000000000000456"); ++ assertThat(spanContext.getTraceId()).isEqualTo("8a3c60f7d188f8fa79d48a391a778fa6"); ++ } ++ ++ @Test ++ void shouldExtractCaseInsensitiveHeaders() { ++ // given ++ Map headers = ++ ImmutableMap.of( ++ "X-b3-traceId", ++ "4fd0b6131f19f39af59518d127b0cafe", ++ "x-b3-spanid", ++ "0000000000000456", ++ "X-B3-Sampled", ++ "true"); ++ ++ // when ++ Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); ++ // then ++ Span span = Span.fromContext(context); ++ SpanContext spanContext = span.getSpanContext(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.getSpanId()).isEqualTo("0000000000000456"); ++ assertThat(spanContext.getTraceId()).isEqualTo("4fd0b6131f19f39af59518d127b0cafe"); ++ } ++ ++ @Test ++ void shouldPreferSystemPropertyOverEnvVariable() { ++ // given ++ systemProperties.set( ++ "com.amazonaws.xray.traceHeader", ++ "Root=1-8a3c60f7-d188f8fa79d48a391a778fa7;Parent=0000000000000789;Sampled=0"); ++ environmentVariables.set( ++ "_X_AMZN_TRACE_ID", ++ "Root=1-8a3c60f7-d188f8fa79d48a391a778fa6;Parent=0000000000000456;Sampled=1"); ++ ++ // when ++ Context context = ParentContextExtractor.extract(headers, INSTRUMENTER); ++ // then ++ Span span = Span.fromContext(context); ++ SpanContext spanContext = span.getSpanContext(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.isValid()).isTrue(); ++ assertThat(spanContext.getSpanId()).isEqualTo("0000000000000789"); ++ assertThat(spanContext.getTraceId()).isEqualTo("d188f8fa79d48a391a778fa7"); ++ } ++} +diff --git a/instrumentation/aws-lambda/aws-lambda-core-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/AbstractAwsLambdaTest.java b/instrumentation/aws-lambda/aws-lambda-core-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/AbstractAwsLambdaTest.java +index e088efa906..544da9b1bb 100644 +--- a/instrumentation/aws-lambda/aws-lambda-core-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/AbstractAwsLambdaTest.java ++++ b/instrumentation/aws-lambda/aws-lambda-core-1.0/testing/src/main/java/io/opentelemetry/instrumentation/awslambdacore/v1_0/AbstractAwsLambdaTest.java +@@ -12,8 +12,6 @@ import static org.mockito.Mockito.when; + + import com.amazonaws.services.lambda.runtime.Context; + import com.amazonaws.services.lambda.runtime.RequestHandler; +-import io.opentelemetry.api.common.AttributeKey; +-import io.opentelemetry.api.common.Attributes; + import io.opentelemetry.api.trace.SpanKind; + import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; + import io.opentelemetry.sdk.trace.data.StatusData; +@@ -102,22 +100,8 @@ public abstract class AbstractAwsLambdaTest { + span -> + span.hasName("my_function") + .hasKind(SpanKind.SERVER) +- .hasLinksSatisfying( +- links -> +- assertThat(links) +- .singleElement() +- .satisfies( +- link -> { +- assertThat(link.getSpanContext().getTraceId()) +- .isEqualTo("8a3c60f7d188f8fa79d48a391a778fa6"); +- assertThat(link.getSpanContext().getSpanId()) +- .isEqualTo("0000000000000456"); +- assertThat(link.getAttributes()) +- .isEqualTo( +- Attributes.of( +- AttributeKey.stringKey("source"), +- "x-ray-env")); +- })) ++ .hasTraceId("8a3c60f7d188f8fa79d48a391a778fa6") ++ .hasParentSpanId("0000000000000456") + .hasAttributesSatisfyingExactly( + equalTo(SemanticAttributes.FAAS_INVOCATION_ID, "1-22-333")))); + } +diff --git a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java +index 305e3e62a0..844ee31899 100644 +--- a/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java ++++ b/instrumentation/aws-lambda/aws-lambda-events-2.2/library/src/main/java/io/opentelemetry/instrumentation/awslambdaevents/v2_2/internal/SqsMessageSpanLinksExtractor.java +@@ -9,48 +9,22 @@ 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.context.propagation.TextMapGetter; +-import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator; + import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksBuilder; + import io.opentelemetry.instrumentation.api.instrumenter.SpanLinksExtractor; +-import java.util.Collections; +-import java.util.Locale; +-import java.util.Map; ++import io.opentelemetry.instrumentation.awslambdacore.v1_0.internal.ParentContextExtractor; + + class SqsMessageSpanLinksExtractor implements SpanLinksExtractor { + private static final String AWS_TRACE_HEADER_SQS_ATTRIBUTE_KEY = "AWSTraceHeader"; + +- // lower-case map getter used for extraction +- static final String AWS_TRACE_HEADER_PROPAGATOR_KEY = "x-amzn-trace-id"; +- + @Override + public void extract(SpanLinksBuilder spanLinks, Context parentContext, SQSMessage message) { + String parentHeader = message.getAttributes().get(AWS_TRACE_HEADER_SQS_ATTRIBUTE_KEY); + if (parentHeader != null) { +- Context xrayContext = +- AwsXrayPropagator.getInstance() +- .extract( +- Context.root(), // We don't want the ambient context. +- Collections.singletonMap(AWS_TRACE_HEADER_PROPAGATOR_KEY, parentHeader), +- MapGetter.INSTANCE); +- SpanContext messageSpanCtx = Span.fromContext(xrayContext).getSpanContext(); +- if (messageSpanCtx.isValid()) { +- spanLinks.addLink(messageSpanCtx); ++ SpanContext parentCtx = ++ Span.fromContext(ParentContextExtractor.fromXrayHeader(parentHeader)).getSpanContext(); ++ if (parentCtx.isValid()) { ++ spanLinks.addLink(parentCtx); + } + } + } +- +- private enum MapGetter implements TextMapGetter> { +- INSTANCE; +- +- @Override +- public Iterable keys(Map map) { +- return map.keySet(); +- } +- +- @Override +- public String get(Map map, String s) { +- return map.get(s.toLowerCase(Locale.ROOT)); +- } +- } + } +diff --git a/version.gradle.kts b/version.gradle.kts +index cc1414c0bf..db8a59b046 100644 +--- a/version.gradle.kts ++++ b/version.gradle.kts +@@ -1,5 +1,5 @@ +-val stableVersion = "1.32.1" +-val alphaVersion = "1.32.1-alpha" ++val stableVersion = "1.32.1-adot-lambda1" ++val alphaVersion = "1.32.1-adot-lambda1-alpha" + + allprojects { + if (findProperty("otel.stable") != "true") { \ No newline at end of file diff --git a/lambda-layer/settings.gradle.kts b/lambda-layer/settings.gradle.kts new file mode 100644 index 0000000000..b37d925e37 --- /dev/null +++ b/lambda-layer/settings.gradle.kts @@ -0,0 +1,16 @@ +pluginManagement { + plugins { + id("com.diffplug.spotless") version "6.13.0" + id("com.github.ben-manes.versions") version "0.50.0" + id("com.github.johnrengelman.shadow") version "8.1.1" + } +} + +dependencyResolutionManagement { + repositories { + mavenCentral() + mavenLocal() + } +} + +rootProject.name = "aws-otel-lambda-java"