diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts index b654433cefe6..e04a0ce1b5a0 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/build.gradle.kts @@ -15,6 +15,22 @@ muzzle { module.set("aws-java-sdk-core") versions.set("[1.10.33,)") assertInverse.set(true) + + excludeInstrumentationName("aws-sdk-1.11-sqs") + } + + fail { + group.set("com.amazonaws") + module.set("aws-java-sdk-core") + versions.set("[1.10.33,)") + + excludeInstrumentationName("aws-sdk-1.11-core") + } + + pass { + group.set("com.amazonaws") + module.set("aws-java-sdk-sqs") + versions.set("[1.10.33,)") } } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsAdviceBridge.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsAdviceBridge.java new file mode 100644 index 000000000000..efa6fe9a3428 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsAdviceBridge.java @@ -0,0 +1,15 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v1_11; + +public final class SqsAdviceBridge { + private SqsAdviceBridge() {} + + public static void referenceForMuzzleOnly() { + throw new UnsupportedOperationException( + SqsImpl.class.getName() + " referencing for muzzle, should never be actually called"); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AbstractAwsSdkInstrumentationModule.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AbstractAwsSdkInstrumentationModule.java new file mode 100644 index 000000000000..0e515eb28abf --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AbstractAwsSdkInstrumentationModule.java @@ -0,0 +1,60 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Collections.singletonList; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.List; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; + +// TODO: Copy & paste with only trivial adaptions from v2 +abstract class AbstractAwsSdkInstrumentationModule extends InstrumentationModule { + + protected AbstractAwsSdkInstrumentationModule(String additionalInstrumentationName) { + super("aws-sdk", "aws-sdk-1.11", additionalInstrumentationName); + } + + @Override + public boolean isHelperClass(String className) { + return className.startsWith("io.opentelemetry.contrib.awsxray."); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // We don't actually transform it but want to make sure we only apply the instrumentation when + // our key dependency is present. + return hasClassesNamed("com.amazonaws.AmazonWebServiceClient"); + } + + @Override + public List typeInstrumentations() { + return singletonList(new ResourceInjectingTypeInstrumentation()); + } + + abstract void doTransform(TypeTransformer transformer); + + // A type instrumentation is needed to trigger resource injection. + public class ResourceInjectingTypeInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + // This is essentially the entry point of the AWS SDK, all clients implement it. We can ensure + // our interceptor service definition is injected as early as possible if we typematch against + // it. + return named("com.amazonaws.AmazonWebServiceClient"); + } + + @Override + public void transform(TypeTransformer transformer) { + doTransform(transformer); + } + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSdkInstrumentationModule.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSdkInstrumentationModule.java index 99d72487993e..048bbdfc41a6 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSdkInstrumentationModule.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/AwsSdkInstrumentationModule.java @@ -15,7 +15,7 @@ @AutoService(InstrumentationModule.class) public class AwsSdkInstrumentationModule extends InstrumentationModule { public AwsSdkInstrumentationModule() { - super("aws-sdk", "aws-sdk-1.11"); + super("aws-sdk", "aws-sdk-1.11", "aws-sdk-1.11-core"); } @Override diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SqsInstrumentationModule.java b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SqsInstrumentationModule.java new file mode 100644 index 000000000000..cc61cf6d9cc0 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/awssdk/v1_11/SqsInstrumentationModule.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.awssdk.v1_11; + +import static net.bytebuddy.matcher.ElementMatchers.none; + +import com.google.auto.service.AutoService; +import io.opentelemetry.instrumentation.awssdk.v1_11.SqsAdviceBridge; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; + +@AutoService(InstrumentationModule.class) +public class SqsInstrumentationModule extends AbstractAwsSdkInstrumentationModule { + + public SqsInstrumentationModule() { + super("aws-sdk-1.11-sqs"); + } + + @Override + public void doTransform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + none(), SqsInstrumentationModule.class.getName() + "$RegisterAdvice"); + } + + @SuppressWarnings("unused") + public static class RegisterAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit() { + // (indirectly) using SqsImpl class here to make sure it is available from SqsAccess + // (injected into app classloader) and checked by Muzzle + SqsAdviceBridge.referenceForMuzzleOnly(); + } + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts index 99277d1ea885..aa9e0634a867 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/build.gradle.kts @@ -6,6 +6,8 @@ dependencies { implementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator") library("com.amazonaws:aws-java-sdk-core:1.11.0") + library("com.amazonaws:aws-java-sdk-sqs:1.11.106") + compileOnly(project(":muzzle")) testImplementation(project(":instrumentation:aws-sdk:aws-sdk-1.11:testing")) @@ -15,5 +17,4 @@ dependencies { testLibrary("com.amazonaws:aws-java-sdk-kinesis:1.11.106") testLibrary("com.amazonaws:aws-java-sdk-dynamodb:1.11.106") testLibrary("com.amazonaws:aws-java-sdk-sns:1.11.106") - testLibrary("com.amazonaws:aws-java-sdk-sqs:1.11.106") } diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/PluginImplUtil.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/PluginImplUtil.java new file mode 100644 index 000000000000..364bfffe5318 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/PluginImplUtil.java @@ -0,0 +1,56 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v1_11; + +import java.util.logging.Level; +import java.util.logging.Logger; + +final class PluginImplUtil { // TODO: Copy & paste from v2 + private PluginImplUtil() {} + + private static final Logger logger = Logger.getLogger(PluginImplUtil.class.getName()); + + /** + * Check if the given {@code moduleNameImpl} is present. + * + *

For library instrumentations, the Impls will always be available but might fail to + * load/link/initialize if the corresponding SDK classes are not on the class path. For javaagent, + * the Impl is available only when the corresponding InstrumentationModule was successfully + * applied (muzzle passed). + * + *

Note that an present-but-incompatible library can only be reliably detected by Muzzle. In + * library-mode, users need to ensure they are using a compatible SDK (component) versions + * themselves. + * + * @param implSimpleClassName The simple name of the impl class, e.g. {@code "SqsImpl"}. * + */ + static boolean isImplPresent(String implSimpleClassName) { + // Computing the full name dynamically name here because library instrumentation classes are + // relocated when embedded in the agent. + // We use getName().replace() instead of getPackage() because the latter is not guaranteed to + // work in all cases (e.g., we might be loaded into a custom classloader that doesn't handle it) + String implFullClassName = + PluginImplUtil.class.getName().replace(".PluginImplUtil", "." + implSimpleClassName); + try { + Class.forName(implFullClassName); + return true; + } catch (ClassNotFoundException | LinkageError e) { + // ClassNotFoundException will happen when muzzle disabled us in javaagent mode; LinkageError + // (most likely a NoClassDefFoundError, potentially wrapped in an ExceptionInInitializerError) + // should be thrown when the class is loaded in library mode (where the Impl class itself can + // always be found) but a dependency failed to load (most likely because the corresponding SDK + // dependency is not on the class path). + logger.log( + Level.FINE, + e, + () -> + implFullClassName + + " not present. " + + "Most likely, corresponding SDK component is either not on classpath or incompatible."); + return false; + } + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsAccess.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsAccess.java new file mode 100644 index 000000000000..12fe4bae8d67 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsAccess.java @@ -0,0 +1,31 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v1_11; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.Request; +import com.amazonaws.Response; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle; + +final class SqsAccess { + private SqsAccess() {} + + private static final boolean enabled = PluginImplUtil.isImplPresent("SqsImpl"); + + @NoMuzzle + static boolean afterResponse( + Request request, + Response response, + Instrumenter, Response> consumerInstrumenter) { + return enabled && SqsImpl.afterResponse(request, response, consumerInstrumenter); + } + + @NoMuzzle + static boolean beforeMarshalling(AmazonWebServiceRequest request) { + return enabled && SqsImpl.beforeMarshalling(request); + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsImpl.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsImpl.java new file mode 100644 index 000000000000..f4cd92757999 --- /dev/null +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsImpl.java @@ -0,0 +1,70 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.awssdk.v1_11; + +import com.amazonaws.AmazonWebServiceRequest; +import com.amazonaws.Request; +import com.amazonaws.Response; +import com.amazonaws.services.sqs.AmazonSQS; +import com.amazonaws.services.sqs.model.Message; +import com.amazonaws.services.sqs.model.ReceiveMessageRequest; +import com.amazonaws.services.sqs.model.ReceiveMessageResult; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; + +final class SqsImpl { + static { + // Force loading of SQS class; this ensures that an exception is thrown at this point when the + // SQS library is not present, which will cause SqsAccess to have enabled=false in library mode. + @SuppressWarnings("unused") + String ensureLoadedDummy = AmazonSQS.class.getName(); + } + + private SqsImpl() {} + + static boolean afterResponse( + Request request, + Response response, + Instrumenter, Response> consumerInstrumenter) { + if (response.getAwsResponse() instanceof ReceiveMessageResult) { + afterConsumerResponse(request, response, consumerInstrumenter); + return true; + } + return false; + } + + /** Create and close CONSUMER span for each message consumed. */ + private static void afterConsumerResponse( + Request request, + Response response, + Instrumenter, Response> consumerInstrumenter) { + ReceiveMessageResult receiveMessageResult = (ReceiveMessageResult) response.getAwsResponse(); + for (Message message : receiveMessageResult.getMessages()) { + createConsumerSpan(message, request, response, consumerInstrumenter); + } + } + + private static void createConsumerSpan( + Message message, + Request request, + Response response, + Instrumenter, Response> consumerInstrumenter) { + Context parentContext = SqsParentContext.ofSystemAttributes(message.getAttributes()); + Context context = consumerInstrumenter.start(parentContext, request); + consumerInstrumenter.end(context, request, response, null); + } + + static boolean beforeMarshalling(AmazonWebServiceRequest rawRequest) { + if (rawRequest instanceof ReceiveMessageRequest) { + ReceiveMessageRequest request = (ReceiveMessageRequest) rawRequest; + if (!request.getAttributeNames().contains(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE)) { + request.withAttributeNames(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE); + } + return true; + } + return false; + } +} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsMessageAccess.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsMessageAccess.java deleted file mode 100644 index 84726bab934f..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsMessageAccess.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v1_11; - -import static java.lang.invoke.MethodType.methodType; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.Collections; -import java.util.Map; -import javax.annotation.Nullable; - -/** - * Reflective access to aws-sdk-java-sqs class Message. - * - *

We currently don't have a good pattern of instrumenting a core library with various plugins - * that need plugin-specific instrumentation - if we accessed the class directly, Muzzle would - * prevent the entire instrumentation from loading when the plugin isn't available. We need to - * carefully check this class has all reflection errors result in no-op, and in the future we will - * hopefully come up with a better pattern. - */ -final class SqsMessageAccess { - - @Nullable private static final MethodHandle GET_ATTRIBUTES; - - static { - Class messageClass = null; - try { - messageClass = Class.forName("com.amazonaws.services.sqs.model.Message"); - } catch (Throwable t) { - // Ignore. - } - if (messageClass != null) { - MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - MethodHandle getAttributes = null; - try { - getAttributes = lookup.findVirtual(messageClass, "getAttributes", methodType(Map.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - // Ignore - } - GET_ATTRIBUTES = getAttributes; - } else { - GET_ATTRIBUTES = null; - } - } - - @SuppressWarnings("unchecked") - static Map getAttributes(Object message) { - if (GET_ATTRIBUTES == null) { - return Collections.emptyMap(); - } - try { - return (Map) GET_ATTRIBUTES.invoke(message); - } catch (Throwable t) { - return Collections.emptyMap(); - } - } - - private SqsMessageAccess() {} -} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsReceiveMessageRequestAccess.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsReceiveMessageRequestAccess.java deleted file mode 100644 index 122e66f8c0d3..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsReceiveMessageRequestAccess.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v1_11; - -import static java.lang.invoke.MethodType.methodType; - -import com.amazonaws.AmazonWebServiceRequest; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nullable; - -/** - * Reflective access to aws-sdk-java-sqs class ReceiveMessageRequest. - * - *

We currently don't have a good pattern of instrumenting a core library with various plugins - * that need plugin-specific instrumentation - if we accessed the class directly, Muzzle would - * prevent the entire instrumentation from loading when the plugin isn't available. We need to - * carefully check this class has all reflection errors result in no-op, and in the future we will - * hopefully come up with a better pattern. - */ -final class SqsReceiveMessageRequestAccess { - - @Nullable private static final MethodHandle WITH_ATTRIBUTE_NAMES; - @Nullable private static final MethodHandle GET_ATTRIBUTE_NAMES; - - static { - Class receiveMessageRequestClass = null; - try { - receiveMessageRequestClass = - Class.forName("com.amazonaws.services.sqs.model.ReceiveMessageRequest"); - } catch (Throwable t) { - // Ignore. - } - if (receiveMessageRequestClass != null) { - MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - MethodHandle withAttributeNames = null; - try { - withAttributeNames = - lookup.findVirtual( - receiveMessageRequestClass, - "withAttributeNames", - methodType(receiveMessageRequestClass, String[].class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - // Ignore - } - WITH_ATTRIBUTE_NAMES = withAttributeNames; - - MethodHandle getAttributeNames = null; - try { - getAttributeNames = - lookup.findVirtual( - receiveMessageRequestClass, "getAttributeNames", methodType(List.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - // Ignore - } - GET_ATTRIBUTE_NAMES = getAttributeNames; - } else { - WITH_ATTRIBUTE_NAMES = null; - GET_ATTRIBUTE_NAMES = null; - } - } - - static boolean isInstance(AmazonWebServiceRequest request) { - return request - .getClass() - .getName() - .equals("com.amazonaws.services.sqs.model.ReceiveMessageRequest"); - } - - static void withAttributeNames(AmazonWebServiceRequest request, String name) { - if (WITH_ATTRIBUTE_NAMES == null) { - return; - } - try { - WITH_ATTRIBUTE_NAMES.invoke(request, name); - } catch (Throwable throwable) { - // Ignore - } - } - - @SuppressWarnings("unchecked") - static List getAttributeNames(AmazonWebServiceRequest request) { - if (GET_ATTRIBUTE_NAMES == null) { - return Collections.emptyList(); - } - try { - return (List) GET_ATTRIBUTE_NAMES.invoke(request); - } catch (Throwable t) { - return Collections.emptyList(); - } - } - - private SqsReceiveMessageRequestAccess() {} -} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsReceiveMessageResultAccess.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsReceiveMessageResultAccess.java deleted file mode 100644 index 27fb6e255f92..000000000000 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/SqsReceiveMessageResultAccess.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright The OpenTelemetry Authors - * SPDX-License-Identifier: Apache-2.0 - */ - -package io.opentelemetry.instrumentation.awssdk.v1_11; - -import static java.lang.invoke.MethodType.methodType; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.util.Collections; -import java.util.List; -import javax.annotation.Nullable; - -/** - * Reflective access to aws-sdk-java-sqs class ReceiveMessageResult. - * - *

We currently don't have a good pattern of instrumenting a core library with various plugins - * that need plugin-specific instrumentation - if we accessed the class directly, Muzzle would - * prevent the entire instrumentation from loading when the plugin isn't available. We need to - * carefully check this class has all reflection errors result in no-op, and in the future we will - * hopefully come up with a better pattern. - */ -final class SqsReceiveMessageResultAccess { - - @Nullable private static final MethodHandle GET_MESSAGES; - - static { - Class receiveMessageResultClass = null; - try { - receiveMessageResultClass = - Class.forName("com.amazonaws.services.sqs.model.ReceiveMessageResult"); - } catch (Throwable t) { - // Ignore. - } - if (receiveMessageResultClass != null) { - MethodHandles.Lookup lookup = MethodHandles.publicLookup(); - MethodHandle getMessages = null; - try { - getMessages = - lookup.findVirtual(receiveMessageResultClass, "getMessages", methodType(List.class)); - } catch (NoSuchMethodException | IllegalAccessException e) { - // Ignore - } - GET_MESSAGES = getMessages; - } else { - GET_MESSAGES = null; - } - } - - static List getMessages(Object result) { - if (GET_MESSAGES == null) { - return Collections.emptyList(); - } - try { - return (List) GET_MESSAGES.invoke(result); - } catch (Throwable t) { - return Collections.emptyList(); - } - } - - private SqsReceiveMessageResultAccess() {} -} diff --git a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java index a6755552197c..11d2f3a6fed3 100644 --- a/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java +++ b/instrumentation/aws-sdk/aws-sdk-1.11/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v1_11/TracingRequestHandler.java @@ -14,7 +14,6 @@ import io.opentelemetry.context.Context; import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator; import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; -import java.util.List; import javax.annotation.Nullable; /** Tracing Request Handler. */ @@ -58,40 +57,18 @@ public void beforeRequest(Request request) { @Override @CanIgnoreReturnValue public AmazonWebServiceRequest beforeMarshalling(AmazonWebServiceRequest request) { - if (SqsReceiveMessageRequestAccess.isInstance(request)) { - if (!SqsReceiveMessageRequestAccess.getAttributeNames(request) - .contains(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE)) { - SqsReceiveMessageRequestAccess.withAttributeNames( - request, SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE); - } - } + // TODO: We are modifying the request in-place instead of using clone() as recommended + // by the Javadoc in the interface. + SqsAccess.beforeMarshalling(request); return request; } @Override public void afterResponse(Request request, Response response) { - if (SqsReceiveMessageRequestAccess.isInstance(request.getOriginalRequest())) { - afterConsumerResponse(request, response); - } + SqsAccess.afterResponse(request, response, consumerInstrumenter); finish(request, response, null); } - /** Create and close CONSUMER span for each message consumed. */ - private void afterConsumerResponse(Request request, Response response) { - Object receiveMessageResult = response.getAwsResponse(); - List messages = SqsReceiveMessageResultAccess.getMessages(receiveMessageResult); - for (Object message : messages) { - createConsumerSpan(message, request, response); - } - } - - private void createConsumerSpan(Object message, Request request, Response response) { - Context parentContext = - SqsParentContext.ofSystemAttributes(SqsMessageAccess.getAttributes(message)); - Context context = consumerInstrumenter.start(parentContext, request); - consumerInstrumenter.end(context, request, response, null); - } - @Override public void afterError(Request request, Response response, Exception e) { finish(request, response, e); diff --git a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/PluginImplUtil.java b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/PluginImplUtil.java index f5043ae13257..3ba1611a541e 100644 --- a/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/PluginImplUtil.java +++ b/instrumentation/aws-sdk/aws-sdk-2.2/library/src/main/java/io/opentelemetry/instrumentation/awssdk/v2_2/PluginImplUtil.java @@ -8,7 +8,7 @@ import java.util.logging.Level; import java.util.logging.Logger; -final class PluginImplUtil { +final class PluginImplUtil { // TODO: Copy & pasted to v1 private PluginImplUtil() {} private static final Logger logger = Logger.getLogger(PluginImplUtil.class.getName()); @@ -16,33 +16,40 @@ private PluginImplUtil() {} /** * Check if the given {@code moduleNameImpl} is present. * - *

For library instrumentations, the Impls will always be available but might fail to load if - * the corresponding SDK classes are not on the class path. For javaagent, the Impl is available - * only when the corresponding InstrumentationModule was successfully applied (muzzle passed). + *

For library instrumentations, the Impls will always be available but might fail to + * load/link/initialize if the corresponding SDK classes are not on the class path. For javaagent, + * the Impl is available only when the corresponding InstrumentationModule was successfully + * applied (muzzle passed). * - *

Note that an present-but-incompatible library can only be detected by Muzzle. In + *

Note that an present-but-incompatible library can only be reliably detected by Muzzle. In * library-mode, users need to ensure they are using a compatible SDK (component) versions * themselves. * * @param implSimpleClassName The simple name of the impl class, e.g. {@code "SqsImpl"}. * */ static boolean isImplPresent(String implSimpleClassName) { + // Computing the full name dynamically name here because library instrumentation classes are + // relocated when embedded in the agent. // We use getName().replace() instead of getPackage() because the latter is not guaranteed to // work in all cases (e.g., we might be loaded into a custom classloader that doesn't handle it) String implFullClassName = PluginImplUtil.class.getName().replace(".PluginImplUtil", "." + implSimpleClassName); try { - // using package name here because library instrumentation classes are relocated when embedded - // in the agent Class.forName(implFullClassName); return true; } catch (ClassNotFoundException | LinkageError e) { // ClassNotFoundException will happen when muzzle disabled us in javaagent mode; LinkageError - // (most likely NoClassDefFoundError) should happen when the class is loaded in library mode - // (where it is always available) but a dependency failed to load (most likely because the - // corresponding SDK dependency is not on the class path). + // (most likely a NoClassDefFoundError, potentially wrapped in an ExceptionInInitializerError) + // should be thrown when the class is loaded in library mode (where the Impl class itself can + // always be found) but a dependency failed to load (most likely because the corresponding SDK + // dependency is not on the class path). logger.log( - Level.FINE, e, () -> implFullClassName + " not present, probably incompatible version"); + Level.FINE, + e, + () -> + implFullClassName + + " not present. " + + "Most likely, corresponding SDK component is either not on classpath or incompatible."); return false; } }