package brave.propagation; import java.io.Closeable; import java.util.concurrent.Callable; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import javax.annotation.Nullable; /** * This makes a given span the current span by placing it in scope (usually but not always a thread * local scope). * * <p>This type is an SPI, and intended to be used by implementors looking to change thread-local * storage, or integrate with other contexts such as logging (MDC). * * <h3>Design</h3> * * This design was inspired by com.google.instrumentation.trace.ContextUtils, * com.google.inject.servlet.RequestScoper and com.github.kristofa.brave.CurrentSpan */ public abstract class CurrentTraceContext { /** Returns the current span in scope or null if there isn't one. */ public abstract @Nullable TraceContext get(); /** * Sets the current span in scope until the returned object is closed. It is a programming * error to drop or never close the result. Using try-with-resources is preferred for this reason. * * @param currentSpan span to place into scope or null to clear the scope */ public abstract Scope newScope(@Nullable TraceContext currentSpan); /** A span remains in the scope it was bound to until close is called. */ public interface Scope extends Closeable { /** No exceptions are thrown when unbinding a span scope. */ @Override void close(); } /** Default implementation which is backed by a static inheritable thread local */ public static final class Default extends CurrentTraceContext { // static as we want one context per thread, not one context per thread-instance. // if this is not static, patterns that coordinate via statics (like Tracer.current()) will break. // static ThreadLocal was also used in Brave 3's ThreadLocalServerClientAndLocalSpanState static final InheritableThreadLocal<TraceContext> local = new InheritableThreadLocal<>(); @Override public TraceContext get() { return local.get(); } @Override public Scope newScope(TraceContext currentSpan) { final TraceContext previous = local.get(); local.set(currentSpan); return () -> local.set(previous); } } /** Wraps the input so that it executes with the same context as now. */ public <C> Callable<C> wrap(Callable<C> task) { final TraceContext invocationContext = get(); return () -> { try (Scope scope = newScope(invocationContext)) { return task.call(); } }; } /** Wraps the input so that it executes with the same context as now. */ public Runnable wrap(Runnable task) { final TraceContext invocationContext = get(); return () -> { try (Scope scope = newScope(invocationContext)) { task.run(); } }; } /** * Decorates the input such that the {@link #get() current trace context} at the time a task is * scheduled is made current when the task is executed. */ public Executor executor(Executor delegate) { class CurrentTraceContextExecutor implements Executor { @Override public void execute(Runnable task) { delegate.execute(CurrentTraceContext.this.wrap(task)); } } return new CurrentTraceContextExecutor(); } /** * Decorates the input such that the {@link #get() current trace context} at the time a task is * scheduled is made current when the task is executed. */ public ExecutorService executorService(ExecutorService delegate) { class CurrentTraceContextExecutorService extends brave.internal.WrappingExecutorService { @Override protected ExecutorService delegate() { return delegate; } @Override protected <C> Callable<C> wrap(Callable<C> task) { return CurrentTraceContext.this.wrap(task); } @Override protected Runnable wrap(Runnable task) { return CurrentTraceContext.this.wrap(task); } } return new CurrentTraceContextExecutorService(); } }