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();
相關文章
相關標籤/搜索