3. 特点

  • 将跟踪和跨度 ID 添加到 Slf4J MDC,以便您可以从日志聚合器中的给定跟踪或跨度中提取所有日志,如以下示例日志所示:spring-doc.cadn.net.cn

    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] ...

    请注意[appname,traceId,spanId,exportable]MDC 的条目:spring-doc.cadn.net.cn

  • 提供对常见分布式跟踪数据模型的抽象:跟踪、跨度(形成 DAG)、注释和键值注释。 Spring Cloud Sleuth 松散地基于 HTrace,但与 Zipkin (Dapper) 兼容。spring-doc.cadn.net.cn

  • Sleuth 记录时序信息以帮助进行延迟分析。 通过使用 sleuth,您可以查明应用程序中延迟的原因。spring-doc.cadn.net.cn

  • Sleuth 的编写方式是不会记录太多,也不会导致生产应用程序崩溃。 为此,侦探:spring-doc.cadn.net.cn

  • 检测来自 Spring 应用程序(servlet 过滤器、异步端点、rest 模板、计划作、消息通道、Zuul 过滤器和 Feign 客户端)的常见入口和出口点。spring-doc.cadn.net.cn

  • Sleuth 包括跨 HTTP 或消息传递边界加入跟踪的默认逻辑。 例如,HTTP 传播在与 Zipkin 兼容的请求标头上工作。spring-doc.cadn.net.cn

  • 侦探可以在进程之间传播上下文(也称为包袱)。 因此,如果您在 Span 上设置了行李元素,它会通过 HTTP 或消息传递向下游发送到其他进程。spring-doc.cadn.net.cn

  • 提供一种通过注释创建或继续跨度以及添加标记和日志的方法。spring-doc.cadn.net.cn

  • 如果spring-cloud-sleuth-zipkin在类路径上,应用程序会生成并收集与 Zipkin 兼容的跟踪。 默认情况下,它通过 HTTP 将它们发送到 localhost(端口 9411)上的 Zipkin 服务器。 您可以通过设置spring.zipkin.baseUrl.spring-doc.cadn.net.cn

    • 如果您依赖spring-rabbit,您的应用会将跟踪发送到 RabbitMQ 代理而不是 HTTP。spring-doc.cadn.net.cn

    • 如果您依赖spring-kafka,并设置spring.zipkin.sender.type: kafka,您的应用程序会将跟踪发送到 Kafka 代理而不是 HTTP。spring-doc.cadn.net.cn

spring-cloud-sleuth-stream已弃用,不应再使用。
SLF4J MDC 始终处于设置状态,根据示例,登录用户会立即在日志中看到跟踪和跨度 ID 前面显示。 其他日志记录系统必须配置自己的格式化程序才能获得相同的结果。 默认值如下:logging.pattern.level设置为%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}](这是针对登录用户的 Spring Boot 功能)。 如果您不使用 SLF4J,则不会自动应用此模式。

3.1. 勇者简介

从版本开始2.0.0,Spring Cloud Sleuth 使用 Brave 作为跟踪库。 为了您的方便,我们将 Brave 的部分文档嵌入到此处。
在绝大多数情况下,您只需使用TracerSpanCustomizer侦探提供的勇敢的豆子。下面的文档包含 对 Brave 是什么及其工作原理的高度概述。

Brave 是一个库,用于捕获有关分布式作的延迟信息并将其报告给 Zipkin。 大多数用户不直接使用 Brave。他们使用库或框架,而不是代表他们使用 Brave。spring-doc.cadn.net.cn

该模块包括一个跟踪器,用于创建和联接跨度,以模拟潜在分布式工作的延迟。 它还包括用于通过网络边界传播跟踪上下文的库(例如,使用 HTTP 标头)。spring-doc.cadn.net.cn

3.1.1. 跟踪

最重要的是,您需要一个brave.Tracer,配置为向 Zipkin 报告spring-doc.cadn.net.cn

以下示例设置通过 HTTP(而不是 Kafka)将跟踪数据(span)发送到 Zipkin:spring-doc.cadn.net.cn

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 个字符。 你的名字必须明确具体。 大牌会导致延迟问题,有时甚至会抛出异常。

跟踪器创建并联接跨度,以模拟潜在分布式工作的延迟。 它可以采用采样来减少过程中的开销,减少发送到 Zipkin 的数据量,或两者兼而有之。spring-doc.cadn.net.cn

跟踪器返回的跨度在完成时向 Zipkin 报告数据,如果未采样则不执行任何作。 启动跨度后,您可以注释感兴趣的事件或添加包含详细信息或查找键的标记。spring-doc.cadn.net.cn

跨度具有一个上下文,其中包括跟踪标识符,这些跟踪标识符将跨度放置在表示分布式作的树中的正确位置。spring-doc.cadn.net.cn

3.1.2. 本地跟踪

跟踪从不离开进程的代码时,请在作用域范围内运行它。spring-doc.cadn.net.cn

@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类型:spring-doc.cadn.net.cn

@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.
}

上面两个例子都报告了完全相同的完成跨度!spring-doc.cadn.net.cn

在上面的示例中,span 将是新的根 span 或 现有跟踪中的下一个子项。spring-doc.cadn.net.cn

3.1.3. 自定义跨度

获得跨度后,您可以向其添加标签。 这些标记可用作查找键或详细信息。 例如,您可以使用运行时版本添加标记,如以下示例所示:spring-doc.cadn.net.cn

span.tag("clnt/finagle.version", "6.36.0");

向第三方公开自定义跨度的功能时,首选brave.SpanCustomizer而不是brave.Span. 前者更易于理解和测试,并且不会用跨度生命周期钩子来诱惑用户。spring-doc.cadn.net.cn

interface MyTraceCallback {
  void request(Request request, SpanCustomizer customizer);
}

因为brave.Span实现brave.SpanCustomizer,您可以将其传递给用户,如以下示例所示:spring-doc.cadn.net.cn

for (MyTraceCallback callback : userCallbacks) {
  callback.request(request, span);
}

3.1.4. 隐式查找当前跨度

有时,您不知道跟踪是否正在进行中,并且您不希望用户执行空检查。brave.CurrentSpanCustomizer通过将数据添加到正在进行或删除的任何 span 来处理此问题,如以下示例所示:spring-doc.cadn.net.cn

// 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作中的角色相关的标记和事件。spring-doc.cadn.net.cn

以下示例演示如何添加客户端跨度:spring-doc.cadn.net.cn

@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 跟踪中,您可以使用span.finish()以表示已收到响应。在单向跟踪中,您可以使用span.flush()相反,因为您不期望得到回应。spring-doc.cadn.net.cn

以下示例演示客户端如何对单向作进行建模:spring-doc.cadn.net.cn

@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();

以下示例显示了服务器如何处理单向作:spring-doc.cadn.net.cn

@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();