Fix the main branch history (#8817)

This commit is contained in:
Mateusz Rzeszutek 2023-06-28 12:55:41 +02:00 committed by GitHub
parent 47159e6a7c
commit 9e37e724dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 408 additions and 302 deletions

View File

@ -8,7 +8,7 @@ plugins {
}
dependencies {
jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.1.0")
jmhImplementation("org.springframework.boot:spring-boot-starter-web:3.1.1")
}
tasks {

View File

@ -59,8 +59,8 @@ dependencies {
implementation("gradle.plugin.com.google.protobuf:protobuf-gradle-plugin:0.8.18")
implementation("com.github.johnrengelman:shadow:8.1.1")
implementation("org.apache.httpcomponents:httpclient:4.5.14")
implementation("com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.13.3")
implementation("org.owasp:dependency-check-gradle:8.2.1")
implementation("com.gradle.enterprise:com.gradle.enterprise.gradle.plugin:3.13.4")
implementation("org.owasp:dependency-check-gradle:8.3.1")
implementation("ru.vyarus:gradle-animalsniffer-plugin:1.7.0")
// When updating, also update dependencyManagement/build.gradle.kts
implementation("net.bytebuddy:byte-buddy-gradle-plugin:1.14.5")

View File

@ -84,10 +84,10 @@ val DEPENDENCIES = listOf(
"com.github.stefanbirkner:system-lambda:1.2.1",
"com.github.stefanbirkner:system-rules:1.19.0",
"uk.org.webcompere:system-stubs-jupiter:2.0.2",
"com.uber.nullaway:nullaway:0.10.10",
"com.uber.nullaway:nullaway:0.10.11",
"commons-beanutils:commons-beanutils:1.9.4",
"commons-cli:commons-cli:1.5.0",
"commons-codec:commons-codec:1.15",
"commons-codec:commons-codec:1.16.0",
"commons-collections:commons-collections:3.2.2",
"commons-digester:commons-digester:2.1",
"commons-fileupload:commons-fileupload:1.5",

View File

@ -70,7 +70,7 @@ subprojects {
implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom:${versions.opentelemetryJavaagent}"))
implementation(platform("io.opentelemetry.instrumentation:opentelemetry-instrumentation-bom-alpha:${versions.opentelemetryJavaagentAlpha}"))
testImplementation("org.mockito:mockito-core:5.3.1")
testImplementation("org.mockito:mockito-core:5.4.0")
testImplementation(enforcedPlatform("org.junit:junit-bom:${versions.junit}"))
testImplementation("org.junit.jupiter:junit-jupiter-api:${versions.junit}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${versions.junit}")

View File

@ -0,0 +1,80 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.api.instrumenter.network;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes;
import java.net.InetSocketAddress;
import javax.annotation.Nullable;
import org.junit.jupiter.api.Test;
class ClientAttributesExtractorInetSocketAddressTest {
static class TestClientAttributesGetter
implements ClientAttributesGetter<InetSocketAddress, Void> {
@Nullable
@Override
public String getClientAddress(InetSocketAddress request) {
// covered in ClientAttributesExtractorTest
return null;
}
@Nullable
@Override
public Integer getClientPort(InetSocketAddress request) {
// covered in ClientAttributesExtractorTest
return null;
}
@Nullable
@Override
public InetSocketAddress getClientInetSocketAddress(
InetSocketAddress request, @Nullable Void response) {
return request;
}
}
@Test
void fullAddress() {
InetSocketAddress address = new InetSocketAddress("api.github.com", 456);
assertThat(address.getAddress().getHostAddress()).isNotNull();
AttributesExtractor<InetSocketAddress, Void> extractor =
ClientAttributesExtractor.create(new TestClientAttributesGetter());
AttributesBuilder startAttributes = Attributes.builder();
extractor.onStart(startAttributes, Context.root(), address);
assertThat(startAttributes.build()).isEmpty();
AttributesBuilder endAttributes = Attributes.builder();
extractor.onEnd(endAttributes, Context.root(), address, null, null);
assertThat(endAttributes.build())
.containsOnly(
entry(NetworkAttributes.CLIENT_SOCKET_ADDRESS, address.getAddress().getHostAddress()),
entry(NetworkAttributes.CLIENT_SOCKET_PORT, 456L));
}
@Test
void noAttributes() {
AttributesExtractor<InetSocketAddress, Void> extractor =
ClientAttributesExtractor.create(new TestClientAttributesGetter());
AttributesBuilder startAttributes = Attributes.builder();
extractor.onStart(startAttributes, Context.root(), null);
assertThat(startAttributes.build()).isEmpty();
AttributesBuilder endAttributes = Attributes.builder();
extractor.onEnd(endAttributes, Context.root(), null, null, null);
assertThat(endAttributes.build()).isEmpty();
}
}

View File

@ -15,9 +15,23 @@ muzzle {
// several software.amazon.awssdk artifacts are missing for this version
skip("2.17.200")
}
}
muzzle {
fail {
group.set("software.amazon.awssdk")
module.set("aws-core")
versions.set("[2.2.0,)")
// Used by all SDK services, the only case it isn't is an SDK extension such as a custom HTTP
// client, which is not target of instrumentation anyways.
extraDependency("software.amazon.awssdk:protocol-core")
// "fail" asserts that *all* the instrumentation modules fail to load, but the core one is
// actually expected to succeed, so exclude it from checks.
excludeInstrumentationName("aws-sdk-2.2-core")
// several software.amazon.awssdk artifacts are missing for this version
skip("2.17.200")
}
pass {
group.set("software.amazon.awssdk")
module.set("sqs")
@ -53,17 +67,26 @@ dependencies {
latestDepTestLibrary("software.amazon.awssdk:sqs:+")
}
tasks.withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
// TODO run tests both with and without experimental span attributes, with & without extra propagation
systemProperties(mapOf(
"otel.instrumentation.aws-sdk.experimental-span-attributes" to "true",
"otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging" to "true",
))
}
tasks {
val testExperimentalSqs by registering(Test::class) {
group = "verification"
tasks.withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>().configureEach {
mergeServiceFiles {
include("software/amazon/awssdk/global/handlers/execution.interceptors")
systemProperty("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", "true")
}
check {
dependsOn(testExperimentalSqs)
}
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
// TODO run tests both with and without experimental span attributes
systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", "true")
}
withType<com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar>().configureEach {
mergeServiceFiles {
include("software/amazon/awssdk/global/handlers/execution.interceptors")
}
}
}

View File

@ -5,7 +5,16 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2;
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;
abstract class AbstractAwsSdkInstrumentationModule extends InstrumentationModule {
@ -17,4 +26,34 @@ abstract class AbstractAwsSdkInstrumentationModule extends InstrumentationModule
public boolean isHelperClass(String className) {
return className.startsWith("io.opentelemetry.contrib.awsxray.");
}
@Override
public ElementMatcher.Junction<ClassLoader> 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("software.amazon.awssdk.core.interceptor.ExecutionInterceptor");
}
@Override
public List<TypeInstrumentation> 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<TypeDescription> 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("software.amazon.awssdk.core.SdkClient");
}
@Override
public void transform(TypeTransformer transformer) {
doTransform(transformer);
}
}
}

View File

@ -5,19 +5,11 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor;
import io.opentelemetry.javaagent.extension.instrumentation.HelperResourceBuilder;
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;
@AutoService(InstrumentationModule.class)
public class AwsSdkInstrumentationModule extends AbstractAwsSdkInstrumentationModule {
@ -35,30 +27,7 @@ public class AwsSdkInstrumentationModule extends AbstractAwsSdkInstrumentationMo
}
@Override
public ElementMatcher.Junction<ClassLoader> 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("software.amazon.awssdk.core.interceptor.ExecutionInterceptor");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ResourceInjectingTypeInstrumentation());
}
// A type instrumentation is needed to trigger resource injection.
public static class ResourceInjectingTypeInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> 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("software.amazon.awssdk.core.SdkClient");
}
@Override
public void transform(TypeTransformer transformer) {
// Nothing to transform, this type instrumentation is only used for injecting resources.
}
void doTransform(TypeTransformer transformer) {
// Nothing to transform, this type instrumentation is only used for injecting resources.
}
}

View File

@ -5,19 +5,13 @@
package io.opentelemetry.javaagent.instrumentation.awssdk.v2_2;
import static java.util.Collections.singletonList;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.none;
import com.google.auto.service.AutoService;
import io.opentelemetry.instrumentation.awssdk.v2_2.SqsAdviceBridge;
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.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(InstrumentationModule.class)
public class SqsInstrumentationModule extends AbstractAwsSdkInstrumentationModule {
@ -27,21 +21,9 @@ public class SqsInstrumentationModule extends AbstractAwsSdkInstrumentationModul
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new DefaultSqsClientTypeInstrumentation());
}
public static class DefaultSqsClientTypeInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("software.amazon.awssdk.services.sqs.DefaultSqsClient");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), SqsInstrumentationModule.class.getName() + "$RegisterAdvice");
}
public void doTransform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
none(), SqsInstrumentationModule.class.getName() + "$RegisterAdvice");
}
@SuppressWarnings("unused")
@ -50,7 +32,7 @@ public class SqsInstrumentationModule extends AbstractAwsSdkInstrumentationModul
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.init();
SqsAdviceBridge.referenceForMuzzleOnly();
}
}
}

View File

@ -26,5 +26,6 @@ tasks {
test {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
systemProperty("otel.instrumentation.aws-sdk.experimental-span-attributes", true)
systemProperty("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", true)
}
}

View File

@ -8,12 +8,18 @@ package io.opentelemetry.instrumentation.awssdk.v2_2;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.javaagent.tooling.muzzle.NoMuzzle;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.SdkResponse;
import software.amazon.awssdk.core.interceptor.Context;
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest;
import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse;
import software.amazon.awssdk.services.sqs.model.SendMessageRequest;
// helper class for calling methods that use sqs types in SqsImpl
// if SqsImpl is not present these methods are no op
final class SqsAccess {
private SqsAccess() {}
private static final boolean enabled = isSqsImplPresent();
private static boolean isSqsImplPresent() {
@ -31,14 +37,34 @@ final class SqsAccess {
}
@NoMuzzle
static SdkRequest injectIntoSqsSendMessageRequest(
static boolean isSendMessageRequest(SdkRequest request) {
return enabled && request instanceof SendMessageRequest;
}
@NoMuzzle
static SdkRequest injectIntoSendMessageRequest(
TextMapPropagator messagingPropagator,
SdkRequest rawRequest,
io.opentelemetry.context.Context otelContext) {
if (!enabled) {
return rawRequest;
}
return SqsImpl.injectIntoSqsSendMessageRequest(messagingPropagator, rawRequest, otelContext);
assert enabled; // enabled checked already in instance check.
return SqsImpl.injectIntoSendMessageRequest(messagingPropagator, rawRequest, otelContext);
}
@NoMuzzle
static boolean isReceiveMessageRequest(SdkRequest request) {
return enabled && request instanceof ReceiveMessageRequest;
}
@NoMuzzle
public static SdkRequest modifyReceiveMessageRequest(
SdkRequest request, boolean useXrayPropagator, TextMapPropagator messagingPropagator) {
assert enabled; // enabled checked already in instance check.
return SqsImpl.modifyReceiveMessageRequest(request, useXrayPropagator, messagingPropagator);
}
@NoMuzzle
static boolean isReceiveMessageResponse(SdkResponse response) {
return enabled && response instanceof ReceiveMessageResponse;
}
@NoMuzzle
@ -46,11 +72,7 @@ final class SqsAccess {
TracingExecutionInterceptor config,
Context.AfterExecution context,
ExecutionAttributes executionAttributes) {
if (!enabled) {
return;
}
SqsImpl.afterConsumerResponse(config, executionAttributes, context);
assert enabled; // enabled checked already in instance check.
SqsImpl.afterReceiveMessageExecution(config, executionAttributes, context);
}
private SqsAccess() {}
}

View File

@ -8,8 +8,8 @@ package io.opentelemetry.instrumentation.awssdk.v2_2;
public final class SqsAdviceBridge {
private SqsAdviceBridge() {}
public static void init() {
// called from advice
SqsImpl.init(); // Reference the actual, package-private, implementation class for Muzzle
public static void referenceForMuzzleOnly() {
throw new UnsupportedOperationException(
SqsImpl.class.getName() + " referencing for muzzle, should never be actually called");
}
}

View File

@ -7,7 +7,9 @@ package io.opentelemetry.instrumentation.awssdk.v2_2;
import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import software.amazon.awssdk.core.SdkRequest;
import software.amazon.awssdk.core.interceptor.Context;
@ -15,6 +17,7 @@ import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
import software.amazon.awssdk.http.SdkHttpResponse;
import software.amazon.awssdk.services.sqs.model.Message;
import software.amazon.awssdk.services.sqs.model.MessageAttributeValue;
import software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest;
import software.amazon.awssdk.services.sqs.model.ReceiveMessageResponse;
import software.amazon.awssdk.services.sqs.model.SendMessageRequest;
@ -22,11 +25,7 @@ import software.amazon.awssdk.services.sqs.model.SendMessageRequest;
final class SqsImpl {
private SqsImpl() {}
public static void init() {
// called from advice
}
static SdkRequest injectIntoSqsSendMessageRequest(
static SdkRequest injectIntoSendMessageRequest(
TextMapPropagator messagingPropagator,
SdkRequest rawRequest,
io.opentelemetry.context.Context otelContext) {
@ -48,7 +47,7 @@ final class SqsImpl {
}
/** Create and close CONSUMER span for each message consumed. */
static void afterConsumerResponse(
static void afterReceiveMessageExecution(
TracingExecutionInterceptor config,
ExecutionAttributes executionAttributes,
Context.AfterExecution context) {
@ -91,4 +90,44 @@ final class SqsImpl {
consumerInstrumenter.end(context, executionAttributes, httpResponse, null);
}
}
static SdkRequest modifyReceiveMessageRequest(
SdkRequest rawRequest, boolean useXrayPropagator, TextMapPropagator messagingPropagator) {
ReceiveMessageRequest request = (ReceiveMessageRequest) rawRequest;
boolean hasXrayAttribute = true;
List<String> existingAttributeNames = null;
if (useXrayPropagator) {
existingAttributeNames = request.attributeNamesAsStrings();
hasXrayAttribute =
existingAttributeNames.contains(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE);
}
boolean hasMessageAttribute = true;
List<String> existingMessageAttributeNames = null;
if (messagingPropagator != null) {
existingMessageAttributeNames = request.messageAttributeNames();
hasMessageAttribute = existingMessageAttributeNames.containsAll(messagingPropagator.fields());
}
if (hasMessageAttribute && hasXrayAttribute) {
return request;
}
ReceiveMessageRequest.Builder builder = request.toBuilder();
if (!hasXrayAttribute) {
List<String> attributeNames = new ArrayList<>(existingAttributeNames);
attributeNames.add(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE);
builder.attributeNamesWithStrings(attributeNames);
}
if (messagingPropagator != null) {
List<String> messageAttributeNames = new ArrayList<>(existingMessageAttributeNames);
for (String field : messagingPropagator.fields()) {
if (!existingMessageAttributeNames.contains(field)) {
messageAttributeNames.add(field);
}
}
builder.messageAttributeNames(messageAttributeNames);
}
return builder.build();
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.awssdk.v2_2;
import static java.lang.invoke.MethodType.methodType;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
import software.amazon.awssdk.core.SdkRequest;
/**
* Reflective access to aws-sdk-java-sqs class ReceiveMessageRequest.
*
* <p>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.
*
* @see <a
* href="https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sqs/model/ReceiveMessageRequest.html">SDK
* Javadoc</a>
* @see <a
* href="https://github.com/aws/aws-sdk-java-v2/blob/2.2.0/services/sqs/src/main/resources/codegen-resources/service-2.json#L1076-L1110">Definition
* JSON</a>
*/
final class SqsReceiveMessageRequestAccess {
@Nullable private static final MethodHandle ATTRIBUTE_NAMES_WITH_STRINGS;
@Nullable private static final MethodHandle MESSAGE_ATTRIBUTE_NAMES;
static {
Class<?> receiveMessageRequestClass = null;
try {
receiveMessageRequestClass =
Class.forName("software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest$Builder");
} catch (Throwable t) {
// Ignore.
}
if (receiveMessageRequestClass != null) {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodHandle withAttributeNames = null;
try {
withAttributeNames =
lookup.findVirtual(
receiveMessageRequestClass,
"attributeNamesWithStrings",
methodType(receiveMessageRequestClass, Collection.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
// Ignore
}
ATTRIBUTE_NAMES_WITH_STRINGS = withAttributeNames;
MethodHandle messageAttributeNames = null;
try {
messageAttributeNames =
lookup.findVirtual(
receiveMessageRequestClass,
"messageAttributeNames",
methodType(receiveMessageRequestClass, Collection.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
// Ignore
}
MESSAGE_ATTRIBUTE_NAMES = messageAttributeNames;
} else {
ATTRIBUTE_NAMES_WITH_STRINGS = null;
MESSAGE_ATTRIBUTE_NAMES = null;
}
}
static boolean isInstance(SdkRequest request) {
return request
.getClass()
.getName()
.equals("software.amazon.awssdk.services.sqs.model.ReceiveMessageRequest");
}
static void attributeNamesWithStrings(SdkRequest.Builder builder, List<String> attributeNames) {
if (ATTRIBUTE_NAMES_WITH_STRINGS == null) {
return;
}
try {
ATTRIBUTE_NAMES_WITH_STRINGS.invoke(builder, attributeNames);
} catch (Throwable throwable) {
// Ignore
}
}
static void messageAttributeNames(
SdkRequest.Builder builder, List<String> messageAttributeNames) {
if (MESSAGE_ATTRIBUTE_NAMES == null) {
return;
}
try {
MESSAGE_ATTRIBUTE_NAMES.invoke(builder, messageAttributeNames);
} catch (Throwable throwable) {
// Ignore
}
}
private SqsReceiveMessageRequestAccess() {}
@SuppressWarnings({"rawtypes", "unchecked"})
static List<String> getAttributeNames(SdkRequest request) {
Optional<List> optional = request.getValueForField("AttributeNames", List.class);
return optional.isPresent() ? (List<String>) optional.get() : Collections.emptyList();
}
@SuppressWarnings({"rawtypes", "unchecked"})
static List<String> getMessageAttributeNames(SdkRequest request) {
Optional<List> optional = request.getValueForField("MessageAttributeNames", List.class);
return optional.isPresent() ? (List<String>) optional.get() : Collections.emptyList();
}
}

View File

@ -1,23 +0,0 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.awssdk.v2_2;
import software.amazon.awssdk.core.SdkRequest;
/**
* Reflective access to aws-sdk-java-sqs class ReceiveMessageRequest for points where we are not
* sure whether SQS is on the classpath.
*/
final class SqsSendMessageRequestAccess {
static boolean isInstance(SdkRequest request) {
return request
.getClass()
.getName()
.equals("software.amazon.awssdk.services.sqs.model.SendMessageRequest");
}
private SqsSendMessageRequestAccess() {}
}

View File

@ -15,7 +15,6 @@ import io.opentelemetry.context.propagation.TextMapPropagator;
import io.opentelemetry.contrib.awsxray.propagator.AwsXrayPropagator;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nullable;
import software.amazon.awssdk.awscore.AwsResponse;
@ -122,56 +121,17 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
throw throwable;
}
if (SqsReceiveMessageRequestAccess.isInstance(request)) {
return modifySqsReceiveMessageRequest(request);
if (SqsAccess.isReceiveMessageRequest(request)) {
return SqsAccess.modifyReceiveMessageRequest(request, useXrayPropagator, messagingPropagator);
} else if (messagingPropagator != null) {
if (SqsSendMessageRequestAccess.isInstance(request)) {
return SqsAccess.injectIntoSqsSendMessageRequest(messagingPropagator, request, otelContext);
if (SqsAccess.isSendMessageRequest(request)) {
return SqsAccess.injectIntoSendMessageRequest(messagingPropagator, request, otelContext);
}
// TODO: Support SendMessageBatchRequest (and thus SendMessageBatchRequestEntry)
}
return request;
}
private SdkRequest modifySqsReceiveMessageRequest(SdkRequest request) {
boolean hasXrayAttribute = true;
List<String> existingAttributeNames = null;
if (useXrayPropagator) {
existingAttributeNames = SqsReceiveMessageRequestAccess.getAttributeNames(request);
hasXrayAttribute =
existingAttributeNames.contains(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE);
}
boolean hasMessageAttribute = true;
List<String> existingMessageAttributeNames = null;
if (messagingPropagator != null) {
existingMessageAttributeNames =
SqsReceiveMessageRequestAccess.getMessageAttributeNames(request);
hasMessageAttribute = existingMessageAttributeNames.containsAll(messagingPropagator.fields());
}
if (hasMessageAttribute && hasXrayAttribute) {
return request;
}
SdkRequest.Builder builder = request.toBuilder();
if (!hasXrayAttribute) {
List<String> attributeNames = new ArrayList<>(existingAttributeNames);
attributeNames.add(SqsParentContext.AWS_TRACE_SYSTEM_ATTRIBUTE);
SqsReceiveMessageRequestAccess.attributeNamesWithStrings(builder, attributeNames);
}
if (messagingPropagator != null) {
List<String> messageAttributeNames = new ArrayList<>(existingMessageAttributeNames);
for (String field : messagingPropagator.fields()) {
if (!existingMessageAttributeNames.contains(field)) {
messageAttributeNames.add(field);
}
}
SqsReceiveMessageRequestAccess.messageAttributeNames(builder, messageAttributeNames);
}
return builder.build();
}
@Override
public void afterMarshalling(
Context.AfterMarshalling context, ExecutionAttributes executionAttributes) {
@ -265,7 +225,7 @@ final class TracingExecutionInterceptor implements ExecutionInterceptor {
@Override
public void afterExecution(
Context.AfterExecution context, ExecutionAttributes executionAttributes) {
if (SqsReceiveMessageRequestAccess.isInstance(context.request())) {
if (SqsAccess.isReceiveMessageResponse(context.response())) {
SqsAccess.afterReceiveMessageExecution(this, context, executionAttributes);
}

View File

@ -15,7 +15,7 @@ class Aws2ClientTest extends AbstractAws2ClientTest implements LibraryTestTrait
.addExecutionInterceptor(
AwsSdkTelemetry.builder(getOpenTelemetry())
.setCaptureExperimentalSpanAttributes(true)
.setUseConfiguredPropagatorForMessaging(true) // Default on in tests to cover more code
.setUseConfiguredPropagatorForMessaging(isSqsAttributeInjectionEnabled())
.build()
.newExecutionInterceptor())
}

View File

@ -5,12 +5,14 @@
package io.opentelemetry.instrumentation.awssdk.v2_2
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import io.opentelemetry.testing.internal.armeria.common.HttpResponse
import io.opentelemetry.testing.internal.armeria.common.HttpStatus
import io.opentelemetry.testing.internal.armeria.common.MediaType
import io.opentelemetry.testing.internal.armeria.testing.junit5.server.mock.MockWebServerExtension
import org.junit.jupiter.api.Assumptions
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.core.ResponseInputStream
@ -48,6 +50,7 @@ import software.amazon.awssdk.services.s3.model.GetObjectRequest
import software.amazon.awssdk.services.sqs.SqsAsyncClient
import software.amazon.awssdk.services.sqs.SqsClient
import software.amazon.awssdk.services.sqs.model.CreateQueueRequest
import software.amazon.awssdk.services.sqs.model.SendMessageRequest
import spock.lang.Shared
import spock.lang.Unroll
@ -317,7 +320,22 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
return AttributeValue.builder().s(value).build()
}
def isSqsAttributeInjectionEnabled() {
// See io.opentelemetry.instrumentation.awssdk.v2_2.autoconfigure.TracingExecutionInterceptor
return ConfigPropertiesUtil.getBoolean("otel.instrumentation.aws-sdk.experimental-use-propagator-for-messaging", false)
}
void assumeSupportedConfig(service, operation) {
Assumptions.assumeFalse(
service == "Sqs"
&& operation == "SendMessage"
&& isSqsAttributeInjectionEnabled(),
"Cannot check Sqs.SendMessage here due to hard-coded MD5.")
}
def "send #operation request with builder #builder.class.getName() mocked response"() {
assumeSupportedConfig(service, operation)
setup:
configureSdkClient(builder)
def client = builder
@ -384,6 +402,16 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
<ResponseMetadata><RequestId>7a62c49f-347e-4fc4-9331-6e8e7a96aa73</RequestId></ResponseMetadata>
</CreateQueueResponse>
"""
"Sqs" | "SendMessage" | "POST" | "" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
<SendMessageResponse>
<SendMessageResult>
<MD5OfMessageBody>d41d8cd98f00b204e9800998ecf8427e</MD5OfMessageBody>
<MD5OfMessageAttributes>3ae8f24a165a8cedc005670c81a27295</MD5OfMessageAttributes>
<MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
</SendMessageResult>
<ResponseMetadata><RequestId>27daac76-34dd-47df-bd01-1f6e873584a0</RequestId></ResponseMetadata>
</SendMessageResponse>
"""
"Ec2" | "AllocateAddress" | "POST" | "" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2Client.builder() | { c -> c.allocateAddress() } | """
<AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>
@ -399,6 +427,7 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
}
def "send #operation async request with builder #builder.class.getName() mocked response"() {
assumeSupportedConfig(service, operation)
setup:
configureSdkClient(builder)
def client = builder
@ -465,6 +494,16 @@ abstract class AbstractAws2ClientTest extends InstrumentationSpecification {
<ResponseMetadata><RequestId>7a62c49f-347e-4fc4-9331-6e8e7a96aa73</RequestId></ResponseMetadata>
</CreateQueueResponse>
"""
"Sqs" | "SendMessage" | "POST" | "" | "27daac76-34dd-47df-bd01-1f6e873584a0" | SqsAsyncClient.builder() | { c -> c.sendMessage(SendMessageRequest.builder().queueUrl("someurl").messageBody("").build()) } | """
<SendMessageResponse>
<SendMessageResult>
<MD5OfMessageBody>d41d8cd98f00b204e9800998ecf8427e</MD5OfMessageBody>
<MD5OfMessageAttributes>3ae8f24a165a8cedc005670c81a27295</MD5OfMessageAttributes>
<MessageId>5fea7756-0ea4-451a-a703-a558b933e274</MessageId>
</SendMessageResult>
<ResponseMetadata><RequestId>27daac76-34dd-47df-bd01-1f6e873584a0</RequestId></ResponseMetadata>
</SendMessageResponse>
"""
"Ec2" | "AllocateAddress" | "POST" | "" | "59dbff89-35bd-4eac-99ed-be587EXAMPLE" | Ec2AsyncClient.builder() | { c -> c.allocateAddress() } | """
<AllocateAddressResponse xmlns="http://ec2.amazonaws.com/doc/2016-11-15/">
<requestId>59dbff89-35bd-4eac-99ed-be587EXAMPLE</requestId>

View File

@ -6,7 +6,6 @@
package io.opentelemetry.instrumentation.awssdk.v2_2
import io.opentelemetry.instrumentation.test.InstrumentationSpecification
import io.opentelemetry.instrumentation.test.utils.PortUtils
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import org.elasticmq.rest.sqs.SQSRestServerBuilder
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
@ -61,10 +60,10 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification {
abstract ClientOverrideConfiguration.Builder createOverrideConfigurationBuilder()
def setupSpec() {
sqsPort = PortUtils.findOpenPort()
sqs = SQSRestServerBuilder.withPort(sqsPort).withInterface("localhost").start()
sqs = SQSRestServerBuilder.withPort(0).withInterface("localhost").start()
def server = sqs.waitUntilStarted()
sqsPort = server.localAddress().port
println getClass().name + " SQS server started at: localhost:$sqsPort/"
}
def cleanupSpec() {
@ -181,9 +180,10 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification {
when:
client.sendMessage(sendMessageRequest)
client.receiveMessage(receiveMessageRequest)
def resp = client.receiveMessage(receiveMessageRequest)
then:
resp.messages().size() == 1
assertSqsTraces()
}
@ -198,9 +198,10 @@ abstract class AbstractAws2SqsTracingTest extends InstrumentationSpecification {
when:
client.sendMessage(sendMessageRequest).get()
client.receiveMessage(receiveMessageRequest).get()
def resp = client.receiveMessage(receiveMessageRequest).get()
then:
resp.messages().size() == 1
assertSqsTraces()
}
}

View File

@ -12,6 +12,7 @@ import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.ContextDataAccessor;
import io.opentelemetry.instrumentation.log4j.appender.v2_17.internal.LogEventMapper;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import java.time.Instant;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
@ -66,6 +67,7 @@ public final class Log4jHelper {
.logRecordBuilder();
Map<String, String> contextData = ThreadContext.getImmutableContext();
mapper.mapLogEvent(builder, message, level, marker, throwable, contextData);
builder.setTimestamp(Instant.now());
builder.emit();
}

View File

@ -8,6 +8,7 @@ package io.opentelemetry.instrumentation.log4j.appender.v2_17;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.logs.Severity;
@ -16,6 +17,7 @@ import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.sdk.common.InstrumentationScopeInfo;
import io.opentelemetry.sdk.logs.data.LogRecordData;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.time.Instant;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -68,6 +70,8 @@ class Log4j2Test {
String expectedSeverityText)
throws InterruptedException {
Instant start = Instant.now();
// when
if (withParent) {
testing.runWithSpan(
@ -88,6 +92,11 @@ class Log4j2Test {
.hasInstrumentationScope(InstrumentationScopeInfo.builder(expectedLoggerName).build())
.hasSeverity(expectedSeverity)
.hasSeverityText(expectedSeverityText);
assertThat(log.getTimestampEpochNanos())
.isGreaterThanOrEqualTo(MILLISECONDS.toNanos(start.toEpochMilli()))
.isLessThanOrEqualTo(MILLISECONDS.toNanos(Instant.now().toEpochMilli()));
if (logException) {
assertThat(log)
.hasAttributesSatisfyingExactly(

View File

@ -9,6 +9,7 @@ import static io.opentelemetry.api.common.AttributeKey.stringKey;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.Attributes;
@ -24,7 +25,6 @@ import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.time.Instant;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
@ -107,8 +107,8 @@ abstract class OpenTelemetryAppenderConfigTestBase {
satisfies(SemanticAttributes.EXCEPTION_STACKTRACE, v -> v.contains("logWithExtras")));
assertThat(logDataList.get(0).getTimestampEpochNanos())
.isGreaterThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(start.toEpochMilli()))
.isLessThanOrEqualTo(TimeUnit.MILLISECONDS.toNanos(Instant.now().toEpochMilli()));
.isGreaterThanOrEqualTo(MILLISECONDS.toNanos(start.toEpochMilli()))
.isLessThanOrEqualTo(MILLISECONDS.toNanos(Instant.now().toEpochMilli()));
}
@Test

View File

@ -34,6 +34,14 @@ final class BaseClusterInstrumentation implements TypeInstrumentation {
.and(takesArgument(0, named("com.mongodb.selector.ServerSelector")))
.and(takesArgument(1, named("com.mongodb.internal.async.SingleResultCallback"))),
this.getClass().getName() + "$SingleResultCallbackArg1Advice");
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("selectServerAsync"))
.and(takesArgument(0, named("com.mongodb.selector.ServerSelector")))
.and(takesArgument(2, named("com.mongodb.internal.async.SingleResultCallback"))),
this.getClass().getName() + "$SingleResultCallbackArg2Advice");
}
@SuppressWarnings("unused")
@ -45,4 +53,14 @@ final class BaseClusterInstrumentation implements TypeInstrumentation {
callback = new SingleResultCallbackWrapper(Java8BytecodeBridge.currentContext(), callback);
}
}
@SuppressWarnings("unused")
public static class SingleResultCallbackArg2Advice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapCallback(
@Advice.Argument(value = 2, readOnly = false) SingleResultCallback<Object> callback) {
callback = new SingleResultCallbackWrapper(Java8BytecodeBridge.currentContext(), callback);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.mongo.v4_0;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.function.Consumer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
public class DefaultConnectionPoolTaskInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("com.mongodb.internal.connection.DefaultConnectionPool$Task");
}
@Override
public void transform(TypeTransformer transformer) {
// outer class this is passed as arg 0 to constructor
transformer.applyAdviceToMethod(
isConstructor().and(takesArgument(2, Consumer.class)),
this.getClass().getName() + "$TaskAdvice");
}
@SuppressWarnings("unused")
public static class TaskAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void wrapCallback(
@Advice.Argument(value = 2, readOnly = false) Consumer<Object> action) {
action = new TaskWrapper(Java8BytecodeBridge.currentContext(), action);
}
}
}

View File

@ -33,6 +33,7 @@ public class MongoClientInstrumentationModule extends InstrumentationModule {
new InternalStreamConnectionInstrumentation(),
new BaseClusterInstrumentation(),
new DefaultConnectionPoolInstrumentation(),
new DefaultConnectionPoolTaskInstrumentation(),
new AsyncWorkManagerInstrumentation());
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.mongo.v4_0;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.util.function.Consumer;
public class TaskWrapper implements Consumer<Object> {
private final Context context;
private final Consumer<Object> delegate;
public TaskWrapper(Context context, Consumer<Object> delegate) {
this.context = context;
this.delegate = delegate;
}
@Override
public void accept(Object value) {
try (Scope ignored = context.makeCurrent()) {
delegate.accept(value);
}
}
}

View File

@ -1,5 +1,3 @@
473 456 254:1 /docker/containers/be522444b60caf2d3934b8b24b916a8a314f4b68d4595aa419874657e8d103f2/hostname /etc/hostname rw,relatime - ext4 /dev/vda1 rw
root@be522444b60c:/# cat /proc/self/mountinfo
456 375 0:143 / / rw,relatime master:175 - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/CBPR2ETR4Z3UMOOGIIRDVT2P27:/var/lib/docker/overlay2/l/46FCA2JFPCSNFGAR5TSYLLNHLK,upperdir=/var/lib/docker/overlay2/3ef3e5a1a87b4e220c1da9a7901654e945b0ef5398e1b67fccb42fdb7750829e/diff,workdir=/var/lib/docker/overlay2/3ef3e5a1a87b4e220c1da9a7901654e945b0ef5398e1b67fccb42fdb7750829e/work
457 456 0:146 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
466 456 0:147 / /dev rw,nosuid - tmpfs tmpfs rw,size=65536k,mode=755

View File

@ -1,5 +1,3 @@
983 961 0:56 /containers/overlay-containers/2a33efc76e519c137fe6093179653788bed6162d4a15e5131c8e835c968afbe6/userdata/hostname /etc/hostname ro,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,size=783888k,nr_inodes=195972,mode=700,uid=2024,gid=2024,inode64
[root@2a33efc76e51 /]# cat /proc/self/mountinfo
961 812 0:58 / / ro,relatime - overlay overlay rw,lowerdir=/home/dracula/.local/share/containers/storage/overlay/l/4NB35A5Z4YGWDHXYEUZU4FN6BU,upperdir=/home/dracula/.local/share/containers/storage/overlay/a73044caca1b918335d1db6f0052d21d35045136f3aa86976dbad1ec96e2fdde/diff,workdir=/home/dracula/.local/share/containers/storage/overlay/a73044caca1b918335d1db6f0052d21d35045136f3aa86976dbad1ec96e2fdde/work,userxattr
962 961 0:63 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs rw
963 961 0:64 / /run rw,nosuid,nodev,relatime - tmpfs tmpfs rw,uid=2024,gid=2024,inode64

View File

@ -14,7 +14,7 @@ pluginManagement {
}
plugins {
id("com.gradle.enterprise") version "3.13.3"
id("com.gradle.enterprise") version "3.13.4"
id("com.gradle.common-custom-user-data-gradle-plugin") version "1.11"
id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
}

View File

@ -12,7 +12,7 @@ plugins {
}
dependencies {
implementation("com.linecorp.armeria:armeria-grpc:1.24.0")
implementation("com.linecorp.armeria:armeria-grpc:1.24.1")
implementation("io.opentelemetry.proto:opentelemetry-proto")
runtimeOnly("org.slf4j:slf4j-simple")
}

View File

@ -5,7 +5,7 @@ plugins {
}
dependencies {
implementation("com.linecorp.armeria:armeria-junit5:1.24.0")
implementation("com.linecorp.armeria:armeria-junit5:1.24.1")
}
tasks {