Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f4af01e
SPI on aws-sdk v2.2
anahatAWS May 27, 2025
d784099
SPI on aws-sdk v2.2
anahatAWS May 29, 2025
8c6036d
SPI on aws-sdk v2.2
anahatAWS Jun 2, 2025
e9b26d1
Merge branch 'aws-observability:main' into spi
anahatAWS Jun 3, 2025
8effb7a
updated to match ADOT v2.11
anahatAWS Jun 3, 2025
c7f59f7
updated to match ADOT v2.11
anahatAWS Jun 3, 2025
40d5d0b
Added comments to explain code and commented out debugger code
anahatAWS Jun 3, 2025
3061c4a
Removed debugger v2 patching script and debugger comments
anahatAWS Jun 4, 2025
1e54bc2
Merge branch 'aws-observability:main' into spi
anahatAWS Jun 4, 2025
f405de6
new TracingExecutionInterceptor implementation
anahatAWS Jun 5, 2025
bf1b5db
Simplified SPI and removed unused classes
anahatAWS Jun 6, 2025
837a63c
fixes AwsSdkRequest
anahatAWS Jun 6, 2025
e53795f
fixes AwsSdkRequest
anahatAWS Jun 6, 2025
82da476
fixes AwsSdkRequest
anahatAWS Jun 7, 2025
0cfcf97
fixes AwsSdkRequest
anahatAWS Jun 7, 2025
1d72356
fixes AwsSdkRequest
anahatAWS Jun 7, 2025
8b30ec1
fixes AwsSdkRequest
anahatAWS Jun 7, 2025
44ec960
debugging statemets added
anahatAWS Jun 8, 2025
e79bfea
debugging statemets added
anahatAWS Jun 8, 2025
c4bad5a
modified tracing execution interceptor to hook onto Execution Interce…
anahatAWS Jun 8, 2025
de877d3
modified tracing execution interceptor and test for V2
anahatAWS Jun 9, 2025
96bbdcd
modified tracing execution interceptor
anahatAWS Jun 9, 2025
81c95b2
modified tracing execution interceptor
anahatAWS Jun 11, 2025
0bcd848
added instrumenter factory and fixed order of interception with upstream
anahatAWS Jun 11, 2025
b721ecd
removed instrumenter factory and removed unused code
anahatAWS Jun 12, 2025
494e0f6
Merge branch 'aws-observability:main' into testing-spi
anahatAWS Jun 12, 2025
56620b4
added response attribute modifications
anahatAWS Jun 12, 2025
85ab778
Merge remote-tracking branch 'origin/testing-spi' into testing-spi
anahatAWS Jun 12, 2025
9d92388
added response attribute modifications
anahatAWS Jun 12, 2025
d06b996
added response attribute modifications
anahatAWS Jun 12, 2025
16a73ca
added response attribute modifications
anahatAWS Jun 12, 2025
a4c2d9d
added response attribute modifications
anahatAWS Jun 12, 2025
1bc2c1d
added response attribute modifications
anahatAWS Jun 12, 2025
e4f6b8c
added response attribute modifications
anahatAWS Jun 12, 2025
9f2ffc7
added response attribute modifications
anahatAWS Jun 12, 2025
e9b2fc3
fixed formatting
anahatAWS Jun 12, 2025
69cfec5
fixed formatting
anahatAWS Jun 13, 2025
f3dee27
fixed formatting
anahatAWS Jun 13, 2025
6b3f471
fixed formatting
anahatAWS Jun 13, 2025
0d54bca
fixed formatting
anahatAWS Jun 13, 2025
2c4c7bf
added README.md for aws-sdk
anahatAWS Jun 13, 2025
a0fa5c9
added comments to instrumentation module and tracing execution interc…
anahatAWS Jun 13, 2025
f3f4f37
added comments for clarity
anahatAWS Jun 13, 2025
c2e53a4
Merge branch 'aws-observability:main' into testing-spi
anahatAWS Jun 16, 2025
88a1fad
removed instrumenter factory and it's dependencies
anahatAWS Jun 16, 2025
ddf3e6d
Merge remote-tracking branch 'origin/testing-spi' into testing-spi
anahatAWS Jun 16, 2025
8cb3cc6
added comments for clarity and cleaned up build files
anahatAWS Jun 16, 2025
af37016
Added base structure and implementation for v1.11
anahatAWS Jun 18, 2025
0e8524b
added all patched test files and extensions
anahatAWS Jun 18, 2025
a7e713f
removed patching file and fixed dependencies
anahatAWS Jun 18, 2025
d9eae28
removed un-needed dependencies in build.gradle.kts
anahatAWS Jun 19, 2025
e5f643d
added patching filed with v1.11 and v2.2 patches removed
anahatAWS Jun 19, 2025
ad78928
fixed order of interception for v1_11
anahatAWS Jun 19, 2025
05893e5
Added documentation for aws-sdk v1.11 and updated instrumentation/aws…
anahatAWS Jun 20, 2025
1ac6638
Merge branch 'aws-observability:main' into spi-1.11
anahatAWS Jun 20, 2025
734bf71
Removed patching for v1.11 but kept it for v2.2
anahatAWS Jun 20, 2025
dc2538a
Merge remote-tracking branch 'origin/spi-1.11' into spi-1.11
anahatAWS Jun 20, 2025
1714fb2
created constant for OTel handler class for v1.11 (easier to modify i…
anahatAWS Jun 23, 2025
b124d60
removed constant variable for classpath
anahatAWS Jun 23, 2025
3c42555
updated documentation
anahatAWS Jun 23, 2025
8b7d0e1
Merge branch 'aws-observability:main' into spi-1.11
anahatAWS Jun 27, 2025
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
2,084 changes: 0 additions & 2,084 deletions .github/patches/opentelemetry-java-instrumentation.patch

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/scripts/patch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ if [[ -f "$OTEL_JAVA_PATCH" ]]; then
git commit -a -m "ADOT Patch release"
cd -
else
echo "Skiping patching opentelemetry-java"
echo "Skipping patching opentelemetry-java"
fi


Expand Down
9 changes: 9 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ plugins {
id("com.gradleup.shadow") apply false
}

configurations {
/*
We create a separate gradle configuration to grab a published Otel instrumentation agent.
We don't need the agent during development of this extension module.
This agent is used only during integration test.
*/
create("otel") // Explicitly create the 'otel' configuration
}

release {
defaultVersionStrategy = Strategies.getSNAPSHOT()
}
Expand Down
43 changes: 43 additions & 0 deletions instrumentation/aws-sdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## ADOT AWS SDK Instrumentation

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

1. Leverages OpenTelemetry's InstrumentationModule extension mechanism
2. Ensures AWS-specific instrumentation runs after core OpenTelemetry instrumentation
3. Maintains all existing functionality while improving ADOT maintainability

Benefits:

- **Better Extensibility:** Users can easily extend or modify AWS-specific behavior
- **Enhanced Compatibility:** Better alignment with OpenTelemetry's extension mechanisms
- **Clearer Code Organization:** More intuitive structure for future contributions

### AWS SDK v1 Instrumentation Summary
The AdotAwsSdkInstrumentationModule extends the InstrumentationModule from OpenTelemetry's SPI extensions and uses the instrumentation (specified in AdotAwsClientInstrumentation) to
register the AdotTracingRequestHandler. Unlike the direct registration of the TracingExecutionInterceptor in aws-sdk v2, the @Adive mechanism is used to load the AdotTracingRequestHandler when an AWS client is constructed.

- **AdotAwsClientAdvice** (within AdotAwsClientInstrumentation) registers our handler only after the upstream OTel handler is registered. This order matters as it impacts the oder of instrumentation. It is key to first register the upstream aws-sdk handler, and then this handler.

The AdotTracingRequestHandler extends the AWS SDK RequestHandler2. It uses the **beforeRequest** and **afterAttempt** methods
to hook onto the OTel spans during specific phases of the SDK request and response life cycle. This allows it to enrich the
upstream spans with custom **AwsExperimentalAttributes**. These points in the life cycle are crucial as they impact the order in which attributes
are added to the OTel span.

- **beforeRequest** is the latest point where the sdk request can be obtained after it is modified by the upstream aws-sdk v1.11 handler. This ensures the upstream handles the request and applies its changes first.
- **afterAttempt** is the latest point to access the sdk response before the span closes in the upstream afterResponse/afterError methods. This ensures we capture attributes from the final, fully modified response after all upstream handlers have processed it.

_**Note on Execution Ordering:**_

1. OTel creates and starts the span in beforeMarshalling
2. Our beforeRequest runs after OTel's span creation and initial attribute setting
3. Request processing occurs
4. Our afterAttempt runs before OTel closes the span
5. OTel closes the span in afterResponse/afterError

This order ensures our attributes are added after OTel's initial attributes but before span completion.

The AwsExperimentalAttributesExtractor is the main point which connects the other classes within awssdk_v1_11
through the class's experimentalAttributesExtractor variable.

50 changes: 50 additions & 0 deletions instrumentation/aws-sdk/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* 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("groovy")
id("com.gradleup.shadow")
}

