Spring Cloud Sleuth

优质
小牛编辑
132浏览
2023-12-01

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时间戳,则会收到客户端从服务器接收响应所需的整个时间。

可视化SpanTrace将与Zipkin注释一起查看系统:

跟踪信息传播

一个音符的每个颜色表示跨度(7 spans - 从AG)。如果您在笔记中有这样的信息:

Trace Id = X
Span Id = D
Client Sent

这意味着,当前的跨度痕量-ID设置为XSpan -编号设置为ð。它也发出了 客户端发送的事件。

这样,spans的父/子关系的可视化将如下所示:

父子关系

目的

在以下部分中,将考虑上述图像中的示例。

分布式跟踪与Zipkin

共有7个spans。如果您在Zipkin中查看痕迹,您将在第二个曲目中看到这个数字:

痕迹

但是,如果您选择特定的跟踪,那么您将看到4 spans

跟踪信息传播
注意当选择特定的跟踪时,您将看到合并的spans。这意味着如果发送到服务器接收和服务器发送/接收客户端和客户端发送注释的Zipkin有2个spans,那么它们将被显示为一个跨度。

为什么在这种情况下,7和4 spans之间有区别?

  • 2 spans来自http:/start范围。它具有服务器接收(SR)和服务器发送(SS)注释。
  • 2 spans来自service1service2http:/foo端点的RPC呼叫。它在service1方面具有客户端发送(CS)和客户端接收(CR)注释。它还在service2方面具有服务器接收(SR)和服务器发送(SS)注释。在物理上有2个spans,但它们形成与RPC调用相关的1个逻辑跨度。
  • 2 spans来自service2service3http:/bar端点的RPC呼叫。它在service2方面具有客户端发送(CS)和客户接收(CR)注释。它还具有service3端的服务器接收(SR)和服务器发送(SS)注释。在物理上有2个spans,但它们形成与RPC调用相关的1个逻辑跨度。
  • 2 spans来自service2service4http:/baz端点的RPC呼叫。它在service2方面具有客户端发送(CS)和客户接收(CR)注释。它还在service4侧具有服务器接收(SR)和服务器发送(SS)注释。在物理上有2个spans,但它们形成与RPC调用相关的1个逻辑跨度。

因此,如果我们计算spans ,http:/start中有1 来自service1的呼叫service22service2)呼叫service32service2service4。共7个 spans。

逻辑上,我们看到Total Spans的信息:4,因为我们有1个跨度与传入请求相关的service13 spans与RPC调用相关。

可视化错误

Zipkin允许您可视化跟踪中的错误。当异常被抛出并且没有被捕获时,我们在Zipkin可以正确着色的跨度上设置适当的标签。您可以在痕迹列表中看到一条是红色的痕迹。这是因为抛出了一个异常。

如果您点击该轨迹,您将看到类似的图片

错误跟踪

然后,如果您点击其中一个spans,您将看到以下内容

错误跟踪信息传播

你可以看到,你可以很容易的看到错误的原因和整个stacktrace相关的。

实例

Zipkin部署在Pivotal Web Services上点击Pivotal Web Services图标即可实时查看!点击Pivotal Web Services图标直播!

Zipkin中的依赖图将如下所示:

依赖 Zipkin部署在Pivotal Web Services上点击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]]

如果你使用像一个日志聚合工具KibanaSplunk的等您可以订购所发生的事件。基巴纳的例子如下所示:

记录与Kibana的相关性

如果你想使用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模块添加到您的项目中即可。

Maven的
<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>
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到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"
}
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到spring-cloud-starter-sleuth

通过HTTP访问Zipkin

如果你想要Sleuth和Zipkin只需添加spring-cloud-starter-zipkin依赖关系。

Maven的
<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>
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到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"
}
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到spring-cloud-starter-zipkin

通过Spring Cloud Stream使用Zipkin的Sleuth

如果你想要Sleuth和Zipkin只需添加spring-cloud-sleuth-stream依赖关系。

Maven的
<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>
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到spring-cloud-sleuth-stream
  3. 将依赖关系添加到spring-cloud-starter-sleuth中,这样就可以下载依赖关系
  4. 添加一个粘合剂(例如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)
}
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到spring-cloud-sleuth-stream
  3. 将依赖关系添加到spring-cloud-starter-sleuth中,这样就可以下载所有依赖关系
  4. 添加一个粘合剂(例如Rabbit binder)来告诉Spring Cloud Stream应该绑定什么

Spring Cloud Sleuth Stream Zipkin收藏家

如果要启动Spring Cloud Sleuth Stream Zipkin收藏夹,只需添加spring-cloud-sleuth-zipkin-stream依赖关系即可

Maven的
<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>
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到spring-cloud-sleuth-zipkin-stream
  3. 将依赖关系添加到spring-cloud-starter-sleuth - 这样一来,所有的依赖依赖将被下载
  4. 添加一个粘合剂(例如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)
}
  1. 为了不自己选择版本,如果您通过Spring BOM添加依赖关系管理,会更好
  2. 将依赖关系添加到spring-cloud-sleuth-zipkin-stream
  3. 将依赖关系添加到spring-cloud-starter-sleuth - 这样将依赖关系依赖下载
  4. 添加一个粘合剂(例如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);
	}
}