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> collection) throws InterruptedException { + return wrapped.invokeAll(tracedCallables(collection)); + } + + @Override + public List> invokeAll(Collection> collection, long l, TimeUnit timeUnit) + throws InterruptedException { + return wrapped.invokeAll(tracedCallables(collection), l, timeUnit); + } + + @Override + public T invokeAny(Collection> collection) + throws InterruptedException, ExecutionException { + return wrapped.invokeAny(tracedCallables(collection)); + } + + @Override + public T invokeAny(Collection> 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> tracedCallables(Collection> 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