Spring Cloud Sleuth
Adrian Cole,Spencer Gibb,Marcin Grzejszczak,Dave Syer
Dalston.RELEASE
Spring Cloud Sleuth为Spring Cloud实现分布式跟踪解决方案。
术语
Spring Cloud Sleuth借用了Dapper的术语。
Span:工作的基本单位 例如,发送RPC是一个新的跨度,以及向RPC发送响应。Span由跨度的唯一64位ID标识,跨度是其中一部分的跟踪的另一个64位ID。跨度还具有其他数据,例如描述,时间戳记事件,键值注释(标签),导致它们的跨度的ID以及进程ID(通常是IP地址)。
跨距开始和停止,他们跟踪他们的时间信息。创建跨度后,必须在将来的某个时刻停止。
提示 | 启动跟踪的初始范围称为root span 。该跨度的跨度id的值等于跟踪ID。 |
跟踪:一组spans形成树状结构。例如,如果您正在运行分布式大数据存储,则可能会由put请求形成跟踪。
注释: 用于及时记录事件的存在。用于定义请求的开始和停止的一些核心注释是:
- cs - 客户端发送 - 客户端已经发出请求。此注释描绘了跨度的开始。
- sr - 服务器接收 - 服务器端得到请求,并将开始处理它。如果从此时间戳中减去cs时间戳,则会收到网络延迟。
- ss - 服务器发送 - 在完成请求处理后(响应发送回客户端时)注释。如果从此时间戳中减去sr时间戳,则会收到服务器端处理请求所需的时间。
- cr - 客户端接收 - 表示跨度的结束。客户端已成功接收到服务器端的响应。如果从此时间戳中减去cs时间戳,则会收到客户端从服务器接收响应所需的整个时间。
可视化Span和Trace将与Zipkin注释一起查看系统:
一个音符的每个颜色表示跨度(7 spans - 从A到G)。如果您在笔记中有这样的信息:
Trace Id = X
Span Id = D
Client Sent
这意味着,当前的跨度痕量-ID设置为X,Span -编号设置为ð。它也发出了 客户端发送的事件。
这样,spans的父/子关系的可视化将如下所示:
目的
在以下部分中,将考虑上述图像中的示例。
分布式跟踪与Zipkin
共有7个spans。如果您在Zipkin中查看痕迹,您将在第二个曲目中看到这个数字:
但是,如果您选择特定的跟踪,那么您将看到4 spans:
注意 | 当选择特定的跟踪时,您将看到合并的spans。这意味着如果发送到服务器接收和服务器发送/接收客户端和客户端发送注释的Zipkin有2个spans,那么它们将被显示为一个跨度。 |
为什么在这种情况下,7和4 spans之间有区别?
- 2 spans来自
http:/start
范围。它具有服务器接收(SR)和服务器发送(SS)注释。 - 2 spans来自
service1
到service2
到http:/foo
端点的RPC呼叫。它在service1
方面具有客户端发送(CS)和客户端接收(CR)注释。它还在service2
方面具有服务器接收(SR)和服务器发送(SS)注释。在物理上有2个spans,但它们形成与RPC调用相关的1个逻辑跨度。 - 2 spans来自
service2
到service3
到http:/bar
端点的RPC呼叫。它在service2
方面具有客户端发送(CS)和客户接收(CR)注释。它还具有service3
端的服务器接收(SR)和服务器发送(SS)注释。在物理上有2个spans,但它们形成与RPC调用相关的1个逻辑跨度。 - 2 spans来自
service2
到service4
到http:/baz
端点的RPC呼叫。它在service2
方面具有客户端发送(CS)和客户接收(CR)注释。它还在service4
侧具有服务器接收(SR)和服务器发送(SS)注释。在物理上有2个spans,但它们形成与RPC调用相关的1个逻辑跨度。
因此,如果我们计算spans ,http:/start
中有1 个来自service1
的呼叫service2
,2(service2
)呼叫service3
和2(service2
) service4
。共7个 spans。
逻辑上,我们看到Total Spans的信息:4,因为我们有1个跨度与传入请求相关的service1
和3 spans与RPC调用相关。
可视化错误
Zipkin允许您可视化跟踪中的错误。当异常被抛出并且没有被捕获时,我们在Zipkin可以正确着色的跨度上设置适当的标签。您可以在痕迹列表中看到一条是红色的痕迹。这是因为抛出了一个异常。
如果您点击该轨迹,您将看到类似的图片
然后,如果您点击其中一个spans,您将看到以下内容
你可以看到,你可以很容易的看到错误的原因和整个stacktrace相关的。
实例
点击Pivotal Web Services图标即可实时查看!点击Pivotal Web Services图标直播!Zipkin中的依赖图将如下所示:
点击Pivotal Web Services图标即可实时查看!点击Pivotal Web Services图标直播!对数相关
当通过跟踪id等于例如2485ec27856c56f4
来对这四个应用程序的日志进行灰名单时,将会得到以下内容:
service1.log:2016-02-26 11:15:47.561 INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application : Hello from service1. Calling service2
service2.log:2016-02-26 11:15:47.710 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Hello from service2. Calling service3 and then service4
service3.log:2016-02-26 11:15:47.895 INFO [service3,2485ec27856c56f4,1210be13194bfe5,true] 68060 --- [nio-8083-exec-1] i.s.c.sleuth.docs.service3.Application : Hello from service3
service2.log:2016-02-26 11:15:47.924 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Got response from service3 [Hello from service3]
service4.log:2016-02-26 11:15:48.134 INFO [service4,2485ec27856c56f4,1b1845262ffba49d,true] 68061 --- [nio-8084-exec-1] i.s.c.sleuth.docs.service4.Application : Hello from service4
service2.log:2016-02-26 11:15:48.156 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Got response from service4 [Hello from service4]
service1.log:2016-02-26 11:15:48.182 INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application : Got response from service2 [Hello from service2, response from service3 [Hello from service3] and from service4 [Hello from service4]]
如果你使用像一个日志聚合工具Kibana, Splunk的等您可以订购所发生的事件。基巴纳的例子如下所示:
如果你想使用Logstash,这里是Logstash的Grok模式:
filter {
# pattern matching logback pattern
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
}
注意 | 如果您想将Grok与Cloud Foundry的日志一起使用,则必须使用此模式: |
filter {
# pattern matching logback pattern
grok {
match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
}
使用Logstash进行JSON回溯
通常,您不希望将日志存储在文本文件中,而不是将Logstash可以立即选择的JSON文件中存储。为此,您必须执行以下操作(为了可读性,我们将依赖关系传递给groupId:artifactId:version
符号。
依赖关系设置
- 确保Logback位于类路径(
ch.qos.logback:logback-core
) - 添加Logstash Logback编码 - 版本
4.6
的示例:net.logstash.logback:logstash-logback-encoder:4.6
回读设置
您可以在下面找到一个Logback配置(名为logback-spring.xml)的示例:
- 将来自应用程序的信息以JSON格式记录到
build/${spring.application.name}.json
文件 - 已经评论了两个额外的追加者 - 控制台和标准日志文件
- 具有与上一节所述相同的记录模式
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
<!-- Example for logging into the build folder of your project -->
<property name="LOG_FILE" value="${BUILD_FOLDER:-build}/${springAppName}"/>
<!-- You can override this to have a custom pattern -->
<property name="CONSOLE_LOG_PATTERN"
value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<!-- Appender to log to console -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- Minimum logging level to be presented in the console logs-->
<level>DEBUG</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- Appender to log to file -->
<appender name="flatfile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<!-- Appender to log to file in a JSON format -->
<appender name="logstash" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_FILE}.json</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<timeZone>UTC</timeZone>
</timestamp>
<pattern>
<pattern>
{
"severity": "%level",
"service": "${springAppName:-}",
"trace": "%X{X-B3-TraceId:-}",
"span": "%X{X-B3-SpanId:-}",
"parent": "%X{X-B3-ParentSpanId:-}",
"exportable": "%X{X-Span-Export:-}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"rest": "%message"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="console"/>
<!-- uncomment this to have also JSON logs -->
<!--<appender-ref ref="logstash"/>-->
<!--<appender-ref ref="flatfile"/>-->
</root>
</configuration>
注意 | 如果您使用自定义logback-spring.xml ,则必须通过bootstrap application 而不是application 属性文件传递spring.application.name 。否则您的自定义logback文件将不会正确读取该属性。 |
传播Span上下文
跨度上下文是必须传播到任何子进程跨越进程边界的状态。Span背景的一部分是行李。跟踪和跨度ID是跨度上下文的必需部分。行李是可选的部分。
行李是一组密钥:存储在范围上下文中的值对。行李与痕迹一起旅行,并附在每一个跨度上。Spring Cloud如果HTTP标头以baggage-
为前缀,并且以baggage_
开头的消息传递,Sleuth将会明白标题是行李相关的。
重要 | 行李物品的数量或大小目前没有限制。但是,请记住,太多可能会降低系统吞吐量或增加RPC延迟。在极端情况下,由于超出了传输级消息或报头容量,可能会使应用程序崩溃。 |
在跨度上设置行李的示例:
Span initialSpan = this.tracer.createSpan("span");
initialSpan.setBaggageItem("foo", "bar");
行李与Span标签
行李随行旅行(即每个孩子跨度都包含其父母的行李)。Zipkin不了解行李,甚至不会收到这些信息。
标签附加到特定的跨度 - 它们仅针对该特定跨度呈现。但是,您可以通过标签搜索查找跟踪,其中存在具有搜索标签值的跨度。
如果您希望能够根据行李查找跨度,则应在根跨度中添加相应的条目作为标签。
@Autowired Tracer tracer;
Span span = tracer.getCurrentSpan();
String baggageKey = "key";
String baggageValue = "foo";
span.setBaggageItem(baggageKey, baggageValue);
tracer.addTag(baggageKey, baggageValue);
添加到项目中
只有Sleuth(对数相关)
如果您只想从Spring Cloud Sleuth中获利,而没有Zipkin集成,只需将spring-cloud-starter-sleuth
模块添加到您的项目中即可。
<dependencyManagement> (1)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency> (2)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-starter-sleuth
dependencyManagement { (1)
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
}
}
dependencies { (2)
compile "org.springframework.cloud:spring-cloud-starter-sleuth"
}
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-starter-sleuth
通过HTTP访问Zipkin
如果你想要Sleuth和Zipkin只需添加spring-cloud-starter-zipkin
依赖关系。
<dependencyManagement> (1)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency> (2)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zipkin</artifactId>
</dependency>
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-starter-zipkin
dependencyManagement { (1)
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
}
}
dependencies { (2)
compile "org.springframework.cloud:spring-cloud-starter-zipkin"
}
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-starter-zipkin
通过Spring Cloud Stream使用Zipkin的Sleuth
如果你想要Sleuth和Zipkin只需添加spring-cloud-sleuth-stream
依赖关系。
<dependencyManagement> (1)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency> (2)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-stream</artifactId>
</dependency>
<dependency> (3)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- EXAMPLE FOR RABBIT BINDING -->
<dependency> (4)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-sleuth-stream
- 将依赖关系添加到
spring-cloud-starter-sleuth
中,这样就可以下载依赖关系 - 添加一个粘合剂(例如Rabbit binder)来告诉Spring Cloud Stream应该绑定什么
dependencyManagement { (1)
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
}
}
dependencies {
compile "org.springframework.cloud:spring-cloud-sleuth-stream" (2)
compile "org.springframework.cloud:spring-cloud-starter-sleuth" (3)
// Example for Rabbit binding
compile "org.springframework.cloud:spring-cloud-stream-binder-rabbit" (4)
}
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-sleuth-stream
- 将依赖关系添加到
spring-cloud-starter-sleuth
中,这样就可以下载所有依赖关系 - 添加一个粘合剂(例如Rabbit binder)来告诉Spring Cloud Stream应该绑定什么
Spring Cloud Sleuth Stream Zipkin收藏家
如果要启动Spring Cloud Sleuth Stream Zipkin收藏夹,只需添加spring-cloud-sleuth-zipkin-stream
依赖关系即可
<dependencyManagement> (1)
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Camden.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency> (2)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin-stream</artifactId>
</dependency>
<dependency> (3)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!-- EXAMPLE FOR RABBIT BINDING -->
<dependency> (4)
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-sleuth-zipkin-stream
- 将依赖关系添加到
spring-cloud-starter-sleuth
- 这样一来,所有的依赖依赖将被下载 - 添加一个粘合剂(例如Rabbit binder)来告诉Spring Cloud Stream应该绑定什么
dependencyManagement { (1)
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
}
}
dependencies {
compile "org.springframework.cloud:spring-cloud-sleuth-zipkin-stream" (2)
compile "org.springframework.cloud:spring-cloud-starter-sleuth" (3)
// Example for Rabbit binding
compile "org.springframework.cloud:spring-cloud-stream-binder-rabbit" (4)
}
- 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
- 将依赖关系添加到
spring-cloud-sleuth-zipkin-stream
- 将依赖关系添加到
spring-cloud-starter-sleuth
- 这样将依赖关系依赖下载 - 添加一个粘合剂(例如Rabbit binder)来告诉Spring Cloud Stream应该绑定什么
然后使用@EnableZipkinStreamServer
注释注释你的主类:
package example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;
@SpringBootApplication
@EnableZipkinStreamServer
public class ZipkinStreamServerApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(ZipkinStreamServerApplication.class, args);
}
}