-
Notifications
You must be signed in to change notification settings - Fork 344
RFC / WIP: make "auto-finishing" optional and non-Tracer-specific #166
Changes from all commits
6579cfb
40271b4
72ffc0e
648576b
eace002
897625f
d8a47d8
91cf7e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Opinion: I prefer no ActiveSpan type vs adding more options to it. This is generally discussed in #80 so not repeating here |
||
try { | ||
// Do things. | ||
} finally { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,42 @@ | |
* {@link ActiveSpan.Continuation}s, then re-{@link Continuation#activate()}d later. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this PR tries to distance from refcounting. There's a line that still talks about that which might be something to remove in this PR https://github.com/bhs/opentracing-java/blob/91cf7e5e500f635b9b765df3a107e0de32ad5f0e/opentracing-api/src/main/java/io/opentracing/ActiveSpan.java#L108 |
||
* | ||
* <p> | ||
* 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. | ||
* | ||
* <p> | ||
* Below is an illustration of how ActiveSpan and Continuation instances form a tree. | ||
* <pre> | ||
* | ||
* ActiveSpan | ||
* | | ||
* +- .capture() -> Continuation | ||
* | | | ||
* | +- .activate() -> ActiveSpan | ||
* | | | ||
* | +- .capture() -> Continuation | ||
* | | | | ||
* | | +- .activate() -> ActiveSpan | ||
* | | | | ||
* | | +- .deactivate(); | ||
* | +- .capture() -> Continuation | ||
* | | | ||
* | +- .activate() -> ActiveSpan | ||
* | | | ||
* | +- .deactivate(); | ||
* | | ||
* +- .capture() -> Continuation | ||
* | | | ||
* | +- .activate() -> ActiveSpan | ||
* | | | ||
* | +- .deactivate(); | ||
* | | ||
* +- .deactivate(); | ||
* | ||
* </pre> | ||
* | ||
* <p> | ||
* NOTE: {@link ActiveSpan} extends {@link Closeable} rather than {@code AutoCloseable} in order to preserve support | ||
* for JDK1.6. | ||
* | ||
|
@@ -106,4 +142,13 @@ interface Continuation { | |
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" in the capture/activate tree. | ||
*/ | ||
interface Observer { | ||
void afterCapture(ActiveSpan captured, Continuation destination); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These methods look a bit fancy. Isn't it better to just pass the span in all of them? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I didn't want to leave anything out that would be "on hand" in an implementation. Agree that this smells funny, though. |
||
void afterActivate(Continuation source, ActiveSpan justActivated); | ||
void afterDeactivate(ActiveSpan deactivated, Finishable finisher); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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}. | ||
* | ||
* <p> | ||
* If there is an {@link ActiveSpan active span}, it becomes an implicit parent of any newly-created | ||
|
@@ -38,13 +37,19 @@ public interface ActiveSpanSource { | |
*/ | ||
ActiveSpan activeSpan(); | ||
|
||
/** | ||
* @see #makeActive(Span, ActiveSpan.Observer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ambiguous - is it a shortcut for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we want to maintain backward compatibility of behaviour, then I think the javadoc comment for all of the methods that have an Observer variant should say that they represent the case of using the We may also want to consider deprecating these methods, so that for the 1.0 API the Observer has to explicitly be specified, e.g. whether |
||
*/ | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One idea (not saying it's a good one... or that I understand all the implications) for making this be more backwards compatible is to make the |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package io.opentracing; | ||
|
||
/** | ||
* {@link Finishable} factors out the overloaded {@link #finish()} method(s). | ||
* | ||
* @see ActiveSpan.Observer#onDeactivate(ActiveSpan, Finishable) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
* @see Span | ||
*/ | ||
public interface Finishable { | ||
/** | ||
* Sets the end timestamp to now and records the span. | ||
* | ||
* <p>With the exception of calls to {@link BaseSpan#context}, this should be the last call made to the span | ||
* instance. Future calls to {@link #finish} are defined as noops, and future calls to methods other than | ||
* {@link BaseSpan#context} lead to undefined behavior. | ||
* | ||
* @see BaseSpan#context() | ||
*/ | ||
void finish(); | ||
|
||
/** | ||
* Sets an explicit end timestamp and records the span. | ||
* | ||
* <p>With the exception of calls to Span.context(), this should be the last call made to the span instance, and to | ||
* do otherwise leads to undefined behavior. | ||
* | ||
* @param finishMicros an explicit finish time, in microseconds since the epoch | ||
* | ||
* @see BaseSpan#context() | ||
*/ | ||
void finish(long finishMicros); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would this be a good time to consider separating the units from the value? Perhaps a signature like this: |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package io.opentracing.util; | ||
|
||
import io.opentracing.ActiveSpan; | ||
import io.opentracing.ActiveSpanSource; | ||
import io.opentracing.Span; | ||
import io.opentracing.Finishable; | ||
|
||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
/** | ||
* {@link AutoFinisher} is an {@link ActiveSpan.Observer} 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}. | ||
* | ||
* <p> | ||
* Use {@link AutoFinisher} like this: | ||
* <pre><code> | ||
* try (ActiveSpan span = tracer.buildSpan("...").startActive(new AutoFinisher())) { | ||
* // (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 | ||
* </code></pre> | ||
* | ||
* <p> | ||
* Note that {@link AutoFinisher} works by counting the number of extant {@link ActiveSpan} and | ||
* {@link ActiveSpan.Continuation} references to the underlying {@link Span} provided at | ||
* {@link ActiveSpanSource#makeActive(Span, ActiveSpan.Observer)} | ||
* </p> | ||
*/ | ||
public class AutoFinisher implements ActiveSpan.Observer { | ||
private final AtomicInteger refCount; | ||
|
||
public AutoFinisher() { | ||
refCount = new AtomicInteger(1); | ||
} | ||
|
||
@Override | ||
public void afterCapture(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 afterActivate(ActiveSpan.Continuation source, ActiveSpan justActivated) {} | ||
|
||
@Override | ||
public void afterDeactivate(ActiveSpan activeSpan, Finishable finisher) { | ||
if (0 == refCount.decrementAndGet()) { | ||
finisher.finish(); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems the
try()
would still callclose()
and finish the span, whether the AutoFinisher is passed or not.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(I reverted to the old BaseSpan/Span/ActiveSpan class hierarchy)