diff --git a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java index 2bef978b..ec150e6c 100644 --- a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java +++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java @@ -40,7 +40,8 @@ public interface ScopeManager { * This {@link Scope} instance can be accessed at any time through {@link #active()}, * in case it is not possible for the user to store it (when used through middleware * or start/finish event hooks, for example). The corresponding {@link Span} can be - * accessed through {@link #activeSpan()} likewise. + * accessed through {@link #activeSpan()} likewise. You can also get ahold of the + * active {@link Span}'s {@link SpanContext} via {@link #activeSpanContext()}. * *
* Usage: @@ -58,12 +59,65 @@ public interface ScopeManager { * } * * + *
+ * Note: You may only activate spans when you own its life cycle. + * That means you must make sure that no other thread calls {@link Span#finish()} + * while the scope is still active. + * If you can't guarantee that, use {@link #activate(SpanContext)} instead. + * * @param span the {@link Span} that should become the {@link #activeSpan()} * @return a {@link Scope} instance to control the end of the active period for the {@link Span}. It is a * programming error to neglect to call {@link Scope#close()} on the returned instance. */ Scope activate(Span span); + /** + * Similar to {@link #activate(Span)} but used in cases where the thread in which the + * activation is performed does not have control over the life cycle of the span. + * + *
+ * One example of that is when performing an activation in the {@link Runnable#run()} + * method of a traced {@link Runnable} wrapper which is executed by an + * {@link java.util.concurrent.ExecutorService}. + * + *
+ * The returned {@link Scope} represents the active state for the span. + * Once its active period is due, {@link Scope#close()} ought to be called. + * To ease this operation, {@link Scope} supports try-with-resources. + * Observe the span will not be automatically finished when {@link Scope#close()} + * is called. + * + *
+ * This {@link Scope} instance can be accessed at any time through {@link #active()}, + * in case it is not possible for the user to store it (when used through middleware + * or start/finish event hooks, for example). The corresponding {@link SpanContext} can be + * accessed through {@link #activeSpanContext()} likewise. + * In contrast to {@link #activate(Span)}, {@link #activeSpan()} will return {@code null}. + * This prevents users of the {@link #activeSpan()} API to accidentally interacting with + * already {@linkplain Span#finish() finished} spans. + * + * Usage: + *
+ * Span span = tracer.buildSpan("...").start();
+ * try (Scope scope = tracer.scopeManager().activate(span.context())) {
+ * span.setTag("...", "...");
+ * ...
+ * } catch (Exception e) {
+ * span.log(...);
+ * } finally {
+ * // Optionally finish the Span if the operation it represents
+ * // is logically completed at this point.
+ * span.finish();
+ * }
+ *
+ *
+ * @param spanContext the {@link SpanContext} that should become the {@link #activeSpanContext()}
+ * @see #activate(Span)
+ * @return a {@link Scope} instance to control the end of the active period for the {@link Span}. It is a
+ * programming error to neglect to call {@link Scope#close()} on the returned instance.
+ */
+ Scope activate(SpanContext spanContext);
+
/**
* Return the currently active {@link Scope} which can be used to deactivate the currently active
* {@link Span}.
@@ -84,13 +138,25 @@ public interface ScopeManager {
* Return the currently active {@link Span}.
*
* - * Because both {@link #active()} and {@link #activeSpan()} reference the current - * active state, they both will be either null or non-null. + * Note that {@link #activeSpan()} can return {@code null} while {@link #active()} is non-null + * in case of a {@linkplain #activate(SpanContext) span context activation}. * * @return the {@link Span active span}, or null if none could be found. */ Span activeSpan(); + /** + * Return the currently active {@link SpanContext}, which was activated by activating either + * a {@link #activate(Span) Span} or a {@link #activate(SpanContext) SpanContext}. + * + *
+ * Because both {@link #active()} and {@link #activeSpanContext()} reference the current
+ * active state, they both will be either null or non-null.
+ *
+ * @return the {@link SpanContext active span context}, or null if none could be found.
+ */
+ SpanContext activeSpanContext();
+
/**
* @deprecated use {@link #activate(Span)} instead.
* Set the specified {@link Span} as the active instance for the current
diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java
index edd263f3..4812bef8 100644
--- a/opentracing-api/src/main/java/io/opentracing/Tracer.java
+++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java
@@ -31,6 +31,11 @@ public interface Tracer {
*/
Span activeSpan();
+ /**
+ * @return the active {@link SpanContext}. This is a shorthand for {@code Tracer.scopeManager().activeSpanContext()}.
+ */
+ SpanContext activeSpanContext();
+
/**
* Make a {@link Span} instance active for the current context (usually a thread).
* This is a shorthand for {@code Tracer.scopeManager().activate(span)}.
@@ -41,6 +46,18 @@ public interface Tracer {
*/
Scope activateSpan(Span span);
+
+ /**
+ * Make a {@link SpanContext} instance active for the current context (usually a thread).
+ * This is a shorthand for {@code Tracer.scopeManager().activate(spanContext)}.
+ *
+ * @see {@link ScopeManager#activate(SpanContext)}
+ * @return a {@link Scope} instance to control the end of the active period for the {@link SpanContext}. It is a
+ * programming error to neglect to call {@link Scope#close()} on the returned instance,
+ * and it may lead to memory leaks as the {@link Scope} may remain in the thread-local stack.
+ */
+ Scope activateSpanContext(SpanContext spanContext);
+
/**
* Return a new SpanBuilder for a Span with the given `operationName`.
*
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 36336314..c6f0b45a 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
@@ -30,7 +30,6 @@
import io.opentracing.Span;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
-import io.opentracing.noop.NoopScopeManager;
import io.opentracing.propagation.Binary;
import io.opentracing.propagation.Format;
import io.opentracing.propagation.TextMap;
@@ -277,18 +276,19 @@ public Scope activateSpan(Span span) {
return this.scopeManager.activate(span);
}
+ @Override
+ public Scope activateSpanContext(SpanContext spanContext) {
+ return this.scopeManager.activate(spanContext);
+ }
+
synchronized void appendFinishedSpan(MockSpan mockSpan) {
this.finishedSpans.add(mockSpan);
this.onSpanFinished(mockSpan);
}
- private SpanContext activeSpanContext() {
- Span span = activeSpan();
- if (span == null) {
- return null;
- }
-
- return span.context();
+ @Override
+ public SpanContext activeSpanContext() {
+ return scopeManager.activeSpanContext();
}
public final class SpanBuilder implements Tracer.SpanBuilder {
diff --git a/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java b/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java
index 99fbc2fc..291d96b4 100644
--- a/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java
+++ b/opentracing-mock/src/test/java/io/opentracing/mock/MockTracerTest.java
@@ -261,6 +261,23 @@ public void testActiveSpan() {
Assert.assertTrue(mockTracer.finishedSpans().isEmpty());
}
+ @Test
+ public void testActiveSpanContext() {
+ MockTracer mockTracer = new MockTracer();
+ Assert.assertNull(mockTracer.activeSpan());
+ Assert.assertNull(mockTracer.activeSpanContext());
+
+ Span span = mockTracer.buildSpan("foo").start();
+ try (Scope scope = mockTracer.activateSpanContext(span.context())) {
+ Assert.assertNull(mockTracer.activeSpan());
+ Assert.assertEquals(mockTracer.scopeManager().activeSpanContext(), mockTracer.activeSpanContext());
+ }
+
+ Assert.assertNull(mockTracer.activeSpan());
+ Assert.assertNull(mockTracer.activeSpanContext());
+ Assert.assertTrue(mockTracer.finishedSpans().isEmpty());
+ }
+
@Test
public void testActiveSpanFinish() {
MockTracer mockTracer = new MockTracer();
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
index 911ac5a1..f3dfce19 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
@@ -16,6 +16,7 @@
import io.opentracing.Scope;
import io.opentracing.ScopeManager;
import io.opentracing.Span;
+import io.opentracing.SpanContext;
public interface NoopScopeManager extends ScopeManager {
NoopScopeManager INSTANCE = new NoopScopeManagerImpl();
@@ -39,6 +40,11 @@ public Scope activate(Span span) {
return NoopScope.INSTANCE;
}
+ @Override
+ public Scope activate(SpanContext spanContext) {
+ return NoopScope.INSTANCE;
+ }
+
@Override
public Scope active() {
return NoopScope.INSTANCE;
@@ -49,6 +55,11 @@ public Span activeSpan() {
return NoopSpan.INSTANCE;
}
+ @Override
+ public SpanContext activeSpanContext() {
+ return NoopSpanContextImpl.INSTANCE;
+ }
+
static class NoopScopeImpl implements NoopScopeManager.NoopScope {
@Override
public void close() {}
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java
index abed3ad4..c1e26381 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java
@@ -36,11 +36,21 @@ public Span activeSpan() {
return NoopSpanImpl.INSTANCE;
}
+ @Override
+ public SpanContext activeSpanContext() {
+ return NoopSpanContextImpl.INSTANCE;
+ }
+
@Override
public Scope activateSpan(Span span) {
return NoopScopeManager.NoopScope.INSTANCE;
}
+ @Override
+ public Scope activateSpanContext(SpanContext spanContext) {
+ return NoopScopeManager.NoopScope.INSTANCE;
+ }
+
@Override
public SpanBuilder buildSpan(String operationName) { return NoopSpanBuilderImpl.INSTANCE; }
diff --git a/opentracing-testbed/pom.xml b/opentracing-testbed/pom.xml
index 89200e51..69167325 100644
--- a/opentracing-testbed/pom.xml
+++ b/opentracing-testbed/pom.xml
@@ -28,6 +28,7 @@