No matter what, it won't work :) I am now in the mood 'try everything in the world until it works'
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 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 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;
}
- if (classLoaderMap.containsKey(key)) {
- return classLoaderMap.get(key);
+ public static Object bbb(ClassLoaderScopedWeakMapSupplier aaa) {
+ System.out.println("[STD LOG] bbb" + ClassLoaderScopedWeakMapSupplier.class.getName());
+ return aaa.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();
- }
+// /**
+// * 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();
+// }
}

ADD
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,3 +60,23 @@ 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;
@@ -9,49 +10,65 @@ 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 =
- (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 = 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);
+// }
+// });
private static final String SERVER_ATTRIBUTE_KEY_NAME =
HttpServerTracingHandler.class.getName() + ".span";
- 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 = 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 =
- (AttributeKey<Span>)
- ClassLoaderScopedWeakMap.INSTANCE.getOrCreate(
- AttributeKey.class.getClassLoader(),
- CLIENT_ATTRIBUTE_KEY_NAME,
- new ClassLoaderScopedWeakMap.Supplier() {
+ // 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() {
@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,6 +41,8 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
@Override
public String[] helperClassNames() {
return new String[] {
+ "datadog.trace.agent.tooling.ClassLoaderScopedWeakMapSupplier",
+ "datadog.trace.agent.tooling.ClassLoaderScopedWeakMap",
packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator",
// client helpers

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,6 +55,8 @@ public class NettyChannelPipelineInstrumentation extends Instrumenter.Default {
@Override
public String[] helperClassNames() {
return new String[] {
+ "datadog.trace.agent.tooling.ClassLoaderScopedWeakMapSupplier",
+ "datadog.trace.agent.tooling.ClassLoaderScopedWeakMap",
packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator",
// client helpers

MODIFY
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/client/HttpClientRequestTracingHandler.java
dd-java-agent/instrumentation/netty-4.0/src/main/java/datadog/trace/instrumentation/netty40/client/HttpClientRequestTracingHandler.java
@@ -20,6 +20,7 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
@Override
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise prm) {
+ System.out.println("[STD LOG] About to write request handler");
if (!(msg instanceof HttpRequest)) {
ctx.write(msg, prm);
return;

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) {

MODIFY
dd-trace-ot/src/main/java/datadog/trace/common/writer/ListWriter.java
dd-trace-ot/src/main/java/datadog/trace/common/writer/ListWriter.java
@@ -38,7 +38,7 @@ public class ListWriter extends CopyOnWriteArrayList<List<DDSpan>> implements Wr
}
latches.add(latch);
}
- if (!latch.await(20, TimeUnit.SECONDS)) {
+ if (!latch.await(5, TimeUnit.SECONDS)) {
throw new TimeoutException(
"Timeout waiting for " + number + " trace(s). ListWriter.size() == " + size());
}

No matter what, it won't work :) I am now in the mood 'try everything in the world until it works'