Skip to content

Release/v2.11.3 #1146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 1 addition & 24 deletions .github/actions/patch-dependencies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: "Patch dependencies"
description: |
Patches direct dependencies of this project leveraging maven local to publish the results.
This workflow supports patching opentelemetry-java and opentelemetry-java-instrumentation repositories by executing
This workflow supports patching opentelemetry-java and opentelemetry-java-contrib repositories by executing
the `patch.sh` script that will try to patch those repositories and after that will optionally test and then publish
the artifacts to maven local.
To add a patch you have to add a file in the `.github/patches/` directory with the name of the repository that must
Expand Down Expand Up @@ -49,9 +49,6 @@ runs:
if [[ -f .github/patches/opentelemetry-java.patch ]]; then
echo 'patch_otel_java=true' >> $GITHUB_ENV
fi
if [[ -f .github/patches/opentelemetry-java-instrumentation.patch ]]; then
echo 'patch_otel_java_instrumentation=true' >> $GITHUB_ENV
fi
if [[ -f .github/patches/opentelemetry-java-contrib.patch ]]; then
echo 'patch_otel_java_contrib=true' >> $GITHUB_ENV
fi
Expand All @@ -60,7 +57,6 @@ runs:
- name: Clone and patch repositories
run: .github/scripts/patch.sh
if: ${{ env.patch_otel_java == 'true' ||
env.patch_otel_java_instrumentation == 'true' ||
env.patch_otel_java_contrib == 'true' }}
shell: bash

Expand Down Expand Up @@ -101,22 +97,3 @@ runs:
run: rm -rf opentelemetry-java-contrib
if: ${{ env.patch_otel_java_contrib == 'true' }}
shell: bash

- name: Build opentelemetry-java-instrumentation with tests
uses: gradle/gradle-build-action@v2
if: ${{ env.patch_otel_java_instrumentation == 'true' && inputs.run_tests != 'false' }}
with:
arguments: check -x spotlessCheck publishToMavenLocal
build-root-directory: opentelemetry-java-instrumentation

- name: Build opentelemetry java instrumentation
uses: gradle/gradle-build-action@v2
if: ${{ env.patch_otel_java_instrumentation == 'true' && inputs.run_tests == 'false' }}
with:
arguments: publishToMavenLocal
build-root-directory: opentelemetry-java-instrumentation

- name: cleanup opentelmetry-java-instrumentation
run: rm -rf opentelemetry-java-instrumentation
if: ${{ env.patch_otel_java_instrumentation == 'true' }}
shell: bash
4,193 changes: 0 additions & 4,193 deletions .github/patches/opentelemetry-java-instrumentation.patch

This file was deleted.

14 changes: 0 additions & 14 deletions .github/scripts/patch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ set -x -e -u
# This is used so that we can properly clone the upstream repositories.
# This file should define the following variables:
# OTEL_JAVA_VERSION. Tag of the opentelemetry-java repository to use. E.g.: JAVA_OTEL_JAVA_VERSION=v1.21.0
# OTEL_JAVA_INSTRUMENTATION_VERSION. Tag of the opentelemetry-java-instrumentation repository to use, e.g.: OTEL_JAVA_INSTRUMENTATION_VERSION=v1.21.0
# OTEL_JAVA_CONTRIB_VERSION. Tag of the opentelemetry-java-contrib repository. E.g.: OTEL_JAVA_CONTRIB_VERSION=v1.21.0
# This script will fail if a variable that is supposed to exist is referenced.

Expand Down Expand Up @@ -45,16 +44,3 @@ if [[ -f "$OTEL_JAVA_CONTRIB_PATCH" ]]; then
else
echo "Skipping patching opentelemetry-java-contrib"
fi


OTEL_JAVA_INSTRUMENTATION_PATCH=".github/patches/opentelemetry-java-instrumentation.patch"
if [[ -f "$OTEL_JAVA_INSTRUMENTATION_PATCH" ]]; then
git clone https://github.com/open-telemetry/opentelemetry-java-instrumentation.git
cd opentelemetry-java-instrumentation
git checkout ${OTEL_JAVA_INSTRUMENTATION_VERSION} -b tag-${OTEL_JAVA_INSTRUMENTATION_VERSION}
patch -p1 < "../${OTEL_JAVA_INSTRUMENTATION_PATCH}"
git commit -a -m "ADOT Patch release"
cd -
else
echo "Skipping patching opentelemetry-java-instrumentation"
fi
5 changes: 4 additions & 1 deletion dependencyManagement/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ data class DependencySet(val group: String, val version: String, val modules: Li
val testSnapshots = rootProject.findProperty("testUpstreamSnapshots") == "true"

// This is the version of the upstream instrumentation BOM
val otelVersion = "2.11.0-adot3"
val otelVersion = "2.11.0"
val otelSnapshotVersion = "2.12.0"
val otelAlphaVersion = if (!testSnapshots) "$otelVersion-alpha" else "$otelSnapshotVersion-alpha-SNAPSHOT"
val otelJavaAgentVersion = if (!testSnapshots) otelVersion else "$otelSnapshotVersion-SNAPSHOT"
Expand All @@ -40,6 +40,9 @@ val dependencyBoms = listOf(
"com.google.protobuf:protobuf-bom:3.25.1",
"com.linecorp.armeria:armeria-bom:1.26.4",
"io.grpc:grpc-bom:1.59.1",
// netty-bom is a fix for CVE-2025-55163 (https://github.com/advisories/GHSA-prj3-ccx8-p6x4).
// Remove once https://github.com/aws/aws-sdk-java-v2/pull/6344 is released.
"io.netty:netty-bom:4.1.124.Final",
"io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha:$otelAlphaVersion",
"org.apache.logging.log4j:log4j-bom:2.21.1",
"org.junit:junit-bom:5.10.1",
Expand Down
183 changes: 183 additions & 0 deletions instrumentation/aws-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
## ADOT AWS SDK Instrumentation

### Overview
The aws-sdk instrumentation is an SPI-based implementation that extends the upstream OpenTelemetry AWS Java SDK instrumentation.

##### _v1.11 Initialization Workflow_
1. OpenTelemetry Agent Starts
- Loads default instrumentations
- Loads aws-sdk v1.11 instrumentations
- Injects **TracingRequestHandler** into constructor
2. Scans for other SPI implementations
- Finds ADOT’s **AdotAwsSdkInstrumentationModule**
- Injects code that:
- Checks for TracingRequestHandler
- If present, adds **AdotAwsSdkTracingRequestHandler**
3. AWS SDK Client Created
- Constructor runs with injected code:
[AWS Handlers] → TracingRequestHandler → AdotAwsSdkTracingRequestHandler

##### _v2.2 Initialization Workflow_

1. OpenTelemetry Agent starts
- Loads default instrumentations
- Loads aws-sdk instrumentation from opentelemetry-java-instrumentation
- Registers **TracingExecutionInterceptor** (order = 0)
2. Scans for other SPI implementations
- Finds ADOT’s **AdotAwsSdkInstrumentationModule**
- Registers **AdotAwsSdkTracingExecutionInterceptor** (order > 0)

#### _Note on Attribute Collection:_
AWS SDK v1.11 and v2.2 handle attribute collection differently:

**V1.11:**
- Maintains a separate AttributesBuilder during request/response lifecycle
- Collects ADOT-specific attributes alongside upstream processing without interference
- Injects collected attributes into span at the end of the request and response lifecycle hooks


**V2.2:**
- FieldMapper directly modifies spans during request/response processing
- Attributes are added to spans immediately when discovered
- Direct integration with span lifecycle

This architectural difference exists due to upstream AWS SDK injecting attributes into spans differently for v1.11 and v2.2

### AWS SDK v1 Instrumentation Summary
The AdotAwsSdkInstrumentationModule uses the instrumentation (specified in AdotAwsClientInstrumentation) to register the AdotAwsSdkTracingRequestHandler through `typeInstrumentations`.

Key aspects of handler registration:
- `order` method ensures ADOT instrumentation runs after OpenTelemetry's base instrumentation. It is set to the max integer value, as precaution, in case upstream aws-sdk registers more handlers.
- `AdotAwsSdkClientInstrumentation` class adds ADOT handler to list of request handlers

**AdotAwsSdkClientInstrumentation**

AWS SDK v1.11 instrumentation requires ByteBuddy because, unlike v2.2, it doesn't provide an SPI for adding request handlers. While v2.2 uses the ExecutionInterceptor interface and Java's ServiceLoader mechanism, v1.11 maintains a direct list of handlers that can't be modified through a public API. Therefore, we use ByteBuddy to modify the AWS client constructor and inject our handler directly into the requestHandler2s list.

- `AdotAwsSdkClientAdvice` registers our handler only if the upstream aws-sdk span is enabled (i.e. it checks if the upstream handler is present when an AWS SDK client is
initialized).
- Ensures the OpenTelemetry handler is registered first.

**AdotAwsSdkTracingRequestHandler**

The AdotAwsSdkTracingRequestHandler hooks onto OpenTelemetry's spans during specific phases of the SDK request and response life cycle. These hooks are strategically chosen to ensure proper ordering of attribute injection.

1. `beforeRequest`: the latest point where the SDK request can be obtained after it is modified by the upstream aws-sdk v1.11 handler
2. `afterAttempt`: the latest point to access the SDK response before the span closes in the upstream afterResponse/afterError methods
- _NOTE:_ We use afterAttempt not because it's ideal, but because it our last chance to add attributes, even though this means our logic runs multiple times during retries.
- This is a trade-off:
- We get to add our attributes before span closure
- But our code runs redundantly on each retry attempt
- We're constrained by when upstream closes the span

All the span lifecycle hooks provided by AWS SDK RequestHandler2 can be found [here.](https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/handlers/RequestHandler2.html#beforeMarshalling-com.amazonaws.AmazonWebServiceRequest)

_**Important Notes:**_
- The upstream interceptor's last point of request modification occurs in [beforeRequest](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java#L58).
- The upstream interceptor closes the span in [afterResponse](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java#L116) and/or [afterError](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/main/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java#L131). These hooks are inaccessible for span modification.
`afterAttempt` is our final hook point, giving us access to both the fully processed response and active span.

**High-Level Sequence Diagram:**
![img.png](sequence-diagram-1.11.png)

_Class Functionalities:_
- `AdotAwsSdkTracingRequestHandler`
- Hooks into AWS SDK request/response lifecycle
- Adds ADOT-specific attributes to spans extracted by AwsSdkExperimentalAttributesExtractor
- `AwsSdkExperimentalAttributesExtractor`
- Extracts attributes from AWS requests/responses and enriches spans
- Uses RequestAccess to get field values
- Special handling for Bedrock services
- `RequestAccess`
- Provides access to AWS SDK object fields
- Caches method handles for performance
- Uses BedrockJsonParser for parsing LLM payloads
- `BedrockJsonParser`
- Custom JSON parser for Bedrock payloads
- Handles different LLM model formats
- `AwsBedrockResourceType`
- Maps Bedrock class names to resource types
- Provides attribute keys and accessors for each type

### AWS SDK v2 Instrumentation Summary

**AdotAwsSdkInstrumentationModule**

The AdotAwsSdkInstrumentationModule registers the AdotAwsSdkTracingExecutionInterceptor in `registerHelperResources`.

Key aspects of interceptor registration:
- AWS SDK's ExecutionInterceptor loads global interceptors from files named '/software/amazon/awssdk/global/handlers/execution.interceptors' in the classpath
- Interceptors are executed in the order they appear in the classpath - earlier entries run first
- `order` method ensures ADOT instrumentation runs after OpenTelemetry's base instrumentation, maintaining proper sequence of interceptor registration in AWS SDK classpath

**AdotAwsSdkTracingExecutionInterceptor**

The AdotAwsSdkTracingExecutionInterceptor hooks onto OpenTelemetry's spans during specific phases of the SDK request and response life cycle. These hooks are strategically chosen to ensure proper ordering of attribute injection.

1. `beforeTransmission`: the latest point where the SDK request can be obtained after it is modified by the upstream's interceptor
2. `modifyResponse`: the latest point to access the SDK response before the span closes in the upstream afterExecution method

All the span lifecycle hooks provided by AWS SDK ExecutionInterceptor can be found [here.](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/core/interceptor/ExecutionInterceptor.html)

_**Important Notes:**_
- The upstream interceptor's last point of request modification occurs in [beforeTransmission](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/release/v2.11.x/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/TracingExecutionInterceptor.java#L237).
- The upstream interceptor closes the span in [afterExecution](https://github.com/open-telemetry/opentelemetry-java-instrumentation/blob/release/v2.11.x/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/internal/TracingExecutionInterceptor.java#L348). That hook is inaccessible for span modification.
`modifyResponse` is our final hook point, giving us access to both the fully processed response and active span.

**High-Level Sequence Diagram:**
![img.png](sequence-diagram-2.2.png)

_Class Functionalities:_
- `AdotAwsSdkTracingExecutionInterceptor`
- Intercepts AWS SDK calls to create and enrich OpenTelemetry spans with AWS attributes
- Coordinates the attribute mapping process
- `FieldMapper`
- Maps the AWS SDK fields to span attributes
- Coordinates with Serializer for value conversion
- `FieldMapping`
- Defines what fields to map from SDK to spans
- Groups mappings by type (REQUEST/RESPONSE)
- `MethodHandleFacotry`
- Provides fast, cached access to AWS SDK object fields for better performance
- Used by FieldMapper for efficient field value extraction
- `Serializer`
- Converts AWS SDK objects and Bedrock objects into string values that can be used as span attributes
- Works with BedrockJsonParser for LLM responses
- `AwsJsonProtocolFactoryAccess`
- Enables access to AWS SDK's internal JSON serialization capabilities for complex SDK objects
- Uses reflection to access internal SDK classes
- Caches method handles for performance
- `BedrockJasonParser`
- Parses and extracts specific attributes from Bedrock LLM responses for GenAI telemetry

### Commands for Running Groovy Tests

#### aws-sdk v1.11
To run the `BedrockJsonParserTest`:
````
./gradlew :instrumentation:aws-sdk:test --tests "software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.BedrockJsonParserTest"
````

#### aws-sdk v2.2
To run the `BedrockJsonParserTest`:
````
./gradlew :instrumentation:aws-sdk:test --tests "software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v2_2.BedrockJsonParserTest"
````

### Commands for Running Java Tests

#### aws-sdk v1.11
To run the `AwsSdkExperimentalAttributesInjectionTest`:
````
./gradlew :instrumentation:aws-sdk:test --tests "software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AwsSdkExperimentalAttributesInjectionTest"
````

To run the `AdotAwsSdkClientAdviceTest`:
````
./gradlew :instrumentation:aws-sdk:test --tests "software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AdotAwsSdkClientAdviceTest"
````

#### aws-sdk v2.2
To run the `AwsSdkExperimentalAttributesInjectionTest`:
````
./gradlew :instrumentation:aws-sdk:test --tests "software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v2_2.AwsSdkExperimentalAttributesInjectionTest"
44 changes: 44 additions & 0 deletions instrumentation/aws-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright Amazon.com, Inc. or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file 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.
*/

plugins {
java
id("com.gradleup.shadow")
id("groovy")
}

base.archivesBaseName = "aws-instrumentation-aws-sdk"

dependencies {
compileOnly("com.google.code.findbugs:jsr305:3.0.2")
compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api")
compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api")
compileOnly("com.amazonaws:aws-java-sdk-core:1.11.0")
compileOnly("software.amazon.awssdk:aws-core:2.2.0")
compileOnly("software.amazon.awssdk:aws-json-protocol:2.2.0")
compileOnly("net.bytebuddy:byte-buddy")

testImplementation("com.google.guava:guava")
testImplementation("org.mockito:mockito-core:5.14.2")
testImplementation("io.opentelemetry.javaagent:opentelemetry-testing-common")
testImplementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api")

testImplementation("software.amazon.awssdk:aws-core:2.2.0")
testImplementation("com.amazonaws:aws-java-sdk-lambda:1.11.678")
testImplementation("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
testImplementation("com.amazonaws:aws-java-sdk-sns:1.11.106")
testImplementation("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230")
testImplementation("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309")
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading