3. 特点
-
将跟踪和跨度 ID 添加到 Slf4J MDC,以便您可以从日志聚合器中的给定跟踪或跨度中提取所有日志,如以下示例日志所示:
2016-02-02 15:30:57.902 INFO [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ... 2016-02-02 15:30:58.372 ERROR [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ... 2016-02-02 15:31:01.936 INFO [bar,46ab0d418373cbc9,46ab0d418373cbc9,false] 23030 --- [nio-8081-exec-4] ...
请注意 MDC 中的条目:
[appname,traceId,spanId,exportable]
-
spanId
:发生的特定操作的 ID。 -
appname
:记录 span 的应用程序的名称。 -
traceId
:包含跨度的延迟图的 ID。 -
exportable
:是否应将日志导出到 Zipkin。 您何时希望 span 不可导出? 当您希望将某些操作包装在 Span 中并仅将其写入日志时。
-
-
提供对常见分布式跟踪数据模型的抽象:跟踪、跨度(形成 DAG)、注释和键值注释。 Spring Cloud Sleuth 松散地基于 HTrace,但与 Zipkin(Dapper)兼容。
-
Sleuth 记录计时信息以帮助进行延迟分析。 通过使用 sleuth,您可以查明应用程序中延迟的原因。
-
编写 Sleuth 是为了不记录太多,也不会导致您的生产应用程序崩溃。 为此,侦探:
-
在带内传播有关调用图的结构数据,并在带外传播有关其余部分的结构数据。
-
包括 HTTP 等层的固执己见的插桩。
-
包括用于管理卷的采样策略。
-
可以向 Zipkin 系统报告以进行查询和可视化。
-
-
检测来自 Spring 应用程序的常见入口和出口点(servlet 过滤器、异步端点、rest 模板、计划操作、消息通道、Zuul 过滤器和 Feign 客户端)。
-
Sleuth 包含用于跨 HTTP 或消息收发边界加入跟踪的默认逻辑。 例如,HTTP 传播在与 Zipkin 兼容的请求标头上工作。
-
Sleuth 可以在进程之间传播上下文(也称为 baggage)。 因此,如果在 Span 上设置 baggage 元素,则会通过 HTTP 或消息传递将其发送到下游的其他进程。
-
提供一种创建或继续 span 以及通过注释添加标记和日志的方法。
-
如果 位于类路径上,则应用程序会生成并收集与 Zipkin 兼容的跟踪。 默认情况下,它通过 HTTP 将它们发送到 localhost(端口 9411)上的 Zipkin 服务器。 您可以通过设置 来配置服务的位置。
spring-cloud-sleuth-zipkin
spring.zipkin.baseUrl
-
如果您依赖 ,则您的应用程序会将跟踪发送到 RabbitMQ 代理,而不是 HTTP。
spring-rabbit
-
如果您依赖于 和 set ,则您的应用程序会将跟踪发送到 Kafka 代理,而不是 HTTP。
spring-kafka
spring.zipkin.sender.type: kafka
-
spring-cloud-sleuth-stream 已弃用,不应再使用。 |
-
Spring Cloud Sleuth 与 OpenTracing 兼容。
SLF4J MDC 始终处于设置状态,并且 logback 用户会立即在日志中看到每个示例的跟踪和跨度 ID
如前所示。
其他日志记录系统必须配置自己的格式化程序才能获得相同的结果。
默认值如下:设置为 (这是 logback 用户的 Spring Boot 功能)。
如果您不使用 SLF4J,则不会自动应用此模式。logging.pattern.level %5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}] |
3.1. Brave 简介
从 version 开始,Spring Cloud Sleuth 使用 Brave 作为跟踪库。
为方便起见,我们在此处嵌入了 Brave 的部分文档。2.0.0 |
在绝大多数情况下,您只需使用 Sleuth 提供的 Brave 中的 or bean。以下文档包含
对 Brave 是什么及其工作原理的高度概述。Tracer SpanCustomizer |
Brave 是一个库,用于捕获有关分布式操作的延迟信息并将其报告给 Zipkin。 大多数用户不直接使用 Brave。他们使用库或框架,而不是代表他们雇用 Brave。
此模块包括一个跟踪器,用于创建和联接 span,这些 span 对潜在分布式工作的延迟进行建模。 它还包括用于通过网络边界传播跟踪上下文的库(例如,使用 HTTP 标头)。
3.1.1. 跟踪
最重要的是,您需要一个配置为向 Zipkin 报告的 。brave.Tracer
以下示例设置通过 HTTP(而不是 Kafka)将跟踪数据(跨度)发送到 Zipkin:
class MyClass {
private final Tracer tracer;
// Tracer will be autowired
MyClass(Tracer tracer) {
this.tracer = tracer;
}
void doSth() {
Span span = tracer.newTrace().name("encode").start();
// ...
}
}
如果您的 span 包含长度超过 50 个字符的名称,则该名称将被截断为 50 个字符。 你的名字必须明确而具体。 大名称会导致延迟问题,有时甚至会引发异常。 |
跟踪器创建并联接对潜在分布式工作的延迟进行建模的 span。 它可以使用采样来减少过程中的开销,减少发送到 Zipkin 的数据量,或两者兼而有之。
跟踪器返回的 Span 在完成后向 Zipkin 报告数据,如果未采样,则不执行任何操作。 启动 span 后,您可以注释感兴趣的事件或添加包含详细信息或查找键的标签。
Span 具有一个上下文,该上下文包含跟踪标识符,这些标识符将 Span 放置在表示分布式操作的树中的正确位置。
3.1.2. 本地跟踪
在跟踪从未离开进程的代码时,请在范围范围内运行它。
@Autowired Tracer tracer;
// Start a new trace or a span within an existing trace representing an operation
ScopedSpan span = tracer.startScopedSpan("encode");
try {
// The span is in "scope" meaning downstream code such as loggers can see trace IDs
return encoder.encode();
} catch (RuntimeException | Error e) {
span.error(e); // Unless you handle exceptions, you might not know the operation failed!
throw e;
} finally {
span.finish(); // always finish the span
}
当您需要更多功能或更精细的控制时,请使用以下类型:Span
@Autowired Tracer tracer;
// Start a new trace or a span within an existing trace representing an operation
Span span = tracer.nextSpan().name("encode").start();
// Put the span in "scope" so that downstream code such as loggers can see trace IDs
try (SpanInScope ws = tracer.withSpanInScope(span)) {
return encoder.encode();
} catch (RuntimeException | Error e) {
span.error(e); // Unless you handle exceptions, you might not know the operation failed!
throw e;
} finally {
span.finish(); // note the scope is independent of the span. Always finish a span.
}
以上两个示例在完成时都报告了完全相同的跨度!
在上面的示例中,span 将是新的根 span 或 现有跟踪中的 next child。
3.1.3. 自定义 Span
拥有 span 后,您可以向其添加标签。 这些标签可用作查找键或详细信息。 例如,您可以使用运行时版本添加标签,如以下示例所示:
span.tag("clnt/finagle.version", "6.36.0");
向第三方公开自定义 span 的功能时,首选而不是 .
前者更易于理解和测试,并且不会用 span 生命周期钩子来诱惑用户。brave.SpanCustomizer
brave.Span
interface MyTraceCallback {
void request(Request request, SpanCustomizer customizer);
}
由于 implements ,您可以将其传递给 users,如下例所示:brave.Span
brave.SpanCustomizer
for (MyTraceCallback callback : userCallbacks) {
callback.request(request, span);
}
3.1.4. 隐式查找当前 Span
有时,您不知道跟踪是否正在进行,并且您不希望用户执行 null 检查。 通过向正在进行或丢弃的任何 span 添加数据来处理此问题,如以下示例所示:brave.CurrentSpanCustomizer
前任。
// The user code can then inject this without a chance of it being null.
@Autowired SpanCustomizer span;
void userCode() {
span.annotate("tx.started");
...
}
3.1.5. RPC 跟踪
在滚动您自己的 RPC 检测之前,请检查此处编写的检测以及 Zipkin 的列表。 |
RPC 跟踪通常由侦听器自动完成。在后台,他们添加了与其在 RPC 操作中的角色相关的标签和事件。
以下示例显示如何添加客户端 span:
@Autowired Tracing tracing;
@Autowired Tracer tracer;
// before you send a request, add metadata that describes the operation
span = tracer.nextSpan().name(service + "/" + method).kind(CLIENT);
span.tag("myrpc.version", "1.0.0");
span.remoteServiceName("backend");
span.remoteIpAndPort("172.3.4.1", 8108);
// Add the trace context to the request, so it can be propagated in-band
tracing.propagation().injector(Request::addHeader)
.inject(span.context(), request);
// when the request is scheduled, start the span
span.start();
// if there is an error, tag the span
span.tag("error", error.getCode());
// or if there is an exception
span.error(exception);
// when the response is complete, finish the span
span.finish();
单向跟踪
有时,您需要对异步操作进行建模,其中有一个
请求,但没有响应。在正常的 RPC 跟踪中,用于指示已收到响应。在单向跟踪中,您改用 INSTEAD,因为您不希望收到响应。span.finish()
span.flush()
以下示例显示了客户端如何对单向操作进行建模:
@Autowired Tracing tracing;
@Autowired Tracer tracer;
// start a new span representing a client request
oneWaySend = tracer.nextSpan().name(service + "/" + method).kind(CLIENT);
// Add the trace context to the request, so it can be propagated in-band
tracing.propagation().injector(Request::addHeader)
.inject(oneWaySend.context(), request);
// fire off the request asynchronously, totally dropping any response
request.execute();
// start the client side and flush instead of finish
oneWaySend.start().flush();
以下示例显示了服务器如何处理单向操作:
@Autowired Tracing tracing;
@Autowired Tracer tracer;
// pull the context out of the incoming request
extractor = tracing.propagation().extractor(Request::getHeader);
// convert that context to a span which you can name and add tags to
oneWayReceive = nextSpan(tracer, extractor.extract(request))
.name("process-request")
.kind(SERVER)
... add tags etc.
// start the server side and flush instead of finish
oneWayReceive.start().flush();
// you should not modify this span anymore as it is complete. However,
// you can create children to represent follow-up work.
next = tracer.newSpan(oneWayReceive.context()).name("step2").start();