Add all helpers required for ClassLoaderScopedWeakMap usage + re-enable all tests
MODIFY
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ClassLoaderScopedWeakMap.java
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ClassLoaderScopedWeakMap.java
package datadog.trace.agent.tooling;
-//import datadog.trace.bootstrap.WeakMap;
-//import java.util.HashMap;
-//import java.util.Map;
+import datadog.trace.bootstrap.WeakMap;
+import java.util.HashMap;
+import java.util.Map;
/** A registry which is scoped per-classloader able to hold key-value pairs with weak keys. */
public class ClassLoaderScopedWeakMap {
-// public static final ClassLoaderScopedWeakMap INSTANCE = new ClassLoaderScopedWeakMap();
+ public static final ClassLoaderScopedWeakMap INSTANCE = new ClassLoaderScopedWeakMap();
- // private final WeakMap<ClassLoader, Map<Object, Object>> map = WeakMap.Supplier.DEFAULT.get();
+ private final WeakMap<ClassLoader, Map<Object, Object>> map = WeakMap.Supplier.DEFAULT.get();
/**
* Gets the element registered at the specified key or register as new one retrieved by the
* provided supplier.
*/
-// public Object getOrCreate(
-//// public synchronized Object getOrCreate(
-// ClassLoader classLoader, Object key, Supplier valueSupplier) {
-//// Map<Object, Object> classLoaderMap = map.get(classLoader);
-//// if (classLoaderMap == null) {
-//// classLoaderMap = new HashMap<>();
-//// map.put(classLoader, classLoaderMap);
-//// }
-////
-//// if (classLoaderMap.containsKey(key)) {
-//// return classLoaderMap.get(key);
-//// }
-//
-// Object value = valueSupplier.get();
-//// classLoaderMap.put(key, value);
-// return value;
-// }
-
- public static Object aaa(Object aaa) {
- System.out.println("[STD LOG] aaa " + ClassLoaderScopedWeakMapSupplier.class.getName());
- return aaa;
+ public synchronized Object getOrCreate(
+ ClassLoader classLoader, Object key, Supplier valueSupplier) {
+ Map<Object, Object> classLoaderMap = map.get(classLoader);
+ if (classLoaderMap == null) {
+ classLoaderMap = new HashMap<>();
+ map.put(classLoader, classLoaderMap);
}
- public static Object bbb(ClassLoaderScopedWeakMapSupplier aaa) {
- System.out.println("[STD LOG] bbb" + ClassLoaderScopedWeakMapSupplier.class.getName());
- return aaa.get();
+ if (classLoaderMap.containsKey(key)) {
+ return classLoaderMap.get(key);
}
-// /**
-// * Supplies the value to be stored and it is called only when a value does not exists yet in the
-// * registry.
-// */
-// public interface Supplier {
-// Object get();
-// }
+ Object value = valueSupplier.get();
+ classLoaderMap.put(key, value);
+ return value;
+ }
+
+ /**
+ * Supplies the value to be stored and it is called only when a value does not exists yet in the
+ * registry.
+ */
+ public interface Supplier {
+ Object get();
+ }
}

DELETE
dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/ClassLoaderScopedWeakMapSupplier.java
-package datadog.trace.agent.tooling;
-
-public interface ClassLoaderScopedWeakMapSupplier {
- Object get();
-}

MODIFY
dd-java-agent/instrumentation/netty-4.0/netty-4.0.gradle
dd-java-agent/instrumentation/netty-4.0/netty-4.0.gradle
@@ -60,23 +60,3 @@ configurations.testCompile {
}
}
}
-
-test {
- testLogging {
- // Make sure output from
- // standard out or error is shown
- // in Gradle output.
- showStandardStreams = true
-
- // Or we use events method:
- // events 'standard_out', 'standard_error'
-
- // Or set property events:
- // events = ['standard_out', 'standard_error']
-
- // Instead of string values we can
- // use enum values:
- // events org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_OUT,
- // org.gradle.api.tasks.testing.logging.TestLogEvent.STANDARD_ERROR,
- }
-}

