Skip to content

Commit

Permalink
Add agent instrumentation for Ratpack 1.7+ (open-telemetry#12572)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnrengelman authored and Abhishekkr3003 committed Nov 26, 2024
1 parent cc92b86 commit c494ddf
Show file tree
Hide file tree
Showing 36 changed files with 831 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,13 +34,13 @@

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();
Expand Down Expand Up @@ -66,7 +71,7 @@ public Void buildRequest(String method, URI uri, Map<String, String> headers) {
@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();
}

Expand All @@ -78,13 +83,17 @@ public final void sendRequestWithCallback(
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
Expand Down Expand Up @@ -118,32 +127,13 @@ protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
.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);

Expand All @@ -160,7 +150,45 @@ protected Set<AttributeKey<?>> computeHttpAttributes(URI uri) {
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);
}
}
}
33 changes: 33 additions & 0 deletions instrumentation/ratpack/ratpack-1.7/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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")
}
}
Original file line number Diff line number Diff line change
@@ -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)
};
}
}
}
Loading

0 comments on commit c494ddf

Please sign in to comment.