From 6579cfb7eebaf0403c77e2c6550dfb1a4cd7ead4 Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 16 Jul 2017 14:41:07 -0700 Subject: [PATCH 01/13] Add an AutoFinisher that can do the refcounting --- .../main/java/io/opentracing/ActiveSpan.java | 2 + .../noop/NoopActiveSpanSource.java | 5 + .../io/opentracing/util/AutoFinisher.java | 142 ++++++++++++++++++ .../util/ThreadLocalActiveSpan.java | 19 +-- .../util/ThreadLocalActiveSpanSource.java | 2 +- .../util/ThreadLocalActiveSpanTest.java | 8 +- 6 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java index 46e09d08..41de2afd 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java @@ -79,6 +79,8 @@ public interface ActiveSpan extends Closeable, BaseSpan { */ Continuation capture(); + Span wrapped(); + /** * A {@link Continuation} can be used once to activate a Span along with any non-OpenTracing execution * context (e.g., MDC), then deactivate when processing activity moves on to another Span. (In practice, this diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java index ddd150ce..c96f977c 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java @@ -52,6 +52,11 @@ public Continuation capture() { return NoopActiveSpanSource.NoopContinuation.INSTANCE; } + @Override + public Span wrapped() { + return NoopSpan.INSTANCE; + } + @Override public SpanContext context() { return NoopSpanContextImpl.INSTANCE; diff --git a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java new file mode 100644 index 00000000..e6f5182c --- /dev/null +++ b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java @@ -0,0 +1,142 @@ +package io.opentracing.util; + +import io.opentracing.ActiveSpan; +import io.opentracing.Span; +import io.opentracing.SpanContext; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * {@link AutoFinisher} is an {@link ActiveSpan} wrapper that automatically {@link Span#finish()}es the underlying + * {@link Span} when there are zero remaining {@link ActiveSpan}s or {@link ActiveSpan.Continuation}s referencing + * that underlying {@link Span}. + * + *

+ * Use {@link AutoFinisher} like this: + *


+ *     try (ActiveSpan span = new AutoFinisher(tracer.buildSpan("...").startActive())) {
+ *         // (Do work, even if deferred via {@link ActiveSpan#capture()})
+ *         span.setTag( ... );  // etc, etc
+ *     }  // Span finish()es automatically when there are no longer any ActiveSpans or Continuations referring to it
+ * 
+ * + *

+ * Note that {@link AutoFinisher} works by counting the number of extant {@link ActiveSpan} or + * {@link ActiveSpan.Continuation} references to the underlying {@link ActiveSpan} provided at construction time. + *

+ */ +public class AutoFinisher implements ActiveSpan { + private final AtomicInteger refCount; + private final ActiveSpan delegated; + + public AutoFinisher(ActiveSpan delegated) { + this.delegated = delegated; + this.refCount = new AtomicInteger(1); + } + + AutoFinisher(ActiveSpan delegated, AtomicInteger refCount) { + this.refCount = refCount; + this.delegated = delegated; + } + + @Override + public SpanContext context() { + return delegated.context(); + } + + @Override + public ActiveSpan setTag(String key, String value) { + delegated.setTag(key, value); + return this; + } + + @Override + public ActiveSpan setTag(String key, boolean value) { + delegated.setTag(key, value); + return this; + } + + @Override + public ActiveSpan setTag(String key, Number value) { + delegated.setTag(key, value); + return this; + } + + @Override + public ActiveSpan log(Map fields) { + delegated.log(fields); + return this; + } + + @Override + public ActiveSpan log(long timestampMicroseconds, Map fields) { + delegated.log(timestampMicroseconds, fields); + return this; + } + + @Override + public ActiveSpan log(String event) { + delegated.log(event); + return this; + } + + @Override + public ActiveSpan log(long timestampMicroseconds, String event) { + delegated.log(timestampMicroseconds, event); + return this; + } + + @Override + public ActiveSpan setBaggageItem(String key, String value) { + delegated.setBaggageItem(key, value); + return this; + } + + @Override + public String getBaggageItem(String key) { + return delegated.getBaggageItem(key); + } + + @Override + public ActiveSpan setOperationName(String operationName) { + delegated.setOperationName(operationName); + return this; + } + + @Override + public Span wrapped() { + return this.delegated.wrapped(); + } + + @Override + public void deactivate() { + this.delegated.deactivate(); + if (0 == refCount.decrementAndGet()) { + this.delegated.wrapped().finish(); + } + } + + @Override + public void close() { + this.deactivate(); + } + + @Override + public Continuation capture() { + return new AutoFinisher.Continuation(); + } + + class Continuation implements ActiveSpan.Continuation { + Continuation() { + // Always increment the reference count when new Continuations are created (i.e., we assume that all + // Continuations are eventually activate()d). + refCount.incrementAndGet(); + } + + @Override + public ActiveSpan activate() { + return new AutoFinisher(delegated, refCount); + } + } +} diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java index ba90ca43..8de00a82 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java @@ -33,11 +33,9 @@ public class ThreadLocalActiveSpan implements ActiveSpan { private final ThreadLocalActiveSpanSource source; private final Span wrapped; private final ThreadLocalActiveSpan toRestore; - private final AtomicInteger refCount; - ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped, AtomicInteger refCount) { + ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped) { this.source = source; - this.refCount = refCount; this.wrapped = wrapped; this.toRestore = source.tlsSnapshot.get(); source.tlsSnapshot.set(this); @@ -50,10 +48,6 @@ public void deactivate() { return; } source.tlsSnapshot.set(toRestore); - - if (0 == refCount.decrementAndGet()) { - wrapped.finish(); - } } @Override @@ -61,6 +55,11 @@ public Continuation capture() { return new ThreadLocalActiveSpan.Continuation(); } + @Override + public Span wrapped() { + return wrapped; + } + @Override public SpanContext context() { return wrapped.context(); @@ -136,13 +135,11 @@ public String toString() { } private final class Continuation implements ActiveSpan.Continuation { - Continuation() { - refCount.incrementAndGet(); - } + Continuation() {} @Override public ThreadLocalActiveSpan activate() { - return new ThreadLocalActiveSpan(source, wrapped, refCount); + return new ThreadLocalActiveSpan(source, wrapped); } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java index 2cd7e6e2..8f00df7d 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java @@ -36,7 +36,7 @@ public ThreadLocalActiveSpan activeSpan() { @Override public ActiveSpan makeActive(Span span) { - return new ThreadLocalActiveSpan(this, span, new AtomicInteger(1)); + return new ThreadLocalActiveSpan(this, span); } } diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java index 06d49093..7ce4ab3a 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java @@ -39,7 +39,7 @@ public void continuation() throws Exception { Span span = mock(Span.class); // Quasi try-with-resources (this is 1.6). - ActiveSpan activeSpan = source.makeActive(span); + ActiveSpan activeSpan = new AutoFinisher(source.makeActive(span)); ActiveSpan.Continuation continued = null; try { assertNotNull(activeSpan); @@ -72,12 +72,12 @@ public void implicitSpanStack() throws Exception { Span foregroundSpan = mock(Span.class); // Quasi try-with-resources (this is 1.6). - ActiveSpan backgroundActive = source.makeActive(backgroundSpan); + ActiveSpan backgroundActive = new AutoFinisher(source.makeActive(backgroundSpan)); try { assertNotNull(backgroundActive); // Activate a new ActiveSpan on top of the background one. - ActiveSpan foregroundActive = source.makeActive(foregroundSpan); + ActiveSpan foregroundActive = new AutoFinisher(source.makeActive(foregroundSpan)); try { ActiveSpan shouldBeForeground = source.activeSpan(); assertEquals(foregroundActive, shouldBeForeground); @@ -105,7 +105,7 @@ public void implicitSpanStack() throws Exception { public void testDeactivateWhenDifferentSpanIsActive() { Span span = mock(Span.class); - ActiveSpan activeSpan = source.makeActive(span); + ActiveSpan activeSpan = new AutoFinisher(source.makeActive(span)); source.makeActive(mock(Span.class)); activeSpan.deactivate(); From 40271b430551c69bd667af8006454bb9f198694e Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 16 Jul 2017 16:15:57 -0700 Subject: [PATCH 02/13] Fold Span into BaseSpan, add ActiveSpan.Observer --- .../main/java/io/opentracing/ActiveSpan.java | 11 +- .../java/io/opentracing/ActiveSpanSource.java | 3 +- .../main/java/io/opentracing/BaseSpan.java | 143 ------------------ .../src/main/java/io/opentracing/Span.java | 129 +++++++++++++++- .../src/main/java/io/opentracing/Tracer.java | 3 +- .../java/io/opentracing/tag/AbstractTag.java | 4 +- .../java/io/opentracing/tag/BooleanTag.java | 4 +- .../main/java/io/opentracing/tag/IntTag.java | 4 +- .../java/io/opentracing/tag/StringTag.java | 6 +- .../io/opentracing/tag/AbstractTagTest.java | 2 +- .../java/io/opentracing/mock/MockTracer.java | 16 +- .../io/opentracing/mock/MockSpanTest.java | 3 +- .../noop/NoopActiveSpanSource.java | 11 ++ .../io/opentracing/noop/NoopSpanBuilder.java | 8 +- .../java/io/opentracing/noop/NoopTracer.java | 5 + .../io/opentracing/util/AutoFinisher.java | 111 ++------------ .../io/opentracing/util/GlobalTracer.java | 7 +- .../util/ThreadLocalActiveSpan.java | 26 +++- .../util/ThreadLocalActiveSpanSource.java | 7 +- .../util/ThreadLocalActiveSpanTest.java | 8 +- 20 files changed, 229 insertions(+), 282 deletions(-) delete mode 100644 opentracing-api/src/main/java/io/opentracing/BaseSpan.java diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java index 41de2afd..b4bc3c78 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java @@ -16,7 +16,7 @@ import java.io.Closeable; /** - * {@link ActiveSpan} inherits all of the OpenTracing functionality in {@link BaseSpan} and layers on in-process + * {@link ActiveSpan} inherits all of the OpenTracing functionality in {@link Span} and layers on in-process * propagation capabilities. * *

@@ -39,10 +39,10 @@ * @see Tracer.SpanBuilder#startActive() * @see Continuation#activate() * @see ActiveSpanSource - * @see BaseSpan + * @see Span * @see Span */ -public interface ActiveSpan extends Closeable, BaseSpan { +public interface ActiveSpan extends Closeable, Span { /** * Mark the end of the active period for the current thread and {@link ActiveSpan}. When the last * {@link ActiveSpan} is deactivated for a given {@link Span}, it is automatically {@link Span#finish()}ed. @@ -108,4 +108,9 @@ interface Continuation { ActiveSpan activate(); } + interface Observer { + void onCapture(ActiveSpan span); + void onActivate(ActiveSpan span); + void onDeactivate(ActiveSpan span); + } } diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java index 334d00e8..5fd6ecec 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java @@ -31,7 +31,7 @@ public interface ActiveSpanSource { * *

* If there is an {@link ActiveSpan active span}, it becomes an implicit parent of any newly-created - * {@link BaseSpan span} at {@link Tracer.SpanBuilder#startActive()} time (rather than at + * {@link Span span} at {@link Tracer.SpanBuilder#startActive()} time (rather than at * {@link Tracer#buildSpan(String)} time). * * @return the {@link ActiveSpan active span}, or null if none could be found. @@ -47,4 +47,5 @@ public interface ActiveSpanSource { * {@link ActiveSpanSource}-specific context (e.g., the MDC context map) */ ActiveSpan makeActive(Span span); + ActiveSpan makeActive(Span span, ActiveSpan.Observer observer); } diff --git a/opentracing-api/src/main/java/io/opentracing/BaseSpan.java b/opentracing-api/src/main/java/io/opentracing/BaseSpan.java deleted file mode 100644 index b83bc190..00000000 --- a/opentracing-api/src/main/java/io/opentracing/BaseSpan.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * 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.Map; - -/** - * {@link BaseSpan} represents the OpenTracing specification's span contract with the exception of methods to finish - * said span. For those, either use {@link Span#finish()} or {@link ActiveSpan#deactivate()} depending on the - * programming model. - * - * @see Span - * @see ActiveSpan - * @see Tracer.SpanBuilder#startManual() - * @see Tracer.SpanBuilder#startActive() - */ -public interface BaseSpan { - /** - * Retrieve the associated SpanContext. - * - * This may be called at any time, including after calls to finish(). - * - * @return the SpanContext that encapsulates Span state that should propagate across process boundaries. - */ - SpanContext context(); - - /** - * Set a key:value tag on the Span. - */ - S setTag(String key, String value); - - /** Same as {@link #setTag(String, String)}, but for boolean values. */ - S setTag(String key, boolean value); - - /** Same as {@link #setTag(String, String)}, but for numeric values. */ - S setTag(String key, Number value); - - /** - * Log key:value pairs to the Span with the current walltime timestamp. - * - *

CAUTIONARY NOTE: not all Tracer implementations support key:value log fields end-to-end. - * Caveat emptor. - * - *

A contrived example (using Guava, which is not required): - *


-     span.log(
-     ImmutableMap.Builder()
-     .put("event", "soft error")
-     .put("type", "cache timeout")
-     .put("waited.millis", 1500)
-     .build());
-     
- * - * @param fields key:value log fields. Tracer implementations should support String, numeric, and boolean values; - * some may also support arbitrary Objects. - * @return the Span, for chaining - * @see Span#log(String) - */ - S log(Map fields); - - /** - * Like log(Map<String, Object>), but with an explicit timestamp. - * - *

CAUTIONARY NOTE: not all Tracer implementations support key:value log fields end-to-end. - * Caveat emptor. - * - * @param timestampMicroseconds The explicit timestamp for the log record. Must be greater than or equal to the - * Span's start timestamp. - * @param fields key:value log fields. Tracer implementations should support String, numeric, and boolean values; - * some may also support arbitrary Objects. - * @return the Span, for chaining - * @see Span#log(long, String) - */ - S log(long timestampMicroseconds, Map fields); - - /** - * Record an event at the current walltime timestamp. - * - * Shorthand for - * - *


-     span.log(Collections.singletonMap("event", event));
-     
- * - * @param event the event value; often a stable identifier for a moment in the Span lifecycle - * @return the Span, for chaining - */ - S log(String event); - - /** - * Record an event at a specific timestamp. - * - * Shorthand for - * - *

-     span.log(timestampMicroseconds, Collections.singletonMap("event", event));
-     
- * - * @param timestampMicroseconds The explicit timestamp for the log record. Must be greater than or equal to the - * Span's start timestamp. - * @param event the event value; often a stable identifier for a moment in the Span lifecycle - * @return the Span, for chaining - */ - S log(long timestampMicroseconds, String event); - - /** - * Sets a baggage item in the Span (and its SpanContext) as a key/value pair. - * - * Baggage enables powerful distributed context propagation functionality where arbitrary application data can be - * carried along the full path of request execution throughout the system. - * - * Note 1: Baggage is only propagated to the future (recursive) children of this SpanContext. - * - * Note 2: Baggage is sent in-band with every subsequent local and remote calls, so this feature must be used with - * care. - * - * @return this Span instance, for chaining - */ - S setBaggageItem(String key, String value); - - /** - * @return the value of the baggage item identified by the given key, or null if no such item could be found - */ - String getBaggageItem(String key); - - /** - * Sets the string name for the logical operation this span represents. - * - * @return this Span instance, for chaining - */ - S setOperationName(String operationName); -} diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java index 026c4ce5..72b8b6d7 100644 --- a/opentracing-api/src/main/java/io/opentracing/Span.java +++ b/opentracing-api/src/main/java/io/opentracing/Span.java @@ -13,16 +13,131 @@ */ package io.opentracing; +import java.util.Map; + /** - * Represents an in-flight Span that's manually propagated within the given process. Most of - * the API lives in {@link BaseSpan}. - * - *

{@link Span}s are created by the {@link Tracer.SpanBuilder#startManual} method; see {@link ActiveSpan} for - * a {@link BaseSpan} extension designed for automatic in-process propagation. + * {@link Span} represents the OpenTracing specification's Span contract. * - * @see ActiveSpan for automatic propagation (recommended for most intstrumentation!) + * @see ActiveSpan + * @see Tracer.SpanBuilder#startManual() + * @see Tracer.SpanBuilder#startActive() */ -public interface Span extends BaseSpan { +public interface Span { + /** + * Retrieve the associated SpanContext. + * + * This may be called at any time, including after calls to finish(). + * + * @return the SpanContext that encapsulates Span state that should propagate across process boundaries. + */ + SpanContext context(); + + /** + * Set a key:value tag on the Span. + */ + Span setTag(String key, String value); + + /** Same as {@link #setTag(String, String)}, but for boolean values. */ + Span setTag(String key, boolean value); + + /** Same as {@link #setTag(String, String)}, but for numeric values. */ + Span setTag(String key, Number value); + + /** + * Log key:value pairs to the Span with the current walltime timestamp. + * + *

CAUTIONARY NOTE: not all Tracer implementations support key:value log fields end-to-end. + * Caveat emptor. + * + *

A contrived example (using Guava, which is not required): + *


+     span.log(
+     ImmutableMap.Builder()
+     .put("event", "soft error")
+     .put("type", "cache timeout")
+     .put("waited.millis", 1500)
+     .build());
+     
+ * + * @param fields key:value log fields. Tracer implementations should support String, numeric, and boolean values; + * some may also support arbitrary Objects. + * @return the Span, for chaining + * @see Span#log(String) + */ + Span log(Map fields); + + /** + * Like log(Map<String, Object>), but with an explicit timestamp. + * + *

CAUTIONARY NOTE: not all Tracer implementations support key:value log fields end-to-end. + * Caveat emptor. + * + * @param timestampMicroseconds The explicit timestamp for the log record. Must be greater than or equal to the + * Span's start timestamp. + * @param fields key:value log fields. Tracer implementations should support String, numeric, and boolean values; + * some may also support arbitrary Objects. + * @return the Span, for chaining + * @see Span#log(long, String) + */ + Span log(long timestampMicroseconds, Map fields); + + /** + * Record an event at the current walltime timestamp. + * + * Shorthand for + * + *


+     span.log(Collections.singletonMap("event", event));
+     
+ * + * @param event the event value; often a stable identifier for a moment in the Span lifecycle + * @return the Span, for chaining + */ + Span log(String event); + + /** + * Record an event at a specific timestamp. + * + * Shorthand for + * + *

+     span.log(timestampMicroseconds, Collections.singletonMap("event", event));
+     
+ * + * @param timestampMicroseconds The explicit timestamp for the log record. Must be greater than or equal to the + * Span's start timestamp. + * @param event the event value; often a stable identifier for a moment in the Span lifecycle + * @return the Span, for chaining + */ + Span log(long timestampMicroseconds, String event); + + /** + * Sets a baggage item in the Span (and its SpanContext) as a key/value pair. + * + * Baggage enables powerful distributed context propagation functionality where arbitrary application data can be + * carried along the full path of request execution throughout the system. + * + * Note 1: Baggage is only propagated to the future (recursive) children of this SpanContext. + * + * Note 2: Baggage is sent in-band with every subsequent local and remote calls, so this feature must be used with + * care. + * + * @return this Span instance, for chaining + */ + Span setBaggageItem(String key, String value); + + /** + * @return the value of the baggage item identified by the given key, or null if no such item could be found + */ + String getBaggageItem(String key); + + /** + * Sets the string name for the logical operation this span represents. + * + * @return this Span instance, for chaining + */ + Span setOperationName(String operationName); + /** * Sets the end timestamp to now and records the span. * diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java index 25d3d321..1f1a71c3 100644 --- a/opentracing-api/src/main/java/io/opentracing/Tracer.java +++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java @@ -111,7 +111,7 @@ interface SpanBuilder { *

* If parent==null, this is a noop. */ - SpanBuilder asChildOf(BaseSpan parent); + SpanBuilder asChildOf(Span parent); /** * Add a reference from the Span being built to a distinct (usually parent) Span. May be called multiple times @@ -187,6 +187,7 @@ interface SpanBuilder { * @see ActiveSpan */ ActiveSpan startActive(); + ActiveSpan startActive(ActiveSpan.Observer observer); /** * Like {@link #startActive()}, but the returned {@link Span} has not been registered via the diff --git a/opentracing-api/src/main/java/io/opentracing/tag/AbstractTag.java b/opentracing-api/src/main/java/io/opentracing/tag/AbstractTag.java index 4fed5c18..792ca636 100644 --- a/opentracing-api/src/main/java/io/opentracing/tag/AbstractTag.java +++ b/opentracing-api/src/main/java/io/opentracing/tag/AbstractTag.java @@ -13,7 +13,7 @@ */ package io.opentracing.tag; -import io.opentracing.BaseSpan; +import io.opentracing.Span; public abstract class AbstractTag { protected final String key; @@ -26,5 +26,5 @@ public String getKey() { return key; } - protected abstract void set(BaseSpan span, T tagValue); + protected abstract void set(Span span, T tagValue); } diff --git a/opentracing-api/src/main/java/io/opentracing/tag/BooleanTag.java b/opentracing-api/src/main/java/io/opentracing/tag/BooleanTag.java index f9be1ee3..8168d422 100644 --- a/opentracing-api/src/main/java/io/opentracing/tag/BooleanTag.java +++ b/opentracing-api/src/main/java/io/opentracing/tag/BooleanTag.java @@ -13,7 +13,7 @@ */ package io.opentracing.tag; -import io.opentracing.BaseSpan; +import io.opentracing.Span; public class BooleanTag extends AbstractTag { public BooleanTag(String key) { @@ -21,7 +21,7 @@ public BooleanTag(String key) { } @Override - public void set(BaseSpan span, Boolean tagValue) { + public void set(Span span, Boolean tagValue) { span.setTag(super.key, tagValue); } } diff --git a/opentracing-api/src/main/java/io/opentracing/tag/IntTag.java b/opentracing-api/src/main/java/io/opentracing/tag/IntTag.java index 48f4ae71..cb126492 100644 --- a/opentracing-api/src/main/java/io/opentracing/tag/IntTag.java +++ b/opentracing-api/src/main/java/io/opentracing/tag/IntTag.java @@ -13,7 +13,7 @@ */ package io.opentracing.tag; -import io.opentracing.BaseSpan; +import io.opentracing.Span; public class IntTag extends AbstractTag { public IntTag(String key) { @@ -21,7 +21,7 @@ public IntTag(String key) { } @Override - public void set(BaseSpan span, Integer tagValue) { + public void set(Span span, Integer tagValue) { span.setTag(super.key, tagValue); } } diff --git a/opentracing-api/src/main/java/io/opentracing/tag/StringTag.java b/opentracing-api/src/main/java/io/opentracing/tag/StringTag.java index 1fdba21a..65cfd59e 100644 --- a/opentracing-api/src/main/java/io/opentracing/tag/StringTag.java +++ b/opentracing-api/src/main/java/io/opentracing/tag/StringTag.java @@ -13,7 +13,7 @@ */ package io.opentracing.tag; -import io.opentracing.BaseSpan; +import io.opentracing.Span; public class StringTag extends AbstractTag { public StringTag(String key) { @@ -21,11 +21,11 @@ public StringTag(String key) { } @Override - public void set(BaseSpan span, String tagValue) { + public void set(Span span, String tagValue) { span.setTag(super.key, tagValue); } - public void set(BaseSpan span, StringTag tag) { + public void set(Span span, StringTag tag) { span.setTag(super.key, tag.key); } } diff --git a/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java b/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java index 816e259f..400846df 100644 --- a/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java +++ b/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java @@ -17,10 +17,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import io.opentracing.Span; import org.junit.Test; import io.opentracing.ActiveSpan; -import io.opentracing.Span; /** * @author Pavol Loffay 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 15e6fcf8..23372471 100644 --- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java +++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java @@ -15,10 +15,9 @@ import io.opentracing.ActiveSpan; import io.opentracing.ActiveSpanSource; -import io.opentracing.BaseSpan; +import io.opentracing.Span; import io.opentracing.noop.NoopActiveSpanSource; import io.opentracing.References; -import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; @@ -98,6 +97,11 @@ public ActiveSpan makeActive(Span span) { return spanSource.makeActive(span); } + @Override + public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { + return spanSource.makeActive(span, observer); + } + /** * Propagator allows the developer to intercept and verify any calls to inject() and/or extract(). * @@ -218,7 +222,7 @@ public SpanBuilder asChildOf(SpanContext parent) { } @Override - public SpanBuilder asChildOf(BaseSpan parent) { + public SpanBuilder asChildOf(Span parent) { return addReference(References.CHILD_OF, parent.context()); } @@ -272,6 +276,12 @@ public ActiveSpan startActive() { return spanSource.makeActive(span); } + @Override + public ActiveSpan startActive(ActiveSpan.Observer observer) { + MockSpan span = this.startManual(); + return spanSource.makeActive(span, observer); + } + @Override public MockSpan startManual() { if (this.startMicros == 0) { diff --git a/opentracing-mock/src/test/java/io/opentracing/mock/MockSpanTest.java b/opentracing-mock/src/test/java/io/opentracing/mock/MockSpanTest.java index b488f657..22992a38 100644 --- a/opentracing-mock/src/test/java/io/opentracing/mock/MockSpanTest.java +++ b/opentracing-mock/src/test/java/io/opentracing/mock/MockSpanTest.java @@ -13,11 +13,10 @@ */ package io.opentracing.mock; +import io.opentracing.Span; import org.junit.Assert; import org.junit.Test; -import io.opentracing.Span; - /** * @author Pavol Loffay */ diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java index c96f977c..88022d1f 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java @@ -40,6 +40,11 @@ public ActiveSpan makeActive(Span span) { return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; } + @Override + public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { + return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; + } + @Override public ActiveSpan activeSpan() { return null; } @@ -114,6 +119,12 @@ public String getBaggageItem(String key) { public NoopActiveSpan setOperationName(String operationName) { return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; } + + @Override + public void finish() { } + + @Override + public void finish(long finishMicros) { } } static class NoopContinuationImpl implements NoopActiveSpanSource.NoopContinuation { diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java index 06129830..4ebf7209 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java @@ -14,7 +14,6 @@ package io.opentracing.noop; import io.opentracing.ActiveSpan; -import io.opentracing.BaseSpan; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; @@ -42,7 +41,7 @@ public Tracer.SpanBuilder asChildOf(SpanContext parent) { public Tracer.SpanBuilder ignoreActiveSpan() { return this; } @Override - public Tracer.SpanBuilder asChildOf(BaseSpan parent) { + public Tracer.SpanBuilder asChildOf(Span parent) { return this; } @@ -76,6 +75,11 @@ public ActiveSpan startActive() { return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; } + @Override + public ActiveSpan startActive(ActiveSpan.Observer observer) { + return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; + } + @Override public Span startManual() { return NoopSpanImpl.INSTANCE; 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 678a302a..26855f9a 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java @@ -46,5 +46,10 @@ public ActiveSpan activeSpan() { public ActiveSpan makeActive(Span span) { return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; } + + @Override + public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { + return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; + } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java index e6f5182c..ae807c46 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java +++ b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java @@ -8,6 +8,7 @@ import java.util.concurrent.atomic.AtomicInteger; /** + * XXX: fix comment * {@link AutoFinisher} is an {@link ActiveSpan} wrapper that automatically {@link Span#finish()}es the underlying * {@link Span} when there are zero remaining {@link ActiveSpan}s or {@link ActiveSpan.Continuation}s referencing * that underlying {@link Span}. @@ -26,117 +27,27 @@ * {@link ActiveSpan.Continuation} references to the underlying {@link ActiveSpan} provided at construction time. *

*/ -public class AutoFinisher implements ActiveSpan { +public class AutoFinisher implements ActiveSpan.Observer { private final AtomicInteger refCount; - private final ActiveSpan delegated; - public AutoFinisher(ActiveSpan delegated) { - this.delegated = delegated; - this.refCount = new AtomicInteger(1); - } - - AutoFinisher(ActiveSpan delegated, AtomicInteger refCount) { - this.refCount = refCount; - this.delegated = delegated; - } - - @Override - public SpanContext context() { - return delegated.context(); - } - - @Override - public ActiveSpan setTag(String key, String value) { - delegated.setTag(key, value); - return this; - } - - @Override - public ActiveSpan setTag(String key, boolean value) { - delegated.setTag(key, value); - return this; - } - - @Override - public ActiveSpan setTag(String key, Number value) { - delegated.setTag(key, value); - return this; - } - - @Override - public ActiveSpan log(Map fields) { - delegated.log(fields); - return this; - } - - @Override - public ActiveSpan log(long timestampMicroseconds, Map fields) { - delegated.log(timestampMicroseconds, fields); - return this; - } - - @Override - public ActiveSpan log(String event) { - delegated.log(event); - return this; - } - - @Override - public ActiveSpan log(long timestampMicroseconds, String event) { - delegated.log(timestampMicroseconds, event); - return this; + public AutoFinisher() { + refCount = new AtomicInteger(1); } @Override - public ActiveSpan setBaggageItem(String key, String value) { - delegated.setBaggageItem(key, value); - return this; + public void onCapture(ActiveSpan span) { + // Always increment the reference count when new Continuations are created (i.e., we assume that all + // Continuations are eventually activate()d). + refCount.incrementAndGet(); } @Override - public String getBaggageItem(String key) { - return delegated.getBaggageItem(key); - } - - @Override - public ActiveSpan setOperationName(String operationName) { - delegated.setOperationName(operationName); - return this; - } - - @Override - public Span wrapped() { - return this.delegated.wrapped(); - } + public void onActivate(ActiveSpan span) {} @Override - public void deactivate() { - this.delegated.deactivate(); + public void onDeactivate(ActiveSpan span) { if (0 == refCount.decrementAndGet()) { - this.delegated.wrapped().finish(); - } - } - - @Override - public void close() { - this.deactivate(); - } - - @Override - public Continuation capture() { - return new AutoFinisher.Continuation(); - } - - class Continuation implements ActiveSpan.Continuation { - Continuation() { - // Always increment the reference count when new Continuations are created (i.e., we assume that all - // Continuations are eventually activate()d). - refCount.incrementAndGet(); - } - - @Override - public ActiveSpan activate() { - return new AutoFinisher(delegated, refCount); + span.finish(); } } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java index 89b6f073..1000b9ef 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java +++ b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java @@ -14,9 +14,9 @@ package io.opentracing.util; import io.opentracing.ActiveSpan; +import io.opentracing.Span; import io.opentracing.noop.NoopTracer; import io.opentracing.noop.NoopTracerFactory; -import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; @@ -152,4 +152,9 @@ public ActiveSpan activeSpan() { public ActiveSpan makeActive(Span span) { return tracer.makeActive(span); } + + @Override + public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { + return tracer.makeActive(span, observer); + } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java index 8de00a82..9cfe08d8 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java @@ -14,7 +14,6 @@ package io.opentracing.util; import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; import io.opentracing.ActiveSpan; import io.opentracing.ActiveSpanSource; @@ -33,11 +32,17 @@ public class ThreadLocalActiveSpan implements ActiveSpan { private final ThreadLocalActiveSpanSource source; private final Span wrapped; private final ThreadLocalActiveSpan toRestore; + private final Observer observer; ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped) { + this(source, wrapped, null); + } + + ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped, Observer observer) { this.source = source; this.wrapped = wrapped; this.toRestore = source.tlsSnapshot.get(); + this.observer = observer; source.tlsSnapshot.set(this); } @@ -48,6 +53,7 @@ public void deactivate() { return; } source.tlsSnapshot.set(toRestore); + observer.onDeactivate(this); } @Override @@ -124,6 +130,16 @@ public ThreadLocalActiveSpan setOperationName(String operationName) { return this; } + @Override + public void finish() { + wrapped.finish(); + } + + @Override + public void finish(long finishMicros) { + wrapped.finish(finishMicros); + } + @Override public void close() { deactivate(); @@ -135,11 +151,15 @@ public String toString() { } private final class Continuation implements ActiveSpan.Continuation { - Continuation() {} + Continuation() { + observer.onCapture(ThreadLocalActiveSpan.this); + } @Override public ThreadLocalActiveSpan activate() { - return new ThreadLocalActiveSpan(source, wrapped); + ThreadLocalActiveSpan rval = new ThreadLocalActiveSpan(source, wrapped, observer); + observer.onActivate(rval); + return rval; } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java index 8f00df7d..26412929 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java @@ -18,8 +18,6 @@ import io.opentracing.Span; import io.opentracing.Tracer; -import java.util.concurrent.atomic.AtomicInteger; - /** * A simple {@link ActiveSpanSource} implementation built on top of Java's thread-local storage primitive. * @@ -39,4 +37,9 @@ public ActiveSpan makeActive(Span span) { return new ThreadLocalActiveSpan(this, span); } + @Override + public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { + return new ThreadLocalActiveSpan(this, span, observer); + } + } diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java index 7ce4ab3a..33ba0e98 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java @@ -39,7 +39,7 @@ public void continuation() throws Exception { Span span = mock(Span.class); // Quasi try-with-resources (this is 1.6). - ActiveSpan activeSpan = new AutoFinisher(source.makeActive(span)); + ActiveSpan activeSpan = source.makeActive(span, new AutoFinisher()); ActiveSpan.Continuation continued = null; try { assertNotNull(activeSpan); @@ -72,12 +72,12 @@ public void implicitSpanStack() throws Exception { Span foregroundSpan = mock(Span.class); // Quasi try-with-resources (this is 1.6). - ActiveSpan backgroundActive = new AutoFinisher(source.makeActive(backgroundSpan)); + ActiveSpan backgroundActive = source.makeActive(backgroundSpan, new AutoFinisher()); try { assertNotNull(backgroundActive); // Activate a new ActiveSpan on top of the background one. - ActiveSpan foregroundActive = new AutoFinisher(source.makeActive(foregroundSpan)); + ActiveSpan foregroundActive = source.makeActive(foregroundSpan, new AutoFinisher()); try { ActiveSpan shouldBeForeground = source.activeSpan(); assertEquals(foregroundActive, shouldBeForeground); @@ -105,7 +105,7 @@ public void implicitSpanStack() throws Exception { public void testDeactivateWhenDifferentSpanIsActive() { Span span = mock(Span.class); - ActiveSpan activeSpan = new AutoFinisher(source.makeActive(span)); + ActiveSpan activeSpan = source.makeActive(span, new AutoFinisher()); source.makeActive(mock(Span.class)); activeSpan.deactivate(); From 72ffc0e2a1b1b4beaea2e1e178d977307ff27295 Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 16 Jul 2017 21:50:46 -0700 Subject: [PATCH 03/13] Make things spiritually backwards-compatible ... while also moving the refcounting to the util package and creating a cleaner path to something different should we want it. --- .../main/java/io/opentracing/ActiveSpan.java | 14 +++++++++--- .../io/opentracing/util/AutoFinisher.java | 8 +++---- .../util/ThreadLocalActiveSpan.java | 22 +++++++++---------- .../util/ThreadLocalActiveSpanSource.java | 2 +- .../util/ThreadLocalActiveSpanTest.java | 8 +++---- 5 files changed, 31 insertions(+), 23 deletions(-) diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java index b4bc3c78..7274b548 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java @@ -33,6 +33,11 @@ * {@link ActiveSpan.Continuation}s, then re-{@link Continuation#activate()}d later. * *

+ * Propagated {@link ActiveSpan} instances form a tree within a process, where each node is an {@link ActiveSpan} and + * each edge is a {@link ActiveSpan.Continuation}. The {@link ActiveSpan.Observer} interface provides visibility into + * the node/edge creations as well as {@link ActiveSpan} activation/deactivation lifecycles. + * + *

* NOTE: {@link ActiveSpan} extends {@link Closeable} rather than {@code AutoCloseable} in order to preserve support * for JDK1.6. * @@ -108,9 +113,12 @@ interface Continuation { ActiveSpan activate(); } + /** + * + */ interface Observer { - void onCapture(ActiveSpan span); - void onActivate(ActiveSpan span); - void onDeactivate(ActiveSpan span); + void onCapture(ActiveSpan captured, Continuation destination); + void onActivate(Continuation source, ActiveSpan justActivated); + void onDeactivate(ActiveSpan activeSpan); } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java index ae807c46..8b0f1d9e 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java +++ b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java @@ -35,19 +35,19 @@ public AutoFinisher() { } @Override - public void onCapture(ActiveSpan span) { + public void onCapture(ActiveSpan captured, ActiveSpan.Continuation destination) { // Always increment the reference count when new Continuations are created (i.e., we assume that all // Continuations are eventually activate()d). refCount.incrementAndGet(); } @Override - public void onActivate(ActiveSpan span) {} + public void onActivate(ActiveSpan.Continuation source, ActiveSpan justActivated) {} @Override - public void onDeactivate(ActiveSpan span) { + public void onDeactivate(ActiveSpan activeSpan) { if (0 == refCount.decrementAndGet()) { - span.finish(); + activeSpan.finish(); } } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java index 9cfe08d8..96d02c8a 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java @@ -34,10 +34,6 @@ public class ThreadLocalActiveSpan implements ActiveSpan { private final ThreadLocalActiveSpan toRestore; private final Observer observer; - ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped) { - this(source, wrapped, null); - } - ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped, Observer observer) { this.source = source; this.wrapped = wrapped; @@ -53,12 +49,18 @@ public void deactivate() { return; } source.tlsSnapshot.set(toRestore); - observer.onDeactivate(this); + if (observer != null) { + observer.onDeactivate(this); + } } @Override public Continuation capture() { - return new ThreadLocalActiveSpan.Continuation(); + Continuation cont = new ThreadLocalActiveSpan.Continuation(); + if (observer != null) { + observer.onCapture(ThreadLocalActiveSpan.this, cont); + } + return cont; } @Override @@ -151,14 +153,12 @@ public String toString() { } private final class Continuation implements ActiveSpan.Continuation { - Continuation() { - observer.onCapture(ThreadLocalActiveSpan.this); - } - @Override public ThreadLocalActiveSpan activate() { ThreadLocalActiveSpan rval = new ThreadLocalActiveSpan(source, wrapped, observer); - observer.onActivate(rval); + if (observer != null) { + observer.onActivate(this, rval); + } return rval; } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java index 26412929..3140970d 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java @@ -34,7 +34,7 @@ public ThreadLocalActiveSpan activeSpan() { @Override public ActiveSpan makeActive(Span span) { - return new ThreadLocalActiveSpan(this, span); + return new ThreadLocalActiveSpan(this, span, new AutoFinisher()); } @Override diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java index 33ba0e98..06d49093 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java @@ -39,7 +39,7 @@ public void continuation() throws Exception { Span span = mock(Span.class); // Quasi try-with-resources (this is 1.6). - ActiveSpan activeSpan = source.makeActive(span, new AutoFinisher()); + ActiveSpan activeSpan = source.makeActive(span); ActiveSpan.Continuation continued = null; try { assertNotNull(activeSpan); @@ -72,12 +72,12 @@ public void implicitSpanStack() throws Exception { Span foregroundSpan = mock(Span.class); // Quasi try-with-resources (this is 1.6). - ActiveSpan backgroundActive = source.makeActive(backgroundSpan, new AutoFinisher()); + ActiveSpan backgroundActive = source.makeActive(backgroundSpan); try { assertNotNull(backgroundActive); // Activate a new ActiveSpan on top of the background one. - ActiveSpan foregroundActive = source.makeActive(foregroundSpan, new AutoFinisher()); + ActiveSpan foregroundActive = source.makeActive(foregroundSpan); try { ActiveSpan shouldBeForeground = source.activeSpan(); assertEquals(foregroundActive, shouldBeForeground); @@ -105,7 +105,7 @@ public void implicitSpanStack() throws Exception { public void testDeactivateWhenDifferentSpanIsActive() { Span span = mock(Span.class); - ActiveSpan activeSpan = source.makeActive(span, new AutoFinisher()); + ActiveSpan activeSpan = source.makeActive(span); source.makeActive(mock(Span.class)); activeSpan.deactivate(); From 648576b6f67b43316fdb683390c6898470ad5c6f Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 16 Jul 2017 22:02:04 -0700 Subject: [PATCH 04/13] Add some comments --- README.md | 4 ++-- .../src/main/java/io/opentracing/ActiveSpan.java | 3 ++- .../src/main/java/io/opentracing/ActiveSpanSource.java | 10 +++++++--- opentracing-api/src/main/java/io/opentracing/Span.java | 4 ++++ .../src/main/java/io/opentracing/Tracer.java | 8 ++++++++ 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 983cfac3..5b516863 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ The common case starts an `ActiveSpan` that's automatically registered for intra ```java io.opentracing.Tracer tracer = ...; ... -try (ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive()) { +try (ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive(new AutoFinisher())) { // Do things. // // If we create async work, `activeSpan.capture()` allows us to pass the `ActiveSpan` along as well. @@ -68,7 +68,7 @@ The above is semantically equivalent to the more explicit try-finally version: ```java io.opentracing.Tracer tracer = ...; ... -ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive(); +ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive(new AutoFinisher()); try { // Do things. } finally { diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java index 7274b548..13a12b9b 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java @@ -114,7 +114,8 @@ interface Continuation { } /** - * + * The {@link ActiveSpan.Observer} interface learns of capture, activate, and deactivate calls for a given + * {@link ActiveSpan} instance and any of its "descendants" from a capture/activate standpoint. */ interface Observer { void onCapture(ActiveSpan captured, Continuation destination); diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java index 5fd6ecec..4a1c6f5b 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java @@ -26,8 +26,7 @@ public interface ActiveSpanSource { /** - * Return the {@link ActiveSpan active span}. This does not affect the internal reference count for the - * {@link ActiveSpan}. + * Return the {@link ActiveSpan active span}. * *

* If there is an {@link ActiveSpan active span}, it becomes an implicit parent of any newly-created @@ -38,14 +37,19 @@ public interface ActiveSpanSource { */ ActiveSpan activeSpan(); + /** + * @see #makeActive(Span, ActiveSpan.Observer) + */ + ActiveSpan makeActive(Span span); /** * Wrap and "make active" a {@link Span} by encapsulating it – and any active state (e.g., MDC state) in the * current thread – in a new {@link ActiveSpan}. * * @param span the Span to wrap in an {@link ActiveSpan} + * @param observer the observer that learns of activations, deactivations, and captures related to the returned + * {@link ActiveSpan}. May be null (which implies "no observer") * @return an {@link ActiveSpan} that encapsulates the given {@link Span} and any other * {@link ActiveSpanSource}-specific context (e.g., the MDC context map) */ - ActiveSpan makeActive(Span span); ActiveSpan makeActive(Span span, ActiveSpan.Observer observer); } diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java index 72b8b6d7..2f2e9c63 100644 --- a/opentracing-api/src/main/java/io/opentracing/Span.java +++ b/opentracing-api/src/main/java/io/opentracing/Span.java @@ -18,6 +18,10 @@ /** * {@link Span} represents the OpenTracing specification's Span contract. * + *

+ * Note that most application code interacts with {@link ActiveSpan} instances (which make themselves available + * for in-process propagation via the {@link ActiveSpanSource} interface). + * * @see ActiveSpan * @see Tracer.SpanBuilder#startManual() * @see Tracer.SpanBuilder#startActive() diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java index 1f1a71c3..a4f1a79e 100644 --- a/opentracing-api/src/main/java/io/opentracing/Tracer.java +++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java @@ -187,6 +187,14 @@ interface SpanBuilder { * @see ActiveSpan */ ActiveSpan startActive(); + + /** + * Like {@link #startActive()}, but with the given {@link ActiveSpan.Observer} keeping track of the returned + * ActiveSpan. + * + * @param observer + * @return + */ ActiveSpan startActive(ActiveSpan.Observer observer); /** From eace002e7e86b3d01ba12d22564ae84694a74a97 Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Mon, 17 Jul 2017 15:17:29 -0700 Subject: [PATCH 05/13] Remove unused interface method --- opentracing-api/src/main/java/io/opentracing/ActiveSpan.java | 2 -- .../main/java/io/opentracing/noop/NoopActiveSpanSource.java | 5 ----- .../main/java/io/opentracing/util/ThreadLocalActiveSpan.java | 5 ----- 3 files changed, 12 deletions(-) diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java index 13a12b9b..2626bf68 100644 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java @@ -84,8 +84,6 @@ public interface ActiveSpan extends Closeable, Span { */ Continuation capture(); - Span wrapped(); - /** * A {@link Continuation} can be used once to activate a Span along with any non-OpenTracing execution * context (e.g., MDC), then deactivate when processing activity moves on to another Span. (In practice, this diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java index 88022d1f..7da2aa8d 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java @@ -57,11 +57,6 @@ public Continuation capture() { return NoopActiveSpanSource.NoopContinuation.INSTANCE; } - @Override - public Span wrapped() { - return NoopSpan.INSTANCE; - } - @Override public SpanContext context() { return NoopSpanContextImpl.INSTANCE; diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java index 96d02c8a..925c3079 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java @@ -63,11 +63,6 @@ public Continuation capture() { return cont; } - @Override - public Span wrapped() { - return wrapped; - } - @Override public SpanContext context() { return wrapped.context(); From 7bddaaaf31ebdb0914a8b099674a2dbdcc65e767 Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 6 Aug 2017 15:20:33 -0700 Subject: [PATCH 06/13] Sanity-checked new API, about to delete old --- .../main/java/io/opentracing/Activator.java | 18 +++ .../src/main/java/io/opentracing/Span.java | 4 + .../src/main/java/io/opentracing/Tracer.java | 3 + .../java/io/opentracing/mock/MockTracer.java | 14 +++ .../io/opentracing/noop/NoopActivator.java | 53 +++++++++ .../java/io/opentracing/noop/NoopSpan.java | 1 + .../java/io/opentracing/noop/NoopTracer.java | 11 ++ opentracing-usecases/pom.xml | 50 +++++++++ .../usecases/AutoFinishActivator.java | 64 +++++++++++ .../usecases/AutoFinishActivatorTest.java | 103 ++++++++++++++++++ .../io/opentracing/util/GlobalTracer.java | 11 ++ .../util/ThreadLocalActivator.java | 43 ++++++++ .../util/ThreadLocalActiveSpan.java | 13 +++ .../util/ThreadLocalActivatorTest.java | 62 +++++++++++ pom.xml | 1 + 15 files changed, 451 insertions(+) create mode 100644 opentracing-api/src/main/java/io/opentracing/Activator.java create mode 100644 opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java create mode 100644 opentracing-usecases/pom.xml create mode 100644 opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java create mode 100644 opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java create mode 100644 opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java create mode 100644 opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java diff --git a/opentracing-api/src/main/java/io/opentracing/Activator.java b/opentracing-api/src/main/java/io/opentracing/Activator.java new file mode 100644 index 00000000..9c1ac3a7 --- /dev/null +++ b/opentracing-api/src/main/java/io/opentracing/Activator.java @@ -0,0 +1,18 @@ +package io.opentracing; + +import java.io.Closeable; + +/** + * Created by bhs on 7/27/17. + */ +public interface Activator { + interface Scope extends Closeable { + @Override + void close(); + + Span span(); + } + Scope activate(Span span); + + Scope activeScope(); +} diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java index 2f2e9c63..6e5eb66d 100644 --- a/opentracing-api/src/main/java/io/opentracing/Span.java +++ b/opentracing-api/src/main/java/io/opentracing/Span.java @@ -164,4 +164,8 @@ public interface Span { * @see Span#context() */ void finish(long finishMicros); + + // XXX: add these back + // Activator defer(); + // Activator.Scope activate(); // shorthand for `defer().activate()` } diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java index a4f1a79e..a3d58d88 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 extends ActiveSpanSource { + Activator activator(); + void setActivator(Activator activator); + /** * 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 23372471..b738c790 100644 --- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java +++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java @@ -13,9 +13,11 @@ */ package io.opentracing.mock; +import io.opentracing.Activator; import io.opentracing.ActiveSpan; import io.opentracing.ActiveSpanSource; import io.opentracing.Span; +import io.opentracing.noop.NoopActivator; import io.opentracing.noop.NoopActiveSpanSource; import io.opentracing.References; import io.opentracing.SpanContext; @@ -40,6 +42,7 @@ public class MockTracer implements Tracer { private List finishedSpans = new ArrayList<>(); private final Propagator propagator; private ActiveSpanSource spanSource; + private Activator activator; public MockTracer() { this(Propagator.PRINTER); @@ -52,6 +55,7 @@ public MockTracer(ActiveSpanSource spanSource) { public MockTracer(ActiveSpanSource spanSource, Propagator propagator) { this.propagator = propagator; this.spanSource = spanSource; + this.activator = NoopActivator.INSTANCE; } /** @@ -176,6 +180,16 @@ public MockSpan.MockContext extract(Format format, C carrier) { }; } + @Override + public Activator activator() { + return this.activator; + } + + @Override + public void setActivator(Activator activator) { + this.activator = activator; + } + @Override public SpanBuilder buildSpan(String operationName) { return new SpanBuilder(operationName); diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java new file mode 100644 index 00000000..5f8d00f6 --- /dev/null +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java @@ -0,0 +1,53 @@ +/* + * 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.noop; + +import io.opentracing.Activator; +import io.opentracing.Span; +import io.opentracing.SpanContext; + +import java.util.Map; + +/** + * Created by bhs on 8/5/17. + */ +public interface NoopActivator extends Activator { + NoopActivator INSTANCE = new NoopActivatorImpl(); + + interface NoopScope extends Scope { + NoopScope INSTANCE = new NoopActivatorImpl.NoopScopeImpl(); + } +} + +class NoopActivatorImpl implements NoopActivator { + @Override + public Scope activate(Span span) { + return null; + } + + @Override + public Scope activeScope() { + return null; + } + + static class NoopScopeImpl implements NoopActivator.NoopScope { + @Override + public void close() {} + + @Override + public Span span() { + return NoopSpan.INSTANCE; + } + } +} diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java index 7d503066..ae4f4497 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java @@ -13,6 +13,7 @@ */ package io.opentracing.noop; +import io.opentracing.Activator; import io.opentracing.Span; import io.opentracing.SpanContext; 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 26855f9a..4d949bdd 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java @@ -13,6 +13,7 @@ */ package io.opentracing.noop; +import io.opentracing.Activator; import io.opentracing.ActiveSpan; import io.opentracing.Span; import io.opentracing.SpanContext; @@ -25,6 +26,16 @@ public interface NoopTracer extends Tracer { final class NoopTracerImpl implements NoopTracer { final static NoopTracer INSTANCE = new NoopTracerImpl(); + @Override + public Activator activator() { + return null; + } + + @Override + public void setActivator(Activator activator) { + + } + @Override public SpanBuilder buildSpan(String operationName) { return NoopSpanBuilderImpl.INSTANCE; } diff --git a/opentracing-usecases/pom.xml b/opentracing-usecases/pom.xml new file mode 100644 index 00000000..8faccb33 --- /dev/null +++ b/opentracing-usecases/pom.xml @@ -0,0 +1,50 @@ + + + +4.0.0 + + + io.opentracing + parent + 0.30.1-SNAPSHOT + + +opentracing-usecases +OpenTracing-UseCases +OpenTracing use cases for new API/feature development + + + ${project.basedir}/.. + 1.8 + + + + + ${project.groupId} + opentracing-api + + + ${project.groupId} + opentracing-util + + + ${project.groupId} + opentracing-mock + test + + + diff --git a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java new file mode 100644 index 00000000..5eaf0cbd --- /dev/null +++ b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java @@ -0,0 +1,64 @@ +package io.opentracing.usecases; + +import io.opentracing.Activator; +import io.opentracing.Span; +import io.opentracing.Tracer; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by bhs on 8/5/17. + */ +public class AutoFinishActivator implements Activator { + final ThreadLocal tlsScope = new ThreadLocal(); + + @Override + public AutoFinishScope activate(Span span) { + return new AutoFinishScope(new AtomicInteger(1), span); + } + + @Override + public AutoFinishScope activeScope() { + return tlsScope.get(); + } + + public class AutoFinishScope implements Scope { + final AtomicInteger refCount; + private final Span wrapped; + private final AutoFinishScope toRestore; + + AutoFinishScope(AtomicInteger refCount, Span wrapped) { + this.refCount = refCount; + this.wrapped = wrapped; + this.toRestore = AutoFinishActivator.this.tlsScope.get(); + tlsScope.set(this); + } + + public class Continuation { + public Continuation() { + refCount.incrementAndGet(); + } + + public AutoFinishScope activate() { + return new AutoFinishScope(refCount, wrapped); + } + } + + public Continuation defer() { + return new Continuation(); + } + + @Override + public void close() { + if (refCount.decrementAndGet() == 0) { + wrapped.finish(); + } + tlsScope.set(toRestore); + } + + @Override + public Span span() { + return wrapped; + } + } +} diff --git a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java new file mode 100644 index 00000000..dac7fd85 --- /dev/null +++ b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java @@ -0,0 +1,103 @@ +/* + * 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.usecases; + +import io.opentracing.Activator; +import io.opentracing.Span; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class AutoFinishActivatorTest { + private AutoFinishActivator autoFinishActivator; + + @Before + public void before() throws Exception { + autoFinishActivator = new AutoFinishActivator(); + } + + @Test + public void missingActiveSpan() throws Exception { + Activator.Scope missingScope = autoFinishActivator.activeScope(); + Assert.assertNull(missingScope); + } + + @Test + public void makeActiveSpan() throws Exception { + Span span = Mockito.mock(Span.class); + + // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests. + AutoFinishActivator.AutoFinishScope scope = autoFinishActivator.activate(span); + try { + Assert.assertNotNull(scope); + Activator.Scope otherScope = autoFinishActivator.activeScope(); + Assert.assertEquals(otherScope, scope); + } finally { + scope.close(); + } + + // Make sure the Span got finish()ed. + Mockito.verify(span).finish(); + + // And now it's gone: + Activator.Scope missingScope = autoFinishActivator.activeScope(); + Assert.assertNull(missingScope); + } + + @Test + public void deferring() throws Exception { + Span span = Mockito.mock(Span.class); + + AutoFinishActivator.AutoFinishScope.Continuation continuation = null; + { + // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests. + AutoFinishActivator.AutoFinishScope scope = autoFinishActivator.activate(span); + + // Take a reference... + continuation = scope.defer(); + try { + Assert.assertNotNull(scope); + Activator.Scope otherScope = autoFinishActivator.activeScope(); + Assert.assertEquals(otherScope, scope); + } finally { + scope.close(); + } + } + + // Make sure the Span has not been finish()ed yet. + Mockito.verify(span, Mockito.never()).finish(); + + // Activate the Continuation and close that second reference. + AutoFinishActivator.AutoFinishScope reactivated = continuation.activate(); + try { + // Nothing to do. + } finally { + reactivated.close(); + } + + // Make sure the Span got finish()ed this time. + Mockito.verify(span).finish(); + + // And now it's gone: + Activator.Scope missingScope = autoFinishActivator.activeScope(); + Assert.assertNull(missingScope); + } + +} diff --git a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java index 1000b9ef..4a76f841 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java +++ b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java @@ -13,6 +13,7 @@ */ package io.opentracing.util; +import io.opentracing.Activator; import io.opentracing.ActiveSpan; import io.opentracing.Span; import io.opentracing.noop.NoopTracer; @@ -123,6 +124,16 @@ public static synchronized boolean isRegistered() { return !(GlobalTracer.tracer instanceof NoopTracer); } + @Override + public Activator activator() { + return tracer.activator(); + } + + @Override + public void setActivator(Activator activator) { + tracer.setActivator(activator); + } + @Override public SpanBuilder buildSpan(String operationName) { return tracer.buildSpan(operationName); diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java new file mode 100644 index 00000000..09e63f01 --- /dev/null +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java @@ -0,0 +1,43 @@ +package io.opentracing.util; + +import io.opentracing.Activator; +import io.opentracing.ActiveSpan; +import io.opentracing.Span; + +/** + * Created by bhs on 8/1/17. + */ +public class ThreadLocalActivator implements Activator { + final ThreadLocal tlsScope = new ThreadLocal(); + + public class ThreadLocalScope implements Scope { + private final Span wrapped; + private final ThreadLocalScope toRestore; + + ThreadLocalScope(Span wrapped) { + this.wrapped = wrapped; + this.toRestore = ThreadLocalActivator.this.tlsScope.get(); + tlsScope.set(this); + } + + @Override + public void close() { + tlsScope.set(toRestore); + } + + @Override + public Span span() { + return wrapped; + } + } + + @Override + public Scope activate(Span span) { + return new ThreadLocalScope(span); + } + + @Override + public Scope activeScope() { + return tlsScope.get(); + } +} diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java index 925c3079..f203931d 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java @@ -33,6 +33,19 @@ public class ThreadLocalActiveSpan implements ActiveSpan { private final Span wrapped; private final ThreadLocalActiveSpan toRestore; private final Observer observer; + final ThreadLocal tlsSnapshot = new ThreadLocal(); + + // @Override + public ThreadLocalActiveSpan activeSpan() { + return tlsSnapshot.get(); + } + + /* + // @Override + public ActiveSpan makeActive(Span span) { + return new ThreadLocalActiveSpan(this, span, new AutoFinisher()); + } + */ ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped, Observer observer) { this.source = source; diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java new file mode 100644 index 00000000..99d7bf38 --- /dev/null +++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java @@ -0,0 +1,62 @@ +/* + * 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.util; + +import io.opentracing.Activator; +import io.opentracing.Span; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; + +public class ThreadLocalActivatorTest { + private ThreadLocalActivator source; + + @Before + public void before() throws Exception { + source = new ThreadLocalActivator(); + } + + @Test + public void missingActiveSpan() throws Exception { + Activator.Scope missingScope = source.activeScope(); + assertNull(missingScope); + } + + @Test + public void makeActiveSpan() throws Exception { + Span span = mock(Span.class); + + // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests. + Activator.Scope scope = source.activate(span); + try { + assertNotNull(scope); + Activator.Scope otherScope = source.activeScope(); + assertEquals(otherScope, scope); + } finally { + scope.close(); + } + + // Make sure the Span got finish()ed. + verify(span, never()).finish(); + + // And now it's gone: + Activator.Scope missingScope = source.activeScope(); + assertNull(missingScope); + } + +} diff --git a/pom.xml b/pom.xml index a2e5e1af..e68a7c0a 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,7 @@ opentracing-noop opentracing-mock opentracing-util + opentracing-usecases From ea1fb56792b67827fd44ee634791f0221948a6fe Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 6 Aug 2017 15:49:11 -0700 Subject: [PATCH 07/13] Deleted enough to make the tests pass again --- .../main/java/io/opentracing/ActiveSpan.java | 123 ------------- .../java/io/opentracing/ActiveSpanSource.java | 55 ------ .../src/main/java/io/opentracing/Tracer.java | 21 +-- .../io/opentracing/tag/AbstractTagTest.java | 14 -- .../java/io/opentracing/mock/MockTracer.java | 61 +----- .../noop/NoopActiveSpanSource.java | 131 ------------- .../io/opentracing/noop/NoopSpanBuilder.java | 15 +- .../java/io/opentracing/noop/NoopTracer.java | 16 -- .../io/opentracing/util/AutoFinisher.java | 53 ------ .../io/opentracing/util/GlobalTracer.java | 16 -- .../util/ThreadLocalActivator.java | 1 - .../util/ThreadLocalActiveSpan.java | 174 ------------------ .../util/ThreadLocalActiveSpanSource.java | 45 ----- .../util/ThreadLocalActiveSpanSourceTest.java | 60 ------ .../util/ThreadLocalActiveSpanTest.java | 114 ------------ 15 files changed, 21 insertions(+), 878 deletions(-) delete mode 100644 opentracing-api/src/main/java/io/opentracing/ActiveSpan.java delete mode 100644 opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java delete mode 100644 opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java delete mode 100644 opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java delete mode 100644 opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java delete mode 100644 opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java delete mode 100644 opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java delete mode 100644 opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java deleted file mode 100644 index 2626bf68..00000000 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * 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.io.Closeable; - -/** - * {@link ActiveSpan} inherits all of the OpenTracing functionality in {@link Span} and layers on in-process - * propagation capabilities. - * - *

- * In any given thread there is at most one {@link ActiveSpan "active" span} primarily responsible for the work - * accomplished by the surrounding application code. That {@link ActiveSpan} may be accessed via the - * {@link ActiveSpanSource#activeSpan()} method. If the application needs to defer work that should be part of - * the same Span, the Source provides a {@link ActiveSpan#capture} method that returns a {@link Continuation}; - * this continuation may be used to re-activate and continue the {@link Span} in that other asynchronous executor - * and/or thread. - * - *

- * {@link ActiveSpan}s are created via {@link Tracer.SpanBuilder#startActive()} or, less commonly, - * {@link ActiveSpanSource#makeActive}. Per the above, they can be {@link ActiveSpan#capture()}ed as - * {@link ActiveSpan.Continuation}s, then re-{@link Continuation#activate()}d later. - * - *

- * Propagated {@link ActiveSpan} instances form a tree within a process, where each node is an {@link ActiveSpan} and - * each edge is a {@link ActiveSpan.Continuation}. The {@link ActiveSpan.Observer} interface provides visibility into - * the node/edge creations as well as {@link ActiveSpan} activation/deactivation lifecycles. - * - *

- * NOTE: {@link ActiveSpan} extends {@link Closeable} rather than {@code AutoCloseable} in order to preserve support - * for JDK1.6. - * - * @see Tracer.SpanBuilder#startActive() - * @see Continuation#activate() - * @see ActiveSpanSource - * @see Span - * @see Span - */ -public interface ActiveSpan extends Closeable, Span { - /** - * Mark the end of the active period for the current thread and {@link ActiveSpan}. When the last - * {@link ActiveSpan} is deactivated for a given {@link Span}, it is automatically {@link Span#finish()}ed. - * - *

- * NOTE: Calling {@link #deactivate} more than once on a single {@link ActiveSpan} instance leads to undefined - * behavior. - * - * @see Closeable#close() {@link ActiveSpan}s are auto-closeable and may be used in try-with-resources blocks - */ - void deactivate(); - - /** - * A synonym for {@link #deactivate()} that can be used in try-with-resources blocks. - */ - @Override - void close(); - - /** - * "Capture" a new {@link Continuation} associated with this {@link ActiveSpan} and {@link Span}, as well as any - * 3rd-party execution context of interest. The {@link Continuation} may be used as data in a closure or callback - * function where the {@link ActiveSpan} may be resumed and reactivated. - * - *

- * IMPORTANT: the caller MUST {@link Continuation#activate()} and {@link ActiveSpan#deactivate()} the - * returned {@link Continuation} or the associated {@link Span} will never automatically {@link Span#finish()}. - * That is, calling {@link #capture()} increments a refcount that must be decremented somewhere else. - * - *

- * The associated {@link Span} will not {@link Span#finish()} while a {@link Continuation} is outstanding; in - * this way, it provides a reference/pin just like an {@link ActiveSpan} does. - * - * @return a new {@link Continuation} to {@link Continuation#activate()} at the appropriate time. - */ - Continuation capture(); - - /** - * A {@link Continuation} can be used once to activate a Span along with any non-OpenTracing execution - * context (e.g., MDC), then deactivate when processing activity moves on to another Span. (In practice, this - * active period typically extends for the length of a deferred async closure invocation.) - * - *

- * Most users do not directly interact with {@link Continuation}, {@link Continuation#activate()} or - * {@link ActiveSpan#deactivate()}, but rather use {@link ActiveSpanSource}-aware Runnables/Callables/Executors. - * Those higher-level primitives do not need to be defined within the OpenTracing core API, and so - * they are not. - * - * @see ActiveSpanSource#makeActive(Span) - */ - interface Continuation { - /** - * Make the Span (and other execution context) encapsulated by this {@link Continuation} active and - * return it. - * - *

- * NOTE: It is an error to call activate() more than once on a single Continuation instance. - * - * @see ActiveSpanSource#makeActive(Span) - * @return a handle to the newly-activated {@link ActiveSpan} - */ - ActiveSpan activate(); - } - - /** - * The {@link ActiveSpan.Observer} interface learns of capture, activate, and deactivate calls for a given - * {@link ActiveSpan} instance and any of its "descendants" from a capture/activate standpoint. - */ - interface Observer { - void onCapture(ActiveSpan captured, Continuation destination); - void onActivate(Continuation source, ActiveSpan justActivated); - void onDeactivate(ActiveSpan activeSpan); - } -} diff --git a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java b/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java deleted file mode 100644 index 4a1c6f5b..00000000 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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; - -/** - * {@link ActiveSpanSource} allows an existing (possibly thread-local-aware) execution context provider to act as a - * source for an actively-scheduled OpenTracing Span. - * - *

- * {@link ActiveSpanSource} is a super-interface to {@link Tracer}, so note that all {@link Tracer}s fulfill the - * {@link ActiveSpanSource} contract. - * - * @see ActiveSpan - */ -public interface ActiveSpanSource { - - /** - * Return the {@link ActiveSpan active span}. - * - *

- * If there is an {@link ActiveSpan active span}, it becomes an implicit parent of any newly-created - * {@link Span span} at {@link Tracer.SpanBuilder#startActive()} time (rather than at - * {@link Tracer#buildSpan(String)} time). - * - * @return the {@link ActiveSpan active span}, or null if none could be found. - */ - ActiveSpan activeSpan(); - - /** - * @see #makeActive(Span, ActiveSpan.Observer) - */ - ActiveSpan makeActive(Span span); - /** - * Wrap and "make active" a {@link Span} by encapsulating it – and any active state (e.g., MDC state) in the - * current thread – in a new {@link ActiveSpan}. - * - * @param span the Span to wrap in an {@link ActiveSpan} - * @param observer the observer that learns of activations, deactivations, and captures related to the returned - * {@link ActiveSpan}. May be null (which implies "no observer") - * @return an {@link ActiveSpan} that encapsulates the given {@link Span} and any other - * {@link ActiveSpanSource}-specific context (e.g., the MDC context map) - */ - ActiveSpan makeActive(Span span, ActiveSpan.Observer observer); -} diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java index a3d58d88..3b92729c 100644 --- a/opentracing-api/src/main/java/io/opentracing/Tracer.java +++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java @@ -18,7 +18,7 @@ /** * Tracer is a simple, thin interface for Span creation and propagation across arbitrary transports. */ -public interface Tracer extends ActiveSpanSource { +public interface Tracer { Activator activator(); void setActivator(Activator activator); @@ -158,15 +158,15 @@ interface SpanBuilder { SpanBuilder withStartTimestamp(long microseconds); /** - * Returns a newly started and activated {@link ActiveSpan}. + * Returns a newly started and activated {@link Activator.Scope}. * *

- * The returned {@link ActiveSpan} supports try-with-resources. For example: + * The returned {@link Activator.Scope} supports try-with-resources. For example: *


-         *     try (ActiveSpan span = tracer.buildSpan("...").startActive()) {
+         *     try (Activator.Scope span = tracer.buildSpan("...").startActive()) {
          *         // (Do work)
          *         span.setTag( ... );  // etc, etc
-         *     }  // Span finishes automatically unless deferred via {@link ActiveSpan#capture}
+         *     }  // XXX Span finishes automatically unless deferred via {@link ActiveSpan#capture}
          * 
* *

@@ -189,16 +189,7 @@ interface SpanBuilder { * @see ActiveSpanSource * @see ActiveSpan */ - ActiveSpan startActive(); - - /** - * Like {@link #startActive()}, but with the given {@link ActiveSpan.Observer} keeping track of the returned - * ActiveSpan. - * - * @param observer - * @return - */ - ActiveSpan startActive(ActiveSpan.Observer observer); + Activator.Scope startActive(); /** * Like {@link #startActive()}, but the returned {@link Span} has not been registered via the diff --git a/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java b/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java index 400846df..f327851e 100644 --- a/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java +++ b/opentracing-api/src/test/java/io/opentracing/tag/AbstractTagTest.java @@ -20,8 +20,6 @@ import io.opentracing.Span; import org.junit.Test; -import io.opentracing.ActiveSpan; - /** * @author Pavol Loffay */ @@ -45,16 +43,4 @@ public void testSetTagOnSpan() { verify(activeSpan).setTag(key, value); } - - @Test - public void testSetTagOnActiveSpan() { - String value = "foo"; - String key = "bar"; - - ActiveSpan activeSpan = mock(ActiveSpan.class); - StringTag tag = new StringTag(key); - tag.set(activeSpan, value); - - verify(activeSpan).setTag(key, value); - } } 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 b738c790..9519b043 100644 --- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java +++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java @@ -14,11 +14,8 @@ package io.opentracing.mock; import io.opentracing.Activator; -import io.opentracing.ActiveSpan; -import io.opentracing.ActiveSpanSource; import io.opentracing.Span; import io.opentracing.noop.NoopActivator; -import io.opentracing.noop.NoopActiveSpanSource; import io.opentracing.References; import io.opentracing.SpanContext; import io.opentracing.Tracer; @@ -41,28 +38,18 @@ public class MockTracer implements Tracer { private List finishedSpans = new ArrayList<>(); private final Propagator propagator; - private ActiveSpanSource spanSource; private Activator activator; public MockTracer() { this(Propagator.PRINTER); } - public MockTracer(ActiveSpanSource spanSource) { - this(spanSource, Propagator.PRINTER); - } - - public MockTracer(ActiveSpanSource spanSource, Propagator propagator) { - this.propagator = propagator; - this.spanSource = spanSource; - this.activator = NoopActivator.INSTANCE; - } - /** * Create a new MockTracer that passes through any calls to inject() and/or extract(). */ public MockTracer(Propagator propagator) { - this(NoopActiveSpanSource.INSTANCE, propagator); + this.propagator = propagator; + this.activator = NoopActivator.INSTANCE; } /** @@ -91,21 +78,6 @@ public synchronized List finishedSpans() { protected void onSpanFinished(MockSpan mockSpan) { } - @Override - public ActiveSpan activeSpan() { - return spanSource.activeSpan(); - } - - @Override - public ActiveSpan makeActive(Span span) { - return spanSource.makeActive(span); - } - - @Override - public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { - return spanSource.makeActive(span, observer); - } - /** * Propagator allows the developer to intercept and verify any calls to inject() and/or extract(). * @@ -195,15 +167,6 @@ public SpanBuilder buildSpan(String operationName) { return new SpanBuilder(operationName); } - private SpanContext activeSpanContext() { - ActiveSpan handle = this.spanSource.activeSpan(); - if (handle == null) { - return null; - } - - return handle.context(); - } - @Override public void inject(SpanContext spanContext, Format format, C carrier) { this.propagator.inject((MockSpan.MockContext)spanContext, format, carrier); @@ -280,20 +243,13 @@ public SpanBuilder withStartTimestamp(long microseconds) { } @Override - public MockSpan start() { - return startManual(); - } - - @Override - public ActiveSpan startActive() { - MockSpan span = this.startManual(); - return spanSource.makeActive(span); + public Activator.Scope startActive() { + return MockTracer.this.activator().activate(this.startManual()); } @Override - public ActiveSpan startActive(ActiveSpan.Observer observer) { - MockSpan span = this.startManual(); - return spanSource.makeActive(span, observer); + public MockSpan start() { + return startManual(); } @Override @@ -302,7 +258,10 @@ public MockSpan startManual() { this.startMicros = MockSpan.nowMicros(); } if (firstParent == null && !ignoringActiveSpan) { - firstParent = (MockSpan.MockContext) activeSpanContext(); + Activator.Scope activeScope = activator().activeScope(); + if (activeScope != null) { + firstParent = (MockSpan.MockContext) activeScope.span().context(); + } } return new MockSpan(MockTracer.this, operationName, startMicros, initialTags, firstParent); } diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java deleted file mode 100644 index 7da2aa8d..00000000 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * 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.noop; - -import io.opentracing.ActiveSpan; -import io.opentracing.ActiveSpanSource; -import io.opentracing.Span; -import io.opentracing.SpanContext; - -import java.util.Map; - -public interface NoopActiveSpanSource extends ActiveSpanSource { - NoopActiveSpanSource INSTANCE = new NoopActiveSpanSourceImpl(); - - interface NoopActiveSpan extends ActiveSpan { - NoopActiveSpanSource.NoopActiveSpan INSTANCE = new NoopActiveSpanSourceImpl.NoopActiveSpanImpl(); - } - interface NoopContinuation extends ActiveSpan.Continuation { - NoopActiveSpanSource.NoopContinuation INSTANCE = new NoopActiveSpanSourceImpl.NoopContinuationImpl(); - } -} - -/** - * A noop (i.e., cheap-as-possible) implementation of an ActiveSpanSource. - */ -class NoopActiveSpanSourceImpl implements NoopActiveSpanSource { - @Override - public ActiveSpan makeActive(Span span) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public ActiveSpan activeSpan() { return null; } - - static class NoopActiveSpanImpl implements NoopActiveSpanSource.NoopActiveSpan { - @Override - public void deactivate() {} - - @Override - public Continuation capture() { - return NoopActiveSpanSource.NoopContinuation.INSTANCE; - } - - @Override - public SpanContext context() { - return NoopSpanContextImpl.INSTANCE; - } - - @Override - public void close() {} - - @Override - public NoopActiveSpan setTag(String key, String value) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan setTag(String key, boolean value) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan setTag(String key, Number value) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan log(Map fields) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan log(long timestampMicroseconds, Map fields) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan log(String event) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan log(long timestampMicroseconds, String event) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public NoopActiveSpan setBaggageItem(String key, String value) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public String getBaggageItem(String key) { - return null; - } - - @Override - public NoopActiveSpan setOperationName(String operationName) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public void finish() { } - - @Override - public void finish(long finishMicros) { } - } - - static class NoopContinuationImpl implements NoopActiveSpanSource.NoopContinuation { - @Override - public ActiveSpan activate() { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - } -} diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java index 4ebf7209..d71770c7 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java @@ -13,7 +13,7 @@ */ package io.opentracing.noop; -import io.opentracing.ActiveSpan; +import io.opentracing.Activator; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; @@ -66,18 +66,13 @@ public Tracer.SpanBuilder withStartTimestamp(long microseconds) { } @Override - public Span start() { - return startManual(); + public Activator.Scope startActive() { + return NoopActivator.NoopScope.INSTANCE; } @Override - public ActiveSpan startActive() { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public ActiveSpan startActive(ActiveSpan.Observer observer) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; + public Span start() { + return startManual(); } @Override 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 4d949bdd..a49faddd 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java @@ -14,7 +14,6 @@ package io.opentracing.noop; import io.opentracing.Activator; -import io.opentracing.ActiveSpan; import io.opentracing.Span; import io.opentracing.SpanContext; import io.opentracing.Tracer; @@ -47,20 +46,5 @@ public void inject(SpanContext spanContext, Format format, C carrier) {} @Override public String toString() { return NoopTracer.class.getSimpleName(); } - - @Override - public ActiveSpan activeSpan() { - return null; - } - - @Override - public ActiveSpan makeActive(Span span) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } - - @Override - public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; - } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java b/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java deleted file mode 100644 index 8b0f1d9e..00000000 --- a/opentracing-util/src/main/java/io/opentracing/util/AutoFinisher.java +++ /dev/null @@ -1,53 +0,0 @@ -package io.opentracing.util; - -import io.opentracing.ActiveSpan; -import io.opentracing.Span; -import io.opentracing.SpanContext; - -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * XXX: fix comment - * {@link AutoFinisher} is an {@link ActiveSpan} wrapper that automatically {@link Span#finish()}es the underlying - * {@link Span} when there are zero remaining {@link ActiveSpan}s or {@link ActiveSpan.Continuation}s referencing - * that underlying {@link Span}. - * - *

- * Use {@link AutoFinisher} like this: - *


- *     try (ActiveSpan span = new AutoFinisher(tracer.buildSpan("...").startActive())) {
- *         // (Do work, even if deferred via {@link ActiveSpan#capture()})
- *         span.setTag( ... );  // etc, etc
- *     }  // Span finish()es automatically when there are no longer any ActiveSpans or Continuations referring to it
- * 
- * - *

- * Note that {@link AutoFinisher} works by counting the number of extant {@link ActiveSpan} or - * {@link ActiveSpan.Continuation} references to the underlying {@link ActiveSpan} provided at construction time. - *

- */ -public class AutoFinisher implements ActiveSpan.Observer { - private final AtomicInteger refCount; - - public AutoFinisher() { - refCount = new AtomicInteger(1); - } - - @Override - public void onCapture(ActiveSpan captured, ActiveSpan.Continuation destination) { - // Always increment the reference count when new Continuations are created (i.e., we assume that all - // Continuations are eventually activate()d). - refCount.incrementAndGet(); - } - - @Override - public void onActivate(ActiveSpan.Continuation source, ActiveSpan justActivated) {} - - @Override - public void onDeactivate(ActiveSpan activeSpan) { - if (0 == refCount.decrementAndGet()) { - activeSpan.finish(); - } - } -} diff --git a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java index 4a76f841..42f782a9 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java +++ b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java @@ -14,7 +14,6 @@ package io.opentracing.util; import io.opentracing.Activator; -import io.opentracing.ActiveSpan; import io.opentracing.Span; import io.opentracing.noop.NoopTracer; import io.opentracing.noop.NoopTracerFactory; @@ -153,19 +152,4 @@ public SpanContext extract(Format format, C carrier) { public String toString() { return GlobalTracer.class.getSimpleName() + '{' + tracer + '}'; } - - @Override - public ActiveSpan activeSpan() { - return tracer.activeSpan(); - } - - @Override - public ActiveSpan makeActive(Span span) { - return tracer.makeActive(span); - } - - @Override - public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { - return tracer.makeActive(span, observer); - } } diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java index 09e63f01..de833868 100644 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java @@ -1,7 +1,6 @@ package io.opentracing.util; import io.opentracing.Activator; -import io.opentracing.ActiveSpan; import io.opentracing.Span; /** diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java deleted file mode 100644 index f203931d..00000000 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * 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.util; - -import java.util.Map; - -import io.opentracing.ActiveSpan; -import io.opentracing.ActiveSpanSource; -import io.opentracing.Span; -import io.opentracing.SpanContext; -import io.opentracing.Tracer; - -/** - * {@link ThreadLocalActiveSpan} is a simple {@link ActiveSpan} implementation that relies on Java's - * thread-local storage primitive. - * - * @see ActiveSpanSource - * @see Tracer#activeSpan() - */ -public class ThreadLocalActiveSpan implements ActiveSpan { - private final ThreadLocalActiveSpanSource source; - private final Span wrapped; - private final ThreadLocalActiveSpan toRestore; - private final Observer observer; - final ThreadLocal tlsSnapshot = new ThreadLocal(); - - // @Override - public ThreadLocalActiveSpan activeSpan() { - return tlsSnapshot.get(); - } - - /* - // @Override - public ActiveSpan makeActive(Span span) { - return new ThreadLocalActiveSpan(this, span, new AutoFinisher()); - } - */ - - ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped, Observer observer) { - this.source = source; - this.wrapped = wrapped; - this.toRestore = source.tlsSnapshot.get(); - this.observer = observer; - source.tlsSnapshot.set(this); - } - - @Override - public void deactivate() { - if (source.tlsSnapshot.get() != this) { - // This shouldn't happen if users call methods in the expected order. Bail out. - return; - } - source.tlsSnapshot.set(toRestore); - if (observer != null) { - observer.onDeactivate(this); - } - } - - @Override - public Continuation capture() { - Continuation cont = new ThreadLocalActiveSpan.Continuation(); - if (observer != null) { - observer.onCapture(ThreadLocalActiveSpan.this, cont); - } - return cont; - } - - @Override - public SpanContext context() { - return wrapped.context(); - } - - @Override - public ThreadLocalActiveSpan setTag(String key, String value) { - wrapped.setTag(key, value); - return this; - } - - @Override - public ThreadLocalActiveSpan setTag(String key, boolean value) { - wrapped.setTag(key, value); - return this; - } - - @Override - public ThreadLocalActiveSpan setTag(String key, Number value) { - wrapped.setTag(key, value); - return this; - } - - @Override - public ThreadLocalActiveSpan log(Map fields) { - wrapped.log(fields); - return this; - } - - @Override - public ThreadLocalActiveSpan log(long timestampMicroseconds, Map fields) { - wrapped.log(timestampMicroseconds, fields); - return this; - } - - @Override - public ThreadLocalActiveSpan log(String event) { - wrapped.log(event); - return this; - } - - @Override - public ThreadLocalActiveSpan log(long timestampMicroseconds, String event) { - wrapped.log(timestampMicroseconds, event); - return this; - } - - @Override - public ThreadLocalActiveSpan setBaggageItem(String key, String value) { - wrapped.setBaggageItem(key, value); - return this; - } - - @Override - public String getBaggageItem(String key) { - return wrapped.getBaggageItem(key); - } - - @Override - public ThreadLocalActiveSpan setOperationName(String operationName) { - wrapped.setOperationName(operationName); - return this; - } - - @Override - public void finish() { - wrapped.finish(); - } - - @Override - public void finish(long finishMicros) { - wrapped.finish(finishMicros); - } - - @Override - public void close() { - deactivate(); - } - - @Override - public String toString() { - return wrapped.toString(); - } - - private final class Continuation implements ActiveSpan.Continuation { - @Override - public ThreadLocalActiveSpan activate() { - ThreadLocalActiveSpan rval = new ThreadLocalActiveSpan(source, wrapped, observer); - if (observer != null) { - observer.onActivate(this, rval); - } - return rval; - } - } - -} diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java deleted file mode 100644 index 3140970d..00000000 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * 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.util; - -import io.opentracing.ActiveSpan; -import io.opentracing.ActiveSpanSource; -import io.opentracing.Span; -import io.opentracing.Tracer; - -/** - * A simple {@link ActiveSpanSource} implementation built on top of Java's thread-local storage primitive. - * - * @see ThreadLocalActiveSpan - * @see Tracer#activeSpan() - */ -public class ThreadLocalActiveSpanSource implements ActiveSpanSource { - final ThreadLocal tlsSnapshot = new ThreadLocal(); - - @Override - public ThreadLocalActiveSpan activeSpan() { - return tlsSnapshot.get(); - } - - @Override - public ActiveSpan makeActive(Span span) { - return new ThreadLocalActiveSpan(this, span, new AutoFinisher()); - } - - @Override - public ActiveSpan makeActive(Span span, ActiveSpan.Observer observer) { - return new ThreadLocalActiveSpan(this, span, observer); - } - -} diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java deleted file mode 100644 index 180662ee..00000000 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.util; - -import io.opentracing.ActiveSpan; -import io.opentracing.Span; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -public class ThreadLocalActiveSpanSourceTest { - private ThreadLocalActiveSpanSource source; - @Before - public void before() throws Exception { - source = new ThreadLocalActiveSpanSource(); - } - - @Test - public void missingActiveSpan() throws Exception { - ActiveSpan missingSpan = source.activeSpan(); - assertNull(missingSpan); - } - - @Test - public void makeActiveSpan() throws Exception { - Span span = mock(Span.class); - - // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests. - ActiveSpan activeSpan = source.makeActive(span); - try { - assertNotNull(activeSpan); - ActiveSpan otherActiveSpan = source.activeSpan(); - assertEquals(otherActiveSpan, activeSpan); - } finally { - activeSpan.close(); - } - - // Make sure the Span got finish()ed. - verify(span).finish(); - - // And now it's gone: - ActiveSpan missingSpan = source.activeSpan(); - assertNull(missingSpan); - } - -} \ No newline at end of file diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java deleted file mode 100644 index 06d49093..00000000 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * 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.util; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import io.opentracing.ActiveSpan; -import io.opentracing.Span; -import org.junit.Before; -import org.junit.Test; - -public class ThreadLocalActiveSpanTest { - private ThreadLocalActiveSpanSource source; - - @Before - public void before() throws Exception { - source = new ThreadLocalActiveSpanSource(); - } - - @Test - public void continuation() throws Exception { - Span span = mock(Span.class); - - // Quasi try-with-resources (this is 1.6). - ActiveSpan activeSpan = source.makeActive(span); - ActiveSpan.Continuation continued = null; - try { - assertNotNull(activeSpan); - continued = activeSpan.capture(); - } finally { - activeSpan.close(); - } - - // Make sure the Span was not finished since there was a capture(). - verify(span, never()).finish(); - - // Activate the continuation. - try { - activeSpan = continued.activate(); - } finally { - activeSpan.close(); - } - - // Now the Span should be finished. - verify(span, times(1)).finish(); - - // And now it's no longer active. - ActiveSpan missingSpan = source.activeSpan(); - assertNull(missingSpan); - } - - @Test - public void implicitSpanStack() throws Exception { - Span backgroundSpan = mock(Span.class); - Span foregroundSpan = mock(Span.class); - - // Quasi try-with-resources (this is 1.6). - ActiveSpan backgroundActive = source.makeActive(backgroundSpan); - try { - assertNotNull(backgroundActive); - - // Activate a new ActiveSpan on top of the background one. - ActiveSpan foregroundActive = source.makeActive(foregroundSpan); - try { - ActiveSpan shouldBeForeground = source.activeSpan(); - assertEquals(foregroundActive, shouldBeForeground); - } finally { - foregroundActive.close(); - } - - // And now the backgroundActive should be reinstated. - ActiveSpan shouldBeBackground = source.activeSpan(); - assertEquals(backgroundActive, shouldBeBackground); - } finally { - backgroundActive.close(); - } - - // The background and foreground Spans should be finished. - verify(backgroundSpan, times(1)).finish(); - verify(foregroundSpan, times(1)).finish(); - - // And now nothing is active. - ActiveSpan missingSpan = source.activeSpan(); - assertNull(missingSpan); - } - - @Test - public void testDeactivateWhenDifferentSpanIsActive() { - Span span = mock(Span.class); - - ActiveSpan activeSpan = source.makeActive(span); - source.makeActive(mock(Span.class)); - activeSpan.deactivate(); - - verify(span, times(0)).finish(); - } -} From 397bd0753b000346a6e3cb9300717a523ae64c0f Mon Sep 17 00:00:00 2001 From: Ben Sigelman Date: Sun, 6 Aug 2017 16:32:40 -0700 Subject: [PATCH 08/13] Added some comments --- .../main/java/io/opentracing/Activator.java | 18 ------------ .../src/main/java/io/opentracing/Scope.java | 25 +++++++++++++++++ .../java/io/opentracing/ScopeManager.java | 15 ++++++++++ .../src/main/java/io/opentracing/Span.java | 11 ++------ .../src/main/java/io/opentracing/Tracer.java | 12 ++++---- .../java/io/opentracing/mock/MockTracer.java | 24 ++++++++-------- ...opActivator.java => NoopScopeManager.java} | 16 +++++------ .../java/io/opentracing/noop/NoopSpan.java | 1 - .../io/opentracing/noop/NoopSpanBuilder.java | 6 ++-- .../java/io/opentracing/noop/NoopTracer.java | 8 ++---- ...vator.java => AutoFinishScopeManager.java} | 8 +++--- ...t.java => AutoFinishScopeManagerTest.java} | 28 +++++++++---------- .../io/opentracing/util/GlobalTracer.java | 12 ++++---- ...ator.java => ThreadLocalScopeManager.java} | 7 +++-- ....java => ThreadLocalScopeManagerTest.java} | 16 +++++------ 15 files changed, 107 insertions(+), 100 deletions(-) delete mode 100644 opentracing-api/src/main/java/io/opentracing/Activator.java create mode 100644 opentracing-api/src/main/java/io/opentracing/Scope.java create mode 100644 opentracing-api/src/main/java/io/opentracing/ScopeManager.java rename opentracing-noop/src/main/java/io/opentracing/noop/{NoopActivator.java => NoopScopeManager.java} (73%) rename opentracing-usecases/src/main/java/io/opentracing/usecases/{AutoFinishActivator.java => AutoFinishScopeManager.java} (88%) rename opentracing-usecases/src/test/java/io/opentracing/usecases/{AutoFinishActivatorTest.java => AutoFinishScopeManagerTest.java} (72%) rename opentracing-util/src/main/java/io/opentracing/util/{ThreadLocalActivator.java => ThreadLocalScopeManager.java} (80%) rename opentracing-util/src/test/java/io/opentracing/util/{ThreadLocalActivatorTest.java => ThreadLocalScopeManagerTest.java} (79%) diff --git a/opentracing-api/src/main/java/io/opentracing/Activator.java b/opentracing-api/src/main/java/io/opentracing/Activator.java deleted file mode 100644 index 9c1ac3a7..00000000 --- a/opentracing-api/src/main/java/io/opentracing/Activator.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.opentracing; - -import java.io.Closeable; - -/** - * Created by bhs on 7/27/17. - */ -public interface Activator { - interface Scope extends Closeable { - @Override - void close(); - - Span span(); - } - Scope activate(Span span); - - Scope activeScope(); -} diff --git a/opentracing-api/src/main/java/io/opentracing/Scope.java b/opentracing-api/src/main/java/io/opentracing/Scope.java new file mode 100644 index 00000000..55c4914c --- /dev/null +++ b/opentracing-api/src/main/java/io/opentracing/Scope.java @@ -0,0 +1,25 @@ +package io.opentracing; + +import java.io.Closeable; + +/** + * A {@link Scope} formalizes the activation and deactivation of a {@link Span}, usually from a CPU standpoint. + * + *

+ * Many times a {@link Span} will be extant (in that {@link Span#finish()} has not been called) despite being in a + * non-runnable state from a CPU/scheduler standpoint. For instance, a {@link Span} representing the client side of an + * RPC will be unfinished but blocked on IO while the RPC is still outstanding. A {@link Scope} defines when a given + * {@link Span} is scheduled and on the critical path. + */ +public interface Scope extends Closeable { + /** + * End this {@link Scope}. + */ + @Override + void close(); + + /** + * @return the {@link Span} that's been scoped by this {@link Scope} + */ + Span span(); +} diff --git a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java new file mode 100644 index 00000000..4c7a79e7 --- /dev/null +++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java @@ -0,0 +1,15 @@ +package io.opentracing; + +/** + * The {@link ScopeManager} interface abstracts both the activation of {@link Span} instances (via + * {@link ScopeManager#activate(Span)}) and access to an active {@link Span}/{@link Scope} + * (via {@link ScopeManager#activeScope()}). + * + * @see Scope + * @see Tracer#setScopeManager(ScopeManager) + */ +public interface ScopeManager { + Scope activate(Span span); + + Scope activeScope(); +} diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java index 6e5eb66d..548ac4f8 100644 --- a/opentracing-api/src/main/java/io/opentracing/Span.java +++ b/opentracing-api/src/main/java/io/opentracing/Span.java @@ -18,11 +18,8 @@ /** * {@link Span} represents the OpenTracing specification's Span contract. * - *

- * Note that most application code interacts with {@link ActiveSpan} instances (which make themselves available - * for in-process propagation via the {@link ActiveSpanSource} interface). - * - * @see ActiveSpan + * @see Scope + * @see ScopeManager * @see Tracer.SpanBuilder#startManual() * @see Tracer.SpanBuilder#startActive() */ @@ -164,8 +161,4 @@ public interface Span { * @see Span#context() */ void finish(long finishMicros); - - // XXX: add these back - // Activator defer(); - // Activator.Scope activate(); // shorthand for `defer().activate()` } diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java index 3b92729c..25496555 100644 --- a/opentracing-api/src/main/java/io/opentracing/Tracer.java +++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java @@ -20,8 +20,8 @@ */ public interface Tracer { - Activator activator(); - void setActivator(Activator activator); + ScopeManager scopeManager(); + void setScopeManager(ScopeManager scopeManager); /** * Return a new SpanBuilder for a Span with the given `operationName`. @@ -158,12 +158,12 @@ interface SpanBuilder { SpanBuilder withStartTimestamp(long microseconds); /** - * Returns a newly started and activated {@link Activator.Scope}. + * Returns a newly started and activated {@link Scope}. * *

- * The returned {@link Activator.Scope} supports try-with-resources. For example: + * The returned {@link Scope} supports try-with-resources. For example: *


-         *     try (Activator.Scope span = tracer.buildSpan("...").startActive()) {
+         *     try (ScopeManager.Scope span = tracer.buildSpan("...").startActive()) {
          *         // (Do work)
          *         span.setTag( ... );  // etc, etc
          *     }  // XXX Span finishes automatically unless deferred via {@link ActiveSpan#capture}
@@ -189,7 +189,7 @@ interface SpanBuilder {
          * @see ActiveSpanSource
          * @see ActiveSpan
          */
-        Activator.Scope startActive();
+        Scope startActive();
 
         /**
          * Like {@link #startActive()}, but the returned {@link Span} has not been registered via the
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 9519b043..ff94a6b1 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
@@ -13,9 +13,10 @@
  */
 package io.opentracing.mock;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
+import io.opentracing.ScopeManager;
 import io.opentracing.Span;
-import io.opentracing.noop.NoopActivator;
+import io.opentracing.noop.NoopScopeManager;
 import io.opentracing.References;
 import io.opentracing.SpanContext;
 import io.opentracing.Tracer;
@@ -38,7 +39,7 @@
 public class MockTracer implements Tracer {
     private List finishedSpans = new ArrayList<>();
     private final Propagator propagator;
-    private Activator activator;
+    private ScopeManager scopeManager;
 
     public MockTracer() {
         this(Propagator.PRINTER);
@@ -49,7 +50,7 @@ public MockTracer() {
      */
     public MockTracer(Propagator propagator) {
         this.propagator = propagator;
-        this.activator = NoopActivator.INSTANCE;
+        this.scopeManager = NoopScopeManager.INSTANCE;
     }
 
     /**
@@ -153,13 +154,12 @@ public  MockSpan.MockContext extract(Format format, C carrier) {
     }
 
     @Override
-    public Activator activator() {
-        return this.activator;
+    public ScopeManager scopeManager() {
+        return this.scopeManager;
     }
 
-    @Override
-    public void setActivator(Activator activator) {
-        this.activator = activator;
+    public void setScopeManager(ScopeManager scopeManager) {
+        this.scopeManager = scopeManager;
     }
 
     @Override
@@ -243,8 +243,8 @@ public SpanBuilder withStartTimestamp(long microseconds) {
         }
 
         @Override
-        public Activator.Scope startActive() {
-            return MockTracer.this.activator().activate(this.startManual());
+        public Scope startActive() {
+            return MockTracer.this.scopeManager().activate(this.startManual());
         }
 
         @Override
@@ -258,7 +258,7 @@ public MockSpan startManual() {
                 this.startMicros = MockSpan.nowMicros();
             }
             if (firstParent == null && !ignoringActiveSpan) {
-                Activator.Scope activeScope = activator().activeScope();
+                Scope activeScope = scopeManager().activeScope();
                 if (activeScope != null) {
                     firstParent = (MockSpan.MockContext) activeScope.span().context();
                 }
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
similarity index 73%
rename from opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java
rename to opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
index 5f8d00f6..0c6b1615 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActivator.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
@@ -13,24 +13,22 @@
  */
 package io.opentracing.noop;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
+import io.opentracing.ScopeManager;
 import io.opentracing.Span;
-import io.opentracing.SpanContext;
-
-import java.util.Map;
 
 /**
  * Created by bhs on 8/5/17.
  */
-public interface NoopActivator extends Activator {
-    NoopActivator INSTANCE = new NoopActivatorImpl();
+public interface NoopScopeManager extends ScopeManager {
+    NoopScopeManager INSTANCE = new NoopScopeManagerImpl();
 
     interface NoopScope extends Scope {
-        NoopScope INSTANCE = new NoopActivatorImpl.NoopScopeImpl();
+        NoopScope INSTANCE = new NoopScopeManagerImpl.NoopScopeImpl();
     }
 }
 
-class NoopActivatorImpl implements NoopActivator {
+class NoopScopeManagerImpl implements NoopScopeManager {
     @Override
     public Scope activate(Span span) {
         return null;
@@ -41,7 +39,7 @@ public Scope activeScope() {
         return null;
     }
 
-    static class NoopScopeImpl implements NoopActivator.NoopScope {
+    static class NoopScopeImpl implements NoopScopeManager.NoopScope {
         @Override
         public void close() {}
 
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
index ae4f4497..7d503066 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
@@ -13,7 +13,6 @@
  */
 package io.opentracing.noop;
 
-import io.opentracing.Activator;
 import io.opentracing.Span;
 import io.opentracing.SpanContext;
 
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java
index d71770c7..0d68174d 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java
@@ -13,7 +13,7 @@
  */
 package io.opentracing.noop;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
 import io.opentracing.Span;
 import io.opentracing.SpanContext;
 import io.opentracing.Tracer;
@@ -66,8 +66,8 @@ public Tracer.SpanBuilder withStartTimestamp(long microseconds) {
     }
 
     @Override
-    public Activator.Scope startActive() {
-        return NoopActivator.NoopScope.INSTANCE;
+    public Scope startActive() {
+        return NoopScopeManager.NoopScope.INSTANCE;
     }
 
     @Override
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 a49faddd..4d79b8bd 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopTracer.java
@@ -13,8 +13,7 @@
  */
 package io.opentracing.noop;
 
-import io.opentracing.Activator;
-import io.opentracing.Span;
+import io.opentracing.ScopeManager;
 import io.opentracing.SpanContext;
 import io.opentracing.Tracer;
 import io.opentracing.propagation.Format;
@@ -26,12 +25,11 @@ final class NoopTracerImpl implements NoopTracer {
     final static NoopTracer INSTANCE = new NoopTracerImpl();
 
     @Override
-    public Activator activator() {
+    public ScopeManager scopeManager() {
         return null;
     }
 
-    @Override
-    public void setActivator(Activator activator) {
+    public void setScopeManager(ScopeManager scopeManager) {
 
     }
 
diff --git a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
similarity index 88%
rename from opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java
rename to opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
index 5eaf0cbd..f771841e 100644
--- a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishActivator.java
+++ b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
@@ -1,15 +1,15 @@
 package io.opentracing.usecases;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
+import io.opentracing.ScopeManager;
 import io.opentracing.Span;
-import io.opentracing.Tracer;
 
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Created by bhs on 8/5/17.
  */
-public class AutoFinishActivator implements Activator {
+public class AutoFinishScopeManager implements ScopeManager {
     final ThreadLocal tlsScope = new ThreadLocal();
 
     @Override
@@ -30,7 +30,7 @@ public class AutoFinishScope implements Scope {
         AutoFinishScope(AtomicInteger refCount, Span wrapped) {
             this.refCount = refCount;
             this.wrapped = wrapped;
-            this.toRestore = AutoFinishActivator.this.tlsScope.get();
+            this.toRestore = AutoFinishScopeManager.this.tlsScope.get();
             tlsScope.set(this);
         }
 
diff --git a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
similarity index 72%
rename from opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java
rename to opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
index dac7fd85..d67c199f 100644
--- a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishActivatorTest.java
+++ b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
@@ -13,29 +13,27 @@
  */
 package io.opentracing.usecases;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
 import io.opentracing.Span;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
 
-import static org.junit.Assert.*;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-public class AutoFinishActivatorTest {
-    private AutoFinishActivator autoFinishActivator;
+public class AutoFinishScopeManagerTest {
+    private AutoFinishScopeManager autoFinishActivator;
 
     @Before
     public void before() throws Exception {
-        autoFinishActivator = new AutoFinishActivator();
+        autoFinishActivator = new AutoFinishScopeManager();
     }
 
     @Test
     public void missingActiveSpan() throws Exception {
-        Activator.Scope missingScope = autoFinishActivator.activeScope();
+        Scope missingScope = autoFinishActivator.activeScope();
         Assert.assertNull(missingScope);
     }
 
@@ -44,10 +42,10 @@ public void makeActiveSpan() throws Exception {
         Span span = Mockito.mock(Span.class);
 
         // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests.
-        AutoFinishActivator.AutoFinishScope scope = autoFinishActivator.activate(span);
+        AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span);
         try {
             Assert.assertNotNull(scope);
-            Activator.Scope otherScope = autoFinishActivator.activeScope();
+            Scope otherScope = autoFinishActivator.activeScope();
             Assert.assertEquals(otherScope, scope);
         } finally {
             scope.close();
@@ -57,7 +55,7 @@ public void makeActiveSpan() throws Exception {
         Mockito.verify(span).finish();
 
         // And now it's gone:
-        Activator.Scope missingScope = autoFinishActivator.activeScope();
+        Scope missingScope = autoFinishActivator.activeScope();
         Assert.assertNull(missingScope);
     }
 
@@ -65,16 +63,16 @@ public void makeActiveSpan() throws Exception {
     public void deferring() throws Exception {
         Span span = Mockito.mock(Span.class);
 
-        AutoFinishActivator.AutoFinishScope.Continuation continuation = null;
+        AutoFinishScopeManager.AutoFinishScope.Continuation continuation = null;
         {
             // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests.
-            AutoFinishActivator.AutoFinishScope scope = autoFinishActivator.activate(span);
+            AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span);
 
             // Take a reference...
             continuation = scope.defer();
             try {
                 Assert.assertNotNull(scope);
-                Activator.Scope otherScope = autoFinishActivator.activeScope();
+                Scope otherScope = autoFinishActivator.activeScope();
                 Assert.assertEquals(otherScope, scope);
             } finally {
                 scope.close();
@@ -85,7 +83,7 @@ public void deferring() throws Exception {
         Mockito.verify(span, Mockito.never()).finish();
 
         // Activate the Continuation and close that second reference.
-        AutoFinishActivator.AutoFinishScope reactivated = continuation.activate();
+        AutoFinishScopeManager.AutoFinishScope reactivated = continuation.activate();
         try {
             // Nothing to do.
         } finally {
@@ -96,7 +94,7 @@ public void deferring() throws Exception {
         Mockito.verify(span).finish();
 
         // And now it's gone:
-        Activator.Scope missingScope = autoFinishActivator.activeScope();
+        Scope missingScope = autoFinishActivator.activeScope();
         Assert.assertNull(missingScope);
     }
 
diff --git a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java
index 42f782a9..a1f49be4 100644
--- a/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java
+++ b/opentracing-util/src/main/java/io/opentracing/util/GlobalTracer.java
@@ -13,8 +13,7 @@
  */
 package io.opentracing.util;
 
-import io.opentracing.Activator;
-import io.opentracing.Span;
+import io.opentracing.ScopeManager;
 import io.opentracing.noop.NoopTracer;
 import io.opentracing.noop.NoopTracerFactory;
 import io.opentracing.SpanContext;
@@ -124,13 +123,12 @@ public static synchronized boolean isRegistered() {
     }
 
     @Override
-    public Activator activator() {
-        return tracer.activator();
+    public ScopeManager scopeManager() {
+        return tracer.scopeManager();
     }
 
-    @Override
-    public void setActivator(Activator activator) {
-        tracer.setActivator(activator);
+    public void setScopeManager(ScopeManager scopeManager) {
+        tracer.setScopeManager(scopeManager);
     }
 
     @Override
diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
similarity index 80%
rename from opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java
rename to opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
index de833868..ed3bcd77 100644
--- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActivator.java
+++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
@@ -1,12 +1,13 @@
 package io.opentracing.util;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
+import io.opentracing.ScopeManager;
 import io.opentracing.Span;
 
 /**
  * Created by bhs on 8/1/17.
  */
-public class ThreadLocalActivator implements Activator {
+public class ThreadLocalScopeManager implements ScopeManager {
     final ThreadLocal tlsScope = new ThreadLocal();
 
     public class ThreadLocalScope implements Scope {
@@ -15,7 +16,7 @@ public class ThreadLocalScope implements Scope {
 
         ThreadLocalScope(Span wrapped) {
             this.wrapped = wrapped;
-            this.toRestore = ThreadLocalActivator.this.tlsScope.get();
+            this.toRestore = ThreadLocalScopeManager.this.tlsScope.get();
             tlsScope.set(this);
         }
 
diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java
similarity index 79%
rename from opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java
rename to opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java
index 99d7bf38..417175fd 100644
--- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActivatorTest.java
+++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java
@@ -13,7 +13,7 @@
  */
 package io.opentracing.util;
 
-import io.opentracing.Activator;
+import io.opentracing.Scope;
 import io.opentracing.Span;
 import org.junit.Before;
 import org.junit.Test;
@@ -23,17 +23,17 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-public class ThreadLocalActivatorTest {
-    private ThreadLocalActivator source;
+public class ThreadLocalScopeManagerTest {
+    private ThreadLocalScopeManager source;
 
     @Before
     public void before() throws Exception {
-        source = new ThreadLocalActivator();
+        source = new ThreadLocalScopeManager();
     }
 
     @Test
     public void missingActiveSpan() throws Exception {
-        Activator.Scope missingScope = source.activeScope();
+        Scope missingScope = source.activeScope();
         assertNull(missingScope);
     }
 
@@ -42,10 +42,10 @@ public void makeActiveSpan() throws Exception {
         Span span = mock(Span.class);
 
         // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests.
-        Activator.Scope scope = source.activate(span);
+        Scope scope = source.activate(span);
         try {
             assertNotNull(scope);
-            Activator.Scope otherScope = source.activeScope();
+            Scope otherScope = source.activeScope();
             assertEquals(otherScope, scope);
         } finally {
             scope.close();
@@ -55,7 +55,7 @@ public void makeActiveSpan() throws Exception {
         verify(span, never()).finish();
 
         // And now it's gone:
-        Activator.Scope missingScope = source.activeScope();
+        Scope missingScope = source.activeScope();
         assertNull(missingScope);
     }
 

From b59956519f34e5c3b3c7933e1ce2903b5f950bab Mon Sep 17 00:00:00 2001
From: Ben Sigelman 
Date: Sun, 6 Aug 2017 22:26:48 -0700
Subject: [PATCH 09/13] Add more comments and a convenience method

---
 .../src/main/java/io/opentracing/Scope.java           |  2 +-
 .../src/main/java/io/opentracing/ScopeManager.java    | 11 +++++++++++
 .../src/main/java/io/opentracing/Span.java            |  6 ++++++
 .../src/main/java/io/opentracing/mock/MockSpan.java   |  6 ++++++
 .../src/main/java/io/opentracing/noop/NoopSpan.java   |  6 ++++++
 5 files changed, 30 insertions(+), 1 deletion(-)

diff --git a/opentracing-api/src/main/java/io/opentracing/Scope.java b/opentracing-api/src/main/java/io/opentracing/Scope.java
index 55c4914c..96899dbe 100644
--- a/opentracing-api/src/main/java/io/opentracing/Scope.java
+++ b/opentracing-api/src/main/java/io/opentracing/Scope.java
@@ -13,7 +13,7 @@
  */
 public interface Scope extends Closeable {
     /**
-     * End this {@link Scope}.
+     * End this {@link Scope}, updating the {@link ScopeManager#activeScope()} in the process.
      */
     @Override
     void close();
diff --git a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
index 4c7a79e7..8fc979fe 100644
--- a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
+++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
@@ -9,7 +9,18 @@
  * @see Tracer#setScopeManager(ScopeManager)
  */
 public interface ScopeManager {
+    /**
+     * Make a {@link Span} instance active.
+     *
+     * @param span the {@link Span} that should become the {@link #activeScope()}
+     * @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);
 
+    /**
+     * @return the currently active {@link Scope} which can be used to access the currently active
+     * {@link Scope#span()}
+     */
     Scope activeScope();
 }
diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java
index 548ac4f8..1bb891a9 100644
--- a/opentracing-api/src/main/java/io/opentracing/Span.java
+++ b/opentracing-api/src/main/java/io/opentracing/Span.java
@@ -161,4 +161,10 @@ public interface Span {
      * @see Span#context()
      */
     void finish(long finishMicros);
+
+    /**
+     * Defined as equivalent to calling {@code tracer.scopeManager().activate(this)} on the {@link Tracer} instance
+     * that started this {@link Span} instance.
+     */
+    Scope activate();
 }
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 859513aa..eb95fad9 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
@@ -20,6 +20,7 @@
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicLong;
 
+import io.opentracing.Scope;
 import io.opentracing.Span;
 import io.opentracing.SpanContext;
 
@@ -115,6 +116,11 @@ public synchronized void finish(long finishMicros) {
         this.finished = true;
     }
 
+    @Override
+    public Scope activate() {
+        return mockTracer.scopeManager().activate(this);
+    }
+
     @Override
     public MockSpan setTag(String key, String value) {
         return setObjectTag(key, value);
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
index 7d503066..54de2bda 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
@@ -13,6 +13,7 @@
  */
 package io.opentracing.noop;
 
+import io.opentracing.Scope;
 import io.opentracing.Span;
 import io.opentracing.SpanContext;
 
@@ -33,6 +34,11 @@ public void finish() {}
     @Override
     public void finish(long finishMicros) {}
 
+    @Override
+    public Scope activate() {
+        return NoopScopeManagerImpl.NoopScopeImpl.INSTANCE;
+    }
+
     @Override
     public NoopSpan setTag(String key, String value) { return this; }
 

From 5ca30f6d84a734b1d6f799c305387f05a9ee63a1 Mon Sep 17 00:00:00 2001
From: Ben Sigelman 
Date: Sun, 6 Aug 2017 22:32:07 -0700
Subject: [PATCH 10/13] Use 1.8 features

---
 .../usecases/AutoFinishScopeManagerTest.java  | 21 +++++--------------
 1 file changed, 5 insertions(+), 16 deletions(-)

diff --git a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
index d67c199f..5da693dd 100644
--- a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
+++ b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
@@ -41,14 +41,10 @@ public void missingActiveSpan() throws Exception {
     public void makeActiveSpan() throws Exception {
         Span span = Mockito.mock(Span.class);
 
-        // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests.
-        AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span);
-        try {
+        try (AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span)) {
             Assert.assertNotNull(scope);
             Scope otherScope = autoFinishActivator.activeScope();
             Assert.assertEquals(otherScope, scope);
-        } finally {
-            scope.close();
         }
 
         // Make sure the Span got finish()ed.
@@ -66,16 +62,12 @@ public void deferring() throws Exception {
         AutoFinishScopeManager.AutoFinishScope.Continuation continuation = null;
         {
             // We can't use 1.7 features like try-with-resources in this repo without meddling with pom details for tests.
-            AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span);
-
-            // Take a reference...
-            continuation = scope.defer();
-            try {
+            try (AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span)) {
+                // Take a reference...
+                continuation = scope.defer();
                 Assert.assertNotNull(scope);
                 Scope otherScope = autoFinishActivator.activeScope();
                 Assert.assertEquals(otherScope, scope);
-            } finally {
-                scope.close();
             }
         }
 
@@ -83,11 +75,8 @@ public void deferring() throws Exception {
         Mockito.verify(span, Mockito.never()).finish();
 
         // Activate the Continuation and close that second reference.
-        AutoFinishScopeManager.AutoFinishScope reactivated = continuation.activate();
-        try {
+        try (AutoFinishScopeManager.AutoFinishScope reactivated = continuation.activate()) {
             // Nothing to do.
-        } finally {
-            reactivated.close();
         }
 
         // Make sure the Span got finish()ed this time.

From 6ef8be7b09dd258c5a19eb7408b1b28d3963f103 Mon Sep 17 00:00:00 2001
From: Ben Sigelman 
Date: Sun, 6 Aug 2017 22:40:02 -0700
Subject: [PATCH 11/13] Shorten a method name and revert README

---
 README.md                                              |  4 ++--
 .../src/main/java/io/opentracing/Scope.java            |  2 +-
 .../src/main/java/io/opentracing/ScopeManager.java     |  6 +++---
 .../src/main/java/io/opentracing/mock/MockTracer.java  |  2 +-
 .../java/io/opentracing/noop/NoopScopeManager.java     |  2 +-
 .../opentracing/usecases/AutoFinishScopeManager.java   |  2 +-
 .../usecases/AutoFinishScopeManagerTest.java           | 10 +++++-----
 .../io/opentracing/util/ThreadLocalScopeManager.java   |  2 +-
 .../opentracing/util/ThreadLocalScopeManagerTest.java  |  6 +++---
 9 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/README.md b/README.md
index 5b516863..983cfac3 100644
--- a/README.md
+++ b/README.md
@@ -56,7 +56,7 @@ The common case starts an `ActiveSpan` that's automatically registered for intra
 ```java
 io.opentracing.Tracer tracer = ...;
 ...
-try (ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive(new AutoFinisher())) {
+try (ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive()) {
     // Do things.
     //
     // If we create async work, `activeSpan.capture()` allows us to pass the `ActiveSpan` along as well.
@@ -68,7 +68,7 @@ The above is semantically equivalent to the more explicit try-finally version:
 ```java
 io.opentracing.Tracer tracer = ...;
 ...
-ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive(new AutoFinisher());
+ActiveSpan activeSpan = tracer.buildSpan("someWork").startActive();
 try {
     // Do things.
 } finally {
diff --git a/opentracing-api/src/main/java/io/opentracing/Scope.java b/opentracing-api/src/main/java/io/opentracing/Scope.java
index 96899dbe..7b386760 100644
--- a/opentracing-api/src/main/java/io/opentracing/Scope.java
+++ b/opentracing-api/src/main/java/io/opentracing/Scope.java
@@ -13,7 +13,7 @@
  */
 public interface Scope extends Closeable {
     /**
-     * End this {@link Scope}, updating the {@link ScopeManager#activeScope()} in the process.
+     * End this {@link Scope}, updating the {@link ScopeManager#active()} in the process.
      */
     @Override
     void close();
diff --git a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
index 8fc979fe..dfd94f96 100644
--- a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
+++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
@@ -3,7 +3,7 @@
 /**
  * The {@link ScopeManager} interface abstracts both the activation of {@link Span} instances (via
  * {@link ScopeManager#activate(Span)}) and access to an active {@link Span}/{@link Scope}
- * (via {@link ScopeManager#activeScope()}).
+ * (via {@link ScopeManager#active()}).
  *
  * @see Scope
  * @see Tracer#setScopeManager(ScopeManager)
@@ -12,7 +12,7 @@ public interface ScopeManager {
     /**
      * Make a {@link Span} instance active.
      *
-     * @param span the {@link Span} that should become the {@link #activeScope()}
+     * @param span the {@link Span} that should become the {@link #active()}
      * @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.
      */
@@ -22,5 +22,5 @@ public interface ScopeManager {
      * @return the currently active {@link Scope} which can be used to access the currently active
      * {@link Scope#span()}
      */
-    Scope activeScope();
+    Scope active();
 }
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 ff94a6b1..8a11fe4c 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
@@ -258,7 +258,7 @@ public MockSpan startManual() {
                 this.startMicros = MockSpan.nowMicros();
             }
             if (firstParent == null && !ignoringActiveSpan) {
-                Scope activeScope = scopeManager().activeScope();
+                Scope activeScope = scopeManager().active();
                 if (activeScope != null) {
                     firstParent = (MockSpan.MockContext) activeScope.span().context();
                 }
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 0c6b1615..652d37ab 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
@@ -35,7 +35,7 @@ public Scope activate(Span span) {
     }
 
     @Override
-    public Scope activeScope() {
+    public Scope active() {
         return null;
     }
 
diff --git a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
index f771841e..f3c31fd2 100644
--- a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
+++ b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
@@ -18,7 +18,7 @@ public AutoFinishScope activate(Span span) {
     }
 
     @Override
-    public AutoFinishScope activeScope() {
+    public AutoFinishScope active() {
         return tlsScope.get();
     }
 
diff --git a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
index 5da693dd..e244da91 100644
--- a/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
+++ b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java
@@ -33,7 +33,7 @@ public void before() throws Exception {
 
     @Test
     public void missingActiveSpan() throws Exception {
-        Scope missingScope = autoFinishActivator.activeScope();
+        Scope missingScope = autoFinishActivator.active();
         Assert.assertNull(missingScope);
     }
 
@@ -43,7 +43,7 @@ public void makeActiveSpan() throws Exception {
 
         try (AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span)) {
             Assert.assertNotNull(scope);
-            Scope otherScope = autoFinishActivator.activeScope();
+            Scope otherScope = autoFinishActivator.active();
             Assert.assertEquals(otherScope, scope);
         }
 
@@ -51,7 +51,7 @@ public void makeActiveSpan() throws Exception {
         Mockito.verify(span).finish();
 
         // And now it's gone:
-        Scope missingScope = autoFinishActivator.activeScope();
+        Scope missingScope = autoFinishActivator.active();
         Assert.assertNull(missingScope);
     }
 
@@ -66,7 +66,7 @@ public void deferring() throws Exception {
                 // Take a reference...
                 continuation = scope.defer();
                 Assert.assertNotNull(scope);
-                Scope otherScope = autoFinishActivator.activeScope();
+                Scope otherScope = autoFinishActivator.active();
                 Assert.assertEquals(otherScope, scope);
             }
         }
@@ -83,7 +83,7 @@ public void deferring() throws Exception {
         Mockito.verify(span).finish();
 
         // And now it's gone:
-        Scope missingScope = autoFinishActivator.activeScope();
+        Scope missingScope = autoFinishActivator.active();
         Assert.assertNull(missingScope);
     }
 
diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
index ed3bcd77..69a732a2 100644
--- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
+++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
@@ -37,7 +37,7 @@ public Scope activate(Span span) {
     }
 
     @Override
-    public Scope activeScope() {
+    public Scope active() {
         return tlsScope.get();
     }
 }
diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java
index 417175fd..5fd527a8 100644
--- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java
+++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java
@@ -33,7 +33,7 @@ public void before() throws Exception {
 
     @Test
     public void missingActiveSpan() throws Exception {
-        Scope missingScope = source.activeScope();
+        Scope missingScope = source.active();
         assertNull(missingScope);
     }
 
@@ -45,7 +45,7 @@ public void makeActiveSpan() throws Exception {
         Scope scope = source.activate(span);
         try {
             assertNotNull(scope);
-            Scope otherScope = source.activeScope();
+            Scope otherScope = source.active();
             assertEquals(otherScope, scope);
         } finally {
             scope.close();
@@ -55,7 +55,7 @@ public void makeActiveSpan() throws Exception {
         verify(span, never()).finish();
 
         // And now it's gone:
-        Scope missingScope = source.activeScope();
+        Scope missingScope = source.active();
         assertNull(missingScope);
     }
 

From f5f70761cc80fc93ba991edac753c13c5cc36a6b Mon Sep 17 00:00:00 2001
From: Ben Sigelman 
Date: Sun, 6 Aug 2017 22:48:03 -0700
Subject: [PATCH 12/13] Add admonition

---
 opentracing-api/src/main/java/io/opentracing/Tracer.java | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java
index 25496555..ba404ed9 100644
--- a/opentracing-api/src/main/java/io/opentracing/Tracer.java
+++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java
@@ -20,7 +20,16 @@
  */
 public interface Tracer {
 
+    /**
+     * @return the current {@link ScopeManager}, which may be a noop but may not be null.
+     */
     ScopeManager scopeManager();
+
+    /**
+     * Set a new {@link ScopeManager}, presumably as part of initialization. The state of any already active
+     * {@link Scope} instance after this is only defined to do no harm to the process (i.e., not-crash). Again, this is
+     * only intended for use during initialization.
+     */
     void setScopeManager(ScopeManager scopeManager);
 
     /**

From 2eca5c16cf1f3cca6289a900a2557bf8d67031c0 Mon Sep 17 00:00:00 2001
From: Ben Sigelman 
Date: Sun, 13 Aug 2017 14:49:08 -0700
Subject: [PATCH 13/13] Adjust the API to make finish()ing harder to miss

---
 .../src/main/java/io/opentracing/Scope.java   | 50 +++++++++++++++++++
 .../java/io/opentracing/ScopeManager.java     | 14 ++++++
 .../src/main/java/io/opentracing/Span.java    |  1 +
 .../src/main/java/io/opentracing/Tracer.java  |  1 +
 .../java/io/opentracing/mock/MockSpan.java    |  5 ++
 .../java/io/opentracing/mock/MockTracer.java  |  5 ++
 .../io/opentracing/noop/NoopScopeManager.java |  7 ++-
 .../java/io/opentracing/noop/NoopSpan.java    |  7 ++-
 .../io/opentracing/noop/NoopSpanBuilder.java  |  5 ++
 .../usecases/AutoFinishScopeManager.java      | 13 +++++
 .../util/ThreadLocalScopeManager.java         | 30 ++++++++++-
 11 files changed, 134 insertions(+), 4 deletions(-)

diff --git a/opentracing-api/src/main/java/io/opentracing/Scope.java b/opentracing-api/src/main/java/io/opentracing/Scope.java
index 7b386760..caf194ea 100644
--- a/opentracing-api/src/main/java/io/opentracing/Scope.java
+++ b/opentracing-api/src/main/java/io/opentracing/Scope.java
@@ -1,3 +1,16 @@
+/*
+ * 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.io.Closeable;
@@ -22,4 +35,41 @@ public interface Scope extends Closeable {
      * @return the {@link Span} that's been scoped by this {@link Scope}
      */
     Span span();
+
+    /**
+     * {@link Observer} is a simple API for observing the opening/closing of {@link Scope} instances.
+     *
+     * @see ScopeManager#activate(Span, Observer)
+     * @see Tracer.SpanBuilder#startActive(Observer)
+     * @see Span#activate(Observer)
+     */
+    interface Observer {
+        /**
+         * A trivial, static {@link Scope.Observer} that finishes the underlying {@link Span} on scope close.
+         */
+        Observer FINISH_ON_CLOSE = new FinishOnCloseScopeObserverImpl();
+
+        /**
+         * Invoked just after the {@link Scope} becomes active.
+         */
+        void onActivate(Scope scope);
+
+        /**
+         * Invoked just before the {@link Scope} closes / is deactivated.
+         */
+        void onClose(Scope scope);
+    }
+}
+
+/**
+ * @see Scope.Observer#FINISH_ON_CLOSE
+ */
+class FinishOnCloseScopeObserverImpl implements Scope.Observer {
+    @Override
+    public void onActivate(Scope scope) {}
+
+    @Override
+    public void onClose(Scope scope) {
+        scope.span().finish();
+    }
 }
diff --git a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
index dfd94f96..f112ca1f 100644
--- a/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
+++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java
@@ -1,3 +1,16 @@
+/*
+ * 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;
 
 /**
@@ -17,6 +30,7 @@ public interface ScopeManager {
      * programming error to neglect to call {@link Scope#close()} on the returned instance.
      */
     Scope activate(Span span);
+    Scope activate(Span span, Scope.Observer scopeObserver);
 
     /**
      * @return the currently active {@link Scope} which can be used to access the currently active
diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java
index 1bb891a9..67003aac 100644
--- a/opentracing-api/src/main/java/io/opentracing/Span.java
+++ b/opentracing-api/src/main/java/io/opentracing/Span.java
@@ -167,4 +167,5 @@ public interface Span {
      * that started this {@link Span} instance.
      */
     Scope activate();
+    Scope activate(Scope.Observer observer);
 }
diff --git a/opentracing-api/src/main/java/io/opentracing/Tracer.java b/opentracing-api/src/main/java/io/opentracing/Tracer.java
index ba404ed9..286430ef 100644
--- a/opentracing-api/src/main/java/io/opentracing/Tracer.java
+++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java
@@ -199,6 +199,7 @@ interface SpanBuilder {
          * @see ActiveSpan
          */
         Scope startActive();
+        Scope startActive(Scope.Observer observer);
 
         /**
          * Like {@link #startActive()}, but the returned {@link Span} has not been registered via the
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 eb95fad9..565cf1a3 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java
@@ -121,6 +121,11 @@ public Scope activate() {
         return mockTracer.scopeManager().activate(this);
     }
 
+    @Override
+    public Scope activate(Scope.Observer observer) {
+        return mockTracer.scopeManager().activate(this, observer);
+    }
+
     @Override
     public MockSpan setTag(String key, String value) {
         return setObjectTag(key, value);
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 8a11fe4c..06fe300f 100644
--- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
+++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java
@@ -247,6 +247,11 @@ public Scope startActive() {
             return MockTracer.this.scopeManager().activate(this.startManual());
         }
 
+        @Override
+        public Scope startActive(Scope.Observer observer) {
+            return MockTracer.this.scopeManager().activate(this.startManual(), observer);
+        }
+
         @Override
         public MockSpan start() {
             return startManual();
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 652d37ab..e9819663 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java
@@ -31,7 +31,12 @@ interface NoopScope extends Scope {
 class NoopScopeManagerImpl implements NoopScopeManager {
     @Override
     public Scope activate(Span span) {
-        return null;
+        return NoopScope.INSTANCE;
+    }
+
+    @Override
+    public Scope activate(Span span, Scope.Observer scopeObserver) {
+        return NoopScope.INSTANCE;
     }
 
     @Override
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
index 54de2bda..1b44744c 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java
@@ -20,7 +20,7 @@
 import java.util.Map;
 
 public interface NoopSpan extends Span {
-    static final NoopSpan INSTANCE = new NoopSpanImpl();
+    NoopSpan INSTANCE = new NoopSpanImpl();
 }
 
 final class NoopSpanImpl implements NoopSpan {
@@ -39,6 +39,11 @@ public Scope activate() {
         return NoopScopeManagerImpl.NoopScopeImpl.INSTANCE;
     }
 
+    @Override
+    public Scope activate(Scope.Observer observer) {
+        return NoopScopeManagerImpl.NoopScopeImpl.INSTANCE;
+    }
+
     @Override
     public NoopSpan setTag(String key, String value) { return this; }
 
diff --git a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java
index 0d68174d..71fac79b 100644
--- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java
+++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java
@@ -70,6 +70,11 @@ public Scope startActive() {
         return NoopScopeManager.NoopScope.INSTANCE;
     }
 
+    @Override
+    public Scope startActive(Scope.Observer observer) {
+        return NoopScopeManager.NoopScope.INSTANCE;
+    }
+
     @Override
     public Span start() {
         return startManual();
diff --git a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
index f3c31fd2..33e1f5f3 100644
--- a/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
+++ b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java
@@ -1,3 +1,16 @@
+/*
+ * 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.usecases;
 
 import io.opentracing.Scope;
diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
index 69a732a2..b6511f57 100644
--- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
+++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java
@@ -1,3 +1,16 @@
+/*
+ * 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.util;
 
 import io.opentracing.Scope;
@@ -13,15 +26,23 @@ public class ThreadLocalScopeManager implements ScopeManager {
     public class ThreadLocalScope implements Scope {
         private final Span wrapped;
         private final ThreadLocalScope toRestore;
+        private final Scope.Observer scopeObserver;
 
-        ThreadLocalScope(Span wrapped) {
+        ThreadLocalScope(Span wrapped, Scope.Observer scopeObserver) {
             this.wrapped = wrapped;
             this.toRestore = ThreadLocalScopeManager.this.tlsScope.get();
+            this.scopeObserver = scopeObserver;
             tlsScope.set(this);
+            if (this.scopeObserver != null) {
+                this.scopeObserver.onActivate(this);
+            }
         }
 
         @Override
         public void close() {
+            if (this.scopeObserver != null) {
+                this.scopeObserver.onClose(this);
+            }
             tlsScope.set(toRestore);
         }
 
@@ -33,7 +54,12 @@ public Span span() {
 
     @Override
     public Scope activate(Span span) {
-        return new ThreadLocalScope(span);
+        return new ThreadLocalScope(span, null);
+    }
+
+    @Override
+    public Scope activate(Span span, Scope.Observer scopeObserver) {
+        return new ThreadLocalScope(span, scopeObserver);
     }
 
     @Override