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 46e09d08..00000000 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java +++ /dev/null @@ -1,109 +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 BaseSpan} 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. - * - *

- * 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 BaseSpan - * @see Span - */ -public interface ActiveSpan extends Closeable, BaseSpan { - /** - * 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(); - } - -} 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 334d00e8..00000000 --- a/opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java +++ /dev/null @@ -1,50 +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}. This does not affect the internal reference count for the - * {@link ActiveSpan}. - * - *

- * 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 Tracer#buildSpan(String)} time). - * - * @return the {@link ActiveSpan active span}, or null if none could be found. - */ - ActiveSpan activeSpan(); - - /** - * 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} - * @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); -} 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/Scope.java b/opentracing-api/src/main/java/io/opentracing/Scope.java new file mode 100644 index 00000000..caf194ea --- /dev/null +++ b/opentracing-api/src/main/java/io/opentracing/Scope.java @@ -0,0 +1,75 @@ +/* + * 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; + +/** + * 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}, updating the {@link ScopeManager#active()} in the process. + */ + @Override + void close(); + + /** + * @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 new file mode 100644 index 00000000..f112ca1f --- /dev/null +++ b/opentracing-api/src/main/java/io/opentracing/ScopeManager.java @@ -0,0 +1,40 @@ +/* + * Copyright 2016-2017 The OpenTracing Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ +package io.opentracing; + +/** + * 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#active()}). + * + * @see Scope + * @see Tracer#setScopeManager(ScopeManager) + */ +public interface ScopeManager { + /** + * Make a {@link Span} instance active. + * + * @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. + */ + 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 + * {@link Scope#span()} + */ + Scope active(); +} diff --git a/opentracing-api/src/main/java/io/opentracing/Span.java b/opentracing-api/src/main/java/io/opentracing/Span.java index 026c4ce5..67003aac 100644 --- a/opentracing-api/src/main/java/io/opentracing/Span.java +++ b/opentracing-api/src/main/java/io/opentracing/Span.java @@ -13,16 +13,132 @@ */ 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 Scope + * @see ScopeManager + * @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. * @@ -45,4 +161,11 @@ public interface Span extends BaseSpan { * @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(); + 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 25d3d321..286430ef 100644 --- a/opentracing-api/src/main/java/io/opentracing/Tracer.java +++ b/opentracing-api/src/main/java/io/opentracing/Tracer.java @@ -18,7 +18,19 @@ /** * Tracer is a simple, thin interface for Span creation and propagation across arbitrary transports. */ -public interface Tracer extends ActiveSpanSource { +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); /** * Return a new SpanBuilder for a Span with the given `operationName`. @@ -111,7 +123,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 @@ -155,15 +167,15 @@ interface SpanBuilder { SpanBuilder withStartTimestamp(long microseconds); /** - * Returns a newly started and activated {@link ActiveSpan}. + * Returns a newly started and activated {@link Scope}. * *

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


-         *     try (ActiveSpan span = tracer.buildSpan("...").startActive()) {
+         *     try (ScopeManager.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}
          * 
* *

@@ -186,7 +198,8 @@ interface SpanBuilder { * @see ActiveSpanSource * @see ActiveSpan */ - ActiveSpan startActive(); + 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-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..f327851e 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,8 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import org.junit.Test; - -import io.opentracing.ActiveSpan; import io.opentracing.Span; +import org.junit.Test; /** * @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/MockSpan.java b/opentracing-mock/src/main/java/io/opentracing/mock/MockSpan.java index 859513aa..565cf1a3 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,16 @@ public synchronized void finish(long finishMicros) { this.finished = true; } + @Override + 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 15e6fcf8..06fe300f 100644 --- a/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java +++ b/opentracing-mock/src/main/java/io/opentracing/mock/MockTracer.java @@ -13,12 +13,11 @@ */ package io.opentracing.mock; -import io.opentracing.ActiveSpan; -import io.opentracing.ActiveSpanSource; -import io.opentracing.BaseSpan; -import io.opentracing.noop.NoopActiveSpanSource; -import io.opentracing.References; +import io.opentracing.Scope; +import io.opentracing.ScopeManager; import io.opentracing.Span; +import io.opentracing.noop.NoopScopeManager; +import io.opentracing.References; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; @@ -40,26 +39,18 @@ public class MockTracer implements Tracer { private List finishedSpans = new ArrayList<>(); private final Propagator propagator; - private ActiveSpanSource spanSource; + private ScopeManager scopeManager; 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; - } - /** * 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.scopeManager = NoopScopeManager.INSTANCE; } /** @@ -88,16 +79,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); - } - /** * Propagator allows the developer to intercept and verify any calls to inject() and/or extract(). * @@ -173,17 +154,17 @@ public MockSpan.MockContext extract(Format format, C carrier) { } @Override - public SpanBuilder buildSpan(String operationName) { - return new SpanBuilder(operationName); + public ScopeManager scopeManager() { + return this.scopeManager; } - private SpanContext activeSpanContext() { - ActiveSpan handle = this.spanSource.activeSpan(); - if (handle == null) { - return null; - } + public void setScopeManager(ScopeManager scopeManager) { + this.scopeManager = scopeManager; + } - return handle.context(); + @Override + public SpanBuilder buildSpan(String operationName) { + return new SpanBuilder(operationName); } @Override @@ -218,7 +199,7 @@ public SpanBuilder asChildOf(SpanContext parent) { } @Override - public SpanBuilder asChildOf(BaseSpan parent) { + public SpanBuilder asChildOf(Span parent) { return addReference(References.CHILD_OF, parent.context()); } @@ -262,14 +243,18 @@ public SpanBuilder withStartTimestamp(long microseconds) { } @Override - public MockSpan start() { - return startManual(); + public Scope startActive() { + return MockTracer.this.scopeManager().activate(this.startManual()); } @Override - public ActiveSpan startActive() { - MockSpan span = this.startManual(); - return spanSource.makeActive(span); + public Scope startActive(Scope.Observer observer) { + return MockTracer.this.scopeManager().activate(this.startManual(), observer); + } + + @Override + public MockSpan start() { + return startManual(); } @Override @@ -278,7 +263,10 @@ public MockSpan startManual() { this.startMicros = MockSpan.nowMicros(); } if (firstParent == null && !ignoringActiveSpan) { - firstParent = (MockSpan.MockContext) activeSpanContext(); + Scope activeScope = scopeManager().active(); + if (activeScope != null) { + firstParent = (MockSpan.MockContext) activeScope.span().context(); + } } return new MockSpan(MockTracer.this, operationName, startMicros, initialTags, firstParent); } 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 deleted file mode 100644 index ddd150ce..00000000 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopActiveSpanSource.java +++ /dev/null @@ -1,120 +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 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; - } - } - - 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/NoopScopeManager.java b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java new file mode 100644 index 00000000..e9819663 --- /dev/null +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopScopeManager.java @@ -0,0 +1,56 @@ +/* + * 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.Scope; +import io.opentracing.ScopeManager; +import io.opentracing.Span; + +/** + * Created by bhs on 8/5/17. + */ +public interface NoopScopeManager extends ScopeManager { + NoopScopeManager INSTANCE = new NoopScopeManagerImpl(); + + interface NoopScope extends Scope { + NoopScope INSTANCE = new NoopScopeManagerImpl.NoopScopeImpl(); + } +} + +class NoopScopeManagerImpl implements NoopScopeManager { + @Override + public Scope activate(Span span) { + return NoopScope.INSTANCE; + } + + @Override + public Scope activate(Span span, Scope.Observer scopeObserver) { + return NoopScope.INSTANCE; + } + + @Override + public Scope active() { + return null; + } + + static class NoopScopeImpl implements NoopScopeManager.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..1b44744c 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpan.java @@ -13,13 +13,14 @@ */ package io.opentracing.noop; +import io.opentracing.Scope; import io.opentracing.Span; import io.opentracing.SpanContext; import java.util.Map; public interface NoopSpan extends Span { - static final NoopSpan INSTANCE = new NoopSpanImpl(); + NoopSpan INSTANCE = new NoopSpanImpl(); } final class NoopSpanImpl implements NoopSpan { @@ -33,6 +34,16 @@ public void finish() {} @Override public void finish(long finishMicros) {} + @Override + 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 06129830..71fac79b 100644 --- a/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java +++ b/opentracing-noop/src/main/java/io/opentracing/noop/NoopSpanBuilder.java @@ -13,8 +13,7 @@ */ package io.opentracing.noop; -import io.opentracing.ActiveSpan; -import io.opentracing.BaseSpan; +import io.opentracing.Scope; 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; } @@ -67,13 +66,18 @@ public Tracer.SpanBuilder withStartTimestamp(long microseconds) { } @Override - public Span start() { - return startManual(); + public Scope startActive() { + return NoopScopeManager.NoopScope.INSTANCE; } @Override - public ActiveSpan startActive() { - return NoopActiveSpanSource.NoopActiveSpan.INSTANCE; + public Scope startActive(Scope.Observer observer) { + return NoopScopeManager.NoopScope.INSTANCE; + } + + @Override + 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 678a302a..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.ActiveSpan; -import io.opentracing.Span; +import io.opentracing.ScopeManager; import io.opentracing.SpanContext; import io.opentracing.Tracer; import io.opentracing.propagation.Format; @@ -25,6 +24,15 @@ public interface NoopTracer extends Tracer { final class NoopTracerImpl implements NoopTracer { final static NoopTracer INSTANCE = new NoopTracerImpl(); + @Override + public ScopeManager scopeManager() { + return null; + } + + public void setScopeManager(ScopeManager scopeManager) { + + } + @Override public SpanBuilder buildSpan(String operationName) { return NoopSpanBuilderImpl.INSTANCE; } @@ -36,15 +44,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; - } } 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/AutoFinishScopeManager.java b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java new file mode 100644 index 00000000..33e1f5f3 --- /dev/null +++ b/opentracing-usecases/src/main/java/io/opentracing/usecases/AutoFinishScopeManager.java @@ -0,0 +1,77 @@ +/* + * 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; +import io.opentracing.ScopeManager; +import io.opentracing.Span; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by bhs on 8/5/17. + */ +public class AutoFinishScopeManager implements ScopeManager { + final ThreadLocal tlsScope = new ThreadLocal(); + + @Override + public AutoFinishScope activate(Span span) { + return new AutoFinishScope(new AtomicInteger(1), span); + } + + @Override + public AutoFinishScope active() { + 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 = AutoFinishScopeManager.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/AutoFinishScopeManagerTest.java b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java new file mode 100644 index 00000000..e244da91 --- /dev/null +++ b/opentracing-usecases/src/test/java/io/opentracing/usecases/AutoFinishScopeManagerTest.java @@ -0,0 +1,90 @@ +/* + * 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; +import io.opentracing.Span; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class AutoFinishScopeManagerTest { + private AutoFinishScopeManager autoFinishActivator; + + @Before + public void before() throws Exception { + autoFinishActivator = new AutoFinishScopeManager(); + } + + @Test + public void missingActiveSpan() throws Exception { + Scope missingScope = autoFinishActivator.active(); + Assert.assertNull(missingScope); + } + + @Test + public void makeActiveSpan() throws Exception { + Span span = Mockito.mock(Span.class); + + try (AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span)) { + Assert.assertNotNull(scope); + Scope otherScope = autoFinishActivator.active(); + Assert.assertEquals(otherScope, scope); + } + + // Make sure the Span got finish()ed. + Mockito.verify(span).finish(); + + // And now it's gone: + Scope missingScope = autoFinishActivator.active(); + Assert.assertNull(missingScope); + } + + @Test + public void deferring() throws Exception { + Span span = Mockito.mock(Span.class); + + 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. + try (AutoFinishScopeManager.AutoFinishScope scope = autoFinishActivator.activate(span)) { + // Take a reference... + continuation = scope.defer(); + Assert.assertNotNull(scope); + Scope otherScope = autoFinishActivator.active(); + Assert.assertEquals(otherScope, scope); + } + } + + // Make sure the Span has not been finish()ed yet. + Mockito.verify(span, Mockito.never()).finish(); + + // Activate the Continuation and close that second reference. + try (AutoFinishScopeManager.AutoFinishScope reactivated = continuation.activate()) { + // Nothing to do. + } + + // Make sure the Span got finish()ed this time. + Mockito.verify(span).finish(); + + // And now it's gone: + Scope missingScope = autoFinishActivator.active(); + 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 89b6f073..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,10 +13,9 @@ */ package io.opentracing.util; -import io.opentracing.ActiveSpan; +import io.opentracing.ScopeManager; 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; @@ -123,6 +122,15 @@ public static synchronized boolean isRegistered() { return !(GlobalTracer.tracer instanceof NoopTracer); } + @Override + public ScopeManager scopeManager() { + return tracer.scopeManager(); + } + + public void setScopeManager(ScopeManager scopeManager) { + tracer.setScopeManager(scopeManager); + } + @Override public SpanBuilder buildSpan(String operationName) { return tracer.buildSpan(operationName); @@ -142,14 +150,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); - } } 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 ba90ca43..00000000 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpan.java +++ /dev/null @@ -1,149 +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 java.util.concurrent.atomic.AtomicInteger; - -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 AtomicInteger refCount; - - ThreadLocalActiveSpan(ThreadLocalActiveSpanSource source, Span wrapped, AtomicInteger refCount) { - this.source = source; - this.refCount = refCount; - this.wrapped = wrapped; - this.toRestore = source.tlsSnapshot.get(); - 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 (0 == refCount.decrementAndGet()) { - wrapped.finish(); - } - } - - @Override - public Continuation capture() { - return new ThreadLocalActiveSpan.Continuation(); - } - - @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 close() { - deactivate(); - } - - @Override - public String toString() { - return wrapped.toString(); - } - - private final class Continuation implements ActiveSpan.Continuation { - Continuation() { - refCount.incrementAndGet(); - } - - @Override - public ThreadLocalActiveSpan activate() { - return new ThreadLocalActiveSpan(source, wrapped, refCount); - } - } - -} 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 2cd7e6e2..00000000 --- a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalActiveSpanSource.java +++ /dev/null @@ -1,42 +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; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * 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 AtomicInteger(1)); - } - -} diff --git a/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java new file mode 100644 index 00000000..b6511f57 --- /dev/null +++ b/opentracing-util/src/main/java/io/opentracing/util/ThreadLocalScopeManager.java @@ -0,0 +1,69 @@ +/* + * 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; +import io.opentracing.ScopeManager; +import io.opentracing.Span; + +/** + * Created by bhs on 8/1/17. + */ +public class ThreadLocalScopeManager implements ScopeManager { + final ThreadLocal tlsScope = new ThreadLocal(); + + public class ThreadLocalScope implements Scope { + private final Span wrapped; + private final ThreadLocalScope toRestore; + private final Scope.Observer scopeObserver; + + 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); + } + + @Override + public Span span() { + return wrapped; + } + } + + @Override + public Scope activate(Span span) { + return new ThreadLocalScope(span, null); + } + + @Override + public Scope activate(Span span, Scope.Observer scopeObserver) { + return new ThreadLocalScope(span, scopeObserver); + } + + @Override + public Scope active() { + return tlsScope.get(); + } +} 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(); - } -} diff --git a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java similarity index 67% rename from opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java rename to opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java index 180662ee..5fd527a8 100644 --- a/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalActiveSpanSourceTest.java +++ b/opentracing-util/src/test/java/io/opentracing/util/ThreadLocalScopeManagerTest.java @@ -13,26 +13,28 @@ */ package io.opentracing.util; -import io.opentracing.ActiveSpan; +import io.opentracing.Scope; 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 ThreadLocalActiveSpanSourceTest { - private ThreadLocalActiveSpanSource source; +public class ThreadLocalScopeManagerTest { + private ThreadLocalScopeManager source; + @Before public void before() throws Exception { - source = new ThreadLocalActiveSpanSource(); + source = new ThreadLocalScopeManager(); } @Test public void missingActiveSpan() throws Exception { - ActiveSpan missingSpan = source.activeSpan(); - assertNull(missingSpan); + Scope missingScope = source.active(); + assertNull(missingScope); } @Test @@ -40,21 +42,21 @@ 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); + Scope scope = source.activate(span); try { - assertNotNull(activeSpan); - ActiveSpan otherActiveSpan = source.activeSpan(); - assertEquals(otherActiveSpan, activeSpan); + assertNotNull(scope); + Scope otherScope = source.active(); + assertEquals(otherScope, scope); } finally { - activeSpan.close(); + scope.close(); } // Make sure the Span got finish()ed. - verify(span).finish(); + verify(span, never()).finish(); // And now it's gone: - ActiveSpan missingSpan = source.activeSpan(); - assertNull(missingSpan); + Scope missingScope = source.active(); + assertNull(missingScope); } -} \ No newline at end of file +} 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