Add agent instrumentation for Ratpack 1.7+ (#12572)

This commit is contained in:
John Engelman 2024-11-20 11:31:30 -06:00 committed by GitHub
parent 11773aa889
commit 8a3f1eb03d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 831 additions and 124 deletions

View File

@ -5,29 +5,13 @@
package io.opentelemetry.javaagent.instrumentation.ratpack;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackForkedHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.extension.RegisterExtension;
class RatpackForkedHttpClientTest extends AbstractRatpackForkedHttpClientTest {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
protected Set<AttributeKey<?>> computeHttpAttributes(URI uri) {
Set<AttributeKey<?>> attributes = new HashSet<>(super.computeHttpAttributes(uri));
// underlying netty instrumentation does not provide these
attributes.remove(SERVER_ADDRESS);
attributes.remove(SERVER_PORT);
return attributes;
}
}

View File

@ -5,29 +5,13 @@
package io.opentelemetry.javaagent.instrumentation.ratpack;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.extension.RegisterExtension;
class RatpackHttpClientTest extends AbstractRatpackHttpClientTest {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
protected Set<AttributeKey<?>> computeHttpAttributes(URI uri) {
Set<AttributeKey<?>> attributes = new HashSet<>(super.computeHttpAttributes(uri));
// underlying netty instrumentation does not provide these
attributes.remove(SERVER_ADDRESS);
attributes.remove(SERVER_PORT);
return attributes;
}
}

View File

@ -5,29 +5,13 @@
package io.opentelemetry.javaagent.instrumentation.ratpack;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackPooledHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import java.net.URI;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.extension.RegisterExtension;
class RatpackPooledHttpClientTest extends AbstractRatpackPooledHttpClientTest {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
protected Set<AttributeKey<?>> computeHttpAttributes(URI uri) {
Set<AttributeKey<?>> attributes = new HashSet<>(super.computeHttpAttributes(uri));
// underlying netty instrumentation does not provide these
attributes.remove(SERVER_ADDRESS);
attributes.remove(SERVER_PORT);
return attributes;
}
}

View File

@ -5,15 +5,20 @@
package io.opentelemetry.instrumentation.ratpack.client;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_ADDRESS;
import static io.opentelemetry.semconv.ServerAttributes.SERVER_PORT;
import io.netty.channel.ConnectTimeoutException;
import io.netty.handler.timeout.ReadTimeoutException;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
import io.opentelemetry.semconv.NetworkAttributes;
import java.net.URI;
import java.time.Duration;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.junit.jupiter.api.AfterAll;
@ -29,13 +34,13 @@ import ratpack.test.exec.ExecHarness;
public abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTest<Void> {
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 +71,7 @@ public abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTe
@Override
public int sendRequest(Void request, String method, URI uri, Map<String, String> headers)
throws Exception {
return exec.yield(unused -> internalSendRequest(client, method, uri, headers))
return exec.yield(execution -> internalSendRequest(client, method, uri, headers))
.getValueOrThrow();
}
@ -78,13 +83,17 @@ public abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTe
Map<String, String> 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
@ -118,32 +127,13 @@ public abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTe
.getValueOrThrow();
});
optionsBuilder.setExpectedClientSpanNameMapper(
(uri, method) -> {
switch (uri.toString()) {
case "http://localhost:61/": // unopened port
case "https://192.0.2.1/": // non routable address
return "CONNECT";
default:
return HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER.apply(
uri, method);
}
});
if (useNettyClientAttributes()) {
optionsBuilder.setExpectedClientSpanNameMapper(
AbstractRatpackHttpClientTest::nettyExpectedClientSpanNameMapper);
}
optionsBuilder.setClientSpanErrorMapper(
(uri, exception) -> {
if (uri.toString().equals("https://192.0.2.1/")) {
return new ConnectTimeoutException(
"connection timed out"
+ (Boolean.getBoolean("testLatestDeps") ? " after 2000 ms" : "")
+ ": /192.0.2.1:443");
} else if (OS.WINDOWS.isCurrentOs() && uri.toString().equals("http://localhost:61/")) {
return new ConnectTimeoutException("connection timed out: localhost/127.0.0.1:61");
} else if (uri.getPath().equals("/read-timeout")) {
return ReadTimeoutException.INSTANCE;
}
return exception;
});
AbstractRatpackHttpClientTest::nettyClientSpanErrorMapper);
optionsBuilder.setHttpAttributes(this::computeHttpAttributes);
@ -160,7 +150,45 @@ public abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTe
case "https://192.0.2.1/": // non routable address
return Collections.emptySet();
default:
return HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES;
HashSet<AttributeKey<?>> attributes =
new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES);
if (useNettyClientAttributes()) {
// underlying netty instrumentation does not provide these
attributes.remove(SERVER_ADDRESS);
attributes.remove(SERVER_PORT);
} else {
// ratpack client instrumentation does not provide this
attributes.remove(NetworkAttributes.NETWORK_PROTOCOL_VERSION);
}
return attributes;
}
}
protected boolean useNettyClientAttributes() {
return true;
}
private static Throwable nettyClientSpanErrorMapper(URI uri, Throwable exception) {
if (uri.toString().equals("https://192.0.2.1/")) {
return new ConnectTimeoutException(
"connection timed out"
+ (Boolean.getBoolean("testLatestDeps") ? " after 2000 ms" : "")
+ ": /192.0.2.1:443");
} else if (OS.WINDOWS.isCurrentOs() && uri.toString().equals("http://localhost:61/")) {
return new ConnectTimeoutException("connection timed out: localhost/127.0.0.1:61");
} else if (uri.getPath().equals("/read-timeout")) {
return ReadTimeoutException.INSTANCE;
}
return exception;
}
private static String nettyExpectedClientSpanNameMapper(URI uri, String method) {
switch (uri.toString()) {
case "http://localhost:61/": // unopened port
case "https://192.0.2.1/": // non routable address
return "CONNECT";
default:
return HttpClientTestOptions.DEFAULT_EXPECTED_CLIENT_SPAN_NAME_MAPPER.apply(uri, method);
}
}
}