MODIFY
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/AttributeKeys.java
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/AttributeKeys.java
package datadog.trace.instrumentation.netty40;
import datadog.trace.agent.tooling.ClassLoaderScopedWeakMap;
-import datadog.trace.agent.tooling.ClassLoaderScopedWeakMapSupplier;
import datadog.trace.context.TraceScope;
import datadog.trace.instrumentation.netty40.client.HttpClientTracingHandler;
import datadog.trace.instrumentation.netty40.server.HttpServerTracingHandler;
@@ -10,65 +9,49 @@ import io.opentracing.Span;
public class AttributeKeys {
- static {
- System.out.println("Canone!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
- }
-
private static final String PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY_NAME =
"datadog.trace.instrumentation.netty40.parent.connect.continuation";
public static final AttributeKey<TraceScope.Continuation>
- PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY = new AttributeKey<>(PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY_NAME);
-// public static final AttributeKey<TraceScope.Continuation>
-// PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY =
-// (AttributeKey<TraceScope.Continuation>)
-// ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
-// AttributeKey.class.getClassLoader(),
-// PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY_NAME,
-// new ClassLoaderScopedWeakMap.Supplier() {
-// @Override
-// public Object get() {
-// return new AttributeKey<>(PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY_NAME);
-// }
-// });
+ PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY =
+ (AttributeKey<TraceScope.Continuation>)
+ ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
+ AttributeKey.class.getClassLoader(),
+ PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY_NAME,
+ new ClassLoaderScopedWeakMap.Supplier() {
+ @Override
+ public Object get() {
+ return new AttributeKey<>(PARENT_CONNECT_CONTINUATION_ATTRIBUTE_KEY_NAME);
+ }
+ });
private static final String SERVER_ATTRIBUTE_KEY_NAME =
HttpServerTracingHandler.class.getName() + ".span";
- public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY = new AttributeKey<>(SERVER_ATTRIBUTE_KEY_NAME);
-// public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY =
-// (AttributeKey<Span>)
-// ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
-// AttributeKey.class.getClassLoader(),
-// SERVER_ATTRIBUTE_KEY_NAME,
-// new ClassLoaderScopedWeakMap.Supplier() {
-// @Override
-// public Object get() {
-// return new AttributeKey<>(SERVER_ATTRIBUTE_KEY_NAME);
-// }
-// });
+ public static final AttributeKey<Span> SERVER_ATTRIBUTE_KEY =
+ (AttributeKey<Span>)
+ ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
+ AttributeKey.class.getClassLoader(),
+ SERVER_ATTRIBUTE_KEY_NAME,
+ new ClassLoaderScopedWeakMap.Supplier() {
+ @Override
+ public Object get() {
+ return new AttributeKey<>(SERVER_ATTRIBUTE_KEY_NAME);
+ }
+ });
private static final String CLIENT_ATTRIBUTE_KEY_NAME =
HttpClientTracingHandler.class.getName() + ".span";
- // public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY = new AttributeKey<>(CLIENT_ATTRIBUTE_KEY_NAME);
-// public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY = (AttributeKey<Span>) ClassLoaderScopedWeakMap.aaa(new AttributeKey<>(CLIENT_ATTRIBUTE_KEY_NAME));
- public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY = (AttributeKey<Span>) ClassLoaderScopedWeakMap.bbb(new ClassLoaderScopedWeakMapSupplier() {
+ public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY =
+ (AttributeKey<Span>)
+ ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
+ AttributeKey.class.getClassLoader(),
+ CLIENT_ATTRIBUTE_KEY_NAME,
+ new ClassLoaderScopedWeakMap.Supplier() {
@Override
public Object get() {
- System.out.println("[STD LOG] executing the getter");
return new AttributeKey<>(CLIENT_ATTRIBUTE_KEY_NAME);
}
});
-// public static final AttributeKey<Span> CLIENT_ATTRIBUTE_KEY =
-// (AttributeKey<Span>)
-// ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
-// AttributeKey.class.getClassLoader(),
-// CLIENT_ATTRIBUTE_KEY_NAME,
-// new ClassLoaderScopedWeakMap.Supplier() {
-// @Override
-// public Object get() {
-// return new AttributeKey<>(CLIENT_ATTRIBUTE_KEY_NAME);
-// }
-// });
}

