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

This commit is contained in:
Luca Abbati 2019-07-16 15:38:44 +02:00
parent cb430c566b
commit 840b8b407d
No known key found for this signature in database
GPG Key ID: 74DBB952D9BA17F2
10 changed files with 382 additions and 324 deletions

View File

@ -1,42 +1,53 @@
package datadog.trace.agent.tooling; package datadog.trace.agent.tooling;
import datadog.trace.bootstrap.WeakMap; //import datadog.trace.bootstrap.WeakMap;
import java.util.HashMap; //import java.util.HashMap;
import java.util.Map; //import java.util.Map;
/** A registry which is scoped per-classloader able to hold key-value pairs with weak keys. */ /** A registry which is scoped per-classloader able to hold key-value pairs with weak keys. */
public class ClassLoaderScopedWeakMap { 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 * Gets the element registered at the specified key or register as new one retrieved by the
* provided supplier. * provided supplier.
*/ */
public synchronized Object getOrCreate( // public Object getOrCreate(
ClassLoader classLoader, Object key, Supplier valueSupplier) { //// public synchronized Object getOrCreate(
Map<Object, Object> classLoaderMap = map.get(classLoader); // ClassLoader classLoader, Object key, Supplier valueSupplier) {
if (classLoaderMap == null) { //// Map<Object, Object> classLoaderMap = map.get(classLoader);
classLoaderMap = new HashMap<>(); //// if (classLoaderMap == null) {
map.put(classLoader, classLoaderMap); //// 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)) { public static Object bbb(ClassLoaderScopedWeakMapSupplier aaa) {
return classLoaderMap.get(key); System.out.println("[STD LOG] bbb" + ClassLoaderScopedWeakMapSupplier.class.getName());
return aaa.get();
} }
Object value = valueSupplier.get(); // /**
classLoaderMap.put(key, value); // * Supplies the value to be stored and it is called only when a value does not exists yet in the
return value; // * 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();
}
} }

View File

@ -0,0 +1,5 @@
package datadog.trace.agent.tooling;
public interface ClassLoaderScopedWeakMapSupplier {
Object get();
}

View File

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

View File

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

View File

@ -41,6 +41,8 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
@Override @Override
public String[] helperClassNames() { public String[] helperClassNames() {
return new String[] { return new String[] {
"datadog.trace.agent.tooling.ClassLoaderScopedWeakMapSupplier",
"datadog.trace.agent.tooling.ClassLoaderScopedWeakMap",
packageName + ".AttributeKeys", packageName + ".AttributeKeys",
"datadog.trace.agent.decorator.BaseDecorator", "datadog.trace.agent.decorator.BaseDecorator",
// client helpers // client helpers

View File

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

View File

@ -20,6 +20,7 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
@Override @Override
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise prm) { 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)) { if (!(msg instanceof HttpRequest)) {
ctx.write(msg, prm); ctx.write(msg, prm);
return; return;

View File

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

View File

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

View File

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