View File

@ -0,0 +1,33 @@
plugins {
id("otel.javaagent-instrumentation")
}
muzzle {
pass {
group.set("io.ratpack")
module.set("ratpack-core")
versions.set("[1.7.0,)")
}
fail {
group.set("io.ratpack")
module.set("ratpack-core")
versions.set("[1.0,1.7)")
}
}
dependencies {
library("io.ratpack:ratpack-core:1.7.0")
implementation(project(":instrumentation:netty:netty-4.1:library"))
implementation(project(":instrumentation:ratpack:ratpack-1.7:library"))
testImplementation(project(":instrumentation:ratpack:ratpack-1.4:testing"))
testInstrumentation(project(":instrumentation:ratpack:ratpack-1.4:javaagent"))
}
tasks {
withType<Test>().configureEach {
systemProperty("testLatestDeps", findProperty("testLatestDeps") as Boolean)
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
}
}

View File

@ -0,0 +1,93 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7;
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.common.collect.ImmutableList;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryExecInitializer;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryExecInterceptor;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument;
import net.bytebuddy.asm.Advice.AssignReturned.ToFields.ToField;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import ratpack.exec.ExecInitializer;
import ratpack.exec.ExecInterceptor;
public class DefaultExecControllerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("ratpack.exec.internal.DefaultExecController");
}
@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
named("setInitializers")
.and(takesArgument(0, named("com.google.common.collect.ImmutableList"))),
DefaultExecControllerInstrumentation.class.getName() + "$SetInitializersAdvice");
transformer.applyAdviceToMethod(
named("setInterceptors")
.and(takesArgument(0, named("com.google.common.collect.ImmutableList"))),
DefaultExecControllerInstrumentation.class.getName() + "$SetInterceptorsAdvice");
transformer.applyAdviceToMethod(
isConstructor(),
DefaultExecControllerInstrumentation.class.getName() + "$ConstructorAdvice");
}
@SuppressWarnings("unused")
public static class SetInitializersAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static ImmutableList<? extends ExecInitializer> enter(
@Advice.Argument(0) ImmutableList<? extends ExecInitializer> initializers) {
return ImmutableList.<ExecInitializer>builder()
.addAll(initializers)
.add(OpenTelemetryExecInitializer.INSTANCE)
.build();
}
}
@SuppressWarnings("unused")
public static class SetInterceptorsAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static ImmutableList<? extends ExecInterceptor> enter(
@Advice.Argument(0) ImmutableList<? extends ExecInterceptor> interceptors) {
return ImmutableList.<ExecInterceptor>builder()
.addAll(interceptors)
.add(OpenTelemetryExecInterceptor.INSTANCE)
.build();
}
}
@SuppressWarnings("unused")
public static class ConstructorAdvice {
@SuppressWarnings("UnusedVariable")
@Advice.OnMethodExit(suppress = Throwable.class)
@Advice.AssignReturned.ToFields({
@ToField(value = "initializers", index = 0),
@ToField(value = "interceptors", index = 1)
})
public static Object[] exit(
@Advice.FieldValue("initializers") ImmutableList<? extends ExecInitializer> initializers,
@Advice.FieldValue("interceptors") ImmutableList<? extends ExecInterceptor> interceptors) {
return new Object[] {
ImmutableList.of(OpenTelemetryExecInitializer.INSTANCE),
ImmutableList.of(OpenTelemetryExecInterceptor.INSTANCE)
};
}
}
}

View File

@ -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 final class DownstreamWrapper<T> implements Downstream<T> {
private final Downstream<T> delegate;
private final Context parentContext;
private DownstreamWrapper(Downstream<T> 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 <T> Downstream<T> wrapIfNeeded(Downstream<T> 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);
}
}

View File

@ -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<TypeDescription> 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)
@Advice.AssignReturned.ToReturned
public static HttpClient injectTracing(@Advice.Return HttpClient httpClient) throws Exception {
return RatpackSingletons.httpClient().instrument(httpClient);
}
}
}

View File

@ -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<ClassLoader> classLoaderMatcher() {
// Only activate when running ratpack 1.7 or later
return hasClassesNamed("ratpack.exec.util.retry.Delay");
}
@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(
new DefaultExecControllerInstrumentation(),
new HttpClientInstrumentation(),
new RequestActionSupportInstrumentation());
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.netty.v4_1.internal.AttributeKeys;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.ContextHolder;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryHttpClient;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.RatpackClientInstrumenterBuilderFactory;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import ratpack.exec.Execution;
public final class RatpackSingletons {
static {
HTTP_CLIENT =
new OpenTelemetryHttpClient(
RatpackClientInstrumenterBuilderFactory.create(
"io.opentelemetry.ratpack-1.7", GlobalOpenTelemetry.get())
.configure(AgentCommonConfig.get())
.build());
}
private static final OpenTelemetryHttpClient HTTP_CLIENT;
public static OpenTelemetryHttpClient httpClient() {
return HTTP_CLIENT;
}
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() {}
}

View File

