一、基类CurrentTraceContext
先看下完整的类CurrentTraceContext
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);
/**
* Like {@link #newScope(TraceContext)}, except returns {@link Scope#NOOP} if the given context is
* already in scope. This can reduce overhead when scoping callbacks. However, this will not apply
* any changes, notably in {@link TraceContext#extra()}. As such, it should be used carefully and
* only in conditions where redundancy is possible and the intent is primarily to facilitate
* {@link Tracer#currentSpan}. Most often, this is used to eliminate redundant scopes by wrappers.
*
* For example, RxJava includes hooks to wrap types that represent an asynchronous functional
* composition. For example, {@code flowable.parallel().flatMap(Y).sequential()} Assembly hooks
* can ensure each stage of this operation can see the initial trace context. However, other tools
* can also instrument the stages, including vert.x or even agent instrumentation. When wrapping
* callbacks, it can reduce overhead to use {@code maybeScope} as opposed to {@code newScope}.
*
*
Generally speaking, this is best used for wrappers, such as executor services or lifecycle
* hooks, which usually have no current trace context when invoked.
*
*
Implementors note
* For those overriding this method, you must compare {@link TraceContext#traceIdHigh()},
* {@link TraceContext#traceId()} and {@link TraceContext#spanId()} to decide if the contexts are
* equivalent. Due to details of propagation, other data like parent ID are not considered in
* equivalence checks.
*
* @param currentSpan span to place into scope or null to clear the scope
* @return a new scope object or {@link Scope#NOOP} if the input is already the case
*/
public Scope maybeScope(@Nullable TraceContext currentSpan) {
TraceContext currentScope = get();
if (currentSpan == null) {
if (currentScope == null) return Scope.NOOP;
return newScope(null);
}
return currentSpan.equals(currentScope) ? Scope.NOOP : newScope(currentSpan);
}
/** A span remains in the scope it was bound to until close is called. */
public interface Scope extends Closeable {
/**
* Returned when {@link CurrentTraceContext#maybeScope(TraceContext)} detected scope redundancy.
*/
Scope NOOP = new Scope() {
@Override public void close() {
}
@Override public String toString() {
return "NoopScope";
}
};
/** No exceptions are thrown when unbinding a span scope. */
@Override void close();
}
}
这里将Default类单领出来,因为它是一个默认实现。
public static final class Default extends CurrentTraceContext {
static final ThreadLocal DEFAULT = new ThreadLocal<>();
// Inheritable as Brave 3's ThreadLocalServerClientAndLocalSpanState was inheritable
static final InheritableThreadLocal INHERITABLE = new InheritableThreadLocal<>();
final ThreadLocal local;
/** Uses a non-inheritable static thread local */
public static CurrentTraceContext create() {
return new Default(DEFAULT);
}
/**
* Uses an inheritable static thread local which allows arbitrary calls to {@link
* Thread#start()} to automatically inherit this context. This feature is available as it is was
* the default in Brave 3, because some users couldn't control threads in their applications.
*
* This can be a problem in scenarios such as thread pool expansion, leading to data being
* recorded in the wrong span, or spans with the wrong parent. If you are impacted by this,
* switch to {@link #create()}.
*/
public static CurrentTraceContext inheritable() {
return new Default(INHERITABLE);
}
Default(ThreadLocal local) {
if (local == null) throw new NullPointerException("local == null");
this.local = local;
}
@Override public TraceContext get() {
return local.get();
}
@Override public Scope newScope(@Nullable TraceContext currentSpan) {
final TraceContext previous = local.get();
local.set(currentSpan);
class DefaultCurrentTraceContextScope implements Scope {
@Override public void close() {
local.set(previous);
}
}
return new DefaultCurrentTraceContextScope();
}
}
/** Wraps the input so that it executes with the same context as now. */
public Callable wrap(Callable task) {
final TraceContext invocationContext = get();
class CurrentTraceContextCallable implements Callable {
@Override public C call() throws Exception {
try (Scope scope = maybeScope(invocationContext)) {
return task.call();
}
}
}
return new CurrentTraceContextCallable();
}
/** Wraps the input so that it executes with the same context as now. */
// TODO: here and elsewhere consider a volatile reference. When the invocation context equals an
// existing wrapped context, it isn't necessarily the same as fields in context.extra may be
// different and equals does not consider extra. For example, if a new propagation field has been
// added, this should be considered. Doing so via a reference swap could be a lot cheaper than
// re-wrapping and achieve the same goal.
public Runnable wrap(Runnable task) {
final TraceContext invocationContext = get();
class CurrentTraceContextRunnable implements Runnable {
@Override public void run() {
try (Scope scope = maybeScope(invocationContext)) {
task.run();
}
}
}
return new CurrentTraceContextRunnable();
}
/**
* 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 Callable wrap(Callable task) {
return CurrentTraceContext.this.wrap(task);
}
@Override protected Runnable wrap(Runnable task) {
return CurrentTraceContext.this.wrap(task);
}
}
return new CurrentTraceContextExecutorService();
}
这里主要是使用类InheritableThreadLocal来传递线程上下文的信息。
static final InheritableThreadLocal INHERITABLE = new InheritableThreadLocal<>();
提供了两个构造函数,inheritable()和create(),具体的传参主要是在下面方法中。
Default(ThreadLocal local) {
if (local == null) throw new NullPointerException("local == null");
this.local = local;
}
接下来就是wrap类Callable,在调用call()方法前,先获取线程上下文,再把上下文传递给Scope对象。
public Callable wrap(Callable task) {
final TraceContext invocationContext = get();
class CurrentTraceContextCallable implements Callable {
@Override public C call() throws Exception {
try (Scope scope = maybeScope(invocationContext)) {
return task.call();
}
}
}
return new CurrentTraceContextCallable();
}
wrap类Runnable的做法和Callable差不多,就不重复。
再后面就是对Executor和ExecutorService进行wrap了。
主要是对入参是Runnable和Callable类型的对象,都传入上面修饰的子类。
二、Slf4jCurrentTraceContext
它是继承于CurrentTraceContext
重写了方法newScope()。
三、ThreadContextCurrentTraceContext
它也是继承于CurrentTraceContext
这个类本身没有多少代码,主要是对traceId/spanId等的赋值。
主要是看这个关键类ThreadContext。