Tracer 这个类主要用于做 Span 的相关操作。
核心方法有一下几个:
public Span newChild(TraceContext parent);//新建一个子跨度,有parent
public Span newTrace();//新建一个跨度,无parent
public Span nextSpan();//新建一个跨度,根据当前作用域判断是新建自跨度还是新建无关联的跨度
public final Span joinSpan(TraceContext context);//加入一个跨度,这种情况一般是用于 Client -> Server 访问时出现的,在客户端有一条trace记录,在服务端进行trace记录时候,使用了该方法,服务端会生成一条有同样trace ID的记录,合并客户端与服务端这两条trace信息可以则可以得到一条完整的trace日志。
public SpanInScope withSpanInScope(@Nullable Span span);//定义一个跨度的作用域,就是TreadLocal的原理,在该作用域下,你可以随时获得当前 TraceContext
接下来一个个看:
public Span nextSpan() {
//获取当前 TraceContext
TraceContext parent = currentTraceContext.get();
//若存在则新建一个自跨度,否则新建一个无关联跨度
return parent != null ? newChild(parent) : newTrace();
}
public Span newTrace() {
return _toSpan(newRootContext());//直接新建一个TraceContext,新建一个span
}
Span _toSpan(TraceContext decorated) {
if (isNoop(decorated)) return new NoopSpan(decorated);
// 获取或者创建一个挂起的跨度
//这里多了一个新建的对象叫 PendingSpan ,用于收集一条trace上暂时被挂起的未完成的span
PendingSpan pendingSpan = pendingSpans.getOrCreate(decorated, false);
//新建一个跨度(RealSpan是Span的一个实现)
return new RealSpan(decorated, pendingSpans, pendingSpan.state(), pendingSpan.clock(),
finishedSpanHandler);
}
//可看出 nextSpan 就是调用 newChild 或者 newTrace而已
public Span nextSpan() {
TraceContext parent = currentTraceContext.get();
return parent != null ? newChild(parent) : newTrace();
}
//这个方法注解足够,直接看就行了
public final Span joinSpan(TraceContext context) {
if (context == null) throw new NullPointerException("context == null");
if (!supportsJoin) return newChild(context);
int flags = InternalPropagation.instance.flags(context);
if (alwaysSampleLocal && (flags & FLAG_SAMPLED_LOCAL) != FLAG_SAMPLED_LOCAL) {
flags |= FLAG_SAMPLED_LOCAL;
}
// If we are joining a trace, we are sharing IDs with the caller
// If the sampled flag was left unset, we need to make the decision here
if ((flags & FLAG_SAMPLED_SET) != FLAG_SAMPLED_SET) { // cheap check for not yet sampled
// then the caller didn't contribute data
flags = InternalPropagation.sampled(sampler.isSampled(context.traceId()), flags);
} else if ((flags & FLAG_SAMPLED) == FLAG_SAMPLED) {
// we are recording and contributing to the same span ID
flags = flags | FLAG_SHARED;
}
context = InternalPropagation.instance.newTraceContext(
flags | FLAG_LOCAL_ROOT,
context.traceIdHigh(),
context.traceId(),
context.spanId(), // local root
context.parentIdAsLong(),
context.spanId(),
context.extra()
);
//返回一个跨度,使用 propagationFactory.decorate() 装饰一个跨度,实际上该出的代码什么也没装饰,直接返回 context
return _toSpan(propagationFactory.decorate(context));
}
//新建作用域就一句代码,返回一个 SpanInScope对象,该对象用于定义一个作用域,调用它的 close 方法即可结束作用域
public SpanInScope withSpanInScope(@Nullable Span span) {
return new SpanInScope(currentTraceContext.newScope(span != null ? span.context() : null));
}
//看看currentTraceContext.newScope 做了什么
//首先它拥有一个内部对象, local,就是一个 TreadLocal 对象,所以可以解释作用域为什么可以随时获取到所需的 TraceContext
final ThreadLocal<TraceContext> local;
public Scope newScope(@Nullable TraceContext currentSpan) {
//获取之前作用域的 context
final TraceContext previous = local.get();
//设置当前作用域的 context
local.set(currentSpan);
//接下来定义一个作用域的内部类,它的close方法将之前作用域的 context 设置为当前的
//也就是当结束一个作用域的时候,将上一个作用域还原回来,如此控制
class ThreadLocalScope implements Scope {
@Override public void close() {
local.set(previous);
}
}
//新建一个作用域
Scope result = new ThreadLocalScope();
//调用方法装饰这个作用域并返回(利用已设置好的装饰器)
return decorateScope(currentSpan, result);
}
protected Scope decorateScope(@Nullable TraceContext currentSpan, Scope scope) {
int length = scopeDecorators.size();
//遍历装饰器,对作用域进行装饰包装,可忽略这部分拓展,一般使用并不会用上装饰器,有兴趣的同学可以研究下该部分
for (int i = 0; i < length; i++) {
scope = scopeDecorators.get(i).decorateScope(currentSpan, scope);
}
return scope;
}
到此为止,Tracer的基本工作已经大体清楚。
下一章将解读跨度( Span )的内容。