Skip to content

Commit bb4211d

Browse files
authored
Fix aws-sdk-2.2 library instrumentation crashing app when SQS is not on classpath (#8805)
1 parent f2348ea commit bb4211d

File tree

10 files changed

+422
-332
lines changed

10 files changed

+422
-332
lines changed

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

+6-8
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,12 @@ dependencies {
5757
testImplementation(project(":instrumentation:apache-httpclient:apache-httpclient-4.0:javaagent"))
5858
testImplementation(project(":instrumentation:netty:netty-4.1:javaagent"))
5959

60-
latestDepTestLibrary("software.amazon.awssdk:aws-json-protocol:+")
61-
latestDepTestLibrary("software.amazon.awssdk:aws-core:+")
62-
latestDepTestLibrary("software.amazon.awssdk:dynamodb:+")
63-
latestDepTestLibrary("software.amazon.awssdk:ec2:+")
64-
latestDepTestLibrary("software.amazon.awssdk:kinesis:+")
65-
latestDepTestLibrary("software.amazon.awssdk:rds:+")
66-
latestDepTestLibrary("software.amazon.awssdk:s3:+")
67-
latestDepTestLibrary("software.amazon.awssdk:sqs:+")
60+
testLibrary("software.amazon.awssdk:dynamodb:2.2.0")
61+
testLibrary("software.amazon.awssdk:ec2:2.2.0")
62+
testLibrary("software.amazon.awssdk:kinesis:2.2.0")
63+
testLibrary("software.amazon.awssdk:rds:2.2.0")
64+
testLibrary("software.amazon.awssdk:s3:2.2.0")
65+
testLibrary("software.amazon.awssdk:sqs:2.2.0")
6866
}
6967

7068
tasks {

instrumentation/aws-sdk/aws-sdk-2.2/library-autoconfigure/build.gradle.kts

+6-8
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ dependencies {
1212

1313
testImplementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing"))
1414

15-
latestDepTestLibrary("software.amazon.awssdk:aws-core:+")
16-
latestDepTestLibrary("software.amazon.awssdk:aws-json-protocol:+")
17-
latestDepTestLibrary("software.amazon.awssdk:dynamodb:+")
18-
latestDepTestLibrary("software.amazon.awssdk:ec2:+")
19-
latestDepTestLibrary("software.amazon.awssdk:kinesis:+")
20-
latestDepTestLibrary("software.amazon.awssdk:rds:+")
21-
latestDepTestLibrary("software.amazon.awssdk:s3:+")
22-
latestDepTestLibrary("software.amazon.awssdk:sqs:+")
15+
testLibrary("software.amazon.awssdk:dynamodb:2.2.0")
16+
testLibrary("software.amazon.awssdk:ec2:2.2.0")
17+
testLibrary("software.amazon.awssdk:kinesis:2.2.0")
18+
testLibrary("software.amazon.awssdk:rds:2.2.0")
19+
testLibrary("software.amazon.awssdk:s3:2.2.0")
20+
testLibrary("software.amazon.awssdk:sqs:2.2.0")
2321
}
2422

2523
tasks {

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

+26-9
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,35 @@ dependencies {
1212

1313
testImplementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing"))
1414

15-
latestDepTestLibrary("software.amazon.awssdk:aws-core:+")
16-
latestDepTestLibrary("software.amazon.awssdk:aws-json-protocol:+")
17-
latestDepTestLibrary("software.amazon.awssdk:dynamodb:+")
18-
latestDepTestLibrary("software.amazon.awssdk:ec2:+")
19-
latestDepTestLibrary("software.amazon.awssdk:kinesis:+")
20-
latestDepTestLibrary("software.amazon.awssdk:rds:+")
21-
latestDepTestLibrary("software.amazon.awssdk:s3:+")
22-
latestDepTestLibrary("software.amazon.awssdk:sqs:+")
15+
testLibrary("software.amazon.awssdk:dynamodb:2.2.0")
16+
testLibrary("software.amazon.awssdk:ec2:2.2.0")
17+
testLibrary("software.amazon.awssdk:kinesis:2.2.0")
18+
testLibrary("software.amazon.awssdk:rds:2.2.0")
19+
testLibrary("software.amazon.awssdk:s3:2.2.0")
20+
}
21+
22+
testing {
23+
suites {
24+
val testCoreOnly by registering(JvmTestSuite::class) {
25+
sources {
26+
groovy {
27+
setSrcDirs(listOf("src/testCoreOnly/groovy"))
28+
}
29+
}
30+
31+
dependencies {
32+
implementation(project())
33+
implementation(project(":instrumentation:aws-sdk:aws-sdk-2.2:testing"))
34+
implementation("software.amazon.awssdk:aws-core:2.2.0")
35+
implementation("software.amazon.awssdk:aws-json-protocol:2.2.0")
36+
implementation("software.amazon.awssdk:dynamodb:2.2.0")
37+
}
38+
}
39+
}
2340
}
2441

2542
tasks {
26-
test {
43+
withType<Test> {
2744
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
2845
systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", true)
2946
systemProperty("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", true)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.awssdk.v2_2;
7+
8+
import java.util.logging.Level;
9+
import java.util.logging.Logger;
10+
11+
final class PluginImplUtil {
12+
private PluginImplUtil() {}
13+
14+
private static final Logger logger = Logger.getLogger(PluginImplUtil.class.getName());
15+
16+
/**
17+
* Check if the given {@code moduleNameImpl} is present.
18+
*
19+
* <p>For library instrumentations, the Impls will always be available but might fail to load if
20+
* the corresponding SDK classes are not on the class path. For javaagent, the Impl is available
21+
* only when the corresponding InstrumentationModule was successfully applied (muzzle passed).
22+
*
23+
* <p>Note that an present-but-incompatible library can only be detected by Muzzle. In
24+
* library-mode, users need to ensure they are using a compatible SDK (component) versions
25+
* themselves.
26+
*
27+
* @param implSimpleClassName The simple name of the impl class, e.g. {@code "SqsImpl"}. *
28+
*/
29+
static boolean isImplPresent(String implSimpleClassName) {
30+
// We use getName().replace() instead of getPackage() because the latter is not guaranteed to
31+
// work in all cases (e.g., we might be loaded into a custom classloader that doesn't handle it)
32+
String implFullClassName =
33+
PluginImplUtil.class.getName().replace(".PluginImplUtil", "." + implSimpleClassName);
34+
try {
35+
// using package name here because library instrumentation classes are relocated when embedded
36+
// in the agent
37+
Class.forName(implFullClassName);
38+
return true;
39+
} catch (ClassNotFoundException | LinkageError e) {
40+
// ClassNotFoundException will happen when muzzle disabled us in javaagent mode; LinkageError
41+
// (most likely NoClassDefFoundError) should happen when the class is loaded in library mode
42+
// (where it is always available) but a dependency failed to load (most likely because the
43+
// corresponding SDK dependency is not on the class path).
44+
logger.log(
45+
Level.FINE, e, () -> implFullClassName + " not present, probably incompatible version");
46+
return false;
47+
}
48+
}
49+
}

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/SqsAccess.java

+1-15
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,7 @@
2020
final class SqsAccess {
2121
private SqsAccess() {}
2222

23-
private static final boolean enabled = isSqsImplPresent();
24-
25-
private static boolean isSqsImplPresent() {
26-
try {
27-
// for library instrumentation SqsImpl is always available
28-
// for javaagent instrumentation SqsImpl is available only when SqsInstrumentationModule was
29-
// successfully applied (muzzle passed)
30-
// using package name here because library instrumentation classes are relocated when embedded
31-
// in the agent
32-
Class.forName(SqsAccess.class.getName().replace(".SqsAccess", ".SqsImpl"));
33-
return true;
34-
} catch (ClassNotFoundException e) {
35-
return false;
36-
}
37-
}
23+
private static final boolean enabled = PluginImplUtil.isImplPresent("SqsImpl");
3824

3925
@NoMuzzle
4026
static boolean isSendMessageRequest(SdkRequest request) {

instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/SqsImpl.java

+8
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import software.amazon.awssdk.core.interceptor.Context;
1616
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
1717
import software.amazon.awssdk.http.SdkHttpResponse;
18+
import software.amazon.awssdk.services.sqs.SqsClient;
1819
import software.amazon.awssdk.services.sqs.model.Message;
1920
import software.amazon.awssdk.services.sqs.model.MessageAttributeValue;
2021
import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest;
@@ -23,6 +24,13 @@
2324

2425
// this class is only used from SqsAccess from method with @NoMuzzle annotation
2526
final class SqsImpl {
27+
static {
28+
// Force loading of SqsClient; this ensures that an exception is thrown at this point when the
29+
// SQS library is not present, which will cause SqsAccess to have enabled=false in library mode.
30+
@SuppressWarnings("unused")
31+
String ensureLoadedDummy = SqsClient.class.getName();
32+
}
33+
2634
private SqsImpl() {}
2735

2836
static SdkRequest injectIntoSendMessageRequest(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.instrumentation.awssdk.v2_2
7+
8+
import io.opentelemetry.instrumentation.test.LibraryTestTrait
9+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration
10+
11+
class Aws2ClientDynamodbTest extends AbstractAws2ClientCoreTest implements LibraryTestTrait {
12+
@Override
13+
ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder() {
14+
return ClientOverrideConfiguration.builder()
15+
.addExecutionInterceptor(
16+
AwsSdkTelemetry.builder(getOpenTelemetry())
17+
.setCaptureExperimentalSpanAttributes(true)
18+
.setUseConfiguredPropagatorForMessaging(isSqsAttributeInjectionEnabled())
19+
.build()
20+
.newExecutionInterceptor())
21+
}
22+
}

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

+9-8
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ dependencies {
88
api("software.amazon.awssdk:apache-client:2.2.0")
99
// older versions don't play nice with armeria http server
1010
api("software.amazon.awssdk:netty-nio-client:2.11.0")
11-
// When adding libraries, make sure to also add to the library/javaagent build files
12-
// to ensure they are bumped to the latest version under testLatestDeps
13-
api("software.amazon.awssdk:dynamodb:2.2.0")
14-
api("software.amazon.awssdk:ec2:2.2.0")
15-
api("software.amazon.awssdk:kinesis:2.2.0")
16-
api("software.amazon.awssdk:rds:2.2.0")
17-
api("software.amazon.awssdk:s3:2.2.0")
18-
api("software.amazon.awssdk:sqs:2.2.0")
11+
12+
// compileOnly because we never want to pin the low version implicitly; need to add dependencies
13+
// explicitly in user projects, e.g. using testLatestDeps.
14+
compileOnly("software.amazon.awssdk:dynamodb:2.2.0")
15+
compileOnly("software.amazon.awssdk:ec2:2.2.0")
16+
compileOnly("software.amazon.awssdk:kinesis:2.2.0")
17+
compileOnly("software.amazon.awssdk:rds:2.2.0")
18+
compileOnly("software.amazon.awssdk:s3:2.2.0")
19+
compileOnly("software.amazon.awssdk:sqs:2.2.0")
1920

2021
// needed for SQS - using emq directly as localstack references emq v0.15.7 ie WITHOUT AWS trace header propagation
2122
implementation("org.elasticmq:elasticmq-rest-sqs_2.12:1.0.0")

0 commit comments

Comments
 (0)