中文官方的中文手册
API简介
一、opentracing的概念与术语
opentracing 本质是一套tracing字段的规范,并不是监控产品。
这么做是希望各种监控产品能对接进去。
opentracing的概念与术语
二、opentracing 的规范
规范
为了被各厂家支持,opentracing 模糊定义了必要的字段,由使用者具体决定。
其他字段基本上是清晰定义了。
2.1 模糊定义的部分
各个APM产品的调用链关键字段描述都有差异。但是共识是
要有调用链标识
、operationName(名称)
,span的标识
、上游span的标识
、时间戳
、span耗时
。
示例
如下是一个例子,在一级字段例定义了如下格式。但是其他监控产品有可能把traceId
换成trace_Id
。所以这部分opentracing
是模糊定义的
名称 | 描述 | |
---|---|---|
traceId | 调用链唯一标识 | uint64 |
spanId | 本次调用的唯一标识 | uint64 |
parentSpanId | 上游调用块的标识 | uint64 |
start | 请求开始的时间 | string |
duration | 请求耗时,单位为毫秒 | uint64 |
operation | String span的名称 | string |
baggages
一级字段可以为空一个Map类型,这个字段要求所有要在trace所有的span中流动。这个字段不管是进程间,还是跨应用调用,都应该在span间传递。
2.2 清晰定义的部分
tags和logs是也是一级字段,但是可以为空。
不在span间传递,最终被和生成的span存储进数据库中。
tags是span的标签,标识关键信息。
logs偏重于日志的输出。
2.2.1 tags & logs 表 (耳机标签)
tags
tags是应该被应用到整个Span的属性。比如某个时间范围内的,而不是一个特殊的时刻。比如component=spring
并不会针对特定的span,而是对span
而言这个模块产生的消息就是spring
框架产生的。像事件event
等一些特殊的应该放在logs
字段中。
Span tag name | Type | Notes and examples |
---|---|---|
component | string | 软件包,框架,库,或者模块 eg., "grpc" , "django" , "JDBI" . |
db.instance | string | 数据库实例的名称. eg., 如果jdbc.url="jdbc:mysql://127.0.0.1:3306/customers" , ,那么实例名称应该是"customers" . |
db.statement | string | 数据库的查询语句。eg., 对于db.type="sql" , "SELECT * FROM wuser_table" ; 对于 db.type="redis" ,"SET mykey 'WuValue'" . |
db.type | string | 数据库的类型., "sql"代表SQL 类型的database。其他数据库, eg. "cassandra" , "hbase" 或"redis" |
db.user | string | 访问数据的用户名eg., "readonly_user" 或者 "reporting_user" |
error | bool | true 意味应用的程序出现了问题 |
http.method | string | HTTP method 的请求类型 Span. eg., "GET", "POST" |
http.status_code | integer | HTTP 相应的状态码. eg., 200, 503, 404 |
http.url | string | 请求的 URL。eg., "https://domain.net/path/|to?resource=here" |
message_bus.destination | string | 用来交换信息的地址。eg. 一条Kafka 的记录 和 "topic name" 可以被 采集器的 producer 或者 consumer 解析、存储在这个tag |
peer.address | string | 远程的“address” ,适合网络通信。他的值可能是ip:port 、一个hostname , FQDN, 沈甚至一个JDBC子字符串类似 "mysql://prod-db:3306" |
peer.hostname | string | 远程的hostname eg., "opentracing.io", "internal.dns.name" |
peer.ipv4 | string | 远程的 IPv4 eg., “127.0.0.1” |
peer.ipv6 | string | 远程的 IPv6 eg.,"2001:0db8:85a3:0000:0000:8a2e:0370:7334" |
peer.port | integer | 远程的 port. eg.,80 |
peer.service | string | 远程的 service name ,eg., "elasticsearch", "a_custom_microservice", "memcache" |
sampling.priority | integer | 如果 > 0, 选中,\<0 , 丢弃掉 。 |
span.kind | string | 类型"client" 或"server" 对于 RPC, 或者合适的语义比如"producer" ,"consumer" 在消息服务 |
logs
每个span的LOG字段都有时间戳。想日志一箱,
Span log field name | Type | Notes and examples |
---|---|---|
error.kind | string | 这个类型仅仅是("error" ). eg.,"Exception", "OSError" |
error.object | object | 针对与特定语言的支持 (比如., Java, Python),真实的lThrowable/Exception/Error . eg., java的java.lang.UnsupportedOperationException , python的a pythonexceptions.NameError instance |
event | string | 对span生命周期内,一些特定的事件的标识。例如,在浏览器页面加载过程中,获得或释放一个互斥锁就是一个特定的事件域, 标准. E.g., 比如 Zipkin的"cs", "sr", "ss", or "cr" . 或者"initialized" 、 "timed out"、"error" |
message | string | 一个简洁的人类可读的,只有一行的消息"Could not connect to backend", "Cache invalidation succeeded" |
stack | string | 栈信息最终,具体语言相关,先错代码出错的行数,但是不一定是一个error。E.g., "File \"example.py\", line 7, in \<module\>\ncaller()\nFile \"example.py\", line 5, in caller\ncallee()\nFile \"example.py\", line 2, in callee\nraise Exception(\"Yikes\")\n" |
2.2.2 解释
1 描述特殊模型的变量,比如rpc
span.kind
。 如"client" or “server”,标识rpc的访问端Peer Tags
。记录地址信息peer.address, peer.hostname, peer.ipv4, peer.ipv6, peer.port, peer.service
2 HTTP Server Tags
http.url
- string- URL 分布式追踪中,这一阶段的调用的URL地址, 参考 standard URI format.
- Protocol 协议,可选
- Examples:
1. https://domain.net/path/to?resource=here
2. domain.net/path/to/resource
3. http://user:pass@domain.org:8888
+http.method
- string
+ HTTP 请求被处理的方法.
+ Case-insensitive 大小写敏感
+ Examples:
GET, POST, HEAD
http.status_code
- integer- HTTP 返回值
- Examples:
200, 503
+span.kind
- string
server 定义这是服务端类型的span (see “Component Identification, 框架定义”)
3 Captured errors
捕获错误
示例
比如捕获错误的对象
event="error"
error.object=<error object instance>
示例
vent="error"
message="..."
stack="..." (optional)
error.kind="..." (optional)
4 描述database
+db.type, db.instance, db.user, and db.statement
: as described in the table above
peer.address, peer.hostname, peer.ipv4, peer.ipv6, peer.port, peer.service
: optional- tags that describe the database peer
span.kind: "client"
5 Message Bus 信息交换
一般可以按照如下的方式组合
message_bus.destination:
如表中描述
+span.kind
: either"producer"
or"consumer".
+peer.address, peer.hostname, peer.ipv4, peer.ipv6, peer.port, peer.service
: 可选的标签 描述消息broker
三、opentracing 代码结构
3.1 代码结构
代码结构
在maven 仓库里
名称 | 描述 | 依赖 |
---|---|---|
opentracing-api | 是一个纯粹的API没有任何依赖 | - |
opentracing-noop | 实现了API,但是是空实现什么也不干 | opentracing-api |
opentracing-util | 包含了一个GlobalTracer 和基于Thread_local 的简单实现ScopeManager | opentracing-api ; opentracing.noop |
opentracing-mock | mock测试,包含一个简单的MockTracer ,将数据存褚进内存 | opentracing-api ; opentracing.noop ;opentracing-util |
一个存在在测试是的包,用于测试和尝试新特性opentracing-testbed
3.2 核心组件概念介绍
大致将各个模块的作用
代码模型
java-api
opentracing里主要包含以下几个组件:
Tracer
Tracer
是一个调用链生成器,他应该是全局的。最佳实践是应用在启动时,创建一个Tracer
的实例。可以被其他线程访问到,去生成消息。
ScopeManager
这个类是0.30版本之后新加入的组件,这个组件的作用是能够通过它获取当前线程中启用的Span信息,并且可以启用一些处于未启用状态的span。在一些场景中,我们在一个线程中可能同时建立多个span,但是同一时间统一线程只会有一个span在启用,其他的span可能处在下列的状态中:
- 等待子span完成
- 等待某种阻塞方法
- 创建并未开始
对于一个线程Thread来说,一个时刻只有一个
span
是激活状态的(active)。其他spans
,可能是这个span
的上游,或者等待资源(I/O或者childspan的完成)等,这些spans
的特点是,开始了(start)但是没有结束(end)。
这个时候获取当前的span
是很不方便的,所以就有scopeManager()
来管理每个span的scope
。获取当前的span可以使用
io.opentracing.Tracer tracer = ...;
...
Span span = tracer.scopeManager().activeSpan();
if (span != null) {
span.log("...");
}
- 1
- 2
- 3
- 4
- 5
- 6
Span
表示分布式调用链条中的一个调用单元,比方说某个dubbo的调用provider,或者是个http调用的服务提供方,他的边界包含一个请求进到服务内部再由某种途径(http/dubbo等)从当前服务出去。一个span
一般会记录这个调用单元内部的一些信息,例如:
1 生成一个新的span:
spans是树状的,一个span要么是根span,要么来源于另外一个span。
io.opentracing.Tracer tracer = ...;
...
Span span = tracer.buildSpan("someWork").start();
try (Scope scope = tracer.scopeManager().activate(span)) {
// Do things.
} catch(Exception ex) {
Tags.ERROR.set(span, true);
span.log(Map.of(Fields.EVENT, "error", Fields.ERROR_OBJECT, ex, Fields.MESSAGE, ex.getMessage()));
} finally {
span.finish();
}
1 无视存在激活状态span:
那么新的span,将会以激活的span
做为parent。这是如果想无视激活的span
想指定具体的parent。可以使用
io.opentracing.Tracer tracer = ...;
...
Span span = tracer.buildSpan("someWork").ignoreActiveSpan().start();
3 异步的span:
考虑一下一个异步的span,模型如下
[ ServiceHandlerSpan ]
|·FunctionA·|·····waiting on an RPC······|·FunctionB·|
––––––––––––––––––––––––––––-> time
- 1
- 2
- 3
- 4
ServiceHandlerSpan
在运行时被FunctionA
激活了,在FunctionB
继续执行。但是如果是一个RPC,span的生成就必须等待FunctionB
。
ScopeManager
的API可以让在FunctionA
中激活,然后在FunctionB
中获取之前的span
,让它再次被激活。
步骤如下
- 开始一个span
- 在
closure/Runnable/Future
等线程函数开始之初,调用tracer.scopeManager().activate(span)
,去重新激活Span
,并且拿到一个新的Scope
,然后当span不激活时close()
掉 - 调用
span.finish()
结束
io.opentracing.Tracer tracer = ...; ... // STEP 1 ABOVE: start the Span. final Span span = tracer.buildSpan("ServiceHandlerSpan").start(); try (Scope scope = tracer.scopeManager().activate(span)) { // Do work. ...
future <span class="token operator">=</span> CompletableFuture<span class="token punctuation">.</span><span class="token function">supplyAsync</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// STEP 2 ABOVE: reactivate the Span in the callback.</span> <span class="token keyword">try</span> <span class="token punctuation">(</span>Scope scope <span class="token operator">=</span> tracer<span class="token punctuation">.</span><span class="token function">scopeManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">activate</span><span class="token punctuation">(</span>span<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">thenRun</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// STEP 3 ABOVE: finish the Span when the work is done.</span> span<span class="token punctuation">.</span><span class="token function">finish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
}
SpanContext
表示一个span对应的上下文,span和spanContext基本上是一一对应的关系,上下文存储的是一些需要跨越边界的一些信息,例如:
- spanId 当前这个span的id
- traceId 这个span所属的traceId(也就是这次调用链的唯一id)
- baggage 其他的能过跨越多个调用单元的信息
这个SpanContext可以通过某些媒介和方式传递给调用链的下游来做一些处理(例如子Span的id生成、信息的继承打印日志等等)
Carrier
表示的是一个承载spanContext
的媒介,比方说在http调用场景中会有HttpCarrier
,在dubbo调用场景中也会有对应的DubboCarrier
。
Formatter
这个接口负责了具体场景中序列化反序列化
上下文的具体逻辑,例如在HttpCarrier使用中通常就会有一个对应的HttpFormatter。Tracer的注入和提取就是委托给了Formatter
3.3 opentracing-api 详解
这就是opentracing-api
的所有类
3.3.1 log & tag
log.Fields
log仅有一个类
public class Fields {
private Fields() {
}
public static final String ERROR_KIND = "error.kind";
public static final String ERROR_OBJECT = "error.object";
public static final String EVENT = "event";
public static final String MESSAGE = "message";
public static final String STACK = "stack";
}
tag
tag仅有一个接口
public interface Tag<T> {
String getKey();
void set(Span span, T value);
}
tags封装了所有的属性
3.3.2 propagation
负责 放入和解析的类。这里仅仅定义了接口,没有定义实现。
3.3.2.1 Carrier
存放消息的载体。
Map结构的Carrier
TextMap
是Map结构的Carrier
TextMapInject
定义了 put接口
TextMapExtract
定义了一个获取Map
迭代器的方法。
TextMapInjectAdapter
接受map,并写入数据
public class TextMapInjectAdapter implements TextMapInject { protected final Map<String, ? super String> map;
<span class="token keyword">public</span> <span class="token function">TextMapInjectAdapter</span><span class="token punctuation">(</span><span class="token keyword">final</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token operator">?</span> <span class="token keyword">super</span> String<span class="token operator">></span> map<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>map <span class="token operator">=</span> map<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">put</span><span class="token punctuation">(</span>String key<span class="token punctuation">,</span> String value<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>map<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>key<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
TextMapInjectAdapter
接受map,获取迭代器
public class TextMapExtractAdapter implements TextMapExtract { protected final Map<String,String> map;
<span class="token keyword">public</span> <span class="token function">TextMapExtractAdapter</span><span class="token punctuation">(</span><span class="token keyword">final</span> Map<span class="token generics function"><span class="token punctuation"><</span>String<span class="token punctuation">,</span>String<span class="token punctuation">></span></span> map<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>map <span class="token operator">=</span> map<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> Iterator<span class="token operator"><</span>Map<span class="token punctuation">.</span>Entry<span class="token generics function"><span class="token punctuation"><</span>String<span class="token punctuation">,</span> String<span class="token punctuation">></span></span><span class="token operator">></span> <span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> map<span class="token punctuation">.</span><span class="token function">entrySet</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">iterator</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
二进制结构的Carrier
首先ByteBuffer
是java的nio的类,内置一个byte[]数组,length就是Carrier
的大小。
+injectionBuffer
是返回一个ByteBuffer
对象,用于用于SpanContext的注入。当调用Tracer.inject()
会调用到这个方法。
extractionBuffer
返回SpanContext的Carrier。
Tracer.extract()`会调到。
3.3.2.2 Format
TextMap
定义了,解析和放置的行为,比如解析RPC协议和Http协议,就是不同实现类,真正的定义的行为。
Format
更像是一个工厂,里面保存了各种TextMap
的实现。
*Tracer tracer = ...
io.opentracing.propagation.HttpHeaders httpCarrier = new AnHttpHeaderCarrier(httpRequest);
*SpanContext spanCtx = tracer.extract(Format.Builtin.HTTP_HEADERS, httpCarrier)
- 1
- 2
- 3
类似与解码器,下载一个包含多种视频格式的解码器。播放软件,只需将解码器和视频流导入,就能解析固定的格式。
作为开发者,重点要放在TextMap
的定义上
3.3.3 References
代表了span的关系。
- child_of 父子关系
- follows_from 兄弟关系,先后关系
public final class References { private References(){}
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String CHILD_OF <span class="token operator">=</span> <span class="token string">"child_of"</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">final</span> String FOLLOWS_FROM <span class="token operator">=</span> <span class="token string">"follows_from"</span><span class="token punctuation">;</span>
3.3.4 SpanContext
SpanContext
代表了span的状态信息。它必须要被传递给后代的span。
SpanContext的内容可以分为两块:
- user-level 的 Baggage,他要被传递给下游span,可以为空。
- 必要的trace信息,作为span和trace的标识,必须要被携带。比如
trace_id, span_id, sampled 等
3.3.5 Span
- 必须要包含一个SpanContext
- 其余都是放置属性的方法
3.3.6 Scope
代表了一个激活和被激活状态的span 。
往往一个span从开始到finsh完成,期间要等chid_span先完成。child_span运行时,当前的span就处于一个没有激活的状态。
所以Scope
就可以理解代表了附加状态的一个span
,也被用来获取span
。也负责一个span的关闭
public interface Scope extends Closeable {
// 仅有一个方法
void close();
}
- 1
- 2
- 3
- 4
3.3.7 ScopeManager
用来在运行过程中管理Span。
- activate 方法
对于当前的环境(通常是一个Thread),把传入的Span设置为当前的span,就是active
激活的,然后返回一个Scope
来管理这Span
。通常的用法为
Span span = tracer.buildSpan("...").start();
try (Scope scope = tracer.scopeManager().activate(span)) {
span.setTag("...", "...");
...
} catch (Exception e) {
span.log(...);
} finally {
// Optionally finish the Span if the operation it represents
// is logically completed at this point.
span.finish();
}
推荐把Scope
放在try-catch资源中,方便最终调用close
方法关掉。
3.3.7 Tracer
生成调用链信息的,建议是创建一个全局的tracer。方法就如方法名所示。
四、 api的实现
4.1 opentracing-noop
什么也不做的实现
什么也不做的实现,比如如下,就可以看出,span的函数什么也不做
final class NoopSpanImpl implements NoopSpan {
<span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> SpanContext <span class="token function">context</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> NoopSpanContextImpl<span class="token punctuation">.</span>INSTANCE<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">finish</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">finish</span><span class="token punctuation">(</span><span class="token keyword">long</span> finishMicros<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span><span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">setTag</span><span class="token punctuation">(</span>String key<span class="token punctuation">,</span> String value<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">setTag</span><span class="token punctuation">(</span>String key<span class="token punctuation">,</span> <span class="token keyword">boolean</span> value<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">setTag</span><span class="token punctuation">(</span>String key<span class="token punctuation">,</span> Number value<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> NoopSpan <span class="token function">setTag</span><span class="token punctuation">(</span>Tag<span class="token generics function"><span class="token punctuation"><</span>T<span class="token punctuation">></span></span> tag<span class="token punctuation">,</span> T value<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">log</span><span class="token punctuation">(</span>Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token operator">?</span><span class="token operator">></span> fields<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">long</span> timestampMicroseconds<span class="token punctuation">,</span> Map<span class="token operator"><</span>String<span class="token punctuation">,</span> <span class="token operator">?</span><span class="token operator">></span> fields<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">log</span><span class="token punctuation">(</span>String event<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">long</span> timestampMicroseconds<span class="token punctuation">,</span> String event<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">setBaggageItem</span><span class="token punctuation">(</span>String key<span class="token punctuation">,</span> String value<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> String <span class="token function">getBaggageItem</span><span class="token punctuation">(</span>String key<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> null<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> NoopSpan <span class="token function">setOperationName</span><span class="token punctuation">(</span>String operationName<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> String <span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> NoopSpan<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getSimpleName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
4.2 opentracing-util
一个简单的实现
4.2.1 ThreadLocalScope & ThreadLocalScopeManager
Scope和ScopeManager基于ThreadLocal
的实现。
场景
应用启动了开始产生trace消息,他的行为一定是这样的
mainThread
线程,产生一个ThreadLocal<ThreadLocalScopeManager> MA
- 请求过来,产生子线程
subThread
, 子线程复制一个ThreadLocal<ThreadLocalScopeManager> subMA
。 subThread
的subMA
产生(activate)第一个spanA
。此时当前span为A
,subMA
中保留A
相关scopeA
的引用,所以subMA.activeSpan()
返回A
。- span
A
产生(activate)子spanB
。此时B
中先保存A
的引用,然后B
将subMA
中的原先A
的引用,替换成自己。所以当获取当前Span时,就会拿到B
的引用。 B
span的生命周期结束了,然后将subMA
中保留的B
的引用,再次替换成A
。- 然后
A
的生名周期结束了,将subMA
中引用置为null。 - 子线程
subThread
结束了,subMA
的生命周期结束了
总结
ThreadLocalScopeManager
作为一个全局scope的管理者,保留有当前span的scope
的引用。
ThreadLocalscope
需要保留当前ThreadLocalScopeManager
的引用,和父或者上一个span
的引用,并且生成时将ThreadLocalScopeManager
中当前scope
替换成自己。
ThreadLocalscope
结束时利用之前保留的引用进行还原。
这样就实现了一层一层的span
源码
public class ThreadLocalScope implements Scope { private final ThreadLocalScopeManager scopeManager; private final Span wrapped; private final ThreadLocalScope toRestore;
<span class="token function">ThreadLocalScope</span><span class="token punctuation">(</span>ThreadLocalScopeManager scopeManager<span class="token punctuation">,</span> Span wrapped<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>scopeManager <span class="token operator">=</span> scopeManager<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>wrapped <span class="token operator">=</span> wrapped<span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>toRestore <span class="token operator">=</span> scopeManager<span class="token punctuation">.</span>tlsScope<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> scopeManager<span class="token punctuation">.</span>tlsScope<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">close</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>scopeManager<span class="token punctuation">.</span>tlsScope<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">!=</span> <span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// This shouldn't happen if users call methods in the expected order. Bail out.</span> <span class="token keyword">return</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> scopeManager<span class="token punctuation">.</span>tlsScope<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>toRestore<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> Span <span class="token function">span</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> wrapped<span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
public class ThreadLocalScopeManager implements ScopeManager { final ThreadLocal<ThreadLocalScope> tlsScope = new ThreadLocal<ThreadLocalScope>();
<span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> Scope <span class="token function">activate</span><span class="token punctuation">(</span>Span span<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name">ThreadLocalScope</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">,</span> span<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token annotation punctuation">@Override</span> <span class="token keyword">public</span> Span <span class="token function">activeSpan</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> ThreadLocalScope scope <span class="token operator">=</span> tlsScope<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> scope <span class="token operator">==</span> null <span class="token operator">?</span> null <span class="token operator">:</span> scope<span class="token punctuation">.</span><span class="token function">span</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
4.2.2 GlobalTracer
应用启动后,维持一个单例GlobalTracer
。
内部维持一个单例 Tracer
,默认是NoopTracer
。
所以使用时一般是先注册register
新的单测实例。
然后使用get
获取
然后就正常使用。
4.3. opentracing-mock
一个仅仅在内存中放置trace消息的测试程序
基于opentracing-util
的包,产生了两个span。父子关系。
public class MockTest { public static void main(String[] args) { // Initialize MockTracer with the default values. MockTracer tracer = new MockTracer(); MockSpan span = tracer.buildSpan("root").start(); try (Scope scope = tracer.scopeManager().activate(span)) { // Do things. span.setTag(Tags.COMPONENT, "my-own-application"); System.out.println("current: "+tracer.scopeManager().activeSpan().toString());
<span class="token comment">// 第二个</span> MockSpan subspan <span class="token operator">=</span> tracer<span class="token punctuation">.</span><span class="token function">buildSpan</span><span class="token punctuation">(</span><span class="token string">"sub"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">(</span>Scope subscope <span class="token operator">=</span> tracer<span class="token punctuation">.</span><span class="token function">scopeManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">activate</span><span class="token punctuation">(</span>subspan<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> subspan<span class="token punctuation">.</span><span class="token function">setTag</span><span class="token punctuation">(</span>Tags<span class="token punctuation">.</span>COMPONENT<span class="token punctuation">,</span> <span class="token string">"my-own-application2"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"current: "</span><span class="token operator">+</span>tracer<span class="token punctuation">.</span><span class="token function">scopeManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">activeSpan</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> subspan<span class="token punctuation">.</span><span class="token function">finish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">catch</span><span class="token punctuation">(</span>Exception ex<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> Tags<span class="token punctuation">.</span>ERROR<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>span<span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> span<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>System<span class="token punctuation">.</span><span class="token function">currentTimeMillis</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span><span class="token string">"errormsg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span> span<span class="token punctuation">.</span><span class="token function">finish</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> List<span class="token generics function"><span class="token punctuation"><</span>MockSpan<span class="token punctuation">></span></span> spans <span class="token operator">=</span> tracer<span class="token punctuation">.</span><span class="token function">finishedSpans</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span><span class="token punctuation">(</span>MockSpan espan<span class="token operator">:</span>spans<span class="token punctuation">)</span><span class="token punctuation">{<!-- --></span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>espan<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
}
打印
current: {traceId:1, spanId:2, parentId:0, operationName:"root"}
current: {traceId:1, spanId:3, parentId:2, operationName:"sub"}
{traceId:1, spanId:3, parentId:2, operationName:"sub"}
{traceId:1, spanId:2, parentId:0, operationName:"root"}