Copy testbed for OpenTelemetry API (#513)

This commit is contained in:
Bogdan Drutu 2019-08-26 11:09:16 -07:00 committed by GitHub
parent 55717b880c
commit 75811d4f41
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 2265 additions and 0 deletions

View File

@ -0,0 +1,5 @@
# OpenTelemetry SDK Testbed
Testbed for OpenTelemetry API and SDK.
* Java 8 compatible.

View File

@ -0,0 +1,11 @@
description = 'OpenTelemetry SDK Testbed'
dependencies {
api project(':opentelemetry-api')
implementation project(':opentelemetry-sdk'),
libraries.awaitility,
libraries.guava
signature "org.codehaus.mojo.signature:java18:1.0@signature"
}

View File

@ -0,0 +1,231 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed;
import static com.google.common.truth.Truth.assertThat;
import com.google.protobuf.Timestamp;
import io.opentelemetry.proto.trace.v1.AttributeValue;
import io.opentelemetry.proto.trace.v1.Span;
import io.opentelemetry.proto.trace.v1.Span.SpanKind;
import io.opentelemetry.sdk.trace.TracerSdk;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.sdk.trace.export.SimpleSampledSpansProcessor;
import io.opentelemetry.trace.Tracer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import javax.annotation.Nullable;
public final class TestUtils {
private TestUtils() {}
/**
* Creates a new {@code io.opentracing.Tracer} out of the {@code TracerSdk} implementation and
* exporting to the specified {@code InMemorySpanExporter}.
*/
public static Tracer createTracerShim(InMemorySpanExporter exporter) {
TracerSdk tracer = new TracerSdk();
tracer.addSpanProcessor(SimpleSampledSpansProcessor.newBuilder(exporter).build());
return tracer;
}
/** Returns the number of finished {@code Span}s in the specified {@code InMemorySpanExporter}. */
public static Callable<Integer> finishedSpansSize(final InMemorySpanExporter tracer) {
return new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return tracer.getFinishedSpanItems().size();
}
};
}
/** Returns a {@code List} with the {@code Span} matching the specified attribute. */
public static List<Span> getByAttr(List<Span> spans, final String key, final Object value) {
return getByCondition(
spans,
new Condition() {
@Override
public boolean check(Span span) {
AttributeValue attrValue = span.getAttributes().getAttributeMap().get(key);
if (attrValue == null) {
return false;
}
switch (attrValue.getValueCase()) {
case VALUE_NOT_SET:
return false;
case STRING_VALUE:
return value.equals(attrValue.getStringValue());
case INT_VALUE:
return value.equals(attrValue.getIntValue());
case BOOL_VALUE:
return value.equals(attrValue.getBoolValue());
case DOUBLE_VALUE:
return value.equals(attrValue.getDoubleValue());
}
return false;
}
});
}
/**
* Returns one {@code Span} instance matching the specified attribute. In case of more than one
* instance being matched, an {@code IllegalArgumentException} will be thrown.
*/
@Nullable
public static Span getOneByAttr(List<Span> spans, String key, Object value) {
List<Span> found = getByAttr(spans, key, value);
if (found.size() > 1) {
throw new IllegalArgumentException(
"there is more than one span with tag '" + key + "' and value '" + value + "'");
}
return found.isEmpty() ? null : found.get(0);
}
/** Returns a {@code List} with the {@code Span} matching the specified kind. */
public static List<Span> getByKind(List<Span> spans, final SpanKind kind) {
return getByCondition(
spans,
new Condition() {
@Override
public boolean check(Span span) {
return span.getKind() == kind;
}
});
}
/**
* Returns one {@code Span} instance matching the specified kind. In case of more than one
* instance being matched, an {@code IllegalArgumentException} will be thrown.
*/
@Nullable
public static Span getOneByKind(List<Span> spans, final SpanKind kind) {
List<Span> found = getByKind(spans, kind);
if (found.size() > 1) {
throw new IllegalArgumentException("there is more than one span with kind '" + kind + "'");
}
return found.isEmpty() ? null : found.get(0);
}
/** Returns a {@code List} with the {@code Span} matching the specified name. */
public static List<Span> getByName(List<Span> spans, final String name) {
return getByCondition(
spans,
new Condition() {
@Override
public boolean check(Span span) {
return span.getName().equals(name);
}
});
}
/**
* Returns one {@code Span} instance matching the specified name. In case of more than one
* instance being matched, an {@code IllegalArgumentException} will be thrown.
*/
@Nullable
public static Span getOneByName(List<Span> spans, final String name) {
List<Span> found = getByName(spans, name);
if (found.size() > 1) {
throw new IllegalArgumentException("there is more than one span with name '" + name + "'");
}
return found.isEmpty() ? null : found.get(0);
}
interface Condition {
boolean check(Span span);
}
static List<Span> getByCondition(List<Span> spans, Condition cond) {
List<Span> found = new ArrayList<>();
for (Span span : spans) {
if (cond.check(span)) {
found.add(span);
}
}
return found;
}
/** Sleeps for a random period of time, expected to be under 1 second. */
public static void sleep() {
try {
TimeUnit.MILLISECONDS.sleep(new Random().nextInt(500));
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
/** Sleeps the specified milliseconds. */
public static void sleep(long milliseconds) {
try {
TimeUnit.MILLISECONDS.sleep(milliseconds);
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
}
/**
* Sorts the specified {@code List} of {@code Span} by their {@code Span.Timestamp} values,
* returning it as a new {@code List}.
*/
public static List<Span> sortByStartTime(List<Span> spans) {
List<Span> sortedSpans = new ArrayList<>(spans);
Collections.sort(
sortedSpans,
new Comparator<Span>() {
@Override
public int compare(Span o1, Span o2) {
Timestamp t1 = o1.getStartTime();
Timestamp t2 = o2.getStartTime();
if (t1.getSeconds() == t2.getSeconds()) {
return Long.compare(t1.getNanos(), t2.getNanos());
} else {
return Long.compare(t1.getSeconds(), t2.getSeconds());
}
}
});
return sortedSpans;
}
/** Asserts the specified {@code List} of {@code Span} belongs to the same trace. */
public static void assertSameTrace(List<Span> spans) {
for (int i = 0; i < spans.size() - 1; i++) {
// TODO - Include nanos in this comparison.
assertThat(
spans.get(spans.size() - 1).getEndTime().getSeconds()
>= spans.get(i).getEndTime().getSeconds())
.isTrue();
assertThat(spans.get(spans.size() - 1).getTraceId()).isEqualTo(spans.get(i).getTraceId());
assertThat(spans.get(spans.size() - 1).getSpanId()).isEqualTo(spans.get(i).getParentSpanId());
}
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.activespanreplacement;
import static com.google.common.truth.Truth.assertThat;
import static io.opentelemetry.sdk.contrib.trace.testbed.TestUtils.createTracerShim;
import static io.opentelemetry.sdk.contrib.trace.testbed.TestUtils.finishedSpansSize;
import static io.opentelemetry.sdk.contrib.trace.testbed.TestUtils.sleep;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.core.IsEqual.equalTo;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
@SuppressWarnings("FutureReturnValueIgnored")
public class ActiveSpanReplacementTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = createTracerShim(exporter);
private final ExecutorService executor = Executors.newCachedThreadPool();
@Test
public void test() {
// Start an isolated task and query for its result in another task/thread
Span span = tracer.spanBuilder("initial").startSpan();
try (Scope scope = tracer.withSpan(span)) {
// Explicitly pass a Span to be finished once a late calculation is done.
submitAnotherTask(span);
}
await().atMost(15, TimeUnit.SECONDS).until(finishedSpansSize(exporter), equalTo(3));
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertThat(spans).hasSize(3);
assertThat(spans.get(0).getName()).isEqualTo("initial"); // Isolated task
assertThat(spans.get(1).getName()).isEqualTo("subtask");
assertThat(spans.get(2).getName()).isEqualTo("task");
// task/subtask are part of the same trace, and subtask is a child of task
assertThat(spans.get(1).getTraceId()).isEqualTo(spans.get(2).getTraceId());
assertThat(spans.get(2).getSpanId()).isEqualTo(spans.get(1).getParentSpanId());
// initial task is not related in any way to those two tasks
assertThat(spans.get(0).getTraceId()).isNotEqualTo(spans.get(1).getTraceId());
assertThat(spans.get(0).getParentSpanId()).isEmpty();
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
private void submitAnotherTask(final Span initialSpan) {
executor.submit(
new Runnable() {
@Override
public void run() {
// Create a new Span for this task
Span taskSpan = tracer.spanBuilder("task").startSpan();
try (Scope scope = tracer.withSpan(taskSpan)) {
// Simulate work strictly related to the initial Span
// and finish it.
try (Scope initialScope = tracer.withSpan(initialSpan)) {
sleep(50);
} finally {
initialSpan.end();
}
// Restore the span for this task and create a subspan
Span subTaskSpan = tracer.spanBuilder("subtask").startSpan();
try (Scope subTaskScope = tracer.withSpan(subTaskSpan)) {
sleep(50);
} finally {
subTaskSpan.end();
}
} finally {
taskSpan.end();
}
}
});
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.actorpropagation;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Tracer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Phaser;
final class Actor implements AutoCloseable {
private final ExecutorService executor;
private final Tracer tracer;
private final Phaser phaser;
Actor(Tracer tracer, Phaser phaser) {
// Passed along here for testing. Normally should be referenced via GlobalTracer.get().
this.tracer = tracer;
this.phaser = phaser;
executor = Executors.newFixedThreadPool(2);
}
@Override
public void close() {
executor.shutdown();
}
Future<?> tell(final String message) {
final Span parent = tracer.getCurrentSpan();
phaser.register();
return executor.submit(
new Runnable() {
@Override
public void run() {
Span child =
tracer
.spanBuilder("received")
.setParent(parent)
.setSpanKind(Kind.CONSUMER)
.startSpan();
try (Scope ignored = tracer.withSpan(child)) {
phaser.arriveAndAwaitAdvance(); // child tracer started
child.addEvent("received " + message);
phaser.arriveAndAwaitAdvance(); // assert size
} finally {
child.end();
}
phaser.arriveAndAwaitAdvance(); // child tracer finished
phaser.arriveAndAwaitAdvance(); // assert size
}
});
}
Future<String> ask(final String message) {
final Span parent = tracer.getCurrentSpan();
phaser.register();
return executor.submit(
new Callable<String>() {
@Override
public String call() {
Span span =
tracer
.spanBuilder("received")
.setParent(parent)
.setSpanKind(Kind.CONSUMER)
.startSpan();
try {
phaser.arriveAndAwaitAdvance(); // child tracer started
phaser.arriveAndAwaitAdvance(); // assert size
return "received " + message;
} finally {
span.end();
phaser.arriveAndAwaitAdvance(); // child tracer finished
phaser.arriveAndAwaitAdvance(); // assert size
}
}
});
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.actorpropagation;
import static com.google.common.truth.Truth.assertThat;
import io.opentelemetry.context.Scope;
import io.opentelemetry.proto.trace.v1.Span.SpanKind;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.Phaser;
import org.junit.Before;
import org.junit.Test;
/**
* These tests are intended to simulate the kind of async models that are common in java async
* frameworks.
*
* <p>For improved readability, ignore the phaser lines as those are there to ensure deterministic
* execution for the tests without sleeps.
*/
@SuppressWarnings("FutureReturnValueIgnored")
public class ActorPropagationTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private Phaser phaser;
@Before
public void before() {
phaser = new Phaser();
}
@Test
public void testActorTell() {
try (Actor actor = new Actor(tracer, phaser)) {
phaser.register();
Span parent = tracer.spanBuilder("actorTell").setSpanKind(Kind.PRODUCER).startSpan();
parent.setAttribute("component", "example-actor");
try (Scope ignored = tracer.withSpan(parent)) {
actor.tell("my message 1");
actor.tell("my message 2");
} finally {
parent.end();
}
phaser.arriveAndAwaitAdvance(); // child tracer started
assertThat(exporter.getFinishedSpanItems()).hasSize(1);
phaser.arriveAndAwaitAdvance(); // continue...
phaser.arriveAndAwaitAdvance(); // child tracer finished
assertThat(exporter.getFinishedSpanItems()).hasSize(3);
assertThat(TestUtils.getByKind(exporter.getFinishedSpanItems(), SpanKind.CONSUMER))
.hasSize(2);
phaser.arriveAndDeregister(); // continue...
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished.size()).isEqualTo(3);
assertThat(finished.get(0).getTraceId()).isEqualTo(finished.get(1).getTraceId());
assertThat(TestUtils.getByKind(finished, SpanKind.CONSUMER)).hasSize(2);
assertThat(TestUtils.getOneByKind(finished, SpanKind.PRODUCER)).isNotNull();
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
}
@Test
public void testActorAsk() throws ExecutionException, InterruptedException {
try (Actor actor = new Actor(tracer, phaser)) {
phaser.register();
Future<String> future1;
Future<String> future2;
Span span = tracer.spanBuilder("actorAsk").setSpanKind(Kind.PRODUCER).startSpan();
span.setAttribute("component", "example-actor");
try (Scope ignored = tracer.withSpan(span)) {
future1 = actor.ask("my message 1");
future2 = actor.ask("my message 2");
} finally {
span.end();
}
phaser.arriveAndAwaitAdvance(); // child tracer started
assertThat(exporter.getFinishedSpanItems().size()).isEqualTo(1);
phaser.arriveAndAwaitAdvance(); // continue...
phaser.arriveAndAwaitAdvance(); // child tracer finished
assertThat(exporter.getFinishedSpanItems().size()).isEqualTo(3);
assertThat(TestUtils.getByKind(exporter.getFinishedSpanItems(), SpanKind.CONSUMER))
.hasSize(2);
phaser.arriveAndDeregister(); // continue...
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
String message1 = future1.get(); // This really should be a non-blocking callback...
String message2 = future2.get(); // This really should be a non-blocking callback...
assertThat(message1).isEqualTo("received my message 1");
assertThat(message2).isEqualTo("received my message 2");
assertThat(finished.size()).isEqualTo(3);
assertThat(finished.get(0).getTraceId()).isEqualTo(finished.get(1).getTraceId());
assertThat(TestUtils.getByKind(finished, SpanKind.CONSUMER)).hasSize(2);
assertThat(TestUtils.getOneByKind(finished, SpanKind.PRODUCER)).isNotNull();
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.clientserver;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.HttpTextFormat.Setter;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Tracer;
import java.util.concurrent.ArrayBlockingQueue;
final class Client {
private final ArrayBlockingQueue<Message> queue;
private final Tracer tracer;
public Client(ArrayBlockingQueue<Message> queue, Tracer tracer) {
this.queue = queue;
this.tracer = tracer;
}
public void send() throws InterruptedException {
Message message = new Message();
Span span = tracer.spanBuilder("send").setSpanKind(Kind.CLIENT).startSpan();
span.setAttribute("component", "example-client");
try (Scope ignored = tracer.withSpan(span)) {
tracer
.getHttpTextFormat()
.inject(
span.getContext(),
message,
new Setter<Message>() {
@Override
public void put(Message carrier, String key, String value) {
carrier.put(key, value);
}
});
queue.put(message);
} finally {
span.end();
}
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.clientserver;
import java.util.HashMap;
final class Message extends HashMap<String, String> {}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.clientserver;
import io.opentelemetry.context.Scope;
import io.opentelemetry.context.propagation.HttpTextFormat.Getter;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.Tracer;
import java.util.concurrent.ArrayBlockingQueue;
import javax.annotation.Nullable;
final class Server extends Thread {
private final ArrayBlockingQueue<Message> queue;
private final Tracer tracer;
public Server(ArrayBlockingQueue<Message> queue, Tracer tracer) {
this.queue = queue;
this.tracer = tracer;
}
private void process(Message message) {
SpanContext context =
tracer
.getHttpTextFormat()
.extract(
message,
new Getter<Message>() {
@Nullable
@Override
public String get(Message carrier, String key) {
return carrier.get(key);
}
});
Span span =
tracer.spanBuilder("receive").setSpanKind(Kind.SERVER).setParent(context).startSpan();
span.setAttribute("component", "example-server");
try (Scope ignored = tracer.withSpan(span)) {
// Simulate work.
tracer.getCurrentSpan().addEvent("DoWork");
} finally {
span.end();
}
}
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
process(queue.take());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.clientserver;
import static com.google.common.truth.Truth.assertThat;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import io.opentelemetry.proto.trace.v1.Span.SpanKind;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class TestClientServerTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private final ArrayBlockingQueue<Message> queue = new ArrayBlockingQueue<>(10);
private Server server;
@Before
public void before() {
server = new Server(queue, tracer);
server.start();
}
@After
public void after() throws InterruptedException {
server.interrupt();
server.join(5_000L);
}
@Test
public void test() throws Exception {
Client client = new Client(queue, tracer);
client.send();
await().atMost(15, TimeUnit.SECONDS).until(TestUtils.finishedSpansSize(exporter), equalTo(2));
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertEquals(2, finished.size());
finished = TestUtils.sortByStartTime(finished);
assertThat(finished.get(0).getTraceId()).isEqualTo(finished.get(1).getTraceId());
assertThat(finished.get(0).getKind()).isEqualTo(SpanKind.CLIENT);
assertThat(finished.get(1).getKind()).isEqualTo(SpanKind.SERVER);
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.concurrentcommonrequesthandler;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
final class Client {
private final ExecutorService executor = Executors.newCachedThreadPool();
private final RequestHandler requestHandler;
public Client(RequestHandler requestHandler) {
this.requestHandler = requestHandler;
}
public Future<String> send(final Object message) {
final Context context = new Context();
return executor.submit(
new Callable<String>() {
@Override
public String call() throws Exception {
TestUtils.sleep();
executor
.submit(
new Runnable() {
@Override
public void run() {
TestUtils.sleep();
requestHandler.beforeRequest(message, context);
}
})
.get();
executor
.submit(
new Runnable() {
@Override
public void run() {
TestUtils.sleep();
requestHandler.afterResponse(message, context);
}
})
.get();
return message + ":response";
}
});
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.concurrentcommonrequesthandler;
import java.util.HashMap;
final class Context extends HashMap<String, Object> {}

View File

@ -0,0 +1,133 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.concurrentcommonrequesthandler;
import static com.google.common.truth.Truth.assertThat;
import io.opentelemetry.context.Scope;
import io.opentelemetry.proto.trace.v1.Span.SpanKind;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
/**
* There is only one instance of 'RequestHandler' per 'Client'. Methods of 'RequestHandler' are
* executed concurrently in different threads which are reused (common pool). Therefore we cannot
* use current active span and activate span. So one issue here is setting correct parent span.
*/
public class HandlerTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private final Client client = new Client(new RequestHandler(tracer));
@Before
public void before() {
exporter.reset();
}
@Test
public void two_requests() throws Exception {
Future<String> responseFuture = client.send("message");
Future<String> responseFuture2 = client.send("message2");
assertThat(responseFuture.get(15, TimeUnit.SECONDS)).isEqualTo("message:response");
assertThat(responseFuture2.get(15, TimeUnit.SECONDS)).isEqualTo("message2:response");
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished).hasSize(2);
for (io.opentelemetry.proto.trace.v1.Span spanProto : finished) {
assertThat(spanProto.getKind()).isEqualTo(SpanKind.CLIENT);
}
assertThat(finished.get(0).getTraceId()).isNotEqualTo(finished.get(1).getTraceId());
assertThat(finished.get(0).getParentSpanId()).isEmpty();
assertThat(finished.get(1).getParentSpanId()).isEmpty();
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
/** Active parent is not picked up by child. */
@Test
public void parent_not_picked_up() throws Exception {
Span parentSpan = tracer.spanBuilder("parent").startSpan();
try (Scope ignored = tracer.withSpan(parentSpan)) {
String response = client.send("no_parent").get(15, TimeUnit.SECONDS);
assertThat(response).isEqualTo("no_parent:response");
} finally {
parentSpan.end();
}
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished).hasSize(2);
io.opentelemetry.proto.trace.v1.Span child =
TestUtils.getOneByName(finished, RequestHandler.OPERATION_NAME);
assertThat(child).isNotNull();
io.opentelemetry.proto.trace.v1.Span parent = TestUtils.getOneByName(finished, "parent");
assertThat(parent).isNotNull();
// Here check that there is no parent-child relation although it should be because child is
// created when parent is active
assertThat(parent.getSpanId()).isNotEqualTo(child.getParentSpanId());
}
/**
* Solution is bad because parent is per client (we don't have better choice). Therefore all
* client requests will have the same parent. But if client is long living and injected/reused in
* different places then initial parent will not be correct.
*/
@Test
public void bad_solution_to_set_parent() throws Exception {
Client client;
Span parentSpan = tracer.spanBuilder("parent").startSpan();
try (Scope ignored = tracer.withSpan(parentSpan)) {
client = new Client(new RequestHandler(tracer, parentSpan.getContext()));
String response = client.send("correct_parent").get(15, TimeUnit.SECONDS);
assertThat(response).isEqualTo("correct_parent:response");
} finally {
parentSpan.end();
}
// Send second request, now there is no active parent, but it will be set, ups
String response = client.send("wrong_parent").get(15, TimeUnit.SECONDS);
assertThat(response).isEqualTo("wrong_parent:response");
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished).hasSize(3);
finished = TestUtils.sortByStartTime(finished);
io.opentelemetry.proto.trace.v1.Span parent = TestUtils.getOneByName(finished, "parent");
assertThat(parent).isNotNull();
// now there is parent/child relation between first and second span:
assertThat(finished.get(1).getParentSpanId()).isEqualTo(parent.getSpanId());
// third span should not have parent, but it has, damn it
assertThat(finished.get(2).getParentSpanId()).isEqualTo(parent.getSpanId());
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.concurrentcommonrequesthandler;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.SpanContext;
import io.opentelemetry.trace.Tracer;
/**
* One instance per Client. Executed concurrently for all requests of one client. 'beforeRequest'
* and 'afterResponse' are executed in different threads for one 'send'
*/
final class RequestHandler {
static final String OPERATION_NAME = "send";
private final Tracer tracer;
private final SpanContext parentContext;
public RequestHandler(Tracer tracer) {
this(tracer, null);
}
public RequestHandler(Tracer tracer, SpanContext parentContext) {
this.tracer = tracer;
this.parentContext = parentContext;
}
public void beforeRequest(Object request, Context context) {
// we cannot use active span because we don't know in which thread it is executed
// and we cannot therefore activate span. thread can come from common thread pool.
Span.Builder spanBuilder =
tracer.spanBuilder(OPERATION_NAME).setNoParent().setSpanKind(Kind.CLIENT);
if (parentContext != null) {
spanBuilder.setParent(parentContext);
}
context.put("span", spanBuilder.startSpan());
}
public void afterResponse(Object response, Context context) {
Object spanObject = context.get("span");
if (spanObject instanceof Span) {
Span span = (Span) spanObject;
span.end();
}
}
}

View File

@ -0,0 +1,177 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.errorreporting;
import static com.google.common.truth.Truth.assertThat;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertEquals;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Status;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
@SuppressWarnings("FutureReturnValueIgnored")
public final class ErrorReportingTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private final ExecutorService executor = Executors.newCachedThreadPool();
/* Very simple error handling **/
@Test
public void testSimpleError() {
Span span = tracer.spanBuilder("one").startSpan();
try (Scope ignored = tracer.withSpan(span)) {
throw new RuntimeException("Invalid state");
} catch (Exception e) {
span.setStatus(Status.UNKNOWN);
} finally {
span.end();
}
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertThat(spans).hasSize(1);
assertThat(spans.get(0).getStatus().getCode())
.isEqualTo(Status.UNKNOWN.getCanonicalCode().value());
}
/* Error handling in a callback capturing/activating the Span */
@Test
public void testCallbackError() {
final Span span = tracer.spanBuilder("one").startSpan();
executor.submit(
new Runnable() {
@Override
public void run() {
try (Scope ignored = tracer.withSpan(span)) {
throw new RuntimeException("Invalid state");
} catch (Exception exc) {
span.setStatus(Status.UNKNOWN);
} finally {
span.end();
}
}
});
await().atMost(5, TimeUnit.SECONDS).until(TestUtils.finishedSpansSize(exporter), equalTo(1));
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertThat(spans).hasSize(1);
assertThat(spans.get(0).getStatus().getCode())
.isEqualTo(Status.UNKNOWN.getCanonicalCode().value());
}
/* Error handling for a max-retries task (such as url fetching).
* We log the error at each retry. */
@Test
public void testErrorRecovery() {
final int maxRetries = 1;
int retries = 0;
Object res = null;
Span span = tracer.spanBuilder("one").startSpan();
try (Scope ignored = tracer.withSpan(span)) {
while (res == null && retries++ < maxRetries) {
try {
throw new RuntimeException("No url could be fetched");
} catch (final Exception exc) {
span.addEvent("error");
}
}
}
if (res == null) {
span.setStatus(Status.UNKNOWN); // Could not fetch anything.
}
span.end();
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertThat(spans).hasSize(1);
assertThat(spans.get(0).getStatus().getCode())
.isEqualTo(Status.UNKNOWN.getCanonicalCode().value());
List<io.opentelemetry.proto.trace.v1.Span.TimedEvent> events =
spans.get(0).getTimeEvents().getTimedEventList();
assertEquals(events.size(), maxRetries);
assertEquals(events.get(0).getEvent().getName(), "error");
}
/* Error handling for a mocked layer automatically capturing/activating
* the Span for a submitted Runnable. */
@Test
public void testInstrumentationLayer() {
Span span = tracer.spanBuilder("one").startSpan();
try (Scope ignored = tracer.withSpan(span)) {
// ScopedRunnable captures the active Span at this time.
executor.submit(
new ScopedRunnable(
new Runnable() {
@Override
public void run() {
try {
throw new RuntimeException("Invalid state");
} catch (Exception exc) {
tracer.getCurrentSpan().setStatus(Status.UNKNOWN);
} finally {
tracer.getCurrentSpan().end();
}
}
},
tracer));
}
await().atMost(5, TimeUnit.SECONDS).until(TestUtils.finishedSpansSize(exporter), equalTo(1));
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertEquals(spans.size(), 1);
assertEquals(spans.get(0).getStatus().getCode(), Status.UNKNOWN.getCanonicalCode().value());
}
private static class ScopedRunnable implements Runnable {
Runnable runnable;
Tracer tracer;
Span span;
private ScopedRunnable(Runnable runnable, Tracer tracer) {
this.runnable = runnable;
this.tracer = tracer;
this.span = tracer.getCurrentSpan();
}
@Override
public void run() {
// No error reporting is done, as we are a simple wrapper.
try (Scope ignored = tracer.withSpan(span)) {
runnable.run();
}
}
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.latespanfinish;
import static com.google.common.truth.Truth.assertThat;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
@SuppressWarnings("FutureReturnValueIgnored")
public final class LateSpanFinishTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private final ExecutorService executor = Executors.newCachedThreadPool();
@Test
public void test() throws Exception {
// Create a Span manually and use it as parent of a pair of subtasks
Span parentSpan = tracer.spanBuilder("parent").startSpan();
submitTasks(parentSpan);
// Wait for the threadpool to be done first, instead of polling/waiting
executor.shutdown();
executor.awaitTermination(15, TimeUnit.SECONDS);
// Late-finish the parent Span now
parentSpan.end();
// Children finish order is not guaranteed, but parent should finish *last*.
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertThat(spans).hasSize(3);
assertThat(spans.get(0).getName()).startsWith("task");
assertThat(spans.get(1).getName()).startsWith("task");
assertThat(spans.get(2).getName()).isEqualTo("parent");
TestUtils.assertSameTrace(spans);
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
/*
* Fire away a few subtasks, passing a parent Span whose lifetime
* is not tied at-all to the children
*/
private void submitTasks(final Span parentSpan) {
executor.submit(
new Runnable() {
@Override
public void run() {
/* Alternative to calling activate() is to pass it manually to asChildOf() for each
* created Span. */
try (Scope scope = tracer.withSpan(parentSpan)) {
Span childSpan = tracer.spanBuilder("task1").startSpan();
try (Scope childScope = tracer.withSpan(childSpan)) {
TestUtils.sleep(55);
} finally {
childSpan.end();
}
}
}
});
executor.submit(
new Runnable() {
@Override
public void run() {
try (Scope scope = tracer.withSpan(parentSpan)) {
Span childSpan = tracer.spanBuilder("task2").startSpan();
try (Scope childScope = tracer.withSpan(childSpan)) {
TestUtils.sleep(85);
} finally {
childSpan.end();
}
}
}
});
}
}

View File

@ -0,0 +1,53 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.listenerperrequest;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Tracer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
final class Client {
private final ExecutorService executor = Executors.newCachedThreadPool();
private final Tracer tracer;
public Client(Tracer tracer) {
this.tracer = tracer;
}
/** Async execution. */
private Future<Object> execute(final Object message, final ResponseListener responseListener) {
return executor.submit(
new Callable<Object>() {
@Override
public Object call() throws Exception {
// send via wire and get response
Object response = message + ":response";
responseListener.onResponse(response);
return response;
}
});
}
public Future<Object> send(final Object message) {
Span span = tracer.spanBuilder("send").setSpanKind(Kind.CLIENT).startSpan();
return execute(message, new ResponseListener(span));
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.listenerperrequest;
import static com.google.common.truth.Truth.assertThat;
import io.opentelemetry.proto.trace.v1.Span.SpanKind;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import org.junit.Test;
/** Each request has own instance of ResponseListener. */
public class ListenerTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
@Test
public void test() throws Exception {
Client client = new Client(tracer);
Object response = client.send("message").get();
assertThat(response).isEqualTo("message:response");
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished).hasSize(1);
assertThat(finished.get(0).getKind()).isEqualTo(SpanKind.CLIENT);
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.listenerperrequest;
import io.opentelemetry.trace.Span;
/** Response listener per request. Executed in a thread different from 'send' thread */
final class ResponseListener {
private final Span span;
public ResponseListener(Span span) {
this.span = span;
}
/** executed when response is received from server. Any thread. */
public void onResponse(Object response) {
span.end();
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.multiplecallbacks;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
class Client {
private final ExecutorService executor = Executors.newCachedThreadPool();
private final Tracer tracer;
public Client(Tracer tracer) {
this.tracer = tracer;
}
public Future<Object> send(final Object message, final long milliseconds) {
final Span parent = tracer.getCurrentSpan();
return executor.submit(
new Callable<Object>() {
@Override
public Object call() throws Exception {
Span span = tracer.spanBuilder("subtask").setParent(parent).startSpan();
try (Scope subtaskScope = tracer.withSpan(span)) {
// Simulate work.
TestUtils.sleep(milliseconds);
} finally {
span.end();
}
return message + "::response";
}
});
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.multiplecallbacks;
import static com.google.common.truth.Truth.assertThat;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.core.IsEqual.equalTo;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
@SuppressWarnings("FutureReturnValueIgnored")
public class MultipleCallbacksTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
@Test
public void test() {
Client client = new Client(tracer);
Span span = tracer.spanBuilder("parent").startSpan();
try (Scope scope = tracer.withSpan(span)) {
client.send("task1", 300);
client.send("task2", 200);
client.send("task3", 100);
} finally {
span.end();
}
await().atMost(15, TimeUnit.SECONDS).until(TestUtils.finishedSpansSize(exporter), equalTo(4));
// Finish order is not guaranteed, so we rely on *start time* to fetch the parent.
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
spans = TestUtils.sortByStartTime(spans);
assertThat(spans).hasSize(4);
assertThat(spans.get(0).getName()).isEqualTo("parent");
io.opentelemetry.proto.trace.v1.Span parentSpan = spans.get(0);
for (int i = 1; i < 4; i++) {
assertThat(spans.get(i).getTraceId()).isEqualTo(parentSpan.getTraceId());
assertThat(spans.get(i).getParentSpanId()).isEqualTo(parentSpan.getSpanId());
}
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
}

View File

@ -0,0 +1,99 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.nestedcallbacks;
import static com.google.common.truth.Truth.assertThat;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.core.IsEqual.equalTo;
import io.opentelemetry.context.Scope;
import io.opentelemetry.proto.trace.v1.AttributeValue;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
@SuppressWarnings("FutureReturnValueIgnored")
public final class NestedCallbacksTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private final ExecutorService executor = Executors.newCachedThreadPool();
@Test
public void test() {
Span span = tracer.spanBuilder("one").startSpan();
submitCallbacks(span);
await().atMost(15, TimeUnit.SECONDS).until(TestUtils.finishedSpansSize(exporter), equalTo(1));
List<io.opentelemetry.proto.trace.v1.Span> spans = exporter.getFinishedSpanItems();
assertThat(spans).hasSize(1);
assertThat(spans.get(0).getName()).isEqualTo("one");
Map<String, AttributeValue> attrs = spans.get(0).getAttributes().getAttributeMapMap();
assertThat(attrs).hasSize(3);
for (int i = 1; i <= 3; i++) {
assertThat(attrs.get("key" + i).getStringValue()).isEqualTo(Integer.toString(i));
}
assertThat(tracer.getCurrentSpan()).isSameInstanceAs(DefaultSpan.getInvalid());
}
private void submitCallbacks(final Span span) {
executor.submit(
new Runnable() {
@Override
public void run() {
try (Scope ignored = tracer.withSpan(span)) {
span.setAttribute("key1", "1");
executor.submit(
new Runnable() {
@Override
public void run() {
try (Scope ignored = tracer.withSpan(span)) {
span.setAttribute("key2", "2");
executor.submit(
new Runnable() {
@Override
public void run() {
try (Scope ignored = tracer.withSpan(span)) {
span.setAttribute("key3", "3");
} finally {
span.end();
}
}
});
}
}
});
}
}
});
}
}

View File

@ -0,0 +1,96 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.promisepropagation;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.ArrayList;
import java.util.Collection;
final class Promise<T> {
private final PromiseContext context;
private final Tracer tracer;
private final Span parentSpan;
private final Collection<SuccessCallback<T>> successCallbacks = new ArrayList<>();
private final Collection<ErrorCallback> errorCallbacks = new ArrayList<>();
Promise(PromiseContext context, Tracer tracer) {
this.context = context;
// Passed along here for testing. Normally should be referenced via GlobalTracer.get().
this.tracer = tracer;
parentSpan = tracer.getCurrentSpan();
}
void onSuccess(SuccessCallback<T> successCallback) {
successCallbacks.add(successCallback);
}
void onError(ErrorCallback errorCallback) {
errorCallbacks.add(errorCallback);
}
@SuppressWarnings("FutureReturnValueIgnored")
void success(final T result) {
for (final SuccessCallback<T> callback : successCallbacks) {
context.submit(
new Runnable() {
@Override
public void run() {
Span childSpan = tracer.spanBuilder("success").setParent(parentSpan).startSpan();
childSpan.setAttribute("component", "success");
try (Scope ignored = tracer.withSpan(childSpan)) {
callback.accept(result);
} finally {
childSpan.end();
}
context.getPhaser().arriveAndAwaitAdvance(); // trace reported
}
});
}
}
@SuppressWarnings("FutureReturnValueIgnored")
void error(final Throwable error) {
for (final ErrorCallback callback : errorCallbacks) {
context.submit(
new Runnable() {
@Override
public void run() {
Span childSpan = tracer.spanBuilder("error").setParent(parentSpan).startSpan();
childSpan.setAttribute("component", "error");
try (Scope ignored = tracer.withSpan(childSpan)) {
callback.accept(error);
} finally {
childSpan.end();
}
context.getPhaser().arriveAndAwaitAdvance(); // trace reported
}
});
}
}
interface SuccessCallback<T> {
void accept(T t);
}
interface ErrorCallback {
void accept(Throwable t);
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.promisepropagation;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Phaser;
final class PromiseContext implements AutoCloseable {
private final Phaser phaser;
private final ExecutorService executor;
public PromiseContext(Phaser phaser, int concurrency) {
this.phaser = phaser;
executor = Executors.newFixedThreadPool(concurrency);
}
@Override
public void close() {
executor.shutdown();
}
public Future<?> submit(Runnable runnable) {
phaser.register(); // register the work to be done on the executor
return executor.submit(runnable);
}
public Phaser getPhaser() {
return phaser;
}
}

View File

@ -0,0 +1,131 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.promisepropagation;
import static com.google.common.truth.Truth.assertThat;
import com.google.protobuf.ByteString;
import io.opentelemetry.context.Scope;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.Phaser;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.Before;
import org.junit.Test;
/**
* These tests are intended to simulate the kind of async models that are common in java async
* frameworks.
*
* <p>For improved readability, ignore the phaser lines as those are there to ensure deterministic
* execution for the tests without sleeps.
*/
public class PromisePropagationTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = TestUtils.createTracerShim(exporter);
private Phaser phaser;
@Before
public void before() {
phaser = new Phaser();
}
@Test
public void testPromiseCallback() {
phaser.register(); // register test thread
final AtomicReference<String> successResult1 = new AtomicReference<>();
final AtomicReference<String> successResult2 = new AtomicReference<>();
final AtomicReference<Throwable> errorResult = new AtomicReference<>();
try (PromiseContext context = new PromiseContext(phaser, 3)) {
Span parentSpan = tracer.spanBuilder("promises").startSpan();
parentSpan.setAttribute("component", "example-promises");
try (Scope ignored = tracer.withSpan(parentSpan)) {
Promise<String> successPromise = new Promise<>(context, tracer);
successPromise.onSuccess(
new Promise.SuccessCallback<String>() {
@Override
public void accept(String s) {
tracer.getCurrentSpan().addEvent("Promised 1 " + s);
successResult1.set(s);
phaser.arriveAndAwaitAdvance(); // result set
}
});
successPromise.onSuccess(
new Promise.SuccessCallback<String>() {
@Override
public void accept(String s) {
tracer.getCurrentSpan().addEvent("Promised 2 " + s);
successResult2.set(s);
phaser.arriveAndAwaitAdvance(); // result set
}
});
Promise<String> errorPromise = new Promise<>(context, tracer);
errorPromise.onError(
new Promise.ErrorCallback() {
@Override
public void accept(Throwable t) {
errorResult.set(t);
phaser.arriveAndAwaitAdvance(); // result set
}
});
assertThat(exporter.getFinishedSpanItems().size()).isEqualTo(0);
successPromise.success("success!");
errorPromise.error(new Exception("some error."));
} finally {
parentSpan.end();
}
phaser.arriveAndAwaitAdvance(); // wait for results to be set
assertThat(successResult1.get()).isEqualTo("success!");
assertThat(successResult2.get()).isEqualTo("success!");
assertThat(errorResult.get().getMessage()).isEqualTo("some error.");
phaser.arriveAndAwaitAdvance(); // wait for traces to be reported
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished.size()).isEqualTo(4);
String component = "component";
io.opentelemetry.proto.trace.v1.Span parentSpanProto =
TestUtils.getOneByAttr(finished, component, "example-promises");
assertThat(parentSpanProto).isNotNull();
assertThat(parentSpanProto.getParentSpanId().isEmpty()).isTrue();
List<io.opentelemetry.proto.trace.v1.Span> successSpans =
TestUtils.getByAttr(finished, component, "success");
assertThat(successSpans).hasSize(2);
ByteString parentId = parentSpanProto.getSpanId();
for (io.opentelemetry.proto.trace.v1.Span span : successSpans) {
assertThat(span.getParentSpanId()).isEqualTo(parentId);
}
io.opentelemetry.proto.trace.v1.Span errorSpan =
TestUtils.getOneByAttr(finished, component, "error");
assertThat(errorSpan).isNotNull();
assertThat(errorSpan.getParentSpanId()).isEqualTo(parentId);
}
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.statelesscommonrequesthandler;
import io.opentelemetry.sdk.contrib.trace.testbed.TestUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
final class Client {
private final ExecutorService executor = Executors.newCachedThreadPool();
private final RequestHandler requestHandler;
public Client(RequestHandler requestHandler) {
this.requestHandler = requestHandler;
}
/** Send a request....... */
public Future<String> send(final Object message) {
return executor.submit(
new Callable<String>() {
@Override
public String call() throws Exception {
TestUtils.sleep();
requestHandler.beforeRequest(message);
TestUtils.sleep();
requestHandler.afterResponse(message);
return message + ":response";
}
});
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.statelesscommonrequesthandler;
import static io.opentelemetry.sdk.contrib.trace.testbed.TestUtils.createTracerShim;
import static org.junit.Assert.assertEquals;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
/**
* There is only one instance of 'RequestHandler' per 'Client'. Methods of 'RequestHandler' are
* executed in the same thread (beforeRequest() and its resulting afterRequest(), that is).
*/
public final class HandlerTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = createTracerShim(exporter);
private final Client client = new Client(new RequestHandler(tracer));
@Before
public void before() {
exporter.reset();
}
@Test
public void test_requests() throws Exception {
Future<String> responseFuture = client.send("message");
Future<String> responseFuture2 = client.send("message2");
Future<String> responseFuture3 = client.send("message3");
assertEquals("message3:response", responseFuture3.get(5, TimeUnit.SECONDS));
assertEquals("message2:response", responseFuture2.get(5, TimeUnit.SECONDS));
assertEquals("message:response", responseFuture.get(5, TimeUnit.SECONDS));
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertEquals(3, finished.size());
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.statelesscommonrequesthandler;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Span.Kind;
import io.opentelemetry.trace.Tracer;
/**
* One instance per Client. 'beforeRequest' and 'afterResponse' are executed in the same thread for
* one 'send', but as these methods do not expose any object storing state, a thread-local field in
* 'RequestHandler' itself is used to contain the Scope related to Span activation.
*/
@SuppressWarnings("MustBeClosedChecker")
final class RequestHandler {
static final String OPERATION_NAME = "send";
private final Tracer tracer;
private static final ThreadLocal<Scope> tlsScope = new ThreadLocal<>();
public RequestHandler(Tracer tracer) {
this.tracer = tracer;
}
/** beforeRequest handler....... */
public void beforeRequest(Object request) {
Span span = tracer.spanBuilder(OPERATION_NAME).setSpanKind(Kind.SERVER).startSpan();
tlsScope.set(tracer.withSpan(span));
}
/** afterResponse handler....... */
public void afterResponse(Object response) {
// Finish the Span
tracer.getCurrentSpan().end();
// Deactivate the Span
tlsScope.get().close();
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.suspendresumepropagation;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
final class SuspendResume {
private final Tracer tracer;
private final Span span;
public SuspendResume(int id, Tracer tracer) {
// Passed along here for testing. Normally should be referenced via GlobalTracer.get().
this.tracer = tracer;
Span span = tracer.spanBuilder("job " + id).startSpan();
span.setAttribute("component", "suspend-resume");
try (Scope scope = tracer.withSpan(span)) {
this.span = span;
}
}
public void doPart(String name) {
try (Scope scope = tracer.withSpan(span)) {
span.addEvent("part: " + name);
}
}
public void done() {
span.end();
}
}

View File

@ -0,0 +1,64 @@
/*
* Copyright 2019, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.sdk.contrib.trace.testbed.suspendresumepropagation;
import static com.google.common.truth.Truth.assertThat;
import static io.opentelemetry.sdk.contrib.trace.testbed.TestUtils.createTracerShim;
import io.opentelemetry.sdk.trace.export.InMemorySpanExporter;
import io.opentelemetry.trace.Tracer;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
/**
* These tests are intended to simulate the kind of async models that are common in java async
* frameworks.
*/
public class SuspendResumePropagationTest {
private final InMemorySpanExporter exporter = InMemorySpanExporter.create();
private final Tracer tracer = createTracerShim(exporter);
@Before
public void before() {}
@Test
public void testContinuationInterleaving() {
SuspendResume job1 = new SuspendResume(1, tracer);
SuspendResume job2 = new SuspendResume(2, tracer);
// Pretend that the framework is controlling actual execution here.
job1.doPart("some work for 1");
job2.doPart("some work for 2");
job1.doPart("other work for 1");
job2.doPart("other work for 2");
job2.doPart("more work for 2");
job1.doPart("more work for 1");
job1.done();
job2.done();
List<io.opentelemetry.proto.trace.v1.Span> finished = exporter.getFinishedSpanItems();
assertThat(finished.size()).isEqualTo(2);
assertThat(finished.get(0).getName()).isEqualTo("job 1");
assertThat(finished.get(1).getName()).isEqualTo("job 2");
assertThat(finished.get(0).getParentSpanId().isEmpty()).isTrue();
assertThat(finished.get(1).getParentSpanId().isEmpty()).isTrue();
}
}

View File

@ -9,6 +9,7 @@ include ":opentelemetry-opentracing-shim"
include ":opentelemetry-proto"
include ":opentelemetry-sdk"
include ":opentelemetry-sdk-contrib-async-processor"
include ":opentelemetry-sdk-contrib-testbed"
project(':opentelemetry-all').projectDir = "$rootDir/all" as File
project(':opentelemetry-api').projectDir = "$rootDir/api" as File
@ -21,3 +22,4 @@ project(':opentelemetry-opentracing-shim').projectDir = "$rootDir/opentracing_sh
project(':opentelemetry-sdk').projectDir = "$rootDir/sdk" as File
project(':opentelemetry-sdk-contrib-async-processor').projectDir =
"$rootDir/sdk_contrib/async_processor" as File
project(':opentelemetry-sdk-contrib-testbed').projectDir = "$rootDir/sdk_contrib/testbed" as File