Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: ActiveSpanHolder demo diff #4

Merged
merged 6 commits into from
Apr 4, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions opentracing-api/src/main/java/io/opentracing/ActiveSpan.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package io.opentracing;

import java.io.Closeable;

/**
* In any execution context (or any thread, etc), there is at most one "active" {@link Span} primarily responsible for
* the work accomplished by the surrounding application code. That active Span may be accessed via the
* {@link ActiveSpanSource#active()} method. If the application needs to defer work that should be part of the same Span, the
* Source provides a {@link ActiveSpan#defer} 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.
*
* <p>
* {@link ActiveSpan}s are created via {@link Tracer.SpanBuilder#startAndActivate()} or {@link ActiveSpanSource#adopt}. They can
* be {@link ActiveSpan#defer()}ed as {@link ActiveSpan.Continuation}s, then re-{@link Continuation#activate()}d later.
*
* @see ActiveSpanSource
*/
public interface ActiveSpan extends Closeable, Span {
/**
* Mark the end of the active period for the {@link Span} pinned by this {@link ActiveSpan}. When the last
* {@link ActiveSpan} is deactivated for a given {@link Span}, it is automatically {@link Span#finish()}ed.
* <p>
* <p>
* NOTE: It is an error to call deactivate() more than once on a single {@link ActiveSpan}.
*
* @see Closeable#close() {@link ActiveSpan}s are auto-closeable and may be used in try-with-resources blocks
*/
void deactivate();

/**
* "Fork" a new {@link Continuation} associated with this {@link ActiveSpan} and {@link Span}, as well as any
* 3rd-party execution context of interest.
* <p>
* <p>
* 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 active @{Handle} does.
*
* @return a new {@link Continuation} to {@link Continuation#activate()} at the appropriate time.
*/
Continuation defer();

/**
* 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.)
*
* <p>
* 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 need not be defined within the OpenTracing core API, and so they are not.
*
* <p>
* NOTE: {@link Continuation} extends {@link Closeable} rather than AutoCloseable in order to keep support
* for JDK1.6.
*
* @see ActiveSpanSource#adopt(Span)
*/
interface Continuation {
/**
* Make the Span (and other execution context) encapsulated by this Continuation active and return it.
*
* <p>
* NOTE: It is an error to call activate() more than once on a single Continuation instance.
*
* @see ActiveSpanSource#adopt(Span)
* @return a handle to the newly-activated Span
*/
ActiveSpan activate();
}

}
94 changes: 6 additions & 88 deletions opentracing-api/src/main/java/io/opentracing/ActiveSpanSource.java
Original file line number Diff line number Diff line change
@@ -1,108 +1,26 @@
package io.opentracing;

import java.io.Closeable;

/**
* {@link ActiveSpanSource} allows an existing (possibly thread-local-aware) execution context provider to act as a
* source for an actively-scheduled OpenTracing Span.
*
* <p>
* In any execution context (or any thread, etc), there is at most one "active" Span primarily responsible for the
* work accomplished by the surrounding application code. That active Span may be accessed -- via an
* {@link ActiveSpanSource.Handle} -- with the {@link ActiveSpanSource#active()} method. If the application needs
* to defer work that should be part of the same Span, the ActiveSpanSource provides a
* {@link Handle#defer} method that returns a {@link Continuation}; this continuation may be used to re-activate and
* the Span in that other asynchronous executor and/or thread.
*
* <p>
* There are two important use cases for {@link ActiveSpanSource} and {@link ActiveSpanSource.Continuation}:
* <ul>
*
* <li>Accessing the active {@link Handle}/{@link Span}: first, call {@link Tracer#spanSource()}, then use
* {@link ActiveSpanSource#active()} and {@link Handle#span}.
*
* <li>Propagating the active {@link Handle} to another (async) executor. First, call {@link Handle#defer()} to
* defer a reference to the active Span, then pass that to the async method (even via a final local variable
* that's used within a closure). Within that closure, code should call {@link Continuation#activate()} to install
* the deferred {@link Handle}/{@link Span} for subsequent calls to {@link ActiveSpanSource#active)}. (Helper
* libraries can abstract away much of the above behind {@link java.util.concurrent.ExecutorService} wrappers)
*
* </ul>
* @see ActiveSpan
*/
public interface ActiveSpanSource {

interface Handle extends Closeable {
/**
* @return the active {@link Span} pinned by this {@link Handle}.
*/
Span span();

/**
* Mark the end of the active period for the {@link Span} pinned by this {@link Handle}. When the last
* {@link Handle} is deactivated for a given {@link Span}, it is automatically {@link Span#finish()}ed.
*
* <p>
* NOTE: It is an error to call deactivate() more than once on a single {@link Handle}.
*
* @see Closeable#close() {@link Handle}s are auto-closeable and may be used in try-with-resources blocks
*/
void deactivate();

/**
* "Fork" a new {@link Continuation} associated with this {@link Handle} and {@link Span}, as well as any
* 3rd-party execution context of interest.
*
* <p>
* 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 active @{Handle} does.
*
* @return a new {@link Continuation} to {@link Continuation#activate()} at the appropriate time.
*/
Continuation defer();
}

/**
* 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.)
*
* <p>
* Most users do not directly interact with {@link Continuation}, {@link Continuation#activate()} or
* {@link Handle#deactivate()}, but rather use {@link ActiveSpanSource}-aware Runnables/Callables/Executors.
* Those higher-level primitives need not be defined within the OpenTracing core API, and so they are not.
*
* <p>
* NOTE: {@link Continuation} extends {@link java.io.Closeable} rather than AutoCloseable in order to keep support
* for JDK1.6.
*
* @see ActiveSpanSource#adopt(Span)
*/
interface Continuation {
/**
* Make the Span (and other execution context) encapsulated by this Continuation active and return it.
*
* <p>
* NOTE: It is an error to call activate() more than once on a single Continuation instance.
*
* @see ActiveSpanSource#adopt(Span)
* @return a handle to the newly-activated Span
*/
Handle activate();
}

/**
* @return the active {@link Handle}, or null if none could be found. This does not affect the reference count for
* the {@link Handle}.
* @return the active {@link ActiveSpan}, or null if none could be found. This does not affect the reference count for
* the {@link ActiveSpan}.
*/
Handle active();
ActiveSpan active();

/**
* Wrap and "adopt" a @{link Span} by encapsulating it – and any active state (e.g., MDC state) in the execution
* context – in a new @{link Handle}.
*
* @param span the Span just started
* @return a @{link Handle} that encapsulates the given Span and any other ActiveSpanSource-specific context (e.g.,
* @return a @{link Handle} that encapsulates the given Span and any other Source-specific context (e.g.,
* MDC data)
*/
Handle adopt(Span span);
ActiveSpan adopt(Span span);
}
2 changes: 1 addition & 1 deletion opentracing-api/src/main/java/io/opentracing/Span.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
*
* <p>Spans are created by the {@link Tracer#buildSpan} interface.
*/
public interface Span extends Closeable {
public interface Span {
/**
* Retrieve the associated SpanContext.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
package io.opentracing;

import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
* {@link ThreadLocalActiveSpan} is a trivial {@link ActiveSpan} implementation that relies on Java's thread-local
* storage primitive.
*
* @see ActiveSpanSource
* @see Tracer#spanSource()
*/
public class ThreadLocalActiveSpan implements ActiveSpan {
private ThreadLocalActiveSpanSource source;
private final Span wrapped;
private ThreadLocalActiveSpan toRestore = null;
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 defer() {
refCount.incrementAndGet();
return source.makeContinuation(wrapped, refCount);
}

@Override
public SpanContext context() {
return wrapped.context();
}

@Override
public void finish() {
wrapped.finish();
}

@Override
public void finish(long finishMicros) {
wrapped.finish(finishMicros);
}

@Override
public Span setTag(String key, String value) {
return wrapped.setTag(key, value);
}

@Override
public Span setTag(String key, boolean value) {
return wrapped.setTag(key, value);
}

@Override
public Span setTag(String key, Number value) {
return wrapped.setTag(key, value);
}

@Override
public Span log(Map<String, ?> fields) {
return wrapped.log(fields);
}

@Override
public Span log(long timestampMicroseconds, Map<String, ?> fields) {
return wrapped.log(timestampMicroseconds, fields);
}

@Override
public Span log(String event) {
return wrapped.log(event);
}

@Override
public Span log(long timestampMicroseconds, String event) {
return wrapped.log(timestampMicroseconds, event);
}

@Override
public Span setBaggageItem(String key, String value) {
return wrapped.setBaggageItem(key, value);
}

@Override
public String getBaggageItem(String key) {
return wrapped.getBaggageItem(key);
}

@Override
public Span setOperationName(String operationName) {
return wrapped.setOperationName(operationName);
}

@Override
public Span log(String eventName, Object payload) {
return wrapped.log(eventName, payload);
}

@Override
public Span log(long timestampMicroseconds, String eventName, Object payload) {
return wrapped.log(timestampMicroseconds, eventName, payload);
}

@Override
public void close() {

}

static class Continuation implements ActiveSpan.Continuation {
private ThreadLocalActiveSpanSource source;
private final Span span;
private final AtomicInteger refCount;

Continuation(ThreadLocalActiveSpanSource source, Span span, AtomicInteger refCount) {
this.source = source;
this.refCount = refCount;
this.span = span;
}

@Override
public ThreadLocalActiveSpan activate() {
return new ThreadLocalActiveSpan(source, span, refCount);
}
}

}
Loading