Skip to content

Commit bfb646d

Browse files
committed
feat: Add auto-instrumentation support for AWS resources in AWS SDK v1
This PR adds auto-instrumentation support for the following AWS resources: Lambda Secrets Manager SNS Step Functions Tests Run: ./gradlew spotlessCheck ./gradlew clean assemble ./gradlew instrumentation:test ./gradlew :smoke-tests:test No regression issues found. All newly added tests pass. Backward Compatibility: There is no risk of breaking existing functionality. This change only adds instrumentation for additional AWS resources without modifying the existing behavior of the auto-instrumentation library.
1 parent 449fb34 commit bfb646d

File tree

10 files changed

+359
-23
lines changed

10 files changed

+359
-23
lines changed

instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSpanAssertions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ static SpanDataAssert sns(SpanDataAssert span, String topicArn, String rpcMethod
9090
.hasAttributesSatisfyingExactly(
9191
equalTo(stringKey("aws.agent"), "java-aws-sdk"),
9292
equalTo(MESSAGING_DESTINATION_NAME, topicArn),
93+
satisfies(stringKey("aws.sns.topic.arn"), v -> v.isInstanceOf(String.class)),
9394
satisfies(AWS_REQUEST_ID, v -> v.isInstanceOf(String.class)),
9495
equalTo(RPC_METHOD, rpcMethod),
9596
equalTo(RPC_SYSTEM, "aws-api"),

instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ dependencies {
1717
testLibrary("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
1818
testLibrary("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
1919
testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106")
20+
testLibrary("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309")
21+
testLibrary("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230")
22+
testLibrary("com.amazonaws:aws-java-sdk-lambda:1.11.678")
2023

2124
// last version that does not use json protocol
2225
latestDepTestLibrary("com.amazonaws:aws-java-sdk-sqs:1.12.583") // documented limitation

instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsExperimentalAttributes.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,15 @@ final class AwsExperimentalAttributes {
1616
static final AttributeKey<String> AWS_QUEUE_NAME = stringKey("aws.queue.name");
1717
static final AttributeKey<String> AWS_STREAM_NAME = stringKey("aws.stream.name");
1818
static final AttributeKey<String> AWS_TABLE_NAME = stringKey("aws.table.name");
19+
static final AttributeKey<String> AWS_STATE_MACHINE_ARN =
20+
stringKey("aws.stepfunctions.state_machine.arn");
21+
static final AttributeKey<String> AWS_STEP_FUNCTIONS_ACTIVITY_ARN =
22+
stringKey("aws.stepfunctions.activity.arn");
23+
static final AttributeKey<String> AWS_SNS_TOPIC_ARN = stringKey("aws.sns.topic.arn");
24+
static final AttributeKey<String> AWS_SECRET_ARN = stringKey("aws.secretsmanager.secret.arn");
25+
static final AttributeKey<String> AWS_LAMBDA_NAME = stringKey("aws.lambda.function.name");
26+
static final AttributeKey<String> AWS_LAMBDA_RESOURCE_ID =
27+
stringKey("aws.lambda.resource_mapping.id");
1928

2029
private AwsExperimentalAttributes() {}
2130
}

instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/AwsSdkExperimentalAttributesExtractor.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@
77

88
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_AGENT;
99
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_BUCKET_NAME;
10+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_LAMBDA_NAME;
11+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_LAMBDA_RESOURCE_ID;
1012
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_QUEUE_NAME;
1113
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_QUEUE_URL;
14+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_SECRET_ARN;
15+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_SNS_TOPIC_ARN;
16+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_STATE_MACHINE_ARN;
17+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_STEP_FUNCTIONS_ACTIVITY_ARN;
1218
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_STREAM_NAME;
1319
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_TABLE_NAME;
1420

@@ -35,6 +41,19 @@ public void onStart(AttributesBuilder attributes, Context parentContext, Request
3541
setRequestAttribute(attributes, AWS_QUEUE_NAME, originalRequest, RequestAccess::getQueueName);
3642
setRequestAttribute(attributes, AWS_STREAM_NAME, originalRequest, RequestAccess::getStreamName);
3743
setRequestAttribute(attributes, AWS_TABLE_NAME, originalRequest, RequestAccess::getTableName);
44+
setRequestAttribute(
45+
attributes, AWS_STATE_MACHINE_ARN, originalRequest, RequestAccess::getStateMachineArn);
46+
setRequestAttribute(
47+
attributes,
48+
AWS_STEP_FUNCTIONS_ACTIVITY_ARN,
49+
originalRequest,
50+
RequestAccess::getStepFunctionsActivityArn);
51+
setRequestAttribute(
52+
attributes, AWS_SNS_TOPIC_ARN, originalRequest, RequestAccess::getSnsTopicArn);
53+
setRequestAttribute(attributes, AWS_SECRET_ARN, originalRequest, RequestAccess::getSecretArn);
54+
setRequestAttribute(attributes, AWS_LAMBDA_NAME, originalRequest, RequestAccess::getLambdaName);
55+
setRequestAttribute(
56+
attributes, AWS_LAMBDA_RESOURCE_ID, originalRequest, RequestAccess::getLambdaResourceId);
3857
}
3958

4059
private static void setRequestAttribute(
@@ -54,5 +73,18 @@ public void onEnd(
5473
Context context,
5574
Request<?> request,
5675
@Nullable Response<?> response,
57-
@Nullable Throwable error) {}
76+
@Nullable Throwable error) {
77+
if (response != null) {
78+
Object awsResp = response.getAwsResponse();
79+
setRequestAttribute(
80+
attributes, AWS_STATE_MACHINE_ARN, awsResp, RequestAccess::getStateMachineArn);
81+
setRequestAttribute(
82+
attributes,
83+
AWS_STEP_FUNCTIONS_ACTIVITY_ARN,
84+
awsResp,
85+
RequestAccess::getStepFunctionsActivityArn);
86+
setRequestAttribute(attributes, AWS_SNS_TOPIC_ARN, awsResp, RequestAccess::getSnsTopicArn);
87+
setRequestAttribute(attributes, AWS_SECRET_ARN, awsResp, RequestAccess::getSecretArn);
88+
}
89+
}
5890
}

instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/RequestAccess.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,60 @@ protected RequestAccess computeValue(Class<?> type) {
2020
}
2121
};
2222

23+
@Nullable
24+
static String getLambdaName(Object request) {
25+
if (request == null) {
26+
return null;
27+
}
28+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
29+
return invokeOrNull(access.getLambdaName, request);
30+
}
31+
32+
@Nullable
33+
static String getLambdaResourceId(Object request) {
34+
if (request == null) {
35+
return null;
36+
}
37+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
38+
return invokeOrNull(access.getLambdaResourceId, request);
39+
}
40+
41+
@Nullable
42+
static String getSecretArn(Object request) {
43+
if (request == null) {
44+
return null;
45+
}
46+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
47+
return invokeOrNull(access.getSecretArn, request);
48+
}
49+
50+
@Nullable
51+
static String getSnsTopicArn(Object request) {
52+
if (request == null) {
53+
return null;
54+
}
55+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
56+
return invokeOrNull(access.getSnsTopicArn, request);
57+
}
58+
59+
@Nullable
60+
static String getStepFunctionsActivityArn(Object request) {
61+
if (request == null) {
62+
return null;
63+
}
64+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
65+
return invokeOrNull(access.getStepFunctionsActivityArn, request);
66+
}
67+
68+
@Nullable
69+
static String getStateMachineArn(Object request) {
70+
if (request == null) {
71+
return null;
72+
}
73+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
74+
return invokeOrNull(access.getStateMachineArn, request);
75+
}
76+
2377
@Nullable
2478
static String getBucketName(Object request) {
2579
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
@@ -81,6 +135,12 @@ private static String invokeOrNull(@Nullable MethodHandle method, Object obj) {
81135
@Nullable private final MethodHandle getTableName;
82136
@Nullable private final MethodHandle getTopicArn;
83137
@Nullable private final MethodHandle getTargetArn;
138+
@Nullable private final MethodHandle getStateMachineArn;
139+
@Nullable private final MethodHandle getStepFunctionsActivityArn;
140+
@Nullable private final MethodHandle getSnsTopicArn;
141+
@Nullable private final MethodHandle getSecretArn;
142+
@Nullable private final MethodHandle getLambdaName;
143+
@Nullable private final MethodHandle getLambdaResourceId;
84144

85145
private RequestAccess(Class<?> clz) {
86146
getBucketName = findAccessorOrNull(clz, "getBucketName");
@@ -90,6 +150,12 @@ private RequestAccess(Class<?> clz) {
90150
getTableName = findAccessorOrNull(clz, "getTableName");
91151
getTopicArn = findAccessorOrNull(clz, "getTopicArn");
92152
getTargetArn = findAccessorOrNull(clz, "getTargetArn");
153+
getStateMachineArn = findAccessorOrNull(clz, "getStateMachineArn");
154+
getStepFunctionsActivityArn = findAccessorOrNull(clz, "getActivityArn");
155+
getSnsTopicArn = findAccessorOrNull(clz, "getTopicArn");
156+
getSecretArn = findAccessorOrNull(clz, "getARN");
157+
getLambdaName = findAccessorOrNull(clz, "getFunctionName");
158+
getLambdaResourceId = findAccessorOrNull(clz, "getUUID");
93159
}
94160

95161
@Nullable

instrumentation/aws-sdk/aws-sdk-1.11/testing/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ dependencies {
1414
compileOnly("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
1515
compileOnly("com.amazonaws:aws-java-sdk-sns:1.11.106")
1616
compileOnly("com.amazonaws:aws-java-sdk-sqs:1.11.106")
17+
compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.11.309")
18+
compileOnly("com.amazonaws:aws-java-sdk-stepfunctions:1.11.230")
19+
compileOnly("com.amazonaws:aws-java-sdk-lambda:1.11.678")
1720

1821
// needed for SQS - using emq directly as localstack references emq v0.15.7 ie WITHOUT AWS trace header propagation
1922
implementation("org.elasticmq:elasticmq-rest-sqs_2.13")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.awssdk.v1_11;
7+
8+
import static java.util.Collections.singletonMap;
9+
10+
import com.amazonaws.services.lambda.AWSLambda;
11+
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
12+
import com.amazonaws.services.lambda.model.GetEventSourceMappingRequest;
13+
import com.amazonaws.services.lambda.model.GetFunctionRequest;
14+
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
15+
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
16+
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
17+
import io.opentelemetry.testing.internal.armeria.common.MediaType;
18+
import java.util.List;
19+
import java.util.function.Function;
20+
import java.util.stream.Stream;
21+
import org.junit.jupiter.params.ParameterizedTest;
22+
import org.junit.jupiter.params.provider.Arguments;
23+
import org.junit.jupiter.params.provider.MethodSource;
24+
25+
public abstract class AbstractLambdaClientTest extends AbstractBaseAwsClientTest {
26+
27+
public abstract AWSLambdaClientBuilder configureClient(AWSLambdaClientBuilder client);
28+
29+
@Override
30+
protected boolean hasRequestId() {
31+
return false;
32+
}
33+
34+
@ParameterizedTest
35+
@MethodSource("provideArguments")
36+
public void testSendRequestWithMockedResponse(
37+
String operation,
38+
List<AttributeAssertion> additionalAttributes,
39+
Function<AWSLambda, Object> call)
40+
throws Exception {
41+
42+
AWSLambdaClientBuilder clientBuilder = AWSLambdaClientBuilder.standard();
43+
44+
AWSLambda client =
45+
configureClient(clientBuilder)
46+
.withEndpointConfiguration(endpoint)
47+
.withCredentials(credentialsProvider)
48+
.build();
49+
50+
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, ""));
51+
52+
Object response = call.apply(client);
53+
assertRequestWithMockedResponse(
54+
response, client, "AWSLambda", operation, "GET", additionalAttributes);
55+
}
56+
57+
private static Stream<Arguments> provideArguments() {
58+
return Stream.of(
59+
Arguments.of(
60+
"GetEventSourceMapping",
61+
singletonMap("aws.lambda.resource_mapping.id", "uuid"),
62+
(Function<AWSLambda, Object>)
63+
c -> c.getEventSourceMapping(new GetEventSourceMappingRequest().withUUID("uuid"))),
64+
Arguments.of(
65+
"GetFunction",
66+
singletonMap("aws.lambda.function.name", "functionName"),
67+
(Function<AWSLambda, Object>)
68+
c -> c.getFunction(new GetFunctionRequest().withFunctionName("functionName"))));
69+
}
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.awssdk.v1_11;
7+
8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
9+
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
10+
import static java.util.Collections.singletonList;
11+
12+
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
13+
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
14+
import com.amazonaws.services.secretsmanager.model.CreateSecretRequest;
15+
import io.opentelemetry.sdk.testing.assertj.AttributeAssertion;
16+
import io.opentelemetry.testing.internal.armeria.common.HttpResponse;
17+
import io.opentelemetry.testing.internal.armeria.common.HttpStatus;
18+
import io.opentelemetry.testing.internal.armeria.common.MediaType;
19+
import java.util.List;
20+
import org.junit.jupiter.api.Test;
21+
22+
public abstract class AbstractSecretsManagerClientTest extends AbstractBaseAwsClientTest {
23+
24+
public abstract AWSSecretsManagerClientBuilder configureClient(
25+
AWSSecretsManagerClientBuilder client);
26+
27+
@Override
28+
protected boolean hasRequestId() {
29+
return true;
30+
}
31+
32+
@Test
33+
public void sendRequestWithMockedResponse() throws Exception {
34+
AWSSecretsManagerClientBuilder clientBuilder = AWSSecretsManagerClientBuilder.standard();
35+
AWSSecretsManager client =
36+
configureClient(clientBuilder)
37+
.withEndpointConfiguration(endpoint)
38+
.withCredentials(credentialsProvider)
39+
.build();
40+
41+
String body =
42+
"{"
43+
+ "\"ARN\": \"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3\","
44+
+ "\"Name\": \"MyTestDatabaseSecret\","
45+
+ "\"VersionId\": \"EXAMPLE1-90ab-cdef-fedc-ba987SECRET1\""
46+
+ "}";
47+
server.enqueue(HttpResponse.of(HttpStatus.OK, MediaType.PLAIN_TEXT_UTF_8, body));
48+
49+
Object response =
50+
client.createSecret(
51+
new CreateSecretRequest().withName("secretName").withSecretString("secretValue"));
52+
53+
List<AttributeAssertion> additionalAttributes =
54+
singletonList(
55+
equalTo(
56+
stringKey("aws.secretsmanager.secret.arn"),
57+
"arn:aws:secretsmanager:us-west-2:123456789012:secret:MyTestDatabaseSecret-a1b2c3"));
58+
59+
assertRequestWithMockedResponse(
60+
response, client, "AWSSecretsManager", "CreateSecret", "POST", additionalAttributes);
61+
}
62+
}

0 commit comments

Comments
 (0)