@ -0,0 +1,95 @@
/*
* 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.asm.Advice.AssignReturned.ToArguments.ToArgument;
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<TypeDescription> 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() + "$ConnectDownstreamAdvice");
transformer.applyAdviceToMethod(
isMethod().and(named("connect")).and(takesArgument(0, named("ratpack.exec.Downstream"))),
RequestActionSupportInstrumentation.class.getName() + "$ContextAdvice");
}
@SuppressWarnings("unused")
public static class SendAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void injectChannelAttribute(
@Advice.FieldValue("execution") Execution execution, @Advice.Argument(1) Channel channel) {
RatpackSingletons.propagateContextToChannel(execution, channel);
}
}
@SuppressWarnings("unused")
public static class ConnectDownstreamAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
@Advice.AssignReturned.ToArguments(@ToArgument(0))
public static Object wrapDownstream(@Advice.Argument(0) Downstream<?> downstream) {
// Propagate the current context to downstream
// since that is the subsequent code chained to the http client call
return DownstreamWrapper.wrapIfNeeded(downstream);
}
}
@SuppressWarnings("unused")
public static class ContextAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static Scope injectChannelAttribute(
@Advice.FieldValue("execution") Execution execution) {
// Capture the CLIENT span and make it current before calling 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();
}
}
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7;
import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackForkedHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
import org.junit.jupiter.api.extension.RegisterExtension;
class RatpackForkedHttpClientTest extends AbstractRatpackForkedHttpClientTest {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
protected boolean useNettyClientAttributes() {
return false;
}
@Override
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
super.configure(optionsBuilder);
optionsBuilder.setClientSpanErrorMapper(RatpackTestUtils::ratpackClientSpanErrorMapper);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7;
import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
import org.junit.jupiter.api.extension.RegisterExtension;
class RatpackHttpClientTest extends AbstractRatpackHttpClientTest {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
protected boolean useNettyClientAttributes() {
return false;
}
@Override
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
super.configure(optionsBuilder);
optionsBuilder.setClientSpanErrorMapper(RatpackTestUtils::ratpackClientSpanErrorMapper);
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7;
import io.opentelemetry.instrumentation.ratpack.client.AbstractRatpackPooledHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
import org.junit.jupiter.api.extension.RegisterExtension;
class RatpackPooledHttpClientTest extends AbstractRatpackPooledHttpClientTest {
@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();
@Override
protected boolean useNettyClientAttributes() {
return false;
}
@Override
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
super.configure(optionsBuilder);
optionsBuilder.setClientSpanErrorMapper(RatpackTestUtils::ratpackClientSpanErrorMapper);
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.javaagent.instrumentation.ratpack.v1_7;
import io.netty.channel.ConnectTimeoutException;
import java.net.URI;
import org.junit.jupiter.api.condition.OS;
import ratpack.http.client.HttpClientReadTimeoutException;
public final class RatpackTestUtils {
private RatpackTestUtils() {}
public static Throwable ratpackClientSpanErrorMapper(URI uri, Throwable exception) {
if (uri.toString().equals("https://192.0.2.1/")
|| (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;
}
}

View File

@ -7,6 +7,10 @@ package io.opentelemetry.instrumentation.ratpack.v1_7;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryExecInitializer;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryExecInterceptor;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryHttpClient;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryServerHandler;
import ratpack.exec.ExecInitializer;
import ratpack.exec.ExecInterceptor;
import ratpack.handling.HandlerDecorator;

View File

@ -13,6 +13,8 @@ import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor;
import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesExtractorBuilder;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractorBuilder;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.RatpackClientInstrumenterBuilderFactory;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.RatpackServerInstrumenterBuilderFactory;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
@ -31,17 +33,9 @@ public final class RatpackTelemetryBuilder {
RatpackTelemetryBuilder(OpenTelemetry openTelemetry) {
clientBuilder =
DefaultHttpClientInstrumenterBuilder.create(
INSTRUMENTATION_NAME,
openTelemetry,
RatpackHttpClientAttributesGetter.INSTANCE,
RequestHeaderSetter.INSTANCE);
RatpackClientInstrumenterBuilderFactory.create(INSTRUMENTATION_NAME, openTelemetry);
serverBuilder =
DefaultHttpServerInstrumenterBuilder.create(
INSTRUMENTATION_NAME,
openTelemetry,
RatpackHttpAttributesGetter.INSTANCE,
RatpackGetter.INSTANCE);
RatpackServerInstrumenterBuilderFactory.create(INSTRUMENTATION_NAME, openTelemetry);
}
/**

View File

@ -3,13 +3,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.ContextHolder;
import ratpack.exec.ExecInitializer;
import ratpack.exec.Execution;
final class OpenTelemetryExecInitializer implements ExecInitializer {
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class OpenTelemetryExecInitializer implements ExecInitializer {
public static final ExecInitializer INSTANCE = new OpenTelemetryExecInitializer();
@Override

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
@ -11,9 +11,13 @@ import ratpack.exec.ExecInterceptor;
import ratpack.exec.Execution;
import ratpack.func.Block;
final class OpenTelemetryExecInterceptor implements ExecInterceptor {
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
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 {

View File

@ -20,7 +20,7 @@
* limitations under the License.
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import static java.util.logging.Level.WARNING;
@ -32,6 +32,10 @@ import ratpack.handling.Context;
// Copied from
// https://github.com/ratpack/ratpack/blob/master/ratpack-core/src/main/java/ratpack/core/error/internal/DefaultProductionErrorHandler.java
// since it is internal and has had breaking changes.
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
final class OpenTelemetryFallbackErrorHandler implements ClientErrorHandler, ServerErrorHandler {
static final OpenTelemetryFallbackErrorHandler INSTANCE = new OpenTelemetryFallbackErrorHandler();

View File

@ -3,23 +3,26 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.ContextHolder;
import io.opentelemetry.semconv.HttpAttributes;
import ratpack.exec.Execution;
import ratpack.http.client.HttpClient;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;
final class OpenTelemetryHttpClient {
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class OpenTelemetryHttpClient {
private final Instrumenter<RequestSpec, HttpResponse> instrumenter;
OpenTelemetryHttpClient(Instrumenter<RequestSpec, HttpResponse> instrumenter) {
public OpenTelemetryHttpClient(Instrumenter<RequestSpec, HttpResponse> instrumenter) {
this.instrumenter = instrumenter;
}
@ -44,18 +47,30 @@ final class OpenTelemetryHttpClient {
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);
});
});
});
}

View File

@ -3,11 +3,15 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import ratpack.error.ServerErrorHandler;
import ratpack.handling.Context;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
final class OpenTelemetryServerErrorHandler implements ServerErrorHandler {
static final ServerErrorHandler INSTANCE = new OpenTelemetryServerErrorHandler();

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import static io.opentelemetry.instrumentation.api.semconv.http.HttpServerRouteSource.CONTROLLER;
@ -16,11 +16,15 @@ import ratpack.handling.Handler;
import ratpack.http.Request;
import ratpack.http.Response;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class OpenTelemetryServerHandler implements Handler {
private final Instrumenter<Request, Response> instrumenter;
OpenTelemetryServerHandler(Instrumenter<Request, Response> instrumenter) {
public OpenTelemetryServerHandler(Instrumenter<Request, Response> instrumenter) {
this.instrumenter = instrumenter;
}

View File

@ -0,0 +1,29 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpClientInstrumenterBuilder;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class RatpackClientInstrumenterBuilderFactory {
private RatpackClientInstrumenterBuilderFactory() {}
public static DefaultHttpClientInstrumenterBuilder<RequestSpec, HttpResponse> create(
String instrumentationName, OpenTelemetry openTelemetry) {
return DefaultHttpClientInstrumenterBuilder.create(
instrumentationName,
openTelemetry,
RatpackHttpClientAttributesGetter.INSTANCE,
RequestHeaderSetter.INSTANCE);
}
}

View File

@ -3,12 +3,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.context.propagation.TextMapGetter;
import javax.annotation.Nullable;
import ratpack.http.Request;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
enum RatpackGetter implements TextMapGetter<Request> {
INSTANCE;

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesGetter;
import java.util.List;
@ -13,6 +13,10 @@ import ratpack.http.Request;
import ratpack.http.Response;
import ratpack.server.PublicAddress;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
enum RatpackHttpAttributesGetter implements HttpServerAttributesGetter<Request, Response> {
INSTANCE;

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.instrumentation.api.semconv.http.HttpClientAttributesGetter;
import java.util.List;
@ -11,6 +11,10 @@ import javax.annotation.Nullable;
import ratpack.http.client.HttpResponse;
import ratpack.http.client.RequestSpec;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
enum RatpackHttpClientAttributesGetter
implements HttpClientAttributesGetter<RequestSpec, HttpResponse> {
INSTANCE;

View File

@ -0,0 +1,30 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.instrumentation.api.incubator.builder.internal.DefaultHttpServerInstrumenterBuilder;
import ratpack.http.Request;
import ratpack.http.Response;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class RatpackServerInstrumenterBuilderFactory {
private RatpackServerInstrumenterBuilderFactory() {}
public static DefaultHttpServerInstrumenterBuilder<Request, Response> create(
String instrumentationName, OpenTelemetry openTelemetry) {
return DefaultHttpServerInstrumenterBuilder.create(
instrumentationName,
openTelemetry,
RatpackHttpAttributesGetter.INSTANCE,
RatpackGetter.INSTANCE);
}
}

View File

@ -3,12 +3,16 @@
* SPDX-License-Identifier: Apache-2.0
*/
package io.opentelemetry.instrumentation.ratpack.v1_7;
package io.opentelemetry.instrumentation.ratpack.v1_7.internal;
import io.opentelemetry.context.propagation.TextMapSetter;
import ratpack.api.Nullable;
import ratpack.http.client.RequestSpec;
/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
enum RequestHeaderSetter implements TextMapSetter<RequestSpec> {
INSTANCE;

View File

@ -10,7 +10,7 @@ import com.google.inject.Provides
import groovy.transform.CompileStatic
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.trace.SpanKind
import io.opentelemetry.instrumentation.ratpack.v1_7.OpenTelemetryServerHandler
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryServerHandler
import io.opentelemetry.instrumentation.ratpack.v1_7.RatpackFunctionalTest
import io.opentelemetry.instrumentation.ratpack.v1_7.RatpackTelemetry
import io.opentelemetry.sdk.OpenTelemetrySdk

View File

@ -11,6 +11,8 @@ import com.google.common.collect.ImmutableList;
import io.netty.channel.ConnectTimeoutException;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryExecInitializer;
import io.opentelemetry.instrumentation.ratpack.v1_7.internal.OpenTelemetryExecInterceptor;
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpClientTest;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientResult;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
@ -148,16 +150,16 @@ abstract class AbstractRatpackHttpClientTest extends AbstractHttpClientTest<Void
}
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<AttributeKey<?>> getHttpAttributes(URI uri) {
protected Set<AttributeKey<?>> computeHttpAttributes(URI uri) {
Set<AttributeKey<?>> attributes = new HashSet<>(HttpClientTestOptions.DEFAULT_HTTP_ATTRIBUTES);
attributes.remove(NETWORK_PROTOCOL_VERSION);
return attributes;

View File

@ -507,6 +507,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")