base.archivesBaseName = "aws-instrumentation-awssdk"

dependencies {

compileOnly("software.amazon.awssdk:json-utils:2.17.0")
compileOnly("com.google.code.findbugs:jsr305:3.0.2")

// OpenTelemetry dependencies
compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api")
compileOnly("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api")

// AWS SDK dependencies for version 1.11
compileOnly("com.amazonaws:aws-java-sdk-core:1.11.0")
compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106")

// Test dependencies
testImplementation(project(":instrumentation:aws-sdk:testing"))
testImplementation("io.opentelemetry.javaagent:opentelemetry-javaagent-extension-api")

// AWS SDK v1.11 test dependencies
testImplementation("com.amazonaws:aws-java-sdk-sqs:1.11.106")
testImplementation("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309")
testImplementation("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230")
testImplementation("com.amazonaws:aws-java-sdk-lambda:1.11.678")
testImplementation("com.amazonaws:aws-java-sdk-bedrock:1.12.744")
testImplementation("com.amazonaws:aws-java-sdk-bedrockagent:1.12.744")
testImplementation("com.amazonaws:aws-java-sdk-bedrockagentruntime:1.12.744")
testImplementation("com.amazonaws:aws-java-sdk-bedrockruntime:1.12.744")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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.
*/

package software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11;

import static net.bytebuddy.matcher.ElementMatchers.*;

import com.amazonaws.handlers.RequestHandler2;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.List;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class AdotAwsClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.amazonaws.AmazonWebServiceClient")
.and(declaresField(named("requestHandler2s")));
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), AdotAwsClientInstrumentation.class.getName() + "$AdotAwsClientAdvice");
}

@SuppressWarnings("unused")
public static class AdotAwsClientAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void addHandler(
@Advice.FieldValue(value = "requestHandler2s", readOnly = false)
List<RequestHandler2> handlers) {

if (handlers == null) {
return;
}

boolean hasOtelHandler = false;
boolean hasAdotHandler = false;

// Checks if OTel handler is present.
for (RequestHandler2 handler : handlers) {
if (handler
.toString()
.contains(
"io.opentelemetry.javaagent.instrumentation.awssdk.v1_11.TracingRequestHandler")) {
hasOtelHandler = true;
}
if (handler instanceof AdotTracingRequestHandler) {
hasAdotHandler = true;
break;
}
}

// Only adds our ADOT handler if OTel's is present and ours isn't. This ensures upstream
// instrumentation is applied first.
if (hasOtelHandler && !hasAdotHandler) {
handlers.add(new AdotTracingRequestHandler());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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.
*/

package software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11;

import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static net.bytebuddy.matcher.ElementMatchers.*;

import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import net.bytebuddy.matcher.ElementMatcher;

public class AdotAwsSdkInstrumentationModule extends InstrumentationModule {

public AdotAwsSdkInstrumentationModule() {
super("aws-sdk-adot", "aws-sdk-1.11-adot");
}

@Override
public int order() {
// Ensure this runs after OTel (> 0)
return 99;
}

@Override
public List<String> getAdditionalHelperClassNames() {
return Arrays.asList(
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AdotTracingRequestHandler",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AwsSdkExperimentalAttributesExtractor",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AwsExperimentalAttributes",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AwsBedrockResourceType",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.AwsBedrockResourceType$AwsBedrockResourceTypeMap",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.BedrockJsonParser",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.RequestAccess",
"software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11.RequestAccess$1");
}

@Override
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
return hasClassesNamed("com.amazonaws.AmazonWebServiceClient");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return Collections.singletonList(new AdotAwsClientInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* 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.
*/

package software.amazon.opentelemetry.javaagent.instrumentation.awssdk_v1_11;

import com.amazonaws.Request;
import com.amazonaws.Response;
import com.amazonaws.handlers.HandlerAfterAttemptContext;
import com.amazonaws.handlers.RequestHandler2;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;

public class AdotTracingRequestHandler extends RequestHandler2 {
private final AwsSdkExperimentalAttributesExtractor experimentalAttributesExtractor;

public AdotTracingRequestHandler() {
this.experimentalAttributesExtractor = new AwsSdkExperimentalAttributesExtractor();
}

// This is the latest point where the sdk request can be obtained after it is modified by the
// upstream aws-sdk v1.11 handler. This ensures the upstream handles the request and applies its
// changes first.
@Override
public void beforeRequest(Request<?> request) {
Span currentSpan = Span.current();
if (currentSpan != null && currentSpan.getSpanContext().isValid()) {

AttributesBuilder attributes = Attributes.builder();
experimentalAttributesExtractor.onStart(attributes, Context.current(), request);

attributes
.build()
.forEach(
(key, value) -> currentSpan.setAttribute((AttributeKey<String>) key, (String) value));
}
}

// This is the latest point to access the sdk response before the span closes in the upstream
// afterResponse/afterError methods. This ensures we capture attributes from the final, fully
// modified response after all upstream handlers have processed it.
@Override
public void afterAttempt(HandlerAfterAttemptContext context) {
Span currentSpan = Span.current();
if (currentSpan != null && currentSpan.getSpanContext().isValid()) {
Request<?> request = context.getRequest();
Response<?> response = context.getResponse();
Exception exception = context.getException();

AttributesBuilder attributes = Attributes.builder();
experimentalAttributesExtractor.onEnd(
attributes, Context.current(), request, response, exception);

attributes
.build()
.forEach((key, value) -> currentSpan.setAttribute(key.getKey(), value.toString()));
}
}
}
Loading