diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java
index 7a670c46..dae8d1df 100644
--- a/opentracing-api/src/main/java/io/opentracing/Span.java
+++ b/opentracing-api/src/main/java/io/opentracing/Span.java
@@ -1,5 +1,5 @@
/**
- * Copyright 2016 The OpenTracing Authors
+ * Copyright 2016-2017 The OpenTracing 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
@@ -22,6 +22,9 @@
*
Spans are created by the {@link Tracer#buildSpan} interface.
*/
public interface Span extends Closeable {
+
+ SpanManager.Visibility visibility();
+
/**
* Retrieve the associated SpanContext.
*
@@ -42,6 +45,8 @@ public interface Span extends Closeable {
void finish();
/**
+ * Deactivates spans from spanManager.
+ *
* Sets an explicit end timestamp and records the span.
*
*
With the exception of calls to Span.context(), this should be the last call made to the span instance, and to
diff --git a/opentracing-api/src/main/java/io/opentracing/SpanManager.java b/opentracing-api/src/main/java/io/opentracing/SpanManager.java
new file mode 100644
index 00000000..750de3ae
--- /dev/null
+++ b/opentracing-api/src/main/java/io/opentracing/SpanManager.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing;
+
+/**
+ * @author Pavol Loffay
+ */
+public interface SpanManager {
+
+ /**
+ * @param span span to bundle into visibility, there is always only one visibility per span
+ * @return visibility
+ */
+ Visibility bundle(Span span);
+
+ /**
+ * @return not finished active span or null
+ */
+ VisibilityContext active();
+
+ interface Visibility {
+ /**
+ * @return visibility context which is used to activate/deactivate span
+ */
+ VisibilityContext capture();
+
+ /**
+ * @return associated span or null if visibility is marked as finished.
+ */
+ Span span();
+ /**
+ * @return always spanContext
+ */
+ SpanContext context();
+
+ /**
+ * Mark associated span as finished.
+ *
+ * Should be called by {@link Span#finish()} or directly if one does not want to expose span.
+ * This method should be idempotent.
+ *
+ * review note: reverse operation should not be allowed.
+ */
+ void hideSpan();
+ }
+
+ interface VisibilityContext {
+ /**
+ * on/activate - {@link SpanManager#active()} will return this object.
+ */
+ VisibilityContext on();
+ /**
+ * off/deactivate - {@link SpanManager#active()} will not return this object.
+ */
+ VisibilityContext off();
+
+ Visibility visibility();
+ }
+}
diff --git a/opentracing-api/src/main/java/io/opentracing/ThreadLocalSpanManager.java b/opentracing-api/src/main/java/io/opentracing/ThreadLocalSpanManager.java
new file mode 100644
index 00000000..c823a446
--- /dev/null
+++ b/opentracing-api/src/main/java/io/opentracing/ThreadLocalSpanManager.java
@@ -0,0 +1,95 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * @author Pavol Loffay
+ */
+public class ThreadLocalSpanManager implements SpanManager {
+
+ private final ThreadLocal activeContext = new ThreadLocal();
+
+ @Override
+ public Visibility bundle(Span span) {
+ return span.visibility() == null ? new SimpleVisibility(span) : span.visibility();
+ }
+
+ @Override
+ public SimpleLinkedVisibilityContext active() {
+ return activeContext.get();
+ }
+
+ class SimpleVisibility implements Visibility {
+ private final Span span;
+ private AtomicBoolean hideSpan = new AtomicBoolean(false);
+
+ public SimpleVisibility(Span span) {
+ this.span = span;
+ }
+
+ @Override
+ public Span span() {
+ return hideSpan.get() ? null : span;
+ }
+
+ @Override
+ public SpanContext context() {
+ return span.context();
+ }
+
+ @Override
+ public SimpleLinkedVisibilityContext capture() {
+ return new SimpleLinkedVisibilityContext(this);
+ }
+
+ @Override
+ public void hideSpan() {
+ hideSpan.set(true);
+ }
+ }
+
+ class SimpleLinkedVisibilityContext implements SpanManager.VisibilityContext {
+
+ private final SimpleVisibility visibility;
+ private SimpleLinkedVisibilityContext previous;
+
+ public SimpleLinkedVisibilityContext(SimpleVisibility visibility) {
+ this.visibility = visibility;
+ }
+
+ @Override
+ public SimpleLinkedVisibilityContext on() {
+ previous = activeContext.get();
+ activeContext.set(this);
+ return this;
+ }
+
+ @Override
+ public SimpleLinkedVisibilityContext off() {
+ if (this == activeContext.get()) {
+ activeContext.set(previous);
+ }
+ // else should not happen
+
+ return this;
+ }
+
+ @Override
+ public Visibility visibility() {
+ return visibility;
+ }
+ }
+}
diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java
index 10f23b5e..582f9bd7 100644
--- a/opentracing-api/src/main/java/io/opentracing/Tracer.java
+++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java
@@ -20,6 +20,9 @@
*/
public interface Tracer {
+ // span could be directly returned
+ SpanManager spanManager();
+
/**
* Return a new SpanBuilder for a Span with the given `operationName`.
*
@@ -113,6 +116,8 @@ interface SpanBuilder extends SpanContext {
*/
SpanBuilder addReference(String referenceType, SpanContext referencedContext);
+ SpanBuilder asRoot();
+
/** Same as {@link Span#setTag(String, String)}, but for the span being built. */
SpanBuilder withTag(String key, String value);
@@ -127,6 +132,5 @@ interface SpanBuilder extends SpanContext {
/** Returns the started Span. */
Span start();
-
}
}
diff --git a/opentracing-mock/pom.xml b/opentracing-mock/pom.xml
index e48d6690..ff0dd647 100644
--- a/opentracing-mock/pom.xml
+++ b/opentracing-mock/pom.xml
@@ -36,6 +36,26 @@
${project.groupId}
opentracing-api
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.23
+ test
+
+
+ log4j
+ log4j
+ 1.2.17
+ test
+
+
+ org.slf4j
+ slf4j-log4j12
+ 1.7.23
+ test
+
diff --git a/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
index 8685764d..c7405542 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
@@ -18,6 +18,7 @@
import io.opentracing.Span;
import io.opentracing.SpanContext;
+import io.opentracing.SpanManager;
/**
* MockSpans are created via MockTracer.buildSpan(...), but they are also returned via calls to
@@ -29,6 +30,8 @@ public final class MockSpan implements Span {
// A simple-as-possible (consecutive for repeatability) id generator.
private static AtomicLong nextId = new AtomicLong(0);
+ private final SpanManager.Visibility visibility;
+
private final MockTracer mockTracer;
private MockContext context;
private final long parentId; // 0 if there's no parent.
@@ -93,6 +96,11 @@ public List generatedErrors() {
return new ArrayList<>(errors);
}
+ @Override
+ public SpanManager.Visibility visibility() {
+ return visibility;
+ }
+
@Override
public synchronized MockContext context() {
return this.context;
@@ -107,6 +115,7 @@ public void finish() {
public synchronized void finish(long finishMicros) {
finishedCheck("Finishing already finished span");
this.finishMicros = finishMicros;
+ visibility.hideSpan();
this.mockTracer.appendFinishedSpan(this);
this.finished = true;
}
@@ -248,10 +257,17 @@ public long timestampMicros() {
}
}
- MockSpan(MockTracer tracer, String operationName, long startMicros, Map initialTags, MockContext parent) {
+ MockSpan(MockTracer tracer, String operationName, long startMicros, Map initialTags, MockContext
+ parent, boolean activate) {
this.mockTracer = tracer;
this.operationName = operationName;
this.startMicros = startMicros;
+
+ this.visibility = tracer.spanManager().bundle(this);
+ if (activate) {
+ this.visibility.capture().on();
+ }
+
if (initialTags == null) {
this.tags = new HashMap<>();
} else {
diff --git a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
index f41bebbd..a16d4739 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
@@ -23,6 +23,8 @@
import io.opentracing.References;
import io.opentracing.Span;
import io.opentracing.SpanContext;
+import io.opentracing.SpanManager;
+import io.opentracing.ThreadLocalSpanManager;
import io.opentracing.Tracer;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
@@ -38,16 +40,22 @@
public class MockTracer implements Tracer {
private List finishedSpans = new ArrayList<>();
private final Propagator propagator;
+ private final SpanManager spanManager;
public MockTracer() {
- this(Propagator.PRINTER);
+ this(Propagator.PRINTER, new ThreadLocalSpanManager());
}
/**
* Create a new MockTracer that passes through any calls to inject() and/or extract().
*/
public MockTracer(Propagator propagator) {
+ this(propagator, new ThreadLocalSpanManager());
+ }
+
+ public MockTracer(Propagator propagator, SpanManager spanManager) {
this.propagator = propagator;
+ this.spanManager = spanManager;
}
/**
@@ -144,9 +152,14 @@ public MockSpan.MockContext extract(Format format, C carrier) {
};
}
+ @Override
+ public SpanManager spanManager() {
+ return spanManager;
+ }
+
@Override
public SpanBuilder buildSpan(String operationName) {
- return new SpanBuilder(operationName);
+ return new SpanBuilder(operationName, spanManager);
}
@Override
@@ -170,8 +183,14 @@ public final class SpanBuilder implements Tracer.SpanBuilder {
private MockSpan.MockContext firstParent;
private Map initialTags = new HashMap<>();
- SpanBuilder(String operationName) {
+ SpanBuilder(String operationName, SpanManager spanManager) {
this.operationName = operationName;
+
+ SpanManager.VisibilityContext inferredParent = spanManager.active();
+ if (inferredParent != null) {
+ addReference(inferredParent.visibility().span() == null ? References.FOLLOWS_FROM : References.CHILD_OF,
+ inferredParent.visibility().span().context());
+ }
}
@Override
public SpanBuilder asChildOf(SpanContext parent) {
@@ -192,6 +211,12 @@ public SpanBuilder addReference(String referenceType, SpanContext referencedCont
return this;
}
+ @Override
+ public Tracer.SpanBuilder asRoot() {
+ firstParent = null;
+ return this;
+ }
+
@Override
public SpanBuilder withTag(String key, String value) {
this.initialTags.put(key, value);
@@ -221,7 +246,8 @@ public MockSpan start() {
if (this.startMicros == 0) {
this.startMicros = MockSpan.nowMicros();
}
- return new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags, this.firstParent);
+ return new MockSpan(MockTracer.this, this.operationName, this.startMicros, initialTags,
+ this.firstParent, false);
}
@Override
diff --git a/opentracing-mock/src/test/java/io/opentracing/spanmanager/SpanManagerTest.java b/opentracing-mock/src/test/java/io/opentracing/spanmanager/SpanManagerTest.java
new file mode 100644
index 00000000..cd204d44
--- /dev/null
+++ b/opentracing-mock/src/test/java/io/opentracing/spanmanager/SpanManagerTest.java
@@ -0,0 +1,87 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing.spanmanager;
+
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import io.opentracing.mock.MockSpan;
+import io.opentracing.mock.MockTracer;
+import io.opentracing.spanmanager.concurrent.TracedExecutorService;
+
+/**
+ * @author Pavol Loffay
+ */
+public class SpanManagerTest {
+
+ private final MockTracer mockTracer = new MockTracer(MockTracer.Propagator.TEXT_MAP);
+
+ @Before
+ public void before() {
+ mockTracer.reset();
+ }
+
+ @Test
+ public void test() {
+ MockSpan root = mockTracer.buildSpan("root").start();
+ Assert.assertTrue(mockTracer.spanManager().active() == null);
+
+ root.visibility().capture().on();
+ Assert.assertEquals(root, mockTracer.spanManager().active().visibility().span());
+
+ MockSpan child = mockTracer.buildSpan("child").start();
+
+ child.finish();
+ root.finish();
+
+ Assert.assertEquals(root.context().spanId(), child.parentId());
+ }
+
+ @Test
+ public void testTracedRunnable() throws ExecutionException, InterruptedException {
+ {
+ ExecutorService executorService = new TracedExecutorService(Executors.newFixedThreadPool(500),
+ mockTracer.spanManager());
+
+ MockSpan root = mockTracer.buildSpan("root").start();
+ root.visibility().capture().on();
+
+ executorService.submit(new Runnable() {
+ @Override
+ public void run() {
+ Assert.assertNotNull(mockTracer.spanManager().active().visibility().span());
+
+ mockTracer.buildSpan("child")
+ .start()
+ .finish();
+ }
+ });
+
+ executorService.shutdown();
+ executorService.awaitTermination(10, TimeUnit.SECONDS);
+ root.finish();
+ }
+
+ List mockSpans = mockTracer.finishedSpans();
+ Assert.assertEquals(2, mockSpans.size());
+ Assert.assertEquals(mockSpans.get(0).parentId(), mockSpans.get(1).context().spanId());
+ }
+}
diff --git a/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedCallable.java b/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedCallable.java
new file mode 100644
index 00000000..3657df9c
--- /dev/null
+++ b/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedCallable.java
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing.spanmanager.concurrent;
+
+import java.util.concurrent.Callable;
+
+import io.opentracing.SpanManager;
+
+/**
+ * @author Pavol Loffay
+ */
+public class TracedCallable implements Callable{
+
+ private Callable wrapped;
+ private SpanManager.VisibilityContext visibility;
+
+ public TracedCallable(Callable wrapped, SpanManager spanManager) {
+ this.wrapped = wrapped;
+ this.visibility = spanManager.active().visibility().capture();
+
+ }
+
+ @Override
+ public V call() throws Exception {
+ visibility.on();
+ try {
+ return wrapped.call();
+ } finally {
+ visibility.off();
+ }
+ }
+}
diff --git a/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedExecutorService.java b/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedExecutorService.java
new file mode 100644
index 00000000..8d469c54
--- /dev/null
+++ b/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedExecutorService.java
@@ -0,0 +1,118 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing.spanmanager.concurrent;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import io.opentracing.SpanManager;
+
+/**
+ * @author Pavol Loffay
+ */
+public class TracedExecutorService implements ExecutorService {
+
+ private ExecutorService wrapped;
+ private SpanManager spanManager;
+
+ public TracedExecutorService(ExecutorService wrapped, SpanManager spanManager) {
+ this.wrapped = wrapped;
+ this.spanManager = spanManager;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return wrapped.isTerminated();
+ }
+
+ @Override
+ public void shutdown() {
+ wrapped.shutdown();
+ }
+
+ @Override
+ public List shutdownNow() {
+ return wrapped.shutdownNow();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return wrapped.isShutdown();
+ }
+
+ @Override
+ public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
+ return wrapped.awaitTermination(l, timeUnit);
+ }
+
+ @Override
+ public Future submit(Callable callable) {
+ return wrapped.submit(new TracedCallable(callable, spanManager));
+ }
+
+ @Override
+ public Future submit(Runnable runnable, T t) {
+ return wrapped.submit(new TracedRunnable(runnable, spanManager), t);
+ }
+
+ @Override
+ public Future> submit(Runnable runnable) {
+ return wrapped.submit(new TracedRunnable(runnable, spanManager));
+ }
+
+ @Override
+ public List> invokeAll(Collection extends Callable> collection) throws InterruptedException {
+ return wrapped.invokeAll(tracedCallables(collection));
+ }
+
+ @Override
+ public List> invokeAll(Collection extends Callable> collection, long l, TimeUnit timeUnit)
+ throws InterruptedException {
+ return wrapped.invokeAll(tracedCallables(collection), l, timeUnit);
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> collection)
+ throws InterruptedException, ExecutionException {
+ return wrapped.invokeAny(tracedCallables(collection));
+ }
+
+ @Override
+ public T invokeAny(Collection extends Callable> collection, long l, TimeUnit timeUnit)
+ throws InterruptedException, ExecutionException, TimeoutException {
+ return wrapped.invokeAny(tracedCallables(collection), l, timeUnit);
+ }
+
+ @Override
+ public void execute(Runnable runnable) {
+ wrapped.submit(new TracedRunnable(runnable, spanManager));
+ }
+
+ private Collection extends Callable> tracedCallables(Collection extends Callable> callables) {
+ List> tracedCallables = new ArrayList>(callables.size());
+
+ for (Callable callable: callables) {
+ tracedCallables.add(new TracedCallable(callable, spanManager));
+ }
+
+ return tracedCallables;
+ }
+}
diff --git a/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedRunnable.java b/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedRunnable.java
new file mode 100644
index 00000000..f75f1160
--- /dev/null
+++ b/opentracing-mock/src/test/java/io/opentracing/spanmanager/concurrent/TracedRunnable.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing.spanmanager.concurrent;
+
+import io.opentracing.SpanManager;
+
+/**
+ * @author Pavol Loffay
+ */
+public class TracedRunnable implements Runnable {
+
+ private Runnable wrapped;
+ private SpanManager.VisibilityContext visibility;
+
+ public TracedRunnable(Runnable wrapped, SpanManager spanManager) {
+ this.wrapped = wrapped;
+ this.visibility = spanManager.active().visibility().capture();
+ }
+
+ @Override
+ public void run() {
+ visibility.on();
+ try {
+ wrapped.run();
+ } finally {
+ visibility.off();
+ }
+ }
+}
diff --git a/opentracing-mock/src/test/java/io/opentracing/spanmanager/mdc/MDCDemo.java b/opentracing-mock/src/test/java/io/opentracing/spanmanager/mdc/MDCDemo.java
new file mode 100644
index 00000000..f731fb5b
--- /dev/null
+++ b/opentracing-mock/src/test/java/io/opentracing/spanmanager/mdc/MDCDemo.java
@@ -0,0 +1,140 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing.spanmanager.mdc;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import org.slf4j.Logger;
+import org.slf4j.MDC;
+
+import io.opentracing.Span;
+import io.opentracing.SpanManager;
+import io.opentracing.mock.MockSpan;
+import io.opentracing.mock.MockTracer;
+import io.opentracing.spanmanager.concurrent.TracedExecutorService;
+
+/**
+ * @author Pavol Loffay
+ */
+public class MDCDemo {
+ static {
+ org.apache.log4j.BasicConfigurator.configure();
+ }
+ private static final Logger logger = org.slf4j.LoggerFactory.getLogger(MDCDemo.class.getSimpleName());
+ private static final MockTracer tracer = new MockTracer(MockTracer.Propagator.PRINTER, new MDCThreadLocalSpanManager());
+
+ public void singleSpan() {
+ tracer.buildSpan("single").start().finish();
+ }
+
+ public void parentWithChild() throws Exception {
+ Span parent = tracer.buildSpan("parent").start();
+ SpanManager.VisibilityContext parentVisibility = parent.visibility().capture().on();
+ // The child will automatically know about the parent.
+ tracer.buildSpan("child")
+ .start()
+ .finish();
+ parentVisibility.off();
+ parent.finish();
+ }
+
+ public void asyncSpans() throws Exception {
+ final ExecutorService otExecutor = new TracedExecutorService(Executors.newFixedThreadPool(100), tracer.spanManager());
+
+ // Hacky lists of futures we wait for before exiting async Spans.
+ List> futures = new ArrayList<>();
+ final List> subfutures = new ArrayList<>();
+
+ // Create a parent Continuation for all of the async activity.
+ Span parent = tracer.buildSpan("asyncParent").start();
+ SpanManager.VisibilityContext parentVisibility = parent.visibility().capture().on();
+
+ // Create 10 async children.
+ for (int i = 0; i < 10; i++) {
+ final int j = i;
+ MDC.put("parent_number", String.valueOf(j));
+ futures.add(otExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ final Span childSpan = tracer.buildSpan("child_" + j).start();
+ childSpan.visibility().capture().on();
+
+ try {
+ Thread.currentThread().sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ tracer.spanManager().active().visibility().span().log("awoke");
+ subfutures.add(otExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Span active = tracer.spanManager().active().visibility().span();
+ active.log("awoke again");
+ System.out.println(String.format("j=%d, MDC parent number: %s, MDC map: %s",
+ j, MDC.get("parent_number"), MDC.getCopyOfContextMap()));
+ // Create a grandchild for each child... note that we don't *need* to use the
+ // Continuation mechanism.
+ Span grandchild = tracer.buildSpan("grandchild_" + j).start();
+ grandchild.finish();
+ childSpan.finish();
+ }
+ }));
+ }
+ }));
+ }
+
+ // Hacky cleanup... grossness has nothing to do with OT :)
+ for (Future> f : futures) {
+ f.get();
+ }
+ for (Future> f : subfutures) {
+ f.get();
+ }
+
+ parent.finish();
+ parentVisibility.off();
+
+ otExecutor.shutdown();
+ otExecutor.awaitTermination(3, TimeUnit.SECONDS);
+ }
+
+ public static void main(String[] args) throws Exception {
+ MDC.put("method", "main");
+
+ MDCDemo demo = new MDCDemo();
+ demo.singleSpan();
+ demo.parentWithChild();
+ demo.asyncSpans();
+
+ // Print out all mock-Spans
+ List finishedSpans = tracer.finishedSpans();
+ for (MockSpan span : finishedSpans) {
+ logger.info("finished Span '{}', span.log: {} ", span, logEntryToString(span.logEntries()));
+ }
+ }
+
+ public static String logEntryToString(List logEntries) {
+ StringBuilder sb = new StringBuilder();
+ for (MockSpan.LogEntry logEntry: logEntries) {
+ sb.append(logEntry.fields());
+ }
+ return sb.length() == 0 ? "{}" : sb.toString();
+ }
+}
+
diff --git a/opentracing-mock/src/test/java/io/opentracing/spanmanager/mdc/MDCThreadLocalSpanManager.java b/opentracing-mock/src/test/java/io/opentracing/spanmanager/mdc/MDCThreadLocalSpanManager.java
new file mode 100644
index 00000000..9dc0dbc9
--- /dev/null
+++ b/opentracing-mock/src/test/java/io/opentracing/spanmanager/mdc/MDCThreadLocalSpanManager.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright 2016-2017 The OpenTracing 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.opentracing.spanmanager.mdc;
+
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.slf4j.MDC;
+
+import io.opentracing.Span;
+import io.opentracing.SpanContext;
+import io.opentracing.SpanManager;
+
+/**
+ * @author Pavol Loffay
+ */
+public class MDCThreadLocalSpanManager implements SpanManager {
+
+ private final ThreadLocal activeContext = new ThreadLocal<>();
+
+ @Override
+ public Visibility bundle(Span span) {
+ return span.visibility() == null ? new MDCVisibility(span) : span.visibility();
+ }
+
+ @Override
+ public VisibilityContext active() {
+ return activeContext.get();
+ }
+
+ class MDCVisibility implements Visibility {
+ private final Span span;
+ private AtomicBoolean hideSpan = new AtomicBoolean(false);
+
+ public MDCVisibility(Span span) {
+ this.span = span;
+ }
+
+ @Override
+ public MDCLinkedVisibilityContext capture() {
+ return new MDCLinkedVisibilityContext(this);
+ }
+
+ @Override
+ public Span span() {
+ return hideSpan.get() ? null : span;
+ }
+
+ @Override
+ public SpanContext context() {
+ return span.context();
+ }
+
+ @Override
+ public void hideSpan() {
+ hideSpan.set(true);
+ }
+ }
+
+ class MDCLinkedVisibilityContext implements VisibilityContext {
+ private final MDCVisibility visibility;
+ private MDCLinkedVisibilityContext previous;
+ private Map mdcContext;
+
+ public MDCLinkedVisibilityContext(MDCVisibility visibility) {
+ this.visibility = visibility;
+ this.mdcContext = MDC.getCopyOfContextMap();
+ }
+
+ @Override
+ public MDCLinkedVisibilityContext on() {
+ MDC.setContextMap(mdcContext);
+ previous = activeContext.get();
+ activeContext.set(this);
+ return this;
+ }
+
+ @Override
+ public MDCLinkedVisibilityContext off() {
+ if (this == activeContext.get()) {
+ activeContext.set(previous);
+ }
+ // else should not happen
+
+ return this;
+ }
+
+ @Override
+ public Visibility visibility() {
+ return visibility;
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index fe932431..92d68b87 100644
--- a/pom.xml
+++ b/pom.xml
@@ -24,8 +24,8 @@
opentracing-api
- opentracing-noop
- opentracing-impl
+
+
opentracing-mock