“作方法”指南

1. 如何使用 Brave 设置 Sleuth?

将 Sleuth starter 添加到 Classpath 中。spring-doc.cadn.net.cn

Maven 系列
<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train-version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
Gradle
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    implementation "org.springframework.cloud:spring-cloud-starter-sleuth"
}

2. 如何通过HTTP设置Brave和Zipkin的侦探?

将 Sleuth starter 和 Zipkin 添加到 Classpath 中。spring-doc.cadn.net.cn

Maven 系列
<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train-version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
Gradle
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    implementation "org.springframework.cloud:spring-cloud-starter-sleuth"
    implementation "org.springframework.cloud:spring-cloud-sleuth-zipkin"
}

3. 如何通过消息功能设置Brave和Zipkin的侦探?

如果您想使用 RabbitMQ、Kafka 或 ActiveMQ 而不是 HTTP,请添加spring-rabbit,spring-kafkaorg.apache.activemq:activemq-clientDependency。 默认目标名称为Zipkin.spring-doc.cadn.net.cn

如果使用 Kafka,则必须添加 Kafka 依赖项。spring-doc.cadn.net.cn

Maven 系列
<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train-version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>
Gradle
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    implementation "org.springframework.cloud:spring-cloud-starter-sleuth"
    implementation "org.springframework.cloud:spring-cloud-sleuth-zipkin"
    implementation "org.springframework.kafka:spring-kafka"
}

此外,您还需要设置属性spring.zipkin.sender.type属性:spring-doc.cadn.net.cn

spring.zipkin.sender.type: kafka

如果您希望 Sleuth 而不是 RabbitMQ,请添加spring-cloud-starter-sleuth,spring-cloud-sleuth-zipkinspring-rabbit依赖。spring-doc.cadn.net.cn

Maven 系列
<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train-version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.amqp</groupId>
    <artifactId>spring-rabbit</artifactId>
</dependency>
Gradle
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    implementation "org.springframework.cloud:spring-cloud-starter-sleuth"
    implementation "org.springframework.cloud:spring-cloud-sleuth-zipkin"
    implementation "org.springframework.amqp:spring-rabbit"
}

如果您希望 Sleuth 而不是 ActiveMQ,请添加spring-cloud-starter-sleuth,spring-cloud-sleuth-zipkinactivemq-client依赖。spring-doc.cadn.net.cn

Maven 系列
<dependencyManagement>
      <dependencies>
          <dependency>
              <groupId>org.springframework.cloud</groupId>
              <artifactId>spring-cloud-dependencies</artifactId>
              <version>${release.train-version}</version>
              <type>pom</type>
              <scope>import</scope>
          </dependency>
      </dependencies>
</dependencyManagement>

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.activemq</groupId>
    <artifactId>activemq-client</artifactId>
</dependency>
Gradle
dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
    }
}

dependencies {
    implementation "org.springframework.cloud:spring-cloud-starter-sleuth"
    implementation "org.springframework.cloud:spring-cloud-sleuth-zipkin"
    implementation "org.apache.activemq:activemq-client"
}

此外,您还需要设置属性spring.zipkin.sender.type属性:spring-doc.cadn.net.cn

spring.zipkin.sender.type: activemq

4. 如何查看外部系统中的 Spans?

如果你看不到向外部系统(例如 Zipkin)报告的 span,那么很可能是由于以下原因:spring-doc.cadn.net.cn

4.1. 您的 Span 未被采样

为了检查范围是否未被采样,只需查看是否设置了 exportable 标志就足够了。 让我们看一下以下示例:spring-doc.cadn.net.cn

2020-10-21 12:01:16.285  INFO [backend,0b6aaf642574edd3,0b6aaf642574edd3,true] 289589 --- [nio-9000-exec-1] Example              : Hello world!

如果部分[backend,0b6aaf642574edd3,0b6aaf642574edd3,true]true表示正在对 SPAN 进行采样,应报告该范围。spring-doc.cadn.net.cn

4.2. 缺少依赖项

直到 Sleuth 3.0.0 的依赖项spring-cloud-starter-zipkin包括spring-cloud-starter-sleuthdependency 和spring-cloud-sleuth-zipkinDependency。 使用 3.0.0spring-cloud-starter-zipkin已被删除,因此您需要将其更改为spring-cloud-sleuth-zipkin.spring-doc.cadn.net.cn