MODIFY
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/ChannelFutureListenerInstrumentation.java
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/ChannelFutureListenerInstrumentation.java
@@ -41,9 +41,12 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
@Override
public String[] helperClassNames() {
return new String[] {
- "datadog.trace.agent.tooling.ClassLoaderScopedWeakMapSupplier",
+ "datadog.trace.agent.tooling.ClassLoaderScopedWeakMap$Supplier",
"datadog.trace.agent.tooling.ClassLoaderScopedWeakMap",
packageName + ".AttributeKeys",
+ packageName + ".AttributeKeys$1",
+ packageName + ".AttributeKeys$2",
+ packageName + ".AttributeKeys$3",
"datadog.trace.agent.decorator.BaseDecorator",
// client helpers
"datadog.trace.agent.decorator.ClientDecorator",

MODIFY
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/NettyChannelPipelineInstrumentation.java
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/NettyChannelPipelineInstrumentation.java
@@ -55,9 +55,12 @@ public class NettyChannelPipelineInstrumentation extends Instrumenter.Default {
@Override
public String[] helperClassNames() {
return new String[] {
- "datadog.trace.agent.tooling.ClassLoaderScopedWeakMapSupplier",
+ "datadog.trace.agent.tooling.ClassLoaderScopedWeakMap$Supplier",
"datadog.trace.agent.tooling.ClassLoaderScopedWeakMap",
packageName + ".AttributeKeys",
+ packageName + ".AttributeKeys$1",
+ packageName + ".AttributeKeys$2",
+ packageName + ".AttributeKeys$3",
"datadog.trace.agent.decorator.BaseDecorator",
// client helpers
"datadog.trace.agent.decorator.ClientDecorator",

MODIFY
dd-java-agent/instrumentation/netty-4.0/src/test/groovy/Netty40ClientTest.groovy
dd-java-agent/instrumentation/netty-4.0/src/test/groovy/Netty40ClientTest.groovy
@@ -50,45 +50,45 @@ class Netty40ClientTest extends HttpClientTest<NettyHttpClientDecorator> {
false
}
-// def "connection error (unopened port)"() {
-// given:
-// def uri = new URI("http://localhost:$UNUSABLE_PORT/")
-//
-// when:
-// runUnderTrace("parent") {
-// doRequest(method, uri)
-// }
-//
-// then:
-// def ex = thrown(Exception)
-// def thrownException = ex instanceof ExecutionException ? ex.cause : ex
-//
-// and:
-// assertTraces(1) {
-// trace(0, 2) {
-// basicSpan(it, 0, "parent", thrownException)
-//
-// span(1) {
-// operationName "netty.connect"
-// resourceName "netty.connect"
-// childOf span(0)
-// errored true
-// tags {
-// "$Tags.COMPONENT.key" "netty"
-// Class errorClass = ConnectException
-// try {
-// errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException')
-// } catch (ClassNotFoundException e) {
-// // Older versions use 'java.net.ConnectException' and do not have 'io.netty.channel.AbstractChannel$AnnotatedConnectException'
-// }
-// errorTags errorClass, "Connection refused: localhost/127.0.0.1:$UNUSABLE_PORT"
-// defaultTags()
-// }
-// }
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
+ def "connection error (unopened port)"() {
+ given:
+ def uri = new URI("http://localhost:$UNUSABLE_PORT/")
+
+ when:
+ runUnderTrace("parent") {
+ doRequest(method, uri)
+ }
+
+ then:
+ def ex = thrown(Exception)
+ def thrownException = ex instanceof ExecutionException ? ex.cause : ex
+
+ and:
+ assertTraces(1) {
+ trace(0, 2) {
+ basicSpan(it, 0, "parent", thrownException)
+
+ span(1) {
+ operationName "netty.connect"
+ resourceName "netty.connect"
+ childOf span(0)
+ errored true
+ tags {
+ "$Tags.COMPONENT.key" "netty"
+ Class errorClass = ConnectException
+ try {
+ errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException')
+ } catch (ClassNotFoundException e) {
+ // Older versions use 'java.net.ConnectException' and do not have 'io.netty.channel.AbstractChannel$AnnotatedConnectException'
+ }
+ errorTags errorClass, "Connection refused: localhost/127.0.0.1:$UNUSABLE_PORT"
+ defaultTags()
+ }
+ }
+ }
+ }
+
+ where:
+ method = "GET"
+ }
}

MODIFY
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy
dd-java-agent/testing/src/main/groovy/datadog/trace/agent/test/base/HttpClientTest.groovy
@@ -88,232 +88,232 @@ abstract class HttpClientTest<T extends HttpClientDecorator> extends AgentTestRu
where:
path | tagQueryString
"/success" | false
-// "/success" | true
-// "/success?with=params" | false
-// "/success?with=params" | true
-// "/success#with+fragment" | true
-// "/success?with=params#and=fragment" | true
+ "/success" | true
+ "/success?with=params" | false
+ "/success?with=params" | true
+ "/success#with+fragment" | true
+ "/success?with=params#and=fragment" | true
method = "GET"
url = server.address.resolve(path)
}
-// @Unroll
-// def "basic #method request with parent"() {
-// when:
-// def status = runUnderTrace("parent") {
-// doRequest(method, server.address.resolve("/success"))
-// }
-//
-// then:
-// status == 200
-// assertTraces(2) {
-// server.distributedRequestTrace(it, 0, trace(1).last())
-// trace(1, size(2)) {
-// basicSpan(it, 0, "parent")
-// clientSpan(it, 1, span(0), method, false)
-// }
-// }
-//
-// where:
-// method << BODY_METHODS
-// }
-//
-// @Unroll
-// def "basic #method request with split-by-domain"() {
-// when:
-// def status = withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "true") {
-// doRequest(method, server.address.resolve("/success"))
-// }
-//
-// then:
-// status == 200
-// assertTraces(2) {
-// server.distributedRequestTrace(it, 0, trace(1).last())
-// trace(1, size(1)) {
-// clientSpan(it, 0, null, method, true)
-// }
-// }
-//
-// where:
-// method = "HEAD"
-// }
-//
-// def "trace request without propagation"() {
-// when:
-// def status = withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
-// runUnderTrace("parent") {
-// doRequest(method, server.address.resolve("/success"), ["is-dd-server": "false"])
-// }
-// }
-//
-// then:
-// status == 200
-// // only one trace (client).
-// assertTraces(1) {
-// trace(0, size(2)) {
-// basicSpan(it, 0, "parent")
-// clientSpan(it, 1, span(0), method, renameService)
-// }
-// }
-//
-// where:
-// method = "GET"
-// renameService << [false, true]
-// }
-//
-// def "trace request with callback and parent"() {
-// when:
-// def status = runUnderTrace("parent") {
-// doRequest(method, server.address.resolve("/success"), ["is-dd-server": "false"]) {
-// runUnderTrace("child") {}
-// }
-// }
-//
-// then:
-// status == 200
-// // only one trace (client).
-// assertTraces(1) {
-// trace(0, size(3)) {
-// basicSpan(it, 0, "parent")
-// span(1) {
-// operationName "child"
-// childOf span(0)
-// }
-// clientSpan(it, 2, span(0), method, false)
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
-//
-// def "trace request with callback and no parent"() {
-// when:
-// def status = doRequest(method, server.address.resolve("/success"), ["is-dd-server": "false"]) {
-// runUnderTrace("child") {
-// // Ensure consistent ordering of traces for assertion.
-// TEST_WRITER.waitForTraces(1)
-// }
-// }
-//
-// then:
-// status == 200
-// // only one trace (client).
-// assertTraces(2) {
-// trace(0, size(1)) {
-// clientSpan(it, 0, null, method, false)
-// }
-// trace(1, 1) {
-// span(0) {
-// operationName "child"
-// parent()
-// }
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
-//
-// @Unroll
-// def "basic #method request with 1 redirect"() {
-// given:
-// assumeTrue(testRedirects())
-// def uri = server.address.resolve("/redirect")
-//
-// when:
-// def status = doRequest(method, uri)
-//
-// then:
-// status == 200
-// assertTraces(3) {
-// server.distributedRequestTrace(it, 0, trace(2).last())
-// server.distributedRequestTrace(it, 1, trace(2).last())
-// trace(2, size(1)) {
-// clientSpan(it, 0, null, method, false, false, uri)
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
-//
-// @Unroll
-// def "basic #method request with 2 redirects"() {
-// given:
-// assumeTrue(testRedirects())
-// def uri = server.address.resolve("/another-redirect")
-//
-// when:
-// def status = doRequest(method, uri)
-//
-// then:
-// status == 200
-// assertTraces(4) {
-// server.distributedRequestTrace(it, 0, trace(3).last())
-// server.distributedRequestTrace(it, 1, trace(3).last())
-// server.distributedRequestTrace(it, 2, trace(3).last())
-// trace(3, size(1)) {
-// clientSpan(it, 0, null, method, false, false, uri)
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
-//
-// @Unroll
-// def "basic #method request with circular redirects"() {
-// given:
-// assumeTrue(testRedirects())
-// def uri = server.address.resolve("/circular-redirect")
-//
-// when:
-// doRequest(method, uri)//, ["is-dd-server": "false"])
-//
-// then:
-// def ex = thrown(Exception)
-// def thrownException = ex instanceof ExecutionException ? ex.cause : ex
-//
-// and:
-// assertTraces(3) {
-// server.distributedRequestTrace(it, 0, trace(2).last())
-// server.distributedRequestTrace(it, 1, trace(2).last())
-// trace(2, size(1)) {
-// clientSpan(it, 0, null, method, false, false, uri, statusOnRedirectError(), thrownException)
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
-//
-// def "connection error (unopened port)"() {
-// given:
-// assumeTrue(testConnectionFailure())
-// def uri = new URI("http://localhost:$UNUSABLE_PORT/")
-//
-// when:
-// runUnderTrace("parent") {
-// doRequest(method, uri)
-// }
-//
-// then:
-// def ex = thrown(Exception)
-// def thrownException = ex instanceof ExecutionException ? ex.cause : ex
-//
-// and:
-// assertTraces(1) {
-// trace(0, 2) {
-// basicSpan(it, 0, "parent", thrownException)
-// clientSpan(it, 1, span(0), method, false, false, uri, null, thrownException)
-// }
-// }
-//
-// where:
-// method = "GET"
-// }
+ @Unroll
+ def "basic #method request with parent"() {
+ when:
+ def status = runUnderTrace("parent") {
+ doRequest(method, server.address.resolve("/success"))
+ }
+
+ then:
+ status == 200
+ assertTraces(2) {
+ server.distributedRequestTrace(it, 0, trace(1).last())
+ trace(1, size(2)) {
+ basicSpan(it, 0, "parent")
+ clientSpan(it, 1, span(0), method, false)
+ }
+ }
+
+ where:
+ method << BODY_METHODS
+ }
+
+ @Unroll
+ def "basic #method request with split-by-domain"() {
+ when:
+ def status = withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "true") {
+ doRequest(method, server.address.resolve("/success"))
+ }
+
+ then:
+ status == 200
+ assertTraces(2) {
+ server.distributedRequestTrace(it, 0, trace(1).last())
+ trace(1, size(1)) {
+ clientSpan(it, 0, null, method, true)
+ }
+ }
+
+ where:
+ method = "HEAD"
+ }
+
+ def "trace request without propagation"() {
+ when:
+ def status = withConfigOverride(Config.HTTP_CLIENT_HOST_SPLIT_BY_DOMAIN, "$renameService") {
+ runUnderTrace("parent") {
+ doRequest(method, server.address.resolve("/success"), ["is-dd-server": "false"])
+ }
+ }
+
+ then:
+ status == 200
+ // only one trace (client).
+ assertTraces(1) {
+ trace(0, size(2)) {
+ basicSpan(it, 0, "parent")
+ clientSpan(it, 1, span(0), method, renameService)
+ }
+ }
+
+ where:
+ method = "GET"
+ renameService << [false, true]
+ }
+
+ def "trace request with callback and parent"() {
+ when:
+ def status = runUnderTrace("parent") {
+ doRequest(method, server.address.resolve("/success"), ["is-dd-server": "false"]) {
+ runUnderTrace("child") {}
+ }
+ }
+
+ then:
+ status == 200
+ // only one trace (client).
+ assertTraces(1) {
+ trace(0, size(3)) {
+ basicSpan(it, 0, "parent")
+ span(1) {
+ operationName "child"
+ childOf span(0)
+ }
+ clientSpan(it, 2, span(0), method, false)
+ }
+ }
+
+ where:
+ method = "GET"
+ }
+
+ def "trace request with callback and no parent"() {
+ when:
+ def status = doRequest(method, server.address.resolve("/success"), ["is-dd-server": "false"]) {
+ runUnderTrace("child") {
+ // Ensure consistent ordering of traces for assertion.
+ TEST_WRITER.waitForTraces(1)
+ }
+ }
+
+ then:
+ status == 200
+ // only one trace (client).
+ assertTraces(2) {
+ trace(0, size(1)) {
+ clientSpan(it, 0, null, method, false)
+ }
+ trace(1, 1) {
+ span(0) {
+ operationName "child"
+ parent()
+ }
+ }
+ }
+
+ where:
+ method = "GET"
+ }
+
+ @Unroll
+ def "basic #method request with 1 redirect"() {
+ given:
+ assumeTrue(testRedirects())
+ def uri = server.address.resolve("/redirect")
+
+ when:
+ def status = doRequest(method, uri)
+
+ then:
+ status == 200
+ assertTraces(3) {
+ server.distributedRequestTrace(it, 0, trace(2).last())
+ server.distributedRequestTrace(it, 1, trace(2).last())
+ trace(2, size(1)) {
+ clientSpan(it, 0, null, method, false, false, uri)
+ }
+ }
+
+ where:
+ method = "GET"
+ }
+
+ @Unroll
+ def "basic #method request with 2 redirects"() {
+ given:
+ assumeTrue(testRedirects())
+ def uri = server.address.resolve("/another-redirect")
+
+ when:
+ def status = doRequest(method, uri)
+
+ then:
+ status == 200
+ assertTraces(4) {
+ server.distributedRequestTrace(it, 0, trace(3).last())
+ server.distributedRequestTrace(it, 1, trace(3).last())
+ server.distributedRequestTrace(it, 2, trace(3).last())
+ trace(3, size(1)) {
+ clientSpan(it, 0, null, method, false, false, uri)
+ }
+ }
+
+ where:
+ method = "GET"
+ }
+
+ @Unroll
+ def "basic #method request with circular redirects"() {
+ given:
+ assumeTrue(testRedirects())
+ def uri = server.address.resolve("/circular-redirect")
+
+ when:
+ doRequest(method, uri)//, ["is-dd-server": "false"])
+
+ then:
+ def ex = thrown(Exception)
+ def thrownException = ex instanceof ExecutionException ? ex.cause : ex
+
+ and:
+ assertTraces(3) {
+ server.distributedRequestTrace(it, 0, trace(2).last())
+ server.distributedRequestTrace(it, 1, trace(2).last())
+ trace(2, size(1)) {
+ clientSpan(it, 0, null, method, false, false, uri, statusOnRedirectError(), thrownException)
+ }
+ }
+
+ where:
+ method = "GET"
+ }
+
+ def "connection error (unopened port)"() {
+ given:
+ assumeTrue(testConnectionFailure())
+ def uri = new URI("http://localhost:$UNUSABLE_PORT/")
+
+ when:
+ runUnderTrace("parent") {
+ doRequest(method, uri)
+ }
+
+ then:
+ def ex = thrown(Exception)
+ def thrownException = ex instanceof ExecutionException ? ex.cause : ex
+
+ and:
+ assertTraces(1) {
+ trace(0, 2) {
+ basicSpan(it, 0, "parent", thrownException)
+ clientSpan(it, 1, span(0), method, false, false, uri, null, thrownException)
+ }
+ }
+
+ where:
+ method = "GET"
+ }
// parent span must be cast otherwise it breaks debugging classloading (junit loads it early)
void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", boolean renameService = false, boolean tagQueryString = false, URI uri = server.address.resolve("/success"), Integer status = 200, Throwable exception = null) {

Add all helpers required for ClassLoaderScopedWeakMap usage + re-enable all tests