Skip to content

Commit ba2f8d2

Browse files
Oberon00Mateusz Rzeszutek
andauthored
aws-sdk-1.1: Copy SQS plugin/NoMuzzle approach from 2.2 (#8866)
Co-authored-by: Mateusz Rzeszutek <[email protected]>
1 parent 40938cf commit ba2f8d2

File tree

14 files changed

+311
-266
lines changed

14 files changed

+311
-266
lines changed

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,22 @@ muzzle {
1515
module.set("aws-java-sdk-core")
1616
versions.set("[1.10.33,)")
1717
assertInverse.set(true)
18+
19+
excludeInstrumentationName("aws-sdk-1.11-sqs")
20+
}
21+
22+
fail {
23+
group.set("com.amazonaws")
24+
module.set("aws-java-sdk-core")
25+
versions.set("[1.10.33,)")
26+
27+
excludeInstrumentationName("aws-sdk-1.11-core")
28+
}
29+
30+
pass {
31+
group.set("com.amazonaws")
32+
module.set("aws-java-sdk-sqs")
33+
versions.set("[1.10.33,)")
1834
}
1935
}
2036

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
public final class SqsAdviceBridge {
9+
private SqsAdviceBridge() {}
10+
11+
public static void referenceForMuzzleOnly() {
12+
throw new UnsupportedOperationException(
13+
SqsImpl.class.getName() + " referencing for muzzle, should never be actually called");
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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 static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static java.util.Collections.singletonList;
10+
import static net.bytebuddy.matcher.ElementMatchers.named;
11+
12+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
14+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
15+
import java.util.List;
16+
import net.bytebuddy.description.type.TypeDescription;
17+
import net.bytebuddy.matcher.ElementMatcher;
18+
19+
// TODO: Copy & paste with only trivial adaptions from v2
20+
abstract class AbstractAwsSdkInstrumentationModule extends InstrumentationModule {
21+
22+
protected AbstractAwsSdkInstrumentationModule(String additionalInstrumentationName) {
23+
super("aws-sdk", "aws-sdk-1.11", additionalInstrumentationName);
24+
}
25+
26+
@Override
27+
public boolean isHelperClass(String className) {
28+
return className.startsWith("io.opentelemetry.contrib.awsxray.");
29+
}
30+
31+
@Override
32+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
33+
// We don't actually transform it but want to make sure we only apply the instrumentation when
34+
// our key dependency is present.
35+
return hasClassesNamed("com.amazonaws.AmazonWebServiceClient");
36+
}
37+
38+
@Override
39+
public List<TypeInstrumentation> typeInstrumentations() {
40+
return singletonList(new ResourceInjectingTypeInstrumentation());
41+
}
42+
43+
abstract void doTransform(TypeTransformer transformer);
44+
45+
// A type instrumentation is needed to trigger resource injection.
46+
public class ResourceInjectingTypeInstrumentation implements TypeInstrumentation {
47+
@Override
48+
public ElementMatcher<TypeDescription> typeMatcher() {
49+
// This is essentially the entry point of the AWS SDK, all clients implement it. We can ensure
50+
// our interceptor service definition is injected as early as possible if we typematch against
51+
// it.
52+
return named("com.amazonaws.AmazonWebServiceClient");
53+
}
54+
55+
@Override
56+
public void transform(TypeTransformer transformer) {
57+
doTransform(transformer);
58+
}
59+
}
60+
}

instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSdkInstrumentationModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
@AutoService(InstrumentationModule.class)
1616
public class AwsSdkInstrumentationModule extends InstrumentationModule {
1717
public AwsSdkInstrumentationModule() {
18-
super("aws-sdk", "aws-sdk-1.11");
18+
super("aws-sdk", "aws-sdk-1.11", "aws-sdk-1.11-core");
1919
}
2020

2121
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
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 static net.bytebuddy.matcher.ElementMatchers.none;
9+
10+
import com.google.auto.service.AutoService;
11+
import io.opentelemetry.instrumentation.awssdk.v1_11.SqsAdviceBridge;
12+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
13+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
14+
import net.bytebuddy.asm.Advice;
15+
16+
@AutoService(InstrumentationModule.class)
17+
public class SqsInstrumentationModule extends AbstractAwsSdkInstrumentationModule {
18+
19+
public SqsInstrumentationModule() {
20+
super("aws-sdk-1.11-sqs");
21+
}
22+
23+
@Override
24+
public void doTransform(TypeTransformer transformer) {
25+
transformer.applyAdviceToMethod(
26+
none(), SqsInstrumentationModule.class.getName() + "$RegisterAdvice");
27+
}
28+
29+
@SuppressWarnings("unused")
30+
public static class RegisterAdvice {
31+
@Advice.OnMethodExit(suppress = Throwable.class)
32+
public static void onExit() {
33+
// (indirectly) using SqsImpl class here to make sure it is available from SqsAccess
34+
// (injected into app classloader) and checked by Muzzle
35+
SqsAdviceBridge.referenceForMuzzleOnly();
36+
}
37+
}
38+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ dependencies {
66
implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")
77

88
library("com.amazonaws:aws-java-sdk-core:1.11.0")
9+
library("com.amazonaws:aws-java-sdk-sqs:1.11.106")
10+
compileOnly(project(":muzzle"))
911

1012
testImplementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing"))
1113

@@ -15,5 +17,4 @@ dependencies {
1517
testLibrary("com.amazonaws:aws-java-sdk-kinesis:1.11.106")
1618
testLibrary("com.amazonaws:aws-java-sdk-dynamodb:1.11.106")
1719
testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106")
18-
testLibrary("com.amazonaws:aws-java-sdk-sqs:1.11.106")
1920
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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 java.util.logging.Level;
9+
import java.util.logging.Logger;
10+
11+
final class PluginImplUtil { // TODO: Copy & paste from v2
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
20+
* load/link/initialize if the corresponding SDK classes are not on the class path. For javaagent,
21+
* the Impl is available only when the corresponding InstrumentationModule was successfully
22+
* applied (muzzle passed).
23+
*
24+
* <p>Note that an present-but-incompatible library can only be reliably detected by Muzzle. In
25+
* library-mode, users need to ensure they are using a compatible SDK (component) versions
26+
* themselves.
27+
*
28+
* @param implSimpleClassName The simple name of the impl class, e.g. {@code "SqsImpl"}. *
29+
*/
30+
static boolean isImplPresent(String implSimpleClassName) {
31+
// Computing the full name dynamically name here because library instrumentation classes are
32+
// relocated when embedded in the agent.
33+
// We use getName().replace() instead of getPackage() because the latter is not guaranteed to
34+
// work in all cases (e.g., we might be loaded into a custom classloader that doesn't handle it)
35+
String implFullClassName =
36+
PluginImplUtil.class.getName().replace(".PluginImplUtil", "." + implSimpleClassName);
37+
try {
38+
Class.forName(implFullClassName);
39+
return true;
40+
} catch (ClassNotFoundException | LinkageError e) {
41+
// ClassNotFoundException will happen when muzzle disabled us in javaagent mode; LinkageError
42+
// (most likely a NoClassDefFoundError, potentially wrapped in an ExceptionInInitializerError)
43+
// should be thrown when the class is loaded in library mode (where the Impl class itself can
44+
// always be found) but a dependency failed to load (most likely because the corresponding SDK
45+
// dependency is not on the class path).
46+
logger.log(
47+
Level.FINE,
48+
e,
49+
() ->
50+
implFullClassName
51+
+ " not present. "
52+
+ "Most likely, corresponding SDK component is either not on classpath or incompatible.");
53+
return false;
54+
}
55+
}
56+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
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.AmazonWebServiceRequest;
9+
import com.amazonaws.Request;
10+
import com.amazonaws.Response;
11+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
12+
import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle;
13+
14+
final class SqsAccess {
15+
private SqsAccess() {}
16+
17+
private static final boolean enabled = PluginImplUtil.isImplPresent("SqsImpl");
18+
19+
@NoMuzzle
20+
static boolean afterResponse(
21+
Request<?> request,
22+
Response<?> response,
23+
Instrumenter<Request<?>, Response<?>> consumerInstrumenter) {
24+
return enabled && SqsImpl.afterResponse(request, response, consumerInstrumenter);
25+
}
26+
27+
@NoMuzzle
28+
static boolean beforeMarshalling(AmazonWebServiceRequest request) {
29+
return enabled && SqsImpl.beforeMarshalling(request);
30+
}
31+
}
Lines changed: 70 additions & 0 deletions
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 com.amazonaws.AmazonWebServiceRequest;
9+
import com.amazonaws.Request;
10+
import com.amazonaws.Response;
11+
import com.amazonaws.services.sqs.AmazonSQS;
12+
import com.amazonaws.services.sqs.model.Message;
13+
import com.amazonaws.services.sqs.model.ReceiveMessageRequest;
14+
import com.amazonaws.services.sqs.model.ReceiveMessageResult;
15+
import io.opentelemetry.context.Context;
16+
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
17+
18+
final class SqsImpl {
19+
static {
20+
// Force loading of SQS class; this ensures that an exception is thrown at this point when the
21+
// SQS library is not present, which will cause SqsAccess to have enabled=false in library mode.
22+
@SuppressWarnings("unused")
23+
String ensureLoadedDummy = AmazonSQS.class.getName();
24+
}
25+
26+
private SqsImpl() {}
27+
28+
static boolean afterResponse(
29+
Request<?> request,
30+
Response<?> response,
31+
Instrumenter<Request<?>, Response<?>> consumerInstrumenter) {
32+
if (response.getAwsResponse() instanceof ReceiveMessageResult) {
33+
afterConsumerResponse(request, response, consumerInstrumenter);
34+
return true;
35+
}
36+
return false;
37+
}
38+
39+
/** Create and close CONSUMER span for each message consumed. */
40+
private static void afterConsumerResponse(
41+
Request<?> request,
42+
Response<?> response,
43+
Instrumenter<Request<?>, Response<?>> consumerInstrumenter) {
44+
ReceiveMessageResult receiveMessageResult = (ReceiveMessageResult) response.getAwsResponse();
45+
for (Message message : receiveMessageResult.getMessages()) {
46+
createConsumerSpan(message, request, response, consumerInstrumenter);
47+
}
48+
}
49+
50+
private static void createConsumerSpan(
51+
Message message,
52+
Request<?> request,
53+
Response<?> response,
54+
Instrumenter<Request<?>, Response<?>> consumerInstrumenter) {
55+
Context parentContext = SqsParentContext.ofSystemAttributes(message.getAttributes());
56+
Context context = consumerInstrumenter.start(parentContext, request);
57+
consumerInstrumenter.end(context, request, response, null);
58+
}
59+
60+
static boolean beforeMarshalling(AmazonWebServiceRequest rawRequest) {
61+
if (rawRequest instanceof ReceiveMessageRequest) {
62+
ReceiveMessageRequest request = (ReceiveMessageRequest) rawRequest;
63+
if (!request.getAttributeNames().contains(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE)) {
64+
request.withAttributeNames(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE);
65+
}
66+
return true;
67+
}
68+
return false;
69+
}
70+
}

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

Lines changed: 0 additions & 63 deletions
This file was deleted.

0 commit comments

Comments
 (0)