4.3. 连接配置错误

仔细检查远程系统地址是否正确(例如spring.zipkin.baseUrl),如果尝试通过 broker 进行通信,则您的 broker 连接已正确设置。spring-doc.cadn.net.cn

5. 如何让 RestTemplate、WebClient 等工作?

如果您观察到跟踪上下文没有被传播,则 cause 是以下之一:spring-doc.cadn.net.cn

如果缺少 instrumentation 功能,请提交 issue 并请求添加此类 instrumentation。spring-doc.cadn.net.cn

如果出现错误配置,请确保您用于通信的客户端是 Spring bean。 如果您通过newoperator 的插桩将不起作用。spring-doc.cadn.net.cn

插桩工作的示例:spring-doc.cadn.net.cn

import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration(proxyBeanMethods = false)
class MyConfiguration {
    @Bean RestTemplate myRestTemplate() {
        return new RestTemplate();
    }
}

@Service
class MyService {
    private final RestTemplate restTemplate;

    MyService(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    String makeACall() {
        return this.restTemplate.getForObject("http://example.com", String.class);
    }

}

插桩不起作用的示例:spring-doc.cadn.net.cn

@Service
class MyService {

    String makeACall() {
        // This will not work because RestTemplate is not a bean
        return new RestTemplate().getForObject("http://example.com", String.class);
    }

}

6. 如何向 HTTP 服务器响应添加标头?

注册一个将设置服务器响应的筛选器。spring-doc.cadn.net.cn

import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Tracer;

import javax.servlet.Filter;
import org.springframework.web.server.WebFilter;

@Configuration(proxyBeanMethods = false)
class MyConfig {

        // Example of a servlet Filter for non-reactive applications
        @Bean
        Filter traceIdInResponseFilter(Tracer tracer) {
            return (request, response, chain) -> {
                Span currentSpan = tracer.currentSpan();
                if (currentSpan != null) {
                    HttpServletResponse resp = (HttpServletResponse) response;
                    // putting trace id value in [mytraceid] response header
                    resp.addHeader("mytraceid", currentSpan.context().traceId());
                }
                chain.doFilter(request, response);
            };
        }

        // Example of a reactive WebFilter for reactive applications
        @Bean
        WebFilter traceIdInResponseFilter(Tracer tracer) {
            return (exchange, chain) -> {
                Span currentSpan = tracer.currentSpan();
                if (currentSpan != null) {
                    // putting trace id value in [mytraceid] response header
                    exchange.getResponse().getHeaders().add("mytraceid", currentSpan.context().traceId());
                }
                return chain.filter(exchange);
            };
        }
}
需要对 span 进行采样,解析器才能正常工作。这意味着你需要能够将 span 导出到 Zipkin。

7. 如何自定义 HTTP 客户端 Span?

注册一个 beanHttpRequestParser名称为HttpClientRequestParser.NAME为请求端添加自定义。 注册一个 beanHttpResponseParser名称为HttpClientRequestParser.NAME为响应端添加自定义。spring-doc.cadn.net.cn

@Configuration(proxyBeanMethods = false)
public static class ClientParserConfiguration {

    // example for Feign
    @Bean(name = HttpClientRequestParser.NAME)
    HttpRequestParser myHttpClientRequestParser() {
        return (request, context, span) -> {
            // Span customization
            span.name(request.method());
            span.tag("ClientRequest", "Tag");
            Object unwrap = request.unwrap();
            if (unwrap instanceof feign.Request) {
                feign.Request req = (feign.Request) unwrap;
                // Span customization
                span.tag("ClientRequestFeign", req.httpMethod().name());
            }
        };
    }

