將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中包裝某些操做並將其寫入日誌中。Sleuth不進行過多的日誌記錄,而且不會致使生產應用程序崩潰,爲此,Sleuth:git
若是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,則不會自動應用此模式。
從版本2.0.0開始,Spring Cloud Sleuth使用 Brave做爲追蹤庫,爲方便起見,在此處嵌入了Brave的部分文檔。
在絕大多數狀況下,你只需使用Sleuth提供的Brave中的Tracer
或SpanCustomizer
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.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); }
有時,你不知道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儀器以前,請檢查 此處編寫的儀器和 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();