Skip to content

Commit c15aba4

Browse files
committed
Add AWS_LAMBDA_RESOURCE_MAPPING_ID Semantic Convention Support for AWS Lambda SDK
This PR adds support for the AWS_LAMBDA_RESOURCE_MAPPING_ID semantic convention attribute in the AWS Lambda SDK instrumentation library. It also introduces the following two experimental attributes. Work is currently underway to add these keys to the AWS section of the Semantic Conventions (SemConv) registry: aws.lambda.function.arn aws.lambda.function.name Name is extracted from request object. ARN is extracted from response object. Resource Mapping ID is extracted from both request and response objects. This behavior is covered by unit tests. Tests Run: ./gradlew spotlessCheck ./gradlew instrumentation:check ./gradlew :smoke-tests:test All newly added tests pass, and no regressions were found. Backward Compatibility: This change is fully backward compatible. It introduces instrumentation for an additional AWS resource without modifying existing behavior in the auto-instrumentation library.
1 parent 2f0d232 commit c15aba4

File tree

15 files changed

+605
-2
lines changed

15 files changed

+605
-2
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ dependencies {
4444
testLibrary("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
4545
testLibrary("com.amazonaws:aws-java-sdk-ec2:1.11.106")
4646
testLibrary("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
47+
testLibrary("com.amazonaws:aws-java-sdk-lambda:1.11.106")
4748
testLibrary("com.amazonaws:aws-java-sdk-rds:1.11.106")
4849
testLibrary("com.amazonaws:aws-java-sdk-s3:1.11.106")
4950
testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11;
7+
8+
import com.amazonaws.services.lambda.AWSLambdaClientBuilder;
9+
import io.opentelemetry.instrumentation.awssdk.v1_11.AbstractLambdaClientTest;
10+
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
11+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
12+
import org.junit.jupiter.api.extension.RegisterExtension;
13+
14+
class LambdaClientTest extends AbstractLambdaClientTest {
15+
@RegisterExtension
16+
static final InstrumentationExtension testing = AgentInstrumentationExtension.create();
17+
18+
@Override
19+
protected InstrumentationExtension testing() {
20+
return testing;
21+
}
22+
23+
@Override
24+
public AWSLambdaClientBuilder configureClient(AWSLambdaClientBuilder clientBuilder) {
25+
return clientBuilder;
26+
}
27+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies {
1414
testLibrary("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
1515
testLibrary("com.amazonaws:aws-java-sdk-ec2:1.11.106")
1616
testLibrary("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
17+
testLibrary("com.amazonaws:aws-java-sdk-lambda:1.11.106")
1718
testLibrary("com.amazonaws:aws-java-sdk-rds:1.11.106")
1819
testLibrary("com.amazonaws:aws-java-sdk-s3:1.11.106")
1920
testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106")

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,11 @@ final class AwsExperimentalAttributes {
1717
static final AttributeKey<String> AWS_STREAM_NAME = stringKey("aws.stream.name");
1818
static final AttributeKey<String> AWS_TABLE_NAME = stringKey("aws.table.name");
1919

20+
// Work is underway to add these two keys to the SemConv AWS registry, in line with other AWS
21+
// resources.
22+
// https://github.com/open-telemetry/semantic-conventions/blob/main/docs/registry/attributes/aws.md#amazon-lambda-attributes
23+
static final AttributeKey<String> AWS_LAMBDA_ARN = stringKey("aws.lambda.function.arn");
24+
static final AttributeKey<String> AWS_LAMBDA_NAME = stringKey("aws.lambda.function.name");
25+
2026
private AwsExperimentalAttributes() {}
2127
}

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ class AwsSdkAttributesExtractor implements AttributesExtractor<Request<?>, Respo
2626
// Copied from AwsIncubatingAttributes
2727
private static final AttributeKey<String> AWS_SECRETSMANAGER_SECRET_ARN =
2828
stringKey("aws.secretsmanager.secret.arn");
29+
private static final AttributeKey<String> AWS_LAMBDA_RESOURCE_MAPPING_ID =
30+
stringKey("aws.lambda.resource_mapping.id");
2931
private static final AttributeKey<String> AWS_SNS_TOPIC_ARN = stringKey("aws.sns.topic.arn");
3032
private static final AttributeKey<String> AWS_STEP_FUNCTIONS_ACTIVITY_ARN =
3133
stringKey("aws.step_functions.activity.arn");
@@ -46,6 +48,11 @@ private static boolean canGetResponseMetadata() {
4648
@Override
4749
public void onStart(AttributesBuilder attributes, Context parentContext, Request<?> request) {
4850
Object originalRequest = request.getOriginalRequest();
51+
setAttribute(
52+
attributes,
53+
AWS_LAMBDA_RESOURCE_MAPPING_ID,
54+
originalRequest,
55+
RequestAccess::getLambdaResourceMappingId);
4956
setAttribute(attributes, AWS_SNS_TOPIC_ARN, originalRequest, RequestAccess::getSnsTopicArn);
5057
setAttribute(
5158
attributes,
@@ -69,6 +76,11 @@ public void onEnd(
6976
Object awsResp = getAwsResponse(response);
7077
if (awsResp != null) {
7178
setAttribute(attributes, AWS_SECRETSMANAGER_SECRET_ARN, awsResp, RequestAccess::getSecretArn);
79+
setAttribute(
80+
attributes,
81+
AWS_LAMBDA_RESOURCE_MAPPING_ID,
82+
awsResp,
83+
RequestAccess::getLambdaResourceMappingId);
7284
setAttribute(attributes, AWS_SNS_TOPIC_ARN, awsResp, RequestAccess::getSnsTopicArn);
7385
setAttribute(
7486
attributes,

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
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_ARN;
11+
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_LAMBDA_NAME;
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;
1214
import static io.opentelemetry.instrumentation.awssdk.v1_11.AwsExperimentalAttributes.AWS_STREAM_NAME;
@@ -35,6 +37,7 @@ public void onStart(AttributesBuilder attributes, Context parentContext, Request
3537
setRequestAttribute(attributes, AWS_QUEUE_NAME, originalRequest, RequestAccess::getQueueName);
3638
setRequestAttribute(attributes, AWS_STREAM_NAME, originalRequest, RequestAccess::getStreamName);
3739
setRequestAttribute(attributes, AWS_TABLE_NAME, originalRequest, RequestAccess::getTableName);
40+
setRequestAttribute(attributes, AWS_LAMBDA_NAME, originalRequest, RequestAccess::getLambdaName);
3841
}
3942

4043
private static void setRequestAttribute(
@@ -54,5 +57,17 @@ public void onEnd(
5457
Context context,
5558
Request<?> request,
5659
@Nullable Response<?> response,
57-
@Nullable Throwable error) {}
60+
@Nullable Throwable error) {
61+
Object awsResponse = getAwsResponse(response);
62+
if (awsResponse != null) {
63+
setRequestAttribute(attributes, AWS_LAMBDA_ARN, awsResponse, RequestAccess::getLambdaArn);
64+
}
65+
}
66+
67+
private static Object getAwsResponse(Response<?> response) {
68+
if (response == null) {
69+
return null;
70+
}
71+
return response.getAwsResponse();
72+
}
5873
}

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

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,13 @@
88
import java.lang.invoke.MethodHandle;
99
import java.lang.invoke.MethodHandles;
1010
import java.lang.invoke.MethodType;
11+
import java.lang.reflect.Method;
1112
import javax.annotation.Nullable;
1213

1314
final class RequestAccess {
1415
private static final String SECRETS_MANAGER_REQUEST_CLASS_PREFIX =
1516
"com.amazonaws.services.secretsmanager.model.";
17+
private static final String LAMBDA_REQUEST_CLASS_PREFIX = "com.amazonaws.services.lambda.model.";
1618
private static final String STEP_FUNCTIONS_REQUEST_CLASS_PREFIX =
1719
"com.amazonaws.services.stepfunctions.model.";
1820

@@ -24,6 +26,36 @@ protected RequestAccess computeValue(Class<?> type) {
2426
}
2527
};
2628

29+
@Nullable
30+
static String getLambdaArn(Object request) {
31+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
32+
if (access.getLambdaConfiguration == null) {
33+
return null;
34+
}
35+
try {
36+
Object config = access.getLambdaConfiguration.invoke(request);
37+
if (config == null) {
38+
return null;
39+
}
40+
Method method = config.getClass().getMethod("getFunctionArn");
41+
return (String) method.invoke(config);
42+
} catch (Throwable t) {
43+
return null;
44+
}
45+
}
46+
47+
@Nullable
48+
static String getLambdaName(Object request) {
49+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
50+
return invokeOrNull(access.getLambdaName, request);
51+
}
52+
53+
@Nullable
54+
static String getLambdaResourceMappingId(Object request) {
55+
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
56+
return invokeOrNull(access.getLambdaResourceMappingId, request);
57+
}
58+
2759
@Nullable
2860
static String getSecretArn(Object request) {
2961
RequestAccess access = REQUEST_ACCESSORS.get(request.getClass());
@@ -97,6 +129,9 @@ private static String invokeOrNull(@Nullable MethodHandle method, Object obj) {
97129
}
98130

99131
@Nullable private final MethodHandle getBucketName;
132+
@Nullable private final MethodHandle getLambdaConfiguration;
133+
@Nullable private final MethodHandle getLambdaName;
134+
@Nullable private final MethodHandle getLambdaResourceMappingId;
100135
@Nullable private final MethodHandle getQueueUrl;
101136
@Nullable private final MethodHandle getQueueName;
102137
@Nullable private final MethodHandle getSecretArn;
@@ -116,6 +151,10 @@ private RequestAccess(Class<?> clz) {
116151
getTopicArn = findAccessorOrNull(clz, "getTopicArn");
117152
getTargetArn = findAccessorOrNull(clz, "getTargetArn");
118153

154+
boolean isLambda = clz.getName().startsWith(LAMBDA_REQUEST_CLASS_PREFIX);
155+
getLambdaConfiguration = isLambda ? findLambdaGetConfigurationMethod(clz) : null;
156+
getLambdaName = isLambda ? findAccessorOrNull(clz, "getFunctionName") : null;
157+
getLambdaResourceMappingId = isLambda ? findAccessorOrNull(clz, "getUUID") : null;
119158
boolean isSecretsManager = clz.getName().startsWith(SECRETS_MANAGER_REQUEST_CLASS_PREFIX);
120159
getSecretArn = isSecretsManager ? findAccessorOrNull(clz, "getARN") : null;
121160
boolean isStepFunction = clz.getName().startsWith(STEP_FUNCTIONS_REQUEST_CLASS_PREFIX);
@@ -125,9 +164,26 @@ private RequestAccess(Class<?> clz) {
125164

126165
@Nullable
127166
private static MethodHandle findAccessorOrNull(Class<?> clz, String methodName) {
167+
return findAccessorOrNull(clz, methodName, String.class);
168+
}
169+
170+
@Nullable
171+
private static MethodHandle findAccessorOrNull(
172+
Class<?> clz, String methodName, Class<?> returnType) {
128173
try {
129174
return MethodHandles.publicLookup()
130-
.findVirtual(clz, methodName, MethodType.methodType(String.class));
175+
.findVirtual(clz, methodName, MethodType.methodType(returnType));
176+
} catch (Throwable t) {
177+
return null;
178+
}
179+
}
180+
181+
@Nullable
182+
private static MethodHandle findLambdaGetConfigurationMethod(Class<?> clz) {
183+
try {
184+
Class<?> returnType =
185+
Class.forName("com.amazonaws.services.lambda.model.FunctionConfiguration");
186+
return findAccessorOrNull(clz, "getConfiguration", returnType);
131187
} catch (Throwable t) {
132188
return null;
133189
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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 com.amazonaws.services.lambda.AWSLambdaClientBuilder;
9+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
10+
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
11+
import org.junit.jupiter.api.extension.RegisterExtension;
12+
13+
class LambdaClientTest extends AbstractLambdaClientTest {
14+
@RegisterExtension
15+
private static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
16+
17+
@Override
18+
protected InstrumentationExtension testing() {
19+
return testing;
20+
}
21+
22+
@Override
23+
public AWSLambdaClientBuilder configureClient(AWSLambdaClientBuilder clientBuilder) {
24+
return clientBuilder.withRequestHandlers(
25+
AwsSdkTelemetry.builder(testing().getOpenTelemetry())
26+
.setCaptureExperimentalSpanAttributes(true)
27+
.build()
28+
.newRequestHandler());
29+
}
30+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dependencies {
1010
compileOnly("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
1111
compileOnly("com.amazonaws:aws-java-sdk-ec2:1.11.106")
1212
compileOnly("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
13+
compileOnly("com.amazonaws:aws-java-sdk-lambda:1.11.106")
1314
compileOnly("com.amazonaws:aws-java-sdk-rds:1.11.106")
1415
compileOnly("com.amazonaws:aws-java-sdk-s3:1.11.106")
1516
compileOnly("com.amazonaws:aws-java-sdk-secretsmanager:1.12.80")

0 commit comments

Comments
 (0)