Copy testbed for OpenTelemetry API (#513)
This commit is contained in:
parent
55717b880c
commit
75811d4f41
|
|
@ -0,0 +1,5 @@
|
|||
# OpenTelemetry SDK Testbed
|
||||
|
||||
Testbed for OpenTelemetry API and SDK.
|
||||
|
||||
* Java 8 compatible.
|
||||
|
|
@ -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"
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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> {}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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> {}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue