diff --git a/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java b/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java index bb6a43e8aa84..94a993784877 100644 --- a/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java +++ b/instrumentation/ratpack/ratpack-1.4/testing/src/main/java/io/opentelemetry/instrumentation/ratpack/client/AbstractRatpackHttpClientTest.java @@ -29,13 +29,13 @@ public abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTest { - private final ExecHarness exec = ExecHarness.harness(); + protected final ExecHarness exec = ExecHarness.harness(); - private HttpClient client; - private HttpClient singleConnectionClient; + protected HttpClient client; + protected HttpClient singleConnectionClient; @BeforeAll - void setUpClient() throws Exception { + protected void setUpClient() throws Exception { exec.run( unused -> { client = buildHttpClient(); @@ -66,7 +66,8 @@ public Void buildRequest(String method, URI uri, Map headers) { @Override public int sendRequest(Void request, String method, URI uri, Map headers) throws Exception { - return exec.yield(unused -> internalSendRequest(client, method, uri, headers)) + return exec.yield( + execution -> internalSendRequest(client, method, uri, headers)) .getValueOrThrow(); } @@ -78,13 +79,17 @@ public final void sendRequestWithCallback( Map headers, HttpClientResult httpClientResult) throws Exception { - exec.execute( - Operation.of( - () -> - internalSendRequest(client, method, uri, headers) - .result( - result -> - httpClientResult.complete(result::getValue, result.getThrowable())))); + exec.yield( + (e) -> + Operation.of( + () -> + internalSendRequest(client, method, uri, headers) + .result( + result -> + httpClientResult.complete( + result::getValue, result.getThrowable()))) + .promise()) + .getValueOrThrow(); } // overridden in RatpackForkedHttpClientTest diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/build.gradle.kts b/instrumentation/ratpack/ratpack-1.7/javaagent/build.gradle.kts new file mode 100644 index 000000000000..dbc58cadd42d --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/build.gradle.kts @@ -0,0 +1,53 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("io.ratpack") + module.set("ratpack-core") + versions.set("[1.7.0,)") + } +} + +dependencies { + library("io.ratpack:ratpack-core:1.7.0") + + implementation(project(":instrumentation:netty:netty-4.1:library")) + implementation(project(":instrumentation:ratpack:ratpack-1.4:javaagent")) + implementation(project(":instrumentation:ratpack:ratpack-1.7:library")) + + testImplementation(project(":instrumentation:ratpack:ratpack-1.4:testing")) + + testLibrary("io.ratpack:ratpack-test:1.7.0") + + if (JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11)) { + testImplementation("com.sun.activation:jakarta.activation:1.2.2") + } +} + +// to allow all tests to pass we need to choose a specific netty version +if (!(findProperty("testLatestDeps") as Boolean)) { + configurations.configureEach { + if (!name.contains("muzzle")) { + resolutionStrategy { + eachDependency { + // specifying a fixed version for all libraries with io.netty group + if (requested.group == "io.netty" && requested.name != "netty-tcnative") { + useVersion("4.1.37.Final") + } + } + } + } + } +} + +tasks { + withType().configureEach { + systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean) + } +} + +tasks.withType().configureEach { + jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true") +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/DownstreamWrapper.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/DownstreamWrapper.java new file mode 100644 index 000000000000..5bf5d2454fec --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/DownstreamWrapper.java @@ -0,0 +1,55 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import ratpack.exec.Downstream; + +public class DownstreamWrapper implements Downstream { + + private final Downstream delegate; + private final Context parentContext; + + private DownstreamWrapper(Downstream delegate, Context parentContext) { + assert parentContext != null; + this.delegate = delegate; + this.parentContext = parentContext; + } + + @Override + public void success(T value) { + try (Scope ignored = parentContext.makeCurrent()) { + delegate.success(value); + } + } + + @Override + public void error(Throwable throwable) { + try (Scope ignored = parentContext.makeCurrent()) { + delegate.error(throwable); + } + } + + @Override + public void complete() { + try (Scope ignored = parentContext.makeCurrent()) { + delegate.complete(); + } + } + + public static Downstream wrapIfNeeded(Downstream delegate) { + if (delegate instanceof DownstreamWrapper) { + return delegate; + } + Context context = Context.current(); + if (context == Context.root()) { + // Skip wrapping, there is no need to propagate root context. + return delegate; + } + return new DownstreamWrapper<>(delegate, context); + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/HttpClientInstrumentation.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/HttpClientInstrumentation.java new file mode 100644 index 000000000000..d14e25458201 --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/HttpClientInstrumentation.java @@ -0,0 +1,46 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import ratpack.http.client.HttpClient; + +public class HttpClientInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("ratpack.http.client.HttpClient"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isStatic()) + .and(named("of")) + .and(takesArgument(0, named("ratpack.func.Action"))), + HttpClientInstrumentation.class.getName() + "$OfAdvice"); + } + + @SuppressWarnings("unused") + public static class OfAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void injectTracing(@Advice.Return(readOnly = false) HttpClient httpClient) + throws Exception { + httpClient = RatpackSingletons.telemetry().instrumentHttpClient(httpClient); + } + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackInstrumentationModule.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackInstrumentationModule.java new file mode 100644 index 000000000000..59232676bc1f --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackInstrumentationModule.java @@ -0,0 +1,44 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Arrays.asList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class RatpackInstrumentationModule extends InstrumentationModule + implements ExperimentalInstrumentationModule { + public RatpackInstrumentationModule() { + super("ratpack", "ratpack-1.7"); + } + + @Override + public String getModuleGroup() { + // relies on netty + return "netty"; + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + // Only activate when running ratpack 1.7 or later + return hasClassesNamed("ratpack.exec.util.retry.Delay"); + } + + @Override + public List typeInstrumentations() { + return asList( + new ServerRegistryInstrumentation(), + new HttpClientInstrumentation(), + new RequestActionSupportInstrumentation()); + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackSingletons.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackSingletons.java new file mode 100644 index 000000000000..a0304c64a0b8 --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackSingletons.java @@ -0,0 +1,54 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import io.netty.channel.Channel; +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.netty.v4_1.internal.AttributeKeys; +import io.opentelemetry.instrumentation.ratpack.v1_7.RatpackTelemetry; +import io.opentelemetry.instrumentation.ratpack.v1_7.internal.ContextHolder; +import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig; +import io.opentelemetry.javaagent.bootstrap.internal.ExperimentalConfig; +import ratpack.exec.Execution; + +public final class RatpackSingletons { + + static { + TELEMETRY = + RatpackTelemetry.builder(GlobalOpenTelemetry.get()) + .configure(AgentCommonConfig.get()) + .build(); + } + + private static final Instrumenter INSTRUMENTER = + Instrumenter.builder( + GlobalOpenTelemetry.get(), "io.opentelemetry.ratpack-1.7", s -> s) + .setEnabled(ExperimentalConfig.get().controllerTelemetryEnabled()) + .buildInstrumenter(); + + public static Instrumenter instrumenter() { + return INSTRUMENTER; + } + + private static final RatpackTelemetry TELEMETRY; + + public static RatpackTelemetry telemetry() { + return TELEMETRY; + } + + public static void propagateContextToChannel(Execution execution, Channel channel) { + Context parentContext = + execution + .maybeGet(ContextHolder.class) + .map(ContextHolder::context) + .orElse(Context.current()); + channel.attr(AttributeKeys.CLIENT_PARENT_CONTEXT).set(parentContext); + } + + private RatpackSingletons() {} +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RequestActionSupportInstrumentation.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RequestActionSupportInstrumentation.java new file mode 100644 index 000000000000..a156ddb638ba --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RequestActionSupportInstrumentation.java @@ -0,0 +1,84 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPrivate; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.netty.channel.Channel; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.ratpack.v1_7.internal.ContextHolder; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import ratpack.exec.Downstream; +import ratpack.exec.Execution; + +public class RequestActionSupportInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return extendsClass(named("ratpack.http.client.internal.RequestActionSupport")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod() + .and(isPrivate()) + .and(named("send")) + .and(takesArgument(0, named("ratpack.exec.Downstream"))) + .and(takesArgument(1, named("io.netty.channel.Channel"))), + RequestActionSupportInstrumentation.class.getName() + "$SendAdvice"); + transformer.applyAdviceToMethod( + isMethod().and(named("connect")).and(takesArgument(0, named("ratpack.exec.Downstream"))), + RequestActionSupportInstrumentation.class.getName() + "$ConnectAdvice"); + } + + @SuppressWarnings("unused") + public static class SendAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void injectChannelAttribute( + @Advice.FieldValue("execution") Execution execution, + @Advice.Argument(value = 0, readOnly = false) Downstream downstream, + @Advice.Argument(value = 1, readOnly = false) Channel channel) { + RatpackSingletons.propagateContextToChannel(execution, channel); + } + } + + public static class ConnectAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static Scope injectChannelAttribute( + @Advice.FieldValue("execution") Execution execution, + @Advice.Argument(value = 0, readOnly = false) Downstream downstream) { + // Propagate the current context to downstream + // since that the is the subsequent + downstream = DownstreamWrapper.wrapIfNeeded(downstream); + + // Capture the CLIENT span and make it current before cally Netty layer + return execution + .maybeGet(ContextHolder.class) + .map(ContextHolder::context) + .map(Context::makeCurrent) + .orElse(null); + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit(@Advice.Enter Scope scope) { + if (scope != null) { + scope.close(); + } + } + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/ServerRegistryInstrumentation.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/ServerRegistryInstrumentation.java new file mode 100644 index 000000000000..657a2e04cd07 --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/ServerRegistryInstrumentation.java @@ -0,0 +1,42 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import ratpack.registry.Registry; + +public class ServerRegistryInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("ratpack.server.internal.ServerRegistry"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isMethod().and(isStatic()).and(named("buildBaseRegistry")), + ServerRegistryInstrumentation.class.getName() + "$BuildAdvice"); + } + + @SuppressWarnings("unused") + public static class BuildAdvice { + + @Advice.OnMethodExit(suppress = Throwable.class) + public static void injectTracing(@Advice.Return(readOnly = false) Registry registry) + throws Exception { + registry = registry.join(Registry.of(RatpackSingletons.telemetry()::configureServerRegistry)); + } + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackAsyncHttpServerTest.groovy b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackAsyncHttpServerTest.groovy new file mode 100644 index 000000000000..dc1cc42df27f --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackAsyncHttpServerTest.groovy @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package server + +import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackAsyncHttpServerTest +import io.opentelemetry.instrumentation.test.AgentTestTrait +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint +import ratpack.server.RatpackServerSpec + +class RatpackAsyncHttpServerTest extends AbstractRatpackAsyncHttpServerTest implements AgentTestTrait { + @Override + void configure(RatpackServerSpec serverSpec) { + } + + @Override + boolean hasResponseCustomizer(ServerEndpoint endpoint) { + true + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackForkedHttpServerTest.groovy b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackForkedHttpServerTest.groovy new file mode 100644 index 000000000000..87dc2848b1ff --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackForkedHttpServerTest.groovy @@ -0,0 +1,27 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package server + +import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackForkedHttpServerTest +import io.opentelemetry.instrumentation.test.AgentTestTrait +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint +import ratpack.server.RatpackServerSpec + +class RatpackForkedHttpServerTest extends AbstractRatpackForkedHttpServerTest implements AgentTestTrait { + @Override + void configure(RatpackServerSpec serverSpec) { + } + + @Override + boolean hasResponseCustomizer(ServerEndpoint endpoint) { + true + } + + @Override + boolean testHttpPipelining() { + false + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackHttpServerTest.groovy b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackHttpServerTest.groovy new file mode 100644 index 000000000000..3fd085dde585 --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackHttpServerTest.groovy @@ -0,0 +1,22 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package server + +import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackHttpServerTest +import io.opentelemetry.instrumentation.test.AgentTestTrait +import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint +import ratpack.server.RatpackServerSpec + +class RatpackHttpServerTest extends AbstractRatpackHttpServerTest implements AgentTestTrait { + @Override + void configure(RatpackServerSpec serverSpec) { + } + + @Override + boolean hasResponseCustomizer(ServerEndpoint endpoint) { + true + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy new file mode 100644 index 000000000000..e52bd4f93c1e --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/groovy/server/RatpackRoutesTest.groovy @@ -0,0 +1,21 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package server + +import io.opentelemetry.instrumentation.ratpack.server.AbstractRatpackRoutesTest +import io.opentelemetry.instrumentation.test.AgentTestTrait +import ratpack.server.RatpackServerSpec + +class RatpackRoutesTest extends AbstractRatpackRoutesTest implements AgentTestTrait { + @Override + void configure(RatpackServerSpec serverSpec) { + } + + @Override + boolean hasHandlerSpan() { + return true + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackForkedHttpClientTest.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackForkedHttpClientTest.java new file mode 100644 index 000000000000..b9348fd8dcbc --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackForkedHttpClientTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import com.google.common.collect.ImmutableList; +import io.netty.channel.ConnectTimeoutException; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackForkedHttpClientTest; +import io.opentelemetry.instrumentation.ratpack.v1_7.OpenTelemetryExecInitializer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.NetworkAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.extension.RegisterExtension; +import ratpack.exec.internal.DefaultExecController; +import ratpack.http.client.HttpClientReadTimeoutException; + +class RatpackForkedHttpClientTest extends AbstractRatpackForkedHttpClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @BeforeAll + @Override + protected void setUpClient() throws Exception { + exec.run( + unused -> { + ((DefaultExecController) exec.getController()) + .setInitializers(ImmutableList.of(OpenTelemetryExecInitializer.INSTANCE)); + client = buildHttpClient(); + singleConnectionClient = buildHttpClient(spec -> spec.poolSize(1)); + }); + } + + @Override + protected Set> computeHttpAttributes(URI uri) { + Set> attributes = new HashSet<>(super.computeHttpAttributes(uri)); + attributes.remove(NetworkAttributes.NETWORK_PROTOCOL_VERSION); + return attributes; + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + super.configure(optionsBuilder); + optionsBuilder.setExpectedClientSpanNameMapper( + HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER); + optionsBuilder.setClientSpanErrorMapper( + (uri, exception) -> { + if (uri.toString().equals("https://192.0.2.1/")) { + return new ConnectTimeoutException("Connect timeout (PT2S) connecting to " + uri); + } else if (OS.WINDOWS.isCurrentOs() && uri.toString().equals("http://localhost:61/")) { + return new ConnectTimeoutException("Connect timeout (PT2S) connecting to " + uri); + } else if (uri.getPath().equals("/read-timeout")) { + return new HttpClientReadTimeoutException( + "Read timeout (PT2S) waiting on HTTP server at " + uri); + } + return exception; + }); + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackHttpClientTest.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackHttpClientTest.java new file mode 100644 index 000000000000..02f76cbc04e4 --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackHttpClientTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import com.google.common.collect.ImmutableList; +import io.netty.channel.ConnectTimeoutException; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackHttpClientTest; +import io.opentelemetry.instrumentation.ratpack.v1_7.OpenTelemetryExecInitializer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.NetworkAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.extension.RegisterExtension; +import ratpack.exec.internal.DefaultExecController; +import ratpack.http.client.HttpClientReadTimeoutException; + +class RatpackHttpClientTest extends AbstractRatpackHttpClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @BeforeAll + @Override + protected void setUpClient() throws Exception { + exec.run( + unused -> { + ((DefaultExecController) exec.getController()) + .setInitializers(ImmutableList.of(OpenTelemetryExecInitializer.INSTANCE)); + client = buildHttpClient(); + singleConnectionClient = buildHttpClient(spec -> spec.poolSize(1)); + }); + } + + @Override + protected Set> computeHttpAttributes(URI uri) { + Set> attributes = new HashSet<>(super.computeHttpAttributes(uri)); + attributes.remove(NetworkAttributes.NETWORK_PROTOCOL_VERSION); + return attributes; + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + super.configure(optionsBuilder); + optionsBuilder.setExpectedClientSpanNameMapper( + HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER); + optionsBuilder.setClientSpanErrorMapper( + (uri, exception) -> { + if (uri.toString().equals("https://192.0.2.1/")) { + return new ConnectTimeoutException("Connect timeout (PT2S) connecting to " + uri); + } else if (OS.WINDOWS.isCurrentOs() && uri.toString().equals("http://localhost:61/")) { + return new ConnectTimeoutException("Connect timeout (PT2S) connecting to " + uri); + } else if (uri.getPath().equals("/read-timeout")) { + return new HttpClientReadTimeoutException( + "Read timeout (PT2S) waiting on HTTP server at " + uri); + } + return exception; + }); + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackPooledHttpClientTest.java b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackPooledHttpClientTest.java new file mode 100644 index 000000000000..80506e4d2fda --- /dev/null +++ b/instrumentation/ratpack/ratpack-1.7/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/ratpack/v1_7/RatpackPooledHttpClientTest.java @@ -0,0 +1,68 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7; + +import com.google.common.collect.ImmutableList; +import io.netty.channel.ConnectTimeoutException; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackPooledHttpClientTest; +import io.opentelemetry.instrumentation.ratpack.v1_7.OpenTelemetryExecInitializer; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions; +import io.opentelemetry.semconv.NetworkAttributes; +import java.net.URI; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.extension.RegisterExtension; +import ratpack.exec.internal.DefaultExecController; +import ratpack.http.client.HttpClientReadTimeoutException; + +class RatpackPooledHttpClientTest extends AbstractRatpackPooledHttpClientTest { + + @RegisterExtension + static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent(); + + @BeforeAll + @Override + protected void setUpClient() throws Exception { + exec.run( + unused -> { + ((DefaultExecController) exec.getController()) + .setInitializers(ImmutableList.of(OpenTelemetryExecInitializer.INSTANCE)); + client = buildHttpClient(); + singleConnectionClient = buildHttpClient(spec -> spec.poolSize(1)); + }); + } + + @Override + protected Set> computeHttpAttributes(URI uri) { + Set> attributes = new HashSet<>(super.computeHttpAttributes(uri)); + attributes.remove(NetworkAttributes.NETWORK_PROTOCOL_VERSION); + return attributes; + } + + @Override + protected void configure(HttpClientTestOptions.Builder optionsBuilder) { + super.configure(optionsBuilder); + optionsBuilder.setExpectedClientSpanNameMapper( + HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER); + optionsBuilder.setClientSpanErrorMapper( + (uri, exception) -> { + if (uri.toString().equals("https://192.0.2.1/")) { + return new ConnectTimeoutException("Connect timeout (PT2S) connecting to " + uri); + } else if (OS.WINDOWS.isCurrentOs() && uri.toString().equals("http://localhost:61/")) { + return new ConnectTimeoutException("Connect timeout (PT2S) connecting to " + uri); + } else if (uri.getPath().equals("/read-timeout")) { + return new HttpClientReadTimeoutException( + "Read timeout (PT2S) waiting on HTTP server at " + uri); + } + return exception; + }); + } +} diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInitializer.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInitializer.java index 5788db8c5655..bbedaee9caca 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInitializer.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInitializer.java @@ -9,7 +9,7 @@ import ratpack.exec.ExecInitializer; import ratpack.exec.Execution; -final class OpenTelemetryExecInitializer implements ExecInitializer { +public final class OpenTelemetryExecInitializer implements ExecInitializer { public static final ExecInitializer INSTANCE = new OpenTelemetryExecInitializer(); @Override diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInterceptor.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInterceptor.java index f78c0671fe74..07c7a68abc5f 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInterceptor.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryExecInterceptor.java @@ -11,9 +11,9 @@ import ratpack.exec.Execution; import ratpack.func.Block; -final class OpenTelemetryExecInterceptor implements ExecInterceptor { +public final class OpenTelemetryExecInterceptor implements ExecInterceptor { - static final ExecInterceptor INSTANCE = new OpenTelemetryExecInterceptor(); + public static final ExecInterceptor INSTANCE = new OpenTelemetryExecInterceptor(); @Override public void intercept(Execution execution, ExecType type, Block continuation) throws Exception { diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryHttpClient.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryHttpClient.java index aea5fc96faf6..28d8628d9e0a 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryHttpClient.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/OpenTelemetryHttpClient.java @@ -44,18 +44,20 @@ public HttpClient instrument(HttpClient httpClient) throws Exception { httpClientSpec.responseIntercept( httpResponse -> { Execution execution = Execution.current(); - ContextHolder contextHolder = execution.get(ContextHolder.class); - execution.remove(ContextHolder.class); - instrumenter.end( - contextHolder.context(), contextHolder.requestSpec(), httpResponse, null); + execution.maybeGet(ContextHolder.class).ifPresent(contextHolder -> { + execution.remove(ContextHolder.class); + instrumenter.end( + contextHolder.context(), contextHolder.requestSpec(), httpResponse, null); + }); }); httpClientSpec.errorIntercept( ex -> { Execution execution = Execution.current(); - ContextHolder contextHolder = execution.get(ContextHolder.class); - execution.remove(ContextHolder.class); - instrumenter.end(contextHolder.context(), contextHolder.requestSpec(), null, ex); + execution.maybeGet(ContextHolder.class).ifPresent(contextHolder -> { + execution.remove(ContextHolder.class); + instrumenter.end(contextHolder.context(), contextHolder.requestSpec(), null, ex); + }); }); }); } diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/RatpackTelemetryBuilder.java b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/RatpackTelemetryBuilder.java index a7e56610913a..b285dbb4bde2 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/RatpackTelemetryBuilder.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/main/java/io/opentelemetry/instrumentation/ratpack/v1_7/RatpackTelemetryBuilder.java @@ -9,6 +9,7 @@ import io.opentelemetry.api.OpenTelemetry; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder; import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder; +import io.opentelemetry.instrumentation.api.incubator.config.internal.CommonConfig; import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor; import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder; @@ -173,6 +174,13 @@ public RatpackTelemetryBuilder setServerSpanNameExtractor( return this; } + @CanIgnoreReturnValue + public RatpackTelemetryBuilder configure(CommonConfig config) { + clientBuilder.configure(config); + serverBuilder.configure(config); + return this; + } + /** Returns a new {@link RatpackTelemetry} with the configuration of this builder. */ public RatpackTelemetry build() { return new RatpackTelemetry(serverBuilder.build(), clientBuilder.build()); diff --git a/instrumentation/ratpack/ratpack-1.7/library/src/test/java/io/opentelemetry/instrumentation/ratpack/v1_7/AbstractRatpackHttpClientTest.java b/instrumentation/ratpack/ratpack-1.7/library/src/test/java/io/opentelemetry/instrumentation/ratpack/v1_7/AbstractRatpackHttpClientTest.java index affbe4197c3b..45004764b326 100644 --- a/instrumentation/ratpack/ratpack-1.7/library/src/test/java/io/opentelemetry/instrumentation/ratpack/v1_7/AbstractRatpackHttpClientTest.java +++ b/instrumentation/ratpack/ratpack-1.7/library/src/test/java/io/opentelemetry/instrumentation/ratpack/v1_7/AbstractRatpackHttpClientTest.java @@ -147,16 +147,17 @@ protected void configure(HttpClientTestOptions.Builder optionsBuilder) { } return exception; }); + optionsBuilder.setHttpAttributes(this::computeHttpAttributes); optionsBuilder.disableTestRedirects(); + // these tests will pass, but they don't really test anything since REQUEST is Void optionsBuilder.disableTestReusedRequest(); - optionsBuilder.setHttpAttributes(this::getHttpAttributes); optionsBuilder.spanEndsAfterBody(); } - protected Set> getHttpAttributes(URI uri) { + protected Set> computeHttpAttributes(URI uri) { Set> attributes = new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES); attributes.remove(NetworkAttributes.NETWORK_PROTOCOL_VERSION); return attributes; diff --git a/settings.gradle.kts b/settings.gradle.kts index c9e944fdb48c..f59187213ebf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -503,6 +503,7 @@ include(":instrumentation:r2dbc-1.0:testing") include(":instrumentation:rabbitmq-2.7:javaagent") include(":instrumentation:ratpack:ratpack-1.4:javaagent") include(":instrumentation:ratpack:ratpack-1.4:testing") +include(":instrumentation:ratpack:ratpack-1.7:javaagent") include(":instrumentation:ratpack:ratpack-1.7:library") include(":instrumentation:reactor:reactor-3.1:javaagent") include(":instrumentation:reactor:reactor-3.1:library")