Fix the main branch history (#8817)
This commit is contained in:
parent
47159e6a7c
commit
9e37e724dc
|
@ -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 {
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() {}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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() {}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ public class MongoClientInstrumentationModule extends InstrumentationModule {
|
|||
new InternalStreamConnectionInstrumentation(),
|
||||
new BaseClusterInstrumentation(),
|
||||
new DefaultConnectionPoolInstrumentation(),
|
||||
new DefaultConnectionPoolTaskInstrumentation(),
|
||||
new AsyncWorkManagerInstrumentation());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue