Spring Cloud 参考文档(Spring Cloud Sleuth特性)

Spring Cloud Sleuth特性

  • 将trace和span ID添加到Slf4J MDC,所以你能够在日志聚合器中从给定的trace或span提取全部日志,如如下示例日志中所示:html

    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]条目:java

    • spanId:发生的特定操做的ID。
    • appname:记录span的应用程序的名称。
    • traceId:包含span的延迟图的ID。
    • exportable:是否应将日志导出到Zipkin,你但愿何时span不能导出?若是要在Span中包装某些操做并将其写入日志中。
  • 提供对常见分布式追踪数据模型的抽象:trace、span(造成DAG)、annotation和键值annotation,Spring Cloud Sleuth基于HTrace,但与Zipkin(Dapper)兼容。
  • Sleuth记录时间信息以帮助进行延迟分析,经过使用sleuth,你能够查明应用程序中的延迟缘由。
  • Sleuth不进行过多的日志记录,而且不会致使生产应用程序崩溃,为此,Sleuth:git

    • 在带内传播有关你的调用图的结构数据,在带外休息。
    • 包括层的自定义插装,好比HTTP。
    • 包括用于管理卷的采样策略。
    • 能够向Zipkin系统报告用于查询和可视化。
  • 仪器从Spring应用程序中常见的入口和出口点(servlet过滤器、异步端点、rest模板,调度操做,消息通道,Zuul过滤器和Feign客户端)。
  • Sleuth包含默认逻辑,用于跨HTTP或消息传递边界链接trace,例如,HTTP传播适用于与Zipkin兼容的请求headers。
  • Sleuth能够在进程之间传播上下文(也称为baggage),所以,若是你在Span上设置baggage元素,则会经过HTTP或消息传递向下发送到其余进程。
  • 提供建立或继续span以及经过annotations添加标记和日志的方法。
  • 若是spring-cloud-sleuth-zipkin位于类路径上,则应用程序会生成并收集与Zipkin兼容的trace,默认状况下,它经过HTTP将它们发送到localhost上的Zipkin服务器(端口9411),你能够经过设置spring.zipkin.baseUrl来配置服务的位置。github

    • 若是你依赖spring-rabbit,你的应用程序会将trace发送到RabbitMQ代理而不是HTTP。
    • 若是你依赖spring-kafka,并设置spring.zipkin.sender.type:kafka,你的应用程序会将trace发送到Kafka代理而不是HTTP。spring

      spring-cloud-sleuth-stream已弃用,不该再使用。
  • Spring Cloud Sleuth兼容OpenTracing服务器

    若是使用Zipkin,请经过设置 spring.sleuth.sampler.probability来配置导出的span几率(默认值:0.1,即10%),不然,你可能会认为Sleuth没有工做,由于它忽略了一些span。
    始终设置SLF4J MDC,而且logback用户能够根据前面显示的示例当即在日志中看到trace和span ID,其余日志记录系统必须配置本身的格式化程序才能得到相同的结果,默认值以下: logging.pattern.level设置为 %5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}](这是Logback用户的Spring Boot特性),若是你不使用SLF4J,则不会自动应用此模式。

Brave介绍

从版本2.0.0开始,Spring Cloud Sleuth使用 Brave做为追踪库,为方便起见,在此处嵌入了Brave的部分文档。
在绝大多数状况下,你只需使用Sleuth提供的Brave中的 TracerSpanCustomizer bean,下面的文档概述了Brave是什么以及它是如何工做的。

Brave是一个用于捕获和报告关于分布式操做的延迟信息到Zipkin的库,大多数用户不直接使用Brave,他们使用库或框架,而不是表明他们使用Brave。网络

此模块包含一个追踪器,用于建立和链接span,对潜在分布式工做的延迟进行建模,它还包括经过网络边界传播trace上下文的库(例如,使用HTTP头)。app

追踪

最重要的是,你须要一个brave.Tracer,配置为向Zipkin报告框架

如下示例设置经过HTTP(而不是Kafka)将trace数据(spans)发送到Zipkin:less

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后,你能够批注感兴趣的事件或添加包含详细信息或查找键的标记。

Spans具备一个上下文,其中包含trace标识符,该标识符将span放置在表示分布式操做的树中的正确位置。

本地追踪

当追踪代码不离开你的进程,在范围span内运行它。

@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将是新的根span或现有trace中的下一个子span。

自定义span

拥有span后,你能够为其添加标记,标签可用做查找键或详细信息,例如,你可使用运行时版本添加标记,如如下示例所示:

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

当暴露自定义span到第三方的能力时,使用brave.SpanCustomizer而不是brave.Span,前者更易于理解和测试,而且不会使用span生命周期钩子诱惑用户。

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

因为brave.Span实现了brave.SpanCustomizer,你能够将其传递给用户,如如下示例所示:

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

隐式查看当前span

有时,你不知道trace是否正在进行,而且您不但愿用户执行null检查,brave.CurrentSpanCustomizer经过向正在进行或丢弃的任何span添加数据来处理此问题,如如下示例所示:

// The user code can then inject this without a chance of it being null.
@Autowired SpanCustomizer span;

void userCode() {
  span.annotate("tx.started");
  ...
}

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追踪中,你使用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();
相关文章
相关标签/搜索