    // example for Feign
    @Bean(name = HttpClientResponseParser.NAME)
    HttpResponseParser myHttpClientResponseParser() {
        return (response, context, span) -> {
            // Span customization
            span.tag("ClientResponse", "Tag");
            Object unwrap = response.unwrap();
            if (unwrap instanceof feign.Response) {
                feign.Response resp = (feign.Response) unwrap;
                // Span customization
                span.tag("ClientResponseFeign", String.valueOf(resp.status()));
            }
        };
    }

}

8. 如何自定义 HTTP 服务器跨度?

注册一个 beanHttpRequestParser名称为HttpServerRequestParser.NAME为请求端添加自定义。 注册一个 beanHttpResponseParser名称为HttpServerResponseParser.NAME为响应端添加自定义。spring-doc.cadn.net.cn

@Configuration(proxyBeanMethods = false)
public static class ServerParserConfiguration {

    @Bean(name = HttpServerRequestParser.NAME)
    HttpRequestParser myHttpRequestParser() {
        return (request, context, span) -> {
            // Span customization
            span.tag("ServerRequest", "Tag");
            Object unwrap = request.unwrap();
            if (unwrap instanceof HttpServletRequest) {
                HttpServletRequest req = (HttpServletRequest) unwrap;
                // Span customization
                span.tag("ServerRequestServlet", req.getMethod());
            }
        };
    }

    @Bean(name = HttpServerResponseParser.NAME)
    HttpResponseParser myHttpResponseParser() {
        return (response, context, span) -> {
            // Span customization
            span.tag("ServerResponse", "Tag");
            Object unwrap = response.unwrap();
            if (unwrap instanceof HttpServletResponse) {
                HttpServletResponse resp = (HttpServletResponse) unwrap;
                // Span customization
                span.tag("ServerResponseServlet", String.valueOf(resp.getStatus()));
            }
        };
    }

    @Bean
    Filter traceIdInResponseFilter(Tracer tracer) {
        return (request, response, chain) -> {
            Span currentSpan = tracer.currentSpan();
            if (currentSpan != null) {
                HttpServletResponse resp = (HttpServletResponse) response;
                resp.addHeader("mytraceid", currentSpan.context().traceId());
            }
            chain.doFilter(request, response);
        };
    }

}
需要对 span 进行采样,解析器才能正常工作。这意味着你需要能够将 span 导出到 Zipkin。

9. 如何在日志中查看应用程序名称?

假设您尚未更改默认日志记录格式,请将spring.application.nameproperty 中bootstrap.yml,不在application.yml.spring-doc.cadn.net.cn

使用新的 Spring Cloud configuration bootstrap 时,应该不再需要这样做,因为将不再有 Bootstrap 上下文。

10. 如何更改上下文传播机制?

要使用提供的默认值,您可以设置spring.sleuth.propagation.type财产。 该值可以是一个列表,在这种情况下,您将传播更多的跟踪标头。spring-doc.cadn.net.cn

我们支持 BraveAWS,B3,W3Cpropagation 类型。spring-doc.cadn.net.cn

如果要提供自定义传播机制,请将spring.sleuth.propagation.typeproperty 设置为CUSTOM并实现您自己的 Bean (Propagation.Factoryfor Brave)。 您可以在下面找到示例:spring-doc.cadn.net.cn

@Component
class CustomPropagator extends Propagation.Factory implements Propagation<String> {

    @Override
    public List<String> keys() {
        return Arrays.asList("myCustomTraceId", "myCustomSpanId");
    }

    @Override
    public <R> TraceContext.Injector<R> injector(Setter<R, String> setter) {
        return (traceContext, request) -> {
            setter.put(request, "myCustomTraceId", traceContext.traceIdString());
            setter.put(request, "myCustomSpanId", traceContext.spanIdString());
        };
    }

    @Override
    public <R> TraceContext.Extractor<R> extractor(Getter<R, String> getter) {
        return request -> TraceContextOrSamplingFlags.create(TraceContext.newBuilder()
                .traceId(HexCodec.lowerHexToUnsignedLong(getter.get(request, "myCustomTraceId")))
                .spanId(HexCodec.lowerHexToUnsignedLong(getter.get(request, "myCustomSpanId"))).build());
    }

    @Override
    public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
        return StringPropagationAdapter.create(this, keyFactory);
    }

}

11. 如何实现自己的 Tracer?

Spring Cloud Sleuth API 包含跟踪器要实现的所有必要接口。 该项目附带 OpenZipkin Brave 实现。 您可以通过查看org.springframework.cloud.sleuth.brave.bridge模块。spring-doc.cadn.net.cn