2785 lines
121 KiB
HTML
2785 lines
121 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="generator" content="Asciidoctor 1.5.8">
|
|
<meta name="author" content="Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant">
|
|
<title>Spring Cloud Sleuth</title>
|
|
<link rel="stylesheet" href="css/spring.css">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
|
|
|
<style>
|
|
.hidden {
|
|
display: none;
|
|
}
|
|
|
|
.switch {
|
|
border-width: 1px 1px 0 1px;
|
|
border-style: solid;
|
|
border-color: #7a2518;
|
|
display: inline-block;
|
|
}
|
|
|
|
.switch--item {
|
|
padding: 10px;
|
|
background-color: #ffffff;
|
|
color: #7a2518;
|
|
display: inline-block;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.switch--item:not(:first-child) {
|
|
border-width: 0 0 0 1px;
|
|
border-style: solid;
|
|
border-color: #7a2518;
|
|
}
|
|
|
|
.switch--item.selected {
|
|
background-color: #7a2519;
|
|
color: #ffffff;
|
|
}
|
|
</style>
|
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
|
|
<script type="text/javascript">
|
|
function addBlockSwitches() {
|
|
$('.primary').each(function() {
|
|
primary = $(this);
|
|
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
|
|
primary.children('.title').remove();
|
|
});
|
|
$('.secondary').each(function(idx, node) {
|
|
secondary = $(node);
|
|
primary = findPrimary(secondary);
|
|
switchItem = createSwitchItem(secondary, primary.children('.switch'));
|
|
switchItem.content.addClass('hidden');
|
|
findPrimary(secondary).append(switchItem.content);
|
|
secondary.remove();
|
|
});
|
|
}
|
|
|
|
function createBlockSwitch(primary) {
|
|
blockSwitch = $('<div class="switch"></div>');
|
|
primary.prepend(blockSwitch);
|
|
return blockSwitch;
|
|
}
|
|
|
|
function findPrimary(secondary) {
|
|
candidate = secondary.prev();
|
|
while (!candidate.is('.primary')) {
|
|
candidate = candidate.prev();
|
|
}
|
|
return candidate;
|
|
}
|
|
|
|
function createSwitchItem(block, blockSwitch) {
|
|
blockName = block.children('.title').text();
|
|
content = block.children('.content').first().append(block.next('.colist'));
|
|
item = $('<div class="switch--item">' + blockName + '</div>');
|
|
item.on('click', '', content, function(e) {
|
|
$(this).addClass('selected');
|
|
$(this).siblings().removeClass('selected');
|
|
e.data.siblings('.content').addClass('hidden');
|
|
e.data.removeClass('hidden');
|
|
});
|
|
blockSwitch.append(item);
|
|
return {'item': item, 'content': content};
|
|
}
|
|
|
|
function globalSwitch() {
|
|
$('.switch--item').each(function() {
|
|
$(this).off('click');
|
|
$(this).on('click', function() {
|
|
selectedText = $(this).text()
|
|
selectedIndex = $(this).index()
|
|
$(".switch--item").filter(function() { return ($(this).text() === selectedText) }).each(function() {
|
|
$(this).addClass('selected');
|
|
$(this).siblings().removeClass('selected');
|
|
selectedContent = $(this).parent().siblings(".content").eq(selectedIndex)
|
|
selectedContent.removeClass('hidden');
|
|
selectedContent.siblings().addClass('hidden');
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
$(addBlockSwitches);
|
|
$(globalSwitch);
|
|
|
|
</script>
|
|
|
|
</head>
|
|
<body class="book toc2 toc-left">
|
|
<div id="header">
|
|
<h1>Spring Cloud Sleuth</h1>
|
|
<div class="details">
|
|
<span id="author" class="author">Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant</span><br>
|
|
</div>
|
|
<div id="toc" class="toc2">
|
|
<div id="toctitle">Table of Contents</div>
|
|
<ul class="sectlevel1">
|
|
<li><a href="#overview">1. Overview</a></li>
|
|
<li><a href="#features">2. Features</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#contextualizing-errors">2.1. Contextualizing errors</a></li>
|
|
<li><a href="#log-correlation">2.2. Log correlation</a></li>
|
|
<li><a href="#service-dependency-graph">2.3. Service Dependency Graph</a></li>
|
|
<li><a href="#request-scoped-properties-baggage">2.4. Request scoped properties (Baggage)</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#sleuth-adding-project">3. Adding Sleuth to your Project</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#sleuth-with-zipkin-via-http">3.1. Sleuth with Zipkin via HTTP</a></li>
|
|
<li><a href="#sleuth-with-zipkin-over-rabbitmq-or-kafka">3.2. Sleuth with Zipkin over RabbitMQ or Kafka</a></li>
|
|
<li><a href="#overriding-the-auto-configuration-of-zipkin">3.3. Overriding the auto-configuration of Zipkin</a></li>
|
|
<li><a href="#only-sleuth-log-correlation">3.4. Only Sleuth (log correlation)</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#how-sleuth-works">4. How Sleuth works</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#brave-basics">4.1. Brave Basics</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#sampling">5. Sampling</a></li>
|
|
<li><a href="#baggage">6. Baggage</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#java-configuration">6.1. Java configuration</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#instrumentation">7. Instrumentation</a></li>
|
|
<li><a href="#span-lifecycle">8. Span lifecycle</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#creating-and-finishing-spans">8.1. Creating and finishing spans</a></li>
|
|
<li><a href="#continuing-spans">8.2. Continuing Spans</a></li>
|
|
<li><a href="#creating-spans-with-explicit-parent">8.3. Creating a Span with an explicit Parent</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#naming-spans">9. Naming spans</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#spanname-annotation">9.1. <code>@SpanName</code> Annotation</a></li>
|
|
<li><a href="#tostring-method">9.2. <code>toString()</code> method</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#managing-spans-with-annotations">10. Managing Spans with Annotations</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#rationale">10.1. Rationale</a></li>
|
|
<li><a href="#creating-new-spans">10.2. Creating New Spans</a></li>
|
|
<li><a href="#continuing-spans-2">10.3. Continuing Spans</a></li>
|
|
<li><a href="#advanced-tag-setting">10.4. Advanced Tag Setting</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#customizations">11. Customizations</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#http">11.1. HTTP</a></li>
|
|
<li><a href="#tracingfilter">11.2. <code>TracingFilter</code></a></li>
|
|
<li><a href="#messaging">11.3. Messaging</a></li>
|
|
<li><a href="#rpc">11.4. RPC</a></li>
|
|
<li><a href="#custom-service-name">11.5. Custom service name</a></li>
|
|
<li><a href="#customization-of-reported-spans">11.6. Customization of Reported Spans</a></li>
|
|
<li><a href="#host-locator">11.7. Host Locator</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#sending-spans-to-zipkin">12. Sending Spans to Zipkin</a></li>
|
|
<li><a href="#zipkin-stream-span-consumer">13. Zipkin Stream Span Consumer</a></li>
|
|
<li><a href="#integrations">14. Integrations</a>
|
|
<ul class="sectlevel2">
|
|
<li><a href="#opentracing">14.1. OpenTracing</a></li>
|
|
<li><a href="#runnable-and-callable">14.2. Runnable and Callable</a></li>
|
|
<li><a href="#spring-cloud-circuitbreaker">14.3. Spring Cloud CircuitBreaker</a></li>
|
|
<li><a href="#rxjava">14.4. RxJava</a></li>
|
|
<li><a href="#http-integration">14.5. HTTP integration</a></li>
|
|
<li><a href="#http-client-integration">14.6. HTTP Client Integration</a></li>
|
|
<li><a href="#feign">14.7. Feign</a></li>
|
|
<li><a href="#grpc">14.8. gRPC</a></li>
|
|
<li><a href="#asynchronous-communication">14.9. Asynchronous Communication</a></li>
|
|
<li><a href="#messaging-2">14.10. Messaging</a></li>
|
|
<li><a href="#redis">14.11. Redis</a></li>
|
|
<li><a href="#quartz">14.12. Quartz</a></li>
|
|
<li><a href="#project-reactor">14.13. Project Reactor</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#configuration-properties">15. Configuration properties</a></li>
|
|
<li><a href="#running-examples">16. Running examples</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div id="content">
|
|
<div id="preamble">
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p><strong>3.0.0.M1</strong></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="overview"><a class="anchor" href="#overview"></a><a class="link" href="#overview">1. Overview</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth provides Spring Boot auto-configuration for distributed
|
|
tracing. Underneath, Spring Cloud Sleuth is a layer over a Tracer library named
|
|
<a href="https://github.com/openzipkin/brave">Brave</a>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Sleuth configures everything you need to get started. This includes where trace
|
|
data (spans) are reported to, how many traces to keep (sampling), if remote
|
|
fields (baggage) are sent, and which libraries are traced.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>We maintain an <a href="https://github.com/openzipkin/sleuth-webmvc-example">example app</a> where two Spring Boot services collaborate on an
|
|
HTTP request. Sleuth configures these apps, so that timing of these requests are
|
|
recorded into <a href="https://zipkin.io">Zipkin</a>, a distributed tracing system. Tracing
|
|
UIs visualize latency, such as time in one service vs waiting for other
|
|
services.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Here’s an example of what it looks like:</p>
|
|
</div>
|
|
<div class="imageblock">
|
|
<div class="content">
|
|
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-trace-screenshot.png" alt="Zipkin Trace">
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The <a href="https://github.com/openzipkin/sleuth-webmvc-example">source repository</a> of this
|
|
example includes demonstrations of many things, including WebFlux and messaging.
|
|
Most features require only a property or dependency change to work. These
|
|
snippets showcase the value of Spring Cloud Sleuth: Through auto-configuration,
|
|
Sleuth make getting started with distributed tracing easy!</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To keep things simple, the same example is used throughout documentation using
|
|
basic HTTP communication.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="features"><a class="anchor" href="#features"></a><a class="link" href="#features">2. Features</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Sleuth sets up instrumentation not only to track timing, but also to catch
|
|
errors so that they can be analyzed or correlated with logs. This works the
|
|
same way regardless of if the error came from a common instrumented library,
|
|
such as <code>RestTemplate</code>, or your own code annotated with <code>@NewSpan</code> or similar.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Below, we’ll use the word Zipkin to describe the tracing system, and include
|
|
Zipkin screenshots. However, most services accepting <a href="https://zipkin.io/zipkin-api/#/default/post_spans">Zipkin format</a>
|
|
have similar base features. Sleuth can also be configured to send data in other
|
|
formats, something detailed later.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="contextualizing-errors"><a class="anchor" href="#contextualizing-errors"></a><a class="link" href="#contextualizing-errors">2.1. Contextualizing errors</a></h3>
|
|
<div class="paragraph">
|
|
<p>Without distributed tracing, it can be difficult to understand the impact of a
|
|
an exception. For example, it can be hard to know if a specific request caused
|
|
the caller to fail or not.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Zipkin reduces time in triage by contextualizing errors and delays.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Requests colored red in the search screen failed:</p>
|
|
</div>
|
|
<div class="imageblock">
|
|
<div class="content">
|
|
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-error-traces.png" alt="Error Traces">
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you then click on one of the traces, you can understand if the failure
|
|
happened before the request hit another service or not:</p>
|
|
</div>
|
|
<div class="imageblock">
|
|
<div class="content">
|
|
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-error-trace-screenshot.png" alt="Error Traces Info propagation">
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For example, the above error happened in the "backend" service, and caused the
|
|
"frontend" service to fail.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="log-correlation"><a class="anchor" href="#log-correlation"></a><a class="link" href="#log-correlation">2.2. Log correlation</a></h3>
|
|
<div class="paragraph">
|
|
<p>Sleuth configures the logging context with variables including the service name
|
|
(<code>%{spring.zipkin.service.name}</code>) and the trace ID (<code>%{traceId}</code>). These help
|
|
you connect logs with distributed traces and allow you choice in what tools you
|
|
use to troubleshoot your services.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Once you find any log with an error, you can look for the trace ID in the
|
|
message. Paste that into Zipkin to visualize the entire trace, regardless of
|
|
how many services the first request ended up hitting.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code>backend.log: 2020-04-09 17:45:40.516 ERROR [backend,5e8eeec48b08e26882aba313eb08f0a4,dcc1df555b5777b3,true] 97203 --- [nio-9000-exec-1] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown
|
|
frontend.log:2020-04-09 17:45:40.574 ERROR [frontend,5e8eeec48b08e26882aba313eb08f0a4,82aba313eb08f0a4,true] 97192 --- [nio-8081-exec-2] o.s.c.s.i.web.ExceptionLoggingFilter : Uncaught exception thrown</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Above, you’ll notice the trace ID is <code>5e8eeec48b08e26882aba313eb08f0a4</code>, for
|
|
example. This log configuration was automatically setup by Sleuth.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you use a log aggregating tool (such as <a href="https://www.elastic.co/products/kibana">Kibana</a>, <a href="https://www.splunk.com/">Splunk</a>, and others), you can order the events that took place.
|
|
An example from Kibana would resemble the following image:</p>
|
|
</div>
|
|
<div class="imageblock">
|
|
<div class="content">
|
|
<img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/kibana.png" alt="Log correlation with Kibana">
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want to use <a href="https://www.elastic.co/guide/en/logstash/current/index.html">Logstash</a>, the following listing shows the Grok pattern for Logstash:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code>filter {
|
|
# pattern matching logback pattern
|
|
grok {
|
|
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
|
|
}
|
|
date {
|
|
match => ["timestamp", "ISO8601"]
|
|
}
|
|
mutate {
|
|
remove_field => ["timestamp"]
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code>filter {
|
|
# pattern matching logback pattern
|
|
grok {
|
|
match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
|
|
}
|
|
date {
|
|
match => ["timestamp", "ISO8601"]
|
|
}
|
|
mutate {
|
|
remove_field => ["timestamp"]
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="json-logback-with-logstash"><a class="anchor" href="#json-logback-with-logstash"></a><a class="link" href="#json-logback-with-logstash">2.2.1. JSON Logback with Logstash</a></h4>
|
|
<div class="paragraph">
|
|
<p>Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick.
|
|
To do so, you have to do the following (for readability, we pass the dependencies in the <code>groupId:artifactId:version</code> notation).</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Dependencies Setup</strong></p>
|
|
</div>
|
|
<div class="olist arabic">
|
|
<ol class="arabic">
|
|
<li>
|
|
<p>Ensure that Logback is on the classpath (<code>ch.qos.logback:logback-core</code>).</p>
|
|
</li>
|
|
<li>
|
|
<p>Add Logstash Logback encode. For example, to use version <code>4.6</code>, add <code>net.logstash.logback:logstash-logback-encoder:4.6</code>.</p>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Logback Setup</strong></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Consider the following example of a Logback configuration file (logback-spring.xml).</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><?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>
|
|
{
|
|
"timestamp": "@timestamp",
|
|
"severity": "%level",
|
|
"service": "${springAppName:-}",
|
|
"trace": "%X{traceId:-}",
|
|
"span": "%X{spanId:-}",
|
|
"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></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>That Logback configuration file:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>Logs information from the application in a JSON format to a <code>build/${spring.application.name}.json</code> file.</p>
|
|
</li>
|
|
<li>
|
|
<p>Has commented out two additional appenders: console and standard log file.</p>
|
|
</li>
|
|
<li>
|
|
<p>Has the same logging pattern as the one presented in the previous section.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
If you use a custom <code>logback-spring.xml</code>, you must pass the <code>spring.application.name</code> in the <code>bootstrap</code> rather than the <code>application</code> property file.
|
|
Otherwise, your custom logback file does not properly read the property.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="service-dependency-graph"><a class="anchor" href="#service-dependency-graph"></a><a class="link" href="#service-dependency-graph">2.3. Service Dependency Graph</a></h3>
|
|
<div class="paragraph">
|
|
<p>When you consider distributed tracing tracks requests, it makes sense that
|
|
trace data can paint a picture of your architecture.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Zipkin includes a tool to build service dependency diagrams from traces,
|
|
including the count of calls and how many errors exist.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The example application will make a simple diagram like this, but your real
|
|
environment diagram may be more complex.
|
|
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-depedendencies.png[Zipkin Dependencies]</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p><strong>Note</strong>: Production environments will generate a lot of data. You will likely
|
|
need to run a separate service to aggregate the dependency graph. You can learn
|
|
more <a href="https://github.com/openzipkin/zipkin-dependencies/">here</a>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="request-scoped-properties-baggage"><a class="anchor" href="#request-scoped-properties-baggage"></a><a class="link" href="#request-scoped-properties-baggage">2.4. Request scoped properties (Baggage)</a></h3>
|
|
<div class="paragraph">
|
|
<p>Distributed tracing works by propagating fields inside and across services that
|
|
connect the trace together: traceId and spanId notably. The context that holds
|
|
these fields can optionally push other fields that need to be consistent
|
|
regardless of many services are touched. The simple name for these extra fields
|
|
is "Baggage".</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Sleuth allows you to define which baggage are permitted to exist in the trace
|
|
context, including what header names are used.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows setting baggage values:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Span initialSpan = this.tracer.nextSpan().name("span").start();
|
|
BUSINESS_PROCESS.updateValue(initialSpan.context(), "ALM");
|
|
COUNTRY_CODE.updateValue(initialSpan.context(), "FO");</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
There is currently no limitation of the count or size of baggage
|
|
items. Keep in mind that too many can decrease system throughput or increase
|
|
RPC latency. In extreme cases, too much baggage can crash the application, due
|
|
to exceeding transport-level message or header capacity.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="baggage-versus-tags"><a class="anchor" href="#baggage-versus-tags"></a><a class="link" href="#baggage-versus-tags">2.4.1. Baggage versus Tags</a></h4>
|
|
<div class="paragraph">
|
|
<p>Like trace IDs, Baggage is attached to messages or requests, usually as
|
|
headers. Tags are key value pairs sent in a Span to Zipkin. Baggage values are
|
|
not added spans by default, which means you can’t search based on Baggage
|
|
unless you opt-in.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To make baggage also tags, use the property <code>spring.sleuth.baggage.tag-fields</code>
|
|
like so:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yml hljs" data-lang="yml">spring:
|
|
sleuth:
|
|
baggage:
|
|
remoteFields:
|
|
- country-code
|
|
- x-vcap-request-id
|
|
tagFields:
|
|
- country-code</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="sleuth-adding-project"><a class="anchor" href="#sleuth-adding-project"></a><a class="link" href="#sleuth-adding-project">3. Adding Sleuth to your Project</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>This section addresses how to add Sleuth to your project with either Maven or Gradle.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
To ensure that your application name is properly displayed in Zipkin, set the <code>spring.application.name</code> property in <code>bootstrap.yml</code>.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="sleuth-with-zipkin-via-http"><a class="anchor" href="#sleuth-with-zipkin-via-http"></a><a class="link" href="#sleuth-with-zipkin-via-http">3.1. Sleuth with Zipkin via HTTP</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you want both Sleuth and Zipkin, add the <code>spring-cloud-starter-zipkin</code> dependency.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to do so for Maven:</p>
|
|
</div>
|
|
<div class="listingblock primary">
|
|
<div class="title">Maven</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><dependencyManagement> <i class="conum" data-value="1"></i><b>(1)</b>
|
|
<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> <i class="conum" data-value="2"></i><b>(2)</b>
|
|
<groupId>org.springframework.cloud</groupId>
|
|
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
|
</dependency></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="colist arabic">
|
|
<table>
|
|
<tr>
|
|
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
|
<td>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
|
<td>Add the dependency to <code>spring-cloud-starter-zipkin</code>.</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to do so for Gradle:</p>
|
|
</div>
|
|
<div class="listingblock secondary">
|
|
<div class="title">Gradle</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">dependencyManagement { <i class="conum" data-value="1"></i><b>(1)</b>
|
|
imports {
|
|
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
|
|
}
|
|
}
|
|
|
|
dependencies { <i class="conum" data-value="2"></i><b>(2)</b>
|
|
compile "org.springframework.cloud:spring-cloud-starter-zipkin"
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="colist arabic">
|
|
<table>
|
|
<tr>
|
|
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
|
<td>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
|
<td>Add the dependency to <code>spring-cloud-starter-zipkin</code>.</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="sleuth-with-zipkin-over-rabbitmq-or-kafka"><a class="anchor" href="#sleuth-with-zipkin-over-rabbitmq-or-kafka"></a><a class="link" href="#sleuth-with-zipkin-over-rabbitmq-or-kafka">3.2. Sleuth with Zipkin over RabbitMQ or Kafka</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you want to use RabbitMQ or Kafka instead of HTTP, add the <code>spring-rabbit</code> or <code>spring-kafka</code> dependency.
|
|
The default destination name is <code>zipkin</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If using Kafka, you must set the property <code>spring.zipkin.sender.type</code> property accordingly:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring.zipkin.sender.type: kafka</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock caution">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-caution" title="Caution"></i>
|
|
</td>
|
|
<td class="content">
|
|
<code>spring-cloud-sleuth-stream</code> is deprecated and incompatible with these destinations.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want Sleuth over RabbitMQ, add the <code>spring-cloud-starter-zipkin</code> and <code>spring-rabbit</code>
|
|
dependencies.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to do so for Gradle:</p>
|
|
</div>
|
|
<div class="listingblock primary">
|
|
<div class="title">Maven</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><dependencyManagement> <i class="conum" data-value="1"></i><b>(1)</b>
|
|
<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> <i class="conum" data-value="2"></i><b>(2)</b>
|
|
<groupId>org.springframework.cloud</groupId>
|
|
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
|
</dependency>
|
|
<dependency> <i class="conum" data-value="3"></i><b>(3)</b>
|
|
<groupId>org.springframework.amqp</groupId>
|
|
<artifactId>spring-rabbit</artifactId>
|
|
</dependency></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="colist arabic">
|
|
<table>
|
|
<tr>
|
|
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
|
<td>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
|
<td>Add the dependency to <code>spring-cloud-starter-zipkin</code>. That way, all nested dependencies get downloaded.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="3"></i><b>3</b></td>
|
|
<td>To automatically configure RabbitMQ, add the <code>spring-rabbit</code> dependency.</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="listingblock secondary">
|
|
<div class="title">Gradle</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">dependencyManagement { <i class="conum" data-value="1"></i><b>(1)</b>
|
|
imports {
|
|
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
compile "org.springframework.cloud:spring-cloud-starter-zipkin" <i class="conum" data-value="2"></i><b>(2)</b>
|
|
compile "org.springframework.amqp:spring-rabbit" <i class="conum" data-value="3"></i><b>(3)</b>
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="colist arabic">
|
|
<table>
|
|
<tr>
|
|
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
|
<td>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
|
<td>Add the dependency to <code>spring-cloud-starter-zipkin</code>. That way, all nested dependencies get downloaded.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="3"></i><b>3</b></td>
|
|
<td>To automatically configure RabbitMQ, add the <code>spring-rabbit</code> dependency.</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="overriding-the-auto-configuration-of-zipkin"><a class="anchor" href="#overriding-the-auto-configuration-of-zipkin"></a><a class="link" href="#overriding-the-auto-configuration-of-zipkin">3.3. Overriding the auto-configuration of Zipkin</a></h3>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth supports sending traces to multiple tracing systems as of version 2.1.0.
|
|
In order to get this to work, every tracing system needs to have a <code>Reporter<Span></code> and <code>Sender</code>.
|
|
If you want to override the provided beans you need to give them a specific name.
|
|
To do this you can use respectively <code>ZipkinAutoConfiguration.REPORTER_BEAN_NAME</code> and <code>ZipkinAutoConfiguration.SENDER_BEAN_NAME</code>.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
protected static class MyConfig {
|
|
|
|
@Bean(ZipkinAutoConfiguration.REPORTER_BEAN_NAME)
|
|
Reporter<zipkin2.Span> myReporter() {
|
|
return AsyncReporter.create(mySender());
|
|
}
|
|
|
|
@Bean(ZipkinAutoConfiguration.SENDER_BEAN_NAME)
|
|
MySender mySender() {
|
|
return new MySender();
|
|
}
|
|
|
|
static class MySender extends Sender {
|
|
|
|
private boolean spanSent = false;
|
|
|
|
boolean isSpanSent() {
|
|
return this.spanSent;
|
|
}
|
|
|
|
@Override
|
|
public Encoding encoding() {
|
|
return Encoding.JSON;
|
|
}
|
|
|
|
@Override
|
|
public int messageMaxBytes() {
|
|
return Integer.MAX_VALUE;
|
|
}
|
|
|
|
@Override
|
|
public int messageSizeInBytes(List<byte[]> encodedSpans) {
|
|
return encoding().listSizeInBytes(encodedSpans);
|
|
}
|
|
|
|
@Override
|
|
public Call<Void> sendSpans(List<byte[]> encodedSpans) {
|
|
this.spanSent = true;
|
|
return Call.create(null);
|
|
}
|
|
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="only-sleuth-log-correlation"><a class="anchor" href="#only-sleuth-log-correlation"></a><a class="link" href="#only-sleuth-log-correlation">3.4. Only Sleuth (log correlation)</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you want to use only Spring Cloud Sleuth without the Zipkin integration, add the <code>spring-cloud-starter-sleuth</code> module to your project.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to add Sleuth with Maven:</p>
|
|
</div>
|
|
<div class="listingblock primary">
|
|
<div class="title">Maven</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><dependencyManagement> <i class="conum" data-value="1"></i><b>(1)</b>
|
|
<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> <i class="conum" data-value="2"></i><b>(2)</b>
|
|
<groupId>org.springframework.cloud</groupId>
|
|
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
|
</dependency></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="colist arabic">
|
|
<table>
|
|
<tr>
|
|
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
|
<td>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
|
<td>Add the dependency to <code>spring-cloud-starter-sleuth</code>.</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to add Sleuth with Gradle:</p>
|
|
</div>
|
|
<div class="listingblock secondary">
|
|
<div class="title">Gradle</div>
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-groovy hljs" data-lang="groovy">dependencyManagement { <i class="conum" data-value="1"></i><b>(1)</b>
|
|
imports {
|
|
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"
|
|
}
|
|
}
|
|
|
|
dependencies { <i class="conum" data-value="2"></i><b>(2)</b>
|
|
compile "org.springframework.cloud:spring-cloud-starter-sleuth"
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="colist arabic">
|
|
<table>
|
|
<tr>
|
|
<td><i class="conum" data-value="1"></i><b>1</b></td>
|
|
<td>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</td>
|
|
</tr>
|
|
<tr>
|
|
<td><i class="conum" data-value="2"></i><b>2</b></td>
|
|
<td>Add the dependency to <code>spring-cloud-starter-sleuth</code>.</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="how-sleuth-works"><a class="anchor" href="#how-sleuth-works"></a><a class="link" href="#how-sleuth-works">4. How Sleuth works</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth is a layer over <a href="https://github.com/openzipkin/brave">Brave</a>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Brave is a distributed tracing instrumentation library. Brave typically
|
|
intercepts production requests to gather timing data, correlate and propagate
|
|
trace contexts.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Trace data, also called spans, are typically reported to <a href="https://zipkin.io">Zipkin</a>.
|
|
Zipkin is an Open Source tracing system, which includes a UI and various
|
|
collectors, such as HTTP and messaging.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Many Open Source and commercial products accept <a href="https://zipkin.io/zipkin-api/#/default/post_spans">Zipkin format</a>.
|
|
Some options are documented <a href="https://zipkin.io/pages/extensions_choices.html">here</a>,
|
|
but many are not. If you cannot use Zipkin and your product isn’t listed, clarify
|
|
with your support representative and have them update that page. In many cases,
|
|
products already support Zipkin format, they just don’t document it.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Traces connect from service to service using header propagation. The default
|
|
format is <a href="https://github.com/openzipkin/b3-propagation">B3</a>. Similar to data
|
|
formats, you can configure alternate header formats also, provided trace and
|
|
span IDs are compatible with B3. Most notably, this means the trace ID and span
|
|
IDs are lower-case hex, not UUIDs. Besides trace identifiers, other properties
|
|
(Baggage) can also be passed along with the request. Remote Baggage must be
|
|
predefined, but is flexible otherwise.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Sleuth configures everything you need to get started with tracing. Sleuth
|
|
configures where trace data (spans) are reported to, how many traces to keep
|
|
(sampling), if remote fields (baggage) are sent, and which libraries are traced.
|
|
Sleuth also adds annotation based tracing features and some instrumentation not
|
|
available otherwise, such as Reactor. If cannot find the configuration you are
|
|
looking for in the documentation, ask <a href="https://gitter.im/spring-cloud/spring-cloud-sleuth">Gitter</a>
|
|
before assuming something cannot be done.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="brave-basics"><a class="anchor" href="#brave-basics"></a><a class="link" href="#brave-basics">4.1. Brave Basics</a></h3>
|
|
<div class="paragraph">
|
|
<p>Most instrumentation work is done for you by default. Sleuth provides beans to
|
|
allow you to change what’s traced, and it even provides annotations to avoid
|
|
using tracing libraries! All of this is explained later in this document.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>That said, you might want to know more about how things work underneath. Here
|
|
are some pointers.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Here are the most core types you might use:
|
|
* <code>SpanCustomizer</code> - to change the span currently in progress
|
|
* <code>Tracer</code> - to get a start new spans ad-hoc</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Here are the most relevant links from the OpenZipkin Brave project:
|
|
* [Brave’s core library](<a href="https://github.com/openzipkin/brave/tree/master/brave" class="bare">github.com/openzipkin/brave/tree/master/brave</a>)
|
|
* [Baggage (propagated fields)](<a href="https://github.com/openzipkin/brave/tree/master/brave#baggage" class="bare">github.com/openzipkin/brave/tree/master/brave#baggage</a>)
|
|
* [HTTP tracing](<a href="https://github.com/openzipkin/brave/tree/master/instrumentation/http" class="bare">github.com/openzipkin/brave/tree/master/instrumentation/http</a>)</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="sampling"><a class="anchor" href="#sampling"></a><a class="link" href="#sampling">5. Sampling</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>By default Spring Cloud Sleuth doesn’t sample spans.
|
|
That means that traces appear in logs but not in any remote store.
|
|
For testing the default is often enough, and it probably is all you need if you use only the logs (for example, with an ELK aggregator).
|
|
If you export span data to Zipkin, there is also an <code>Sampler.ALWAYS_SAMPLE</code> setting that exports everything, <code>RateLimitingSampler</code> setting that samples X transactions per second (defaults to <code>1000</code>) or <code>ProbabilityBasedSampler</code> setting that samples a fixed fraction of spans.</p>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
The <code>RateLimitingSampler</code> is the default if you use <code>spring-cloud-sleuth-zipkin</code>.
|
|
You can configure the rate limit by setting <code>spring.sleuth.sampler.rate</code>.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>A sampler can be installed by creating a bean definition, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
|
|
public Sampler defaultSampler() {
|
|
return Sampler.ALWAYS_SAMPLE;
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
You can set the HTTP header <code>b3</code> to <code>1</code>, or, when doing messaging, you can set the <code>spanFlags</code> header to <code>1</code>.
|
|
Doing so forces the current request to be sampled regardless of configuration.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In order to use the rate-limited sampler set the <code>spring.sleuth.sampler.rate</code> property to choose an amount of traces to accept on a per-second interval. The minimum number is 0 and the max is 2,147,483,647 (max int).</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="baggage"><a class="anchor" href="#baggage"></a><a class="link" href="#baggage">6. Baggage</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Baggage are fields that are propagated with the trace, optionally out of process. You can use
|
|
properties to define fields that have no special configuration such as name mapping:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><code>spring.sleuth.remote-fields</code> is a list of header names to accept and propagate to remote services.</p>
|
|
</li>
|
|
<li>
|
|
<p><code>spring.sleuth.local-fields</code> is a list of names to propagate locally</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>No prefixing applies with these keys. What you set is literally what is used.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>A name set in either of these properties will result in a <code>BaggageField</code> of the same name.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In order to automatically set the baggage values to Slf4j’s MDC, you have to set
|
|
the <code>spring.sleuth.baggage.correlation-fields</code> property with a list of whitelisted
|
|
local or remote keys. E.g. <code>spring.sleuth.baggage.correlation-fields=country-code</code> will set the
|
|
value of the <code>country-code</code> baggage into MDC.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Remember that adding entries to MDC can drastically decrease the performance of your application!
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want to add the baggage entries as tags, to make it possible to search for spans via the baggage entries, you can set the value of
|
|
<code>spring.sleuth.baggage.tag-fields</code> with a list of whitelisted baggage keys. To disable the feature you have to pass the <code>spring.sleuth.propagation.tag.enabled=false</code> property.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="java-configuration"><a class="anchor" href="#java-configuration"></a><a class="link" href="#java-configuration">6.1. Java configuration</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you need to do anything more advanced than above, do not define properties and instead use a
|
|
<code>@Bean</code> config for the baggage fields you use.
|
|
* <code>BaggagePropagationCustomizer</code> sets up baggage fields
|
|
* Add a <code>SingleBaggageField</code> to control header names for a <code>BaggageField</code>.
|
|
* <code>CorrelationScopeCustomizer</code> sets up MDC fields
|
|
* Add a <code>SingleCorrelationField</code> to change the MDC name of a <code>BaggageField</code> or if updates flush.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="instrumentation"><a class="anchor" href="#instrumentation"></a><a class="link" href="#instrumentation">7. Instrumentation</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth automatically instruments all your Spring applications, so you should not have to do anything to activate it.
|
|
The instrumentation is added by using a variety of technologies according to the stack that is available. For example, for a servlet web application, we use a <code>Filter</code>, and, for Spring Integration, we use <code>ChannelInterceptors</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can customize the keys used in span tags.
|
|
To limit the volume of span data, an HTTP request is, by default, tagged only with a handful of metadata, such as the status code, the host, and the URL.
|
|
You can add request headers by configuring <code>spring.sleuth.keys.http.headers</code> (a list of header names).</p>
|
|
</div>
|
|
<div class="admonitionblock note">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-note" title="Note"></i>
|
|
</td>
|
|
<td class="content">
|
|
Tags are collected and exported only if there is a <code>Sampler</code> that allows it. By default, there is no such <code>Sampler</code>, to ensure that there is no danger of accidentally collecting too much data without configuring something).
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="span-lifecycle"><a class="anchor" href="#span-lifecycle"></a><a class="link" href="#span-lifecycle">8. Span lifecycle</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>You can do the following operations on the Span by means of <code>brave.Tracer</code>:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><a href="#creating-and-finishing-spans">start</a>: When you start a span, its name is assigned and the start timestamp is recorded.</p>
|
|
</li>
|
|
<li>
|
|
<p><a href="#creating-and-finishing-spans">close</a>: The span gets finished (the end time of the span is recorded) and, if the span is sampled, it is eligible for collection (for example, to Zipkin).</p>
|
|
</li>
|
|
<li>
|
|
<p><a href="#continuing-spans">continue</a>: A new instance of span is created.
|
|
It is a copy of the one that it continues.</p>
|
|
</li>
|
|
<li>
|
|
<p><a href="#continuing-spans">detach</a>: The span does not get stopped or closed.
|
|
It only gets removed from the current thread.</p>
|
|
</li>
|
|
<li>
|
|
<p><a href="#creating-spans-with-explicit-parent">create with explicit parent</a>: You can create a new span and set an explicit parent for it.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
Spring Cloud Sleuth creates an instance of <code>Tracer</code> for you. In order to use it, you can autowire it.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="creating-and-finishing-spans"><a class="anchor" href="#creating-and-finishing-spans"></a><a class="link" href="#creating-and-finishing-spans">8.1. Creating and finishing spans</a></h3>
|
|
<div class="paragraph">
|
|
<p>You can manually create spans by using the <code>Tracer</code>, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// Start a span. If there was a span present in this thread it will become
|
|
// the `newSpan`'s parent.
|
|
Span newSpan = this.tracer.nextSpan().name("calculateTax");
|
|
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(newSpan.start())) {
|
|
// ...
|
|
// You can tag a span
|
|
newSpan.tag("taxValue", taxValue);
|
|
// ...
|
|
// You can log an event on a span
|
|
newSpan.annotate("taxCalculated");
|
|
}
|
|
finally {
|
|
// Once done remember to finish the span. This will allow collecting
|
|
// the span to send it to Zipkin
|
|
newSpan.finish();
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In the preceding example, we could see how to create a new instance of the span.
|
|
If there is already a span in this thread, it becomes the parent of the new span.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Always clean after you create a span. Also, always finish any span that you want to send to Zipkin.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
If your span contains a name greater than 50 chars, that name is truncated to 50 chars.
|
|
Your names have to be explicit and concrete. Big names lead to latency issues and sometimes even exceptions.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="continuing-spans"><a class="anchor" href="#continuing-spans"></a><a class="link" href="#continuing-spans">8.2. Continuing Spans</a></h3>
|
|
<div class="paragraph">
|
|
<p>Sometimes, you do not want to create a new span but you want to continue one. An example of such a
|
|
situation might be as follows:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><strong>AOP</strong>: If there was already a span created before an aspect was reached, you might not want to create a new span.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To continue a span, you can use <code>brave.Tracer</code>, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// let's assume that we're in a thread Y and we've received
|
|
// the `initialSpan` from thread X
|
|
Span continuedSpan = this.tracer.toSpan(newSpan.context());
|
|
try {
|
|
// ...
|
|
// You can tag a span
|
|
continuedSpan.tag("taxValue", taxValue);
|
|
// ...
|
|
// You can log an event on a span
|
|
continuedSpan.annotate("taxCalculated");
|
|
}
|
|
finally {
|
|
// Once done remember to flush the span. That means that
|
|
// it will get reported but the span itself is not yet finished
|
|
continuedSpan.flush();
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="creating-spans-with-explicit-parent"><a class="anchor" href="#creating-spans-with-explicit-parent"></a><a class="link" href="#creating-spans-with-explicit-parent">8.3. Creating a Span with an explicit Parent</a></h3>
|
|
<div class="paragraph">
|
|
<p>You might want to start a new span and provide an explicit parent of that span.
|
|
Assume that the parent of a span is in one thread and you want to start a new span in another thread.
|
|
In Brave, whenever you call <code>nextSpan()</code>, it creates a span in reference to the span that is currently in scope.
|
|
You can put the span in scope and then call <code>nextSpan()</code>, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// let's assume that we're in a thread Y and we've received
|
|
// the `initialSpan` from thread X. `initialSpan` will be the parent
|
|
// of the `newSpan`
|
|
Span newSpan = null;
|
|
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(initialSpan)) {
|
|
newSpan = this.tracer.nextSpan().name("calculateCommission");
|
|
// ...
|
|
// You can tag a span
|
|
newSpan.tag("commissionValue", commissionValue);
|
|
// ...
|
|
// You can log an event on a span
|
|
newSpan.annotate("commissionCalculated");
|
|
}
|
|
finally {
|
|
// Once done remember to finish the span. This will allow collecting
|
|
// the span to send it to Zipkin. The tags and events set on the
|
|
// newSpan will not be present on the parent
|
|
if (newSpan != null) {
|
|
newSpan.finish();
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
After creating such a span, you must finish it. Otherwise it is not reported (for example, to Zipkin).
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="naming-spans"><a class="anchor" href="#naming-spans"></a><a class="link" href="#naming-spans">9. Naming spans</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>Picking a span name is not a trivial task. A span name should depict an operation name.
|
|
The name should be low cardinality, so it should not include identifiers.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Since there is a lot of instrumentation going on, some span names are artificial:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><code>controller-method-name</code> when received by a Controller with a method name of <code>controllerMethodName</code></p>
|
|
</li>
|
|
<li>
|
|
<p><code>async</code> for asynchronous operations done with wrapped <code>Callable</code> and <code>Runnable</code> interfaces.</p>
|
|
</li>
|
|
<li>
|
|
<p>Methods annotated with <code>@Scheduled</code> return the simple name of the class.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Fortunately, for asynchronous processing, you can provide explicit naming.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="spanname-annotation"><a class="anchor" href="#spanname-annotation"></a><a class="link" href="#spanname-annotation">9.1. <code>@SpanName</code> Annotation</a></h3>
|
|
<div class="paragraph">
|
|
<p>You can name the span explicitly by using the <code>@SpanName</code> annotation, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@SpanName("calculateTax")
|
|
class TaxCountingRunnable implements Runnable {
|
|
|
|
@Override
|
|
public void run() {
|
|
// perform logic
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In this case, when processed in the following manner, the span is named <code>calculateTax</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Runnable runnable = new TraceRunnable(this.tracing, spanNamer,
|
|
new TaxCountingRunnable());
|
|
Future<?> future = executorService.submit(runnable);
|
|
// ... some additional logic ...
|
|
future.get();</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tostring-method"><a class="anchor" href="#tostring-method"></a><a class="link" href="#tostring-method">9.2. <code>toString()</code> method</a></h3>
|
|
<div class="paragraph">
|
|
<p>It is pretty rare to create separate classes for <code>Runnable</code> or <code>Callable</code>.
|
|
Typically, one creates an anonymous instance of those classes.
|
|
You cannot annotate such classes.
|
|
To overcome that limitation, if there is no <code>@SpanName</code> annotation present, we check whether the class has a custom implementation of the <code>toString()</code> method.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Running such code leads to creating a span named <code>calculateTax</code>, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Runnable runnable = new TraceRunnable(this.tracing, spanNamer, new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
// perform logic
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "calculateTax";
|
|
}
|
|
});
|
|
Future<?> future = executorService.submit(runnable);
|
|
// ... some additional logic ...
|
|
future.get();</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="managing-spans-with-annotations"><a class="anchor" href="#managing-spans-with-annotations"></a><a class="link" href="#managing-spans-with-annotations">10. Managing Spans with Annotations</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>You can manage spans with a variety of annotations.</p>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="rationale"><a class="anchor" href="#rationale"></a><a class="link" href="#rationale">10.1. Rationale</a></h3>
|
|
<div class="paragraph">
|
|
<p>There are a number of good reasons to manage spans with annotations, including:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>API-agnostic means to collaborate with a span. Use of annotations lets users add to a span with no library dependency on a span api.
|
|
Doing so lets Sleuth change its core API to create less impact to user code.</p>
|
|
</li>
|
|
<li>
|
|
<p>Reduced surface area for basic span operations. Without this feature, you must use the span api, which has lifecycle commands that could be used incorrectly.
|
|
By only exposing scope, tag, and log functionality, you can collaborate without accidentally breaking span lifecycle.</p>
|
|
</li>
|
|
<li>
|
|
<p>Collaboration with runtime generated code. With libraries such as Spring Data and Feign, the implementations of interfaces are generated at runtime.
|
|
Consequently, span wrapping of objects was tedious.
|
|
Now you can provide annotations over interfaces and the arguments of those interfaces.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="creating-new-spans"><a class="anchor" href="#creating-new-spans"></a><a class="link" href="#creating-new-spans">10.2. Creating New Spans</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you do not want to create local spans manually, you can use the <code>@NewSpan</code> annotation.
|
|
Also, we provide the <code>@SpanTag</code> annotation to add tags in an automated fashion.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now we can consider some examples of usage.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@NewSpan
|
|
void testMethod();</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Annotating the method without any parameter leads to creating a new span whose name equals the annotated method name.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@NewSpan("customNameOnTestMethod4")
|
|
void testMethod4();</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you provide the value in the annotation (either directly or by setting the <code>name</code> parameter), the created span has the provided value as the name.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// method declaration
|
|
@NewSpan(name = "customNameOnTestMethod5")
|
|
void testMethod5(@SpanTag("testTag") String param);
|
|
|
|
// and method execution
|
|
this.testBean.testMethod5("test");</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can combine both the name and a tag. Let’s focus on the latter.
|
|
In this case, the value of the annotated method’s parameter runtime value becomes the value of the tag.
|
|
In our sample, the tag key is <code>testTag</code>, and the tag value is <code>test</code>.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@NewSpan(name = "customNameOnTestMethod3")
|
|
@Override
|
|
public void testMethod3() {
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can place the <code>@NewSpan</code> annotation on both the class and an interface.
|
|
If you override the interface’s method and provide a different value for the <code>@NewSpan</code> annotation, the most
|
|
concrete one wins (in this case <code>customNameOnTestMethod3</code> is set).</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="continuing-spans-2"><a class="anchor" href="#continuing-spans-2"></a><a class="link" href="#continuing-spans-2">10.3. Continuing Spans</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you want to add tags and annotations to an existing span, you can use the <code>@ContinueSpan</code> annotation, as shown in the following example:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">// method declaration
|
|
@ContinueSpan(log = "testMethod11")
|
|
void testMethod11(@SpanTag("testTag11") String param);
|
|
|
|
// method execution
|
|
this.testBean.testMethod11("test");
|
|
this.testBean.testMethod13();</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>(Note that, in contrast with the <code>@NewSpan</code> annotation ,you can also add logs with the <code>log</code> parameter.)</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>That way, the span gets continued and:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>Log entries named <code>testMethod11.before</code> and <code>testMethod11.after</code> are created.</p>
|
|
</li>
|
|
<li>
|
|
<p>If an exception is thrown, a log entry named <code>testMethod11.afterFailure</code> is also created.</p>
|
|
</li>
|
|
<li>
|
|
<p>A tag with a key of <code>testTag11</code> and a value of <code>test</code> is created.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="advanced-tag-setting"><a class="anchor" href="#advanced-tag-setting"></a><a class="link" href="#advanced-tag-setting">10.4. Advanced Tag Setting</a></h3>
|
|
<div class="paragraph">
|
|
<p>There are 3 different ways to add tags to a span. All of them are controlled by the <code>SpanTag</code> annotation.
|
|
The precedence is as follows:</p>
|
|
</div>
|
|
<div class="olist arabic">
|
|
<ol class="arabic">
|
|
<li>
|
|
<p>Try with a bean of <code>TagValueResolver</code> type and a provided name.</p>
|
|
</li>
|
|
<li>
|
|
<p>If the bean name has not been provided, try to evaluate an expression.
|
|
We search for a <code>TagValueExpressionResolver</code> bean.
|
|
The default implementation uses SPEL expression resolution.
|
|
<strong>IMPORTANT</strong> You can only reference properties from the SPEL expression. Method execution is not allowed due to security constraints.</p>
|
|
</li>
|
|
<li>
|
|
<p>If we do not find any expression to evaluate, return the <code>toString()</code> value of the parameter.</p>
|
|
</li>
|
|
</ol>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="custom-extractor"><a class="anchor" href="#custom-extractor"></a><a class="link" href="#custom-extractor">10.4.1. Custom extractor</a></h4>
|
|
<div class="paragraph">
|
|
<p>The value of the tag for the following method is computed by an implementation of <code>TagValueResolver</code> interface.
|
|
Its class name has to be passed as the value of the <code>resolver</code> attribute.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Consider the following annotated method:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@NewSpan
|
|
public void getAnnotationForTagValueResolver(
|
|
@SpanTag(key = "test", resolver = TagValueResolver.class) String test) {
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Now further consider the following <code>TagValueResolver</code> bean implementation:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean(name = "myCustomTagValueResolver")
|
|
public TagValueResolver tagValueResolver() {
|
|
return parameter -> "Value from myCustomTagValueResolver";
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The two preceding examples lead to setting a tag value equal to <code>Value from myCustomTagValueResolver</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="resolving-expressions-for-a-value"><a class="anchor" href="#resolving-expressions-for-a-value"></a><a class="link" href="#resolving-expressions-for-a-value">10.4.2. Resolving Expressions for a Value</a></h4>
|
|
<div class="paragraph">
|
|
<p>Consider the following annotated method:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@NewSpan
|
|
public void getAnnotationForTagValueExpression(@SpanTag(key = "test",
|
|
expression = "'hello' + ' characters'") String test) {
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>No custom implementation of a <code>TagValueExpressionResolver</code> leads to evaluation of the SPEL expression, and a tag with a value of <code>4 characters</code> is set on the span.
|
|
If you want to use some other expression resolution mechanism, you can create your own implementation of the bean.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="using-the-tostring-method"><a class="anchor" href="#using-the-tostring-method"></a><a class="link" href="#using-the-tostring-method">10.4.3. Using the <code>toString()</code> method</a></h4>
|
|
<div class="paragraph">
|
|
<p>Consider the following annotated method:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@NewSpan
|
|
public void getAnnotationForArgumentToString(@SpanTag("test") Long param) {
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Running the preceding method with a value of <code>15</code> leads to setting a tag with a String value of <code>"15"</code>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="customizations"><a class="anchor" href="#customizations"></a><a class="link" href="#customizations">11. Customizations</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>The <code>Tracer</code> object is fully managed by sleuth, so you rarely need to affect it. That said,
|
|
Sleuth supports a number of <code>Customizer</code> types, that allow you to configure
|
|
anything not already done by Sleuth with auto-configuration or properties.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you define one of the following as a <code>Bean</code>, Sleuth will invoke it to
|
|
customize behaviour:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>RpcTracingCustomizer - for RPC tagging and sampling policy</p>
|
|
</li>
|
|
<li>
|
|
<p>HttpTracingCustomizer - for HTTP tagging and sampling policy</p>
|
|
</li>
|
|
<li>
|
|
<p>MessagingTracingCustomizer - for messaging tagging and sampling policy</p>
|
|
</li>
|
|
<li>
|
|
<p>CurrentTraceContextCustomizer - to integrate decorators such as correlation.</p>
|
|
</li>
|
|
<li>
|
|
<p>BaggagePropagationCustomizer - for propagating baggage fields in process and over headers</p>
|
|
</li>
|
|
<li>
|
|
<p>CorrelationScopeDecoratorCustomizer - for scope decorations such as MDC (logging) field correlation</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="http"><a class="anchor" href="#http"></a><a class="link" href="#http">11.1. HTTP</a></h3>
|
|
<div class="sect3">
|
|
<h4 id="data-policy"><a class="anchor" href="#data-policy"></a><a class="link" href="#data-policy">11.1.1. Data Policy</a></h4>
|
|
<div class="paragraph">
|
|
<p>The default span data policy for HTTP requests is described in Brave:
|
|
<a href="https://github.com/openzipkin/brave/tree/master/instrumentation/http#span-data-policy" class="bare">github.com/openzipkin/brave/tree/master/instrumentation/http#span-data-policy</a></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To add different data to the span, you need to register a bean of type
|
|
<code>brave.http.HttpRequestParser</code> or <code>brave.http.HttpResponseParser</code> based on when
|
|
the data is collected.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The bean names correspond to the request or response side, and whether it is
|
|
a client or server. For example, <code>sleuthHttpClientRequestParser</code> changes what
|
|
is collected before a client request is sent to the server.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For your convenience <code>@HttpClientRequestParser</code>, <code>@HttpClientResponseParser</code>
|
|
and corresponding server annotations can be used to inject the proper beans
|
|
or to reference the bean names via their static String <code>NAME</code> fields.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Here’s an example adding the HTTP url in addition to defaults:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
class Config {
|
|
@Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME })
|
|
HttpRequestParser sleuthHttpServerRequestParser() {
|
|
return (req, context, span) -> {
|
|
HttpRequestParser.DEFAULT.parse(req, context, span);
|
|
String url = req.url();
|
|
if (url != null) {
|
|
span.tag("http.url", url);
|
|
}
|
|
};
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="sampling-2"><a class="anchor" href="#sampling-2"></a><a class="link" href="#sampling-2">11.1.2. Sampling</a></h4>
|
|
<div class="paragraph">
|
|
<p>If client /server sampling is required, just register a bean of type
|
|
<code>brave.sampler.SamplerFunction<HttpRequest></code> and name the bean
|
|
<code>sleuthHttpClientSampler</code> for client sampler and <code>sleuthHttpServerSampler</code>
|
|
for server sampler.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For your convenience the <code>@HttpClientSampler</code> and <code>@HttpServerSampler</code>
|
|
annotations can be used to inject the proper beans or to reference the bean
|
|
names via their static String <code>NAME</code> fields.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Check out Brave’s code to see an example of how to make a path-based sampler
|
|
<a href="https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy" class="bare">github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy</a></p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want to completely rewrite the <code>HttpTracing</code> bean you can use the <code>SkipPatternProvider</code>
|
|
interface to retrieve the URL <code>Pattern</code> for spans that should be not sampled. Below you can see
|
|
an example of usage of <code>SkipPatternProvider</code> inside a server side, <code>Sampler<HttpRequest></code>.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
class Config {
|
|
@Bean(name = HttpServerSampler.NAME)
|
|
SamplerFunction<HttpRequest> myHttpSampler(SkipPatternProvider provider) {
|
|
Pattern pattern = provider.skipPattern();
|
|
return request -> {
|
|
String url = request.path();
|
|
boolean shouldSkip = pattern.matcher(url).matches();
|
|
if (shouldSkip) {
|
|
return false;
|
|
}
|
|
return null;
|
|
};
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="tracingfilter"><a class="anchor" href="#tracingfilter"></a><a class="link" href="#tracingfilter">11.2. <code>TracingFilter</code></a></h3>
|
|
<div class="paragraph">
|
|
<p>You can also modify the behavior of the <code>TracingFilter</code>, which is the component that is responsible for processing the input HTTP request and adding tags basing on the HTTP response.
|
|
You can customize the tags or modify the response headers by registering your own instance of the <code>TracingFilter</code> bean.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In the following example, we register the <code>TracingFilter</code> bean, add the <code>ZIPKIN-TRACE-ID</code> response header containing the current Span’s trace id, and add a tag with key <code>custom</code> and a value <code>tag</code> to the span.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Component
|
|
@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)
|
|
class MyFilter extends GenericFilterBean {
|
|
|
|
private final Tracer tracer;
|
|
|
|
MyFilter(Tracer tracer) {
|
|
this.tracer = tracer;
|
|
}
|
|
|
|
@Override
|
|
public void doFilter(ServletRequest request, ServletResponse response,
|
|
FilterChain chain) throws IOException, ServletException {
|
|
Span currentSpan = this.tracer.currentSpan();
|
|
if (currentSpan == null) {
|
|
chain.doFilter(request, response);
|
|
return;
|
|
}
|
|
// for readability we're returning trace id in a hex form
|
|
((HttpServletResponse) response).addHeader("ZIPKIN-TRACE-ID",
|
|
currentSpan.context().traceIdString());
|
|
// we can also add some custom tags
|
|
currentSpan.tag("custom", "tag");
|
|
chain.doFilter(request, response);
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="messaging"><a class="anchor" href="#messaging"></a><a class="link" href="#messaging">11.3. Messaging</a></h3>
|
|
<div class="paragraph">
|
|
<p>Sleuth automatically configures the <code>MessagingTracing</code> bean which serves as a
|
|
foundation for Messaging instrumentation such as Kafka or JMS.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If a customization of producer / consumer sampling of messaging traces is required,
|
|
just register a bean of type <code>brave.sampler.SamplerFunction<MessagingRequest></code> and
|
|
name the bean <code>sleuthProducerSampler</code> for producer sampler and <code>sleuthConsumerSampler</code>
|
|
for consumer sampler.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For your convenience the <code>@ProducerSampler</code> and <code>@ConsumerSampler</code>
|
|
annotations can be used to inject the proper beans or to reference the bean
|
|
names via their static String <code>NAME</code> fields.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Ex. Here’s a sampler that traces 100 consumer requests per second, except for
|
|
the "alerts" channel. Other requests will use a global rate provided by the
|
|
<code>Tracing</code> component.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
class Config {
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For more, see <a href="https://github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy" class="bare">github.com/openzipkin/brave/tree/master/instrumentation/messaging#sampling-policy</a></p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="rpc"><a class="anchor" href="#rpc"></a><a class="link" href="#rpc">11.4. RPC</a></h3>
|
|
<div class="paragraph">
|
|
<p>Sleuth automatically configures the <code>RpcTracing</code> bean which serves as a
|
|
foundation for RPC instrumentation such as gRPC or Dubbo.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If a customization of client / server sampling of the RPC traces is required,
|
|
just register a bean of type <code>brave.sampler.SamplerFunction<RpcRequest></code> and
|
|
name the bean <code>sleuthRpcClientSampler</code> for client sampler and
|
|
<code>sleuthRpcServerSampler</code> for server sampler.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For your convenience the <code>@RpcClientSampler</code> and <code>@RpcServerSampler</code>
|
|
annotations can be used to inject the proper beans or to reference the bean
|
|
names via their static String <code>NAME</code> fields.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Ex. Here’s a sampler that traces 100 "GetUserToken" server requests per second.
|
|
This doesn’t start new traces for requests to the health check service. Other
|
|
requests will use the global sampling configuration.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
class Config {
|
|
@Bean(name = RpcServerSampler.NAME)
|
|
SamplerFunction<RpcRequest> myRpcSampler() {
|
|
Matcher<RpcRequest> userAuth = and(serviceEquals("users.UserService"),
|
|
methodEquals("GetUserToken"));
|
|
return RpcRuleSampler.newBuilder()
|
|
.putRule(serviceEquals("grpc.health.v1.Health"), Sampler.NEVER_SAMPLE)
|
|
.putRule(userAuth, RateLimitingSampler.create(100)).build();
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>For more, see <a href="https://github.com/openzipkin/brave/tree/master/instrumentation/rpc#sampling-policy" class="bare">github.com/openzipkin/brave/tree/master/instrumentation/rpc#sampling-policy</a></p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="custom-service-name"><a class="anchor" href="#custom-service-name"></a><a class="link" href="#custom-service-name">11.5. Custom service name</a></h3>
|
|
<div class="paragraph">
|
|
<p>By default, Sleuth assumes that, when you send a span to Zipkin, you want the span’s service name to be equal to the value of the <code>spring.application.name</code> property.
|
|
That is not always the case, though.
|
|
There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
|
|
To achieve that, you can pass the following property to your application to override that value (the example is for a service named <code>myService</code>):</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring.zipkin.service.name: myService</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="customization-of-reported-spans"><a class="anchor" href="#customization-of-reported-spans"></a><a class="link" href="#customization-of-reported-spans">11.6. Customization of Reported Spans</a></h3>
|
|
<div class="paragraph">
|
|
<p>Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
|
|
You can do so by using the <code>FinishedSpanHandler</code> interface.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>In Sleuth, we generate spans with a fixed name.
|
|
Some users want to modify the name depending on values of tags.
|
|
You can implement the <code>FinishedSpanHandler</code> interface to alter that name.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to register two beans that implement <code>FinishedSpanHandler</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Bean
|
|
FinishedSpanHandler handlerOne() {
|
|
return new FinishedSpanHandler() {
|
|
@Override
|
|
public boolean handle(TraceContext traceContext, MutableSpan span) {
|
|
span.name("foo");
|
|
return true; // keep this span
|
|
}
|
|
};
|
|
}
|
|
|
|
@Bean
|
|
FinishedSpanHandler handlerTwo() {
|
|
return new FinishedSpanHandler() {
|
|
@Override
|
|
public boolean handle(TraceContext traceContext, MutableSpan span) {
|
|
span.name(span.name() + " bar");
|
|
return true; // keep this span
|
|
}
|
|
};
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The preceding example results in changing the name of the reported span to <code>foo bar</code>, just before it gets reported (for example, to Zipkin).</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="host-locator"><a class="anchor" href="#host-locator"></a><a class="link" href="#host-locator">11.7. Host Locator</a></h3>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
This section is about defining <strong>host</strong> from service discovery.
|
|
It is <strong>NOT</strong> about finding Zipkin through service discovery.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To define the host that corresponds to a particular span, we need to resolve the host name and port.
|
|
The default approach is to take these values from server properties.
|
|
If those are not set, we try to retrieve the host name from the network interfaces.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the <code>spring.zipkin.locator.discovery.enabled</code> property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring.zipkin.locator.discovery.enabled: true</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="sending-spans-to-zipkin"><a class="anchor" href="#sending-spans-to-zipkin"></a><a class="link" href="#sending-spans-to-zipkin">12. Sending Spans to Zipkin</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>By default, if you add <code>spring-cloud-starter-zipkin</code> as a dependency to your project, when the span is closed, it is sent to Zipkin over HTTP.
|
|
The communication is asynchronous.
|
|
You can configure the URL by setting the <code>spring.zipkin.baseUrl</code> property, as follows:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring.zipkin.baseUrl: https://192.168.99.100:9411/</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want to find Zipkin through service discovery, you can pass the Zipkin’s service ID inside the URL, as shown in the following example for <code>zipkinserver</code> service ID:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring.zipkin.baseUrl: https://zipkinserver/</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To disable this feature just set <code>spring.zipkin.discoveryClientEnabled</code> to `false.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>When the Discovery Client feature is enabled, Sleuth uses
|
|
<code>LoadBalancerClient</code> to find the URL of the Zipkin Server. It means
|
|
that you can set up the load balancing configuration e.g. via Ribbon.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">zipkinserver:
|
|
ribbon:
|
|
ListOfServers: host1,host2</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you have web, rabbit, activemq or kafka together on the classpath, you might need to pick the means by which you would like to send spans to zipkin.
|
|
To do so, set <code>web</code>, <code>rabbit</code>, <code>activemq</code> or <code>kafka</code> to the <code>spring.zipkin.sender.type</code> property.
|
|
The following example shows setting the sender type for <code>web</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-yaml hljs" data-lang="yaml">spring.zipkin.sender.type: web</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To customize the <code>RestTemplate</code> that sends spans to Zipkin via HTTP, you can register
|
|
the <code>ZipkinRestTemplateCustomizer</code> bean.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
class MyConfig {
|
|
@Bean ZipkinRestTemplateCustomizer myCustomizer() {
|
|
return new ZipkinRestTemplateCustomizer() {
|
|
@Override
|
|
void customize(RestTemplate restTemplate) {
|
|
// customize the RestTemplate
|
|
}
|
|
};
|
|
}
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If, however, you would like to control the full process of creating the <code>RestTemplate</code>
|
|
object, you will have to create a bean of <code>zipkin2.reporter.Sender</code> type.</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java"> @Bean Sender myRestTemplateSender(ZipkinProperties zipkin,
|
|
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
|
|
RestTemplate restTemplate = mySuperCustomRestTemplate();
|
|
zipkinRestTemplateCustomizer.customize(restTemplate);
|
|
return myCustomSender(zipkin, restTemplate);
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="zipkin-stream-span-consumer"><a class="anchor" href="#zipkin-stream-span-consumer"></a><a class="link" href="#zipkin-stream-span-consumer">13. Zipkin Stream Span Consumer</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
We recommend using Zipkin’s native support for message-based span sending.
|
|
Starting from the Edgware release, the Zipkin Stream server is deprecated.
|
|
In the Finchley release, it got removed.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If for some reason you need to create the deprecated Stream Zipkin server, see the <a href="https://cloud.spring.io/spring-cloud-static/Dalston.SR4/multi/multi__span_data_as_messages.html#_zipkin_consumer">Dalston Documentation</a>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="integrations"><a class="anchor" href="#integrations"></a><a class="link" href="#integrations">14. Integrations</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="sect2">
|
|
<h3 id="opentracing"><a class="anchor" href="#opentracing"></a><a class="link" href="#opentracing">14.1. OpenTracing</a></h3>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth is compatible with <a href="https://opentracing.io/">OpenTracing</a>.
|
|
If you have OpenTracing on the classpath, we automatically register the OpenTracing <code>Tracer</code> bean.
|
|
If you wish to disable this, set <code>spring.sleuth.opentracing.enabled</code> to <code>false</code></p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="runnable-and-callable"><a class="anchor" href="#runnable-and-callable"></a><a class="link" href="#runnable-and-callable">14.2. Runnable and Callable</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you wrap your logic in <code>Runnable</code> or <code>Callable</code>, you can wrap those classes in their Sleuth representative, as shown in the following example for <code>Runnable</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Runnable runnable = new Runnable() {
|
|
@Override
|
|
public void run() {
|
|
// do some work
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "spanNameFromToStringMethod";
|
|
}
|
|
};
|
|
// Manual `TraceRunnable` creation with explicit "calculateTax" Span name
|
|
Runnable traceRunnable = new TraceRunnable(this.tracing, spanNamer, runnable,
|
|
"calculateTax");
|
|
// Wrapping `Runnable` with `Tracing`. That way the current span will be available
|
|
// in the thread of `Runnable`
|
|
Runnable traceRunnableFromTracer = this.tracing.currentTraceContext()
|
|
.wrap(runnable);</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to do so for <code>Callable</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">Callable<String> callable = new Callable<String>() {
|
|
@Override
|
|
public String call() throws Exception {
|
|
return someLogic();
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "spanNameFromToStringMethod";
|
|
}
|
|
};
|
|
// Manual `TraceCallable` creation with explicit "calculateTax" Span name
|
|
Callable<String> traceCallable = new TraceCallable<>(this.tracing, spanNamer,
|
|
callable, "calculateTax");
|
|
// Wrapping `Callable` with `Tracing`. That way the current span will be available
|
|
// in the thread of `Callable`
|
|
Callable<String> traceCallableFromTracer = this.tracing.currentTraceContext()
|
|
.wrap(callable);</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>That way, you ensure that a new span is created and closed for each execution.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="spring-cloud-circuitbreaker"><a class="anchor" href="#spring-cloud-circuitbreaker"></a><a class="link" href="#spring-cloud-circuitbreaker">14.3. Spring Cloud CircuitBreaker</a></h3>
|
|
<div class="paragraph">
|
|
<p>If you have Spring Cloud CircuitBreaker on the classpath, we will wrap the passed command <code>Supplier</code> and the fallback <code>Function</code> in its trace representations. In order to disable this instrumentation set <code>spring.sleuth.circuitbreaker.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="rxjava"><a class="anchor" href="#rxjava"></a><a class="link" href="#rxjava">14.4. RxJava</a></h3>
|
|
<div class="paragraph">
|
|
<p>We registering a custom <a href="https://github.com/ReactiveX/RxJava/wiki/Plugins#rxjavaschedulershook"><code>RxJavaSchedulersHook</code></a> that wraps all <code>Action0</code> instances in their Sleuth representative, which is called <code>TraceAction</code>.
|
|
The hook either starts or continues a span, depending on whether tracing was already going on before the Action was scheduled.
|
|
To disable the custom <code>RxJavaSchedulersHook</code>, set the <code>spring.sleuth.rxjava.schedulers.hook.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can define a list of regular expressions for thread names for which you do not want spans to be created.
|
|
To do so, provide a comma-separated list of regular expressions in the <code>spring.sleuth.rxjava.schedulers.ignoredthreads</code> property.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
The suggest approach to reactive programming and Sleuth is to use
|
|
the Reactor support.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="http-integration"><a class="anchor" href="#http-integration"></a><a class="link" href="#http-integration">14.5. HTTP integration</a></h3>
|
|
<div class="paragraph">
|
|
<p>Features from this section can be disabled by setting the <code>spring.sleuth.web.enabled</code> property with value equal to <code>false</code>.</p>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="http-filter"><a class="anchor" href="#http-filter"></a><a class="link" href="#http-filter">14.5.1. HTTP Filter</a></h4>
|
|
<div class="paragraph">
|
|
<p>Through the <code>TracingFilter</code>, all sampled incoming requests result in creation of a Span.
|
|
That Span’s name is <code>http:</code> + the path to which the request was sent.
|
|
For example, if the request was sent to <code>/this/that</code> then the name will be <code>http:/this/that</code>.
|
|
You can configure which URIs you would like to skip by setting the <code>spring.sleuth.web.skipPattern</code> property.
|
|
If you have <code>ManagementServerProperties</code> on classpath, its value of <code>contextPath</code> gets appended to the provided skip pattern.
|
|
If you want to reuse the Sleuth’s default skip patterns and just append your own, pass those patterns by using the <code>spring.sleuth.web.additionalSkipPattern</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>By default, all the spring boot actuator endpoints are automatically added to the skip pattern.
|
|
If you want to disable this behaviour set <code>spring.sleuth.web.ignore-auto-configured-skip-patterns</code>
|
|
to <code>true</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To change the order of tracing filter registration, please set the
|
|
<code>spring.sleuth.web.filter-order</code> property.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To disable the filter that logs uncaught exceptions you can disable the
|
|
<code>spring.sleuth.web.exception-throwing-filter-enabled</code> property.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="handlerinterceptor"><a class="anchor" href="#handlerinterceptor"></a><a class="link" href="#handlerinterceptor">14.5.2. HandlerInterceptor</a></h4>
|
|
<div class="paragraph">
|
|
<p>Since we want the span names to be precise, we use a <code>TraceHandlerInterceptor</code> that either wraps an existing <code>HandlerInterceptor</code> or is added directly to the list of existing <code>HandlerInterceptors</code>.
|
|
The <code>TraceHandlerInterceptor</code> adds a special request attribute to the given <code>HttpServletRequest</code>.
|
|
If the the <code>TracingFilter</code> does not see this attribute, it creates a "<code>fallback</code>" span, which is an additional span created on the server side so that the trace is presented properly in the UI.
|
|
If that happens, there is probably missing instrumentation.
|
|
In that case, please file an issue in Spring Cloud Sleuth.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="async-servlet-support"><a class="anchor" href="#async-servlet-support"></a><a class="link" href="#async-servlet-support">14.5.3. Async Servlet support</a></h4>
|
|
<div class="paragraph">
|
|
<p>If your controller returns a <code>Callable</code> or a <code>WebAsyncTask</code>, Spring Cloud Sleuth continues the existing span instead of creating a new one.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="webflux-support"><a class="anchor" href="#webflux-support"></a><a class="link" href="#webflux-support">14.5.4. WebFlux support</a></h4>
|
|
<div class="paragraph">
|
|
<p>Through <code>TraceWebFilter</code>, all sampled incoming requests result in creation of a Span.
|
|
That Span’s name is <code>http:</code> + the path to which the request was sent.
|
|
For example, if the request was sent to <code>/this/that</code>, the name is <code>http:/this/that</code>.
|
|
You can configure which URIs you would like to skip by using the <code>spring.sleuth.web.skipPattern</code> property.
|
|
If you have <code>ManagementServerProperties</code> on the classpath, its value of <code>contextPath</code> gets appended to the provided skip pattern.
|
|
If you want to reuse Sleuth’s default skip patterns and append your own, pass those patterns by using the <code>spring.sleuth.web.additionalSkipPattern</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To change the order of tracing filter registration, please set the
|
|
<code>spring.sleuth.web.filter-order</code> property.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="dubbo-rpc-support"><a class="anchor" href="#dubbo-rpc-support"></a><a class="link" href="#dubbo-rpc-support">14.5.5. Dubbo RPC support</a></h4>
|
|
<div class="paragraph">
|
|
<p>Via the integration with Brave, Spring Cloud Sleuth supports <a href="https://dubbo.apache.org/">Dubbo</a>.
|
|
It’s enough to add the <code>brave-instrumentation-dubbo</code> dependency:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-xml hljs" data-lang="xml"><dependency>
|
|
<groupId>io.zipkin.brave</groupId>
|
|
<artifactId>brave-instrumentation-dubbo</artifactId>
|
|
</dependency></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You need to also set a <code>dubbo.properties</code> file with the following contents:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-properties hljs" data-lang="properties">dubbo.provider.filter=tracing
|
|
dubbo.consumer.filter=tracing</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can read more about Brave - Dubbo integration <a href="https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc">here</a>.
|
|
An example of Spring Cloud Sleuth and Dubbo can be found <a href="https://github.com/openzipkin/sleuth-webmvc-example/compare/add-dubbo-tracing">here</a>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="http-client-integration"><a class="anchor" href="#http-client-integration"></a><a class="link" href="#http-client-integration">14.6. HTTP Client Integration</a></h3>
|
|
<div class="sect3">
|
|
<h4 id="synchronous-rest-template"><a class="anchor" href="#synchronous-rest-template"></a><a class="link" href="#synchronous-rest-template">14.6.1. Synchronous Rest Template</a></h4>
|
|
<div class="paragraph">
|
|
<p>We inject a <code>RestTemplate</code> interceptor to ensure that all the tracing information is passed to the requests.
|
|
Each time a call is made, a new Span is created.
|
|
It gets closed upon receiving the response.
|
|
To block the synchronous <code>RestTemplate</code> features, set <code>spring.sleuth.web.client.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
You have to register <code>RestTemplate</code> as a bean so that the interceptors get injected.
|
|
If you create a <code>RestTemplate</code> instance with a <code>new</code> keyword, the instrumentation does NOT work.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="asynchronous-rest-template"><a class="anchor" href="#asynchronous-rest-template"></a><a class="link" href="#asynchronous-rest-template">14.6.2. Asynchronous Rest Template</a></h4>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Starting with Sleuth <code>2.0.0</code>, we no longer register a bean of <code>AsyncRestTemplate</code> type.
|
|
It is up to you to create such a bean.
|
|
Then we instrument it.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block the <code>AsyncRestTemplate</code> features, set <code>spring.sleuth.web.async.client.enabled</code> to <code>false</code>.
|
|
To disable creation of the default <code>TraceAsyncClientHttpRequestFactoryWrapper</code>, set <code>spring.sleuth.web.async.client.factory.enabled</code>
|
|
to <code>false</code>.
|
|
If you do not want to create <code>AsyncRestClient</code> at all, set <code>spring.sleuth.web.async.client.template.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="multiple-asynchronous-rest-templates"><a class="anchor" href="#multiple-asynchronous-rest-templates"></a><a class="link" href="#multiple-asynchronous-rest-templates">Multiple Asynchronous Rest Templates</a></h5>
|
|
<div class="paragraph">
|
|
<p>Sometimes you need to use multiple implementations of the Asynchronous Rest Template.
|
|
In the following snippet, you can see an example of how to set up such a custom <code>AsyncRestTemplate</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
@EnableAutoConfiguration
|
|
static class Config {
|
|
|
|
@Bean(name = "customAsyncRestTemplate")
|
|
public AsyncRestTemplate traceAsyncRestTemplate() {
|
|
return new AsyncRestTemplate(asyncClientFactory(),
|
|
clientHttpRequestFactory());
|
|
}
|
|
|
|
private ClientHttpRequestFactory clientHttpRequestFactory() {
|
|
ClientHttpRequestFactory clientHttpRequestFactory = new CustomClientHttpRequestFactory();
|
|
// CUSTOMIZE HERE
|
|
return clientHttpRequestFactory;
|
|
}
|
|
|
|
private AsyncClientHttpRequestFactory asyncClientFactory() {
|
|
AsyncClientHttpRequestFactory factory = new CustomAsyncClientHttpRequestFactory();
|
|
// CUSTOMIZE HERE
|
|
return factory;
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="webclient"><a class="anchor" href="#webclient"></a><a class="link" href="#webclient">14.6.3. <code>WebClient</code></a></h4>
|
|
<div class="paragraph">
|
|
<p>We inject a <code>ExchangeFilterFunction</code> implementation that creates a span and, through on-success and on-error callbacks, takes care of closing client-side spans.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.web.client.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
You have to register <code>WebClient</code> as a bean so that the tracing instrumentation gets applied.
|
|
If you create a <code>WebClient</code> instance with a <code>new</code> keyword, the instrumentation does NOT work.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="traverson"><a class="anchor" href="#traverson"></a><a class="link" href="#traverson">14.6.4. Traverson</a></h4>
|
|
<div class="paragraph">
|
|
<p>If you use the <a href="https://docs.spring.io/spring-hateoas/docs/current/reference/html/#client.traverson">Traverson</a> library, you can inject a <code>RestTemplate</code> as a bean into your Traverson object.
|
|
Since <code>RestTemplate</code> is already intercepted, you get full support for tracing in your client. The following pseudo code
|
|
shows how to do that:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Autowired RestTemplate restTemplate;
|
|
|
|
Traverson traverson = new Traverson(URI.create("https://some/address"),
|
|
MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8).setRestOperations(restTemplate);
|
|
// use Traverson</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="apache-httpclientbuilder-and-httpasyncclientbuilder"><a class="anchor" href="#apache-httpclientbuilder-and-httpasyncclientbuilder"></a><a class="link" href="#apache-httpclientbuilder-and-httpasyncclientbuilder">14.6.5. Apache <code>HttpClientBuilder</code> and <code>HttpAsyncClientBuilder</code></a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the <code>HttpClientBuilder</code> and <code>HttpAsyncClientBuilder</code> so that
|
|
tracing context gets injected to the sent requests.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block these features, set <code>spring.sleuth.web.client.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="netty-httpclient"><a class="anchor" href="#netty-httpclient"></a><a class="link" href="#netty-httpclient">14.6.6. Netty <code>HttpClient</code></a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the Netty’s <code>HttpClient</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.web.client.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
You have to register <code>HttpClient</code> as a bean so that the instrumentation happens.
|
|
If you create a <code>HttpClient</code> instance with a <code>new</code> keyword, the instrumentation does NOT work.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="userinforesttemplatecustomizer"><a class="anchor" href="#userinforesttemplatecustomizer"></a><a class="link" href="#userinforesttemplatecustomizer">14.6.7. <code>UserInfoRestTemplateCustomizer</code></a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the Spring Security’s <code>UserInfoRestTemplateCustomizer</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.web.client.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="feign"><a class="anchor" href="#feign"></a><a class="link" href="#feign">14.7. Feign</a></h3>
|
|
<div class="paragraph">
|
|
<p>By default, Spring Cloud Sleuth provides integration with Feign through <code>TraceFeignClientAutoConfiguration</code>.
|
|
You can disable it entirely by setting <code>spring.sleuth.feign.enabled</code> to <code>false</code>.
|
|
If you do so, no Feign-related instrumentation take place.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Part of Feign instrumentation is done through a <code>FeignBeanPostProcessor</code>.
|
|
You can disable it by setting <code>spring.sleuth.feign.processor.enabled</code> to <code>false</code>.
|
|
If you set it to <code>false</code>, Spring Cloud Sleuth does not instrument any of your custom Feign components.
|
|
However, all the default instrumentation is still there.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="grpc"><a class="anchor" href="#grpc"></a><a class="link" href="#grpc">14.8. gRPC</a></h3>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth provides instrumentation for <a href="https://grpc.io/">gRPC</a> through <code>TraceGrpcAutoConfiguration</code>. You can disable it entirely by setting <code>spring.sleuth.grpc.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="variant-1"><a class="anchor" href="#variant-1"></a><a class="link" href="#variant-1">14.8.1. Variant 1</a></h4>
|
|
<div class="sect4">
|
|
<h5 id="dependencies"><a class="anchor" href="#dependencies"></a><a class="link" href="#dependencies">Dependencies</a></h5>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
The gRPC integration relies on two external libraries to instrument clients and servers and both of those libraries must be on the class path to enable the instrumentation.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Maven:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code> <dependency>
|
|
<groupId>io.github.lognet</groupId>
|
|
<artifactId>grpc-spring-boot-starter</artifactId>
|
|
</dependency>
|
|
<dependency>
|
|
<groupId>io.zipkin.brave</groupId>
|
|
<artifactId>brave-instrumentation-grpc</artifactId>
|
|
</dependency></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Gradle:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code> compile("io.github.lognet:grpc-spring-boot-starter")
|
|
compile("io.zipkin.brave:brave-instrumentation-grpc")</code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="server-instrumentation"><a class="anchor" href="#server-instrumentation"></a><a class="link" href="#server-instrumentation">Server Instrumentation</a></h5>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth leverages grpc-spring-boot-starter to register Brave’s gRPC server interceptor with all services annotated with <code>@GRpcService</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="client-instrumentation"><a class="anchor" href="#client-instrumentation"></a><a class="link" href="#client-instrumentation">Client Instrumentation</a></h5>
|
|
<div class="paragraph">
|
|
<p>gRPC clients leverage a <code>ManagedChannelBuilder</code> to construct a <code>ManagedChannel</code> used to communicate to the gRPC server. The native <code>ManagedChannelBuilder</code> provides static methods as entry points for construction of <code>ManagedChannel</code> instances, however, this mechanism is outside the influence of the Spring application context.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Spring Cloud Sleuth provides a <code>SpringAwareManagedChannelBuilder</code> that can be customized through the Spring application context and injected by gRPC clients. <strong>This builder must be used when creating <code>ManagedChannel</code> instances.</strong>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>Sleuth creates a <code>TracingManagedChannelBuilderCustomizer</code> which inject Brave’s client interceptor into the <code>SpringAwareManagedChannelBuilder</code>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="variant-2"><a class="anchor" href="#variant-2"></a><a class="link" href="#variant-2">14.8.2. Variant 2</a></h4>
|
|
<div class="paragraph">
|
|
<p><a href="https://github.com/yidongnan/grpc-spring-boot-starter">Grpc Spring Boot Starter</a> automatically detects the presence of Spring Cloud Sleuth and brave’s instrumentation for gRPC and registers the necessary client and/or server tooling.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="asynchronous-communication"><a class="anchor" href="#asynchronous-communication"></a><a class="link" href="#asynchronous-communication">14.9. Asynchronous Communication</a></h3>
|
|
<div class="sect3">
|
|
<h4 id="async-annotated-methods"><a class="anchor" href="#async-annotated-methods"></a><a class="link" href="#async-annotated-methods">14.9.1. <code>@Async</code> Annotated methods</a></h4>
|
|
<div class="paragraph">
|
|
<p>In Spring Cloud Sleuth, we instrument async-related components so that the tracing information is passed between threads.
|
|
You can disable this behavior by setting the value of <code>spring.sleuth.async.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you annotate your method with <code>@Async</code>, we automatically create a new Span with the following characteristics:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>If the method is annotated with <code>@SpanName</code>, the value of the annotation is the Span’s name.</p>
|
|
</li>
|
|
<li>
|
|
<p>If the method is not annotated with <code>@SpanName</code>, the Span name is the annotated method name.</p>
|
|
</li>
|
|
<li>
|
|
<p>The span is tagged with the method’s class name and method name.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="scheduled-annotated-methods"><a class="anchor" href="#scheduled-annotated-methods"></a><a class="link" href="#scheduled-annotated-methods">14.9.2. <code>@Scheduled</code> Annotated Methods</a></h4>
|
|
<div class="paragraph">
|
|
<p>In Spring Cloud Sleuth, we instrument scheduled method execution so that the tracing information is passed between threads.
|
|
You can disable this behavior by setting the value of <code>spring.sleuth.scheduled.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you annotate your method with <code>@Scheduled</code>, we automatically create a new span with the following characteristics:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p>The span name is the annotated method name.</p>
|
|
</li>
|
|
<li>
|
|
<p>The span is tagged with the method’s class name and method name.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want to skip span creation for some <code>@Scheduled</code> annotated classes, you can set the <code>spring.sleuth.scheduled.skipPattern</code> with a regular expression that matches the fully qualified name of the <code>@Scheduled</code> annotated class.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="executor-executorservice-and-scheduledexecutorservice"><a class="anchor" href="#executor-executorservice-and-scheduledexecutorservice"></a><a class="link" href="#executor-executorservice-and-scheduledexecutorservice">14.9.3. Executor, ExecutorService, and ScheduledExecutorService</a></h4>
|
|
<div class="paragraph">
|
|
<p>We provide <code>LazyTraceExecutor</code>, <code>TraceableExecutorService</code>, and <code>TraceableScheduledExecutorService</code>. Those implementations create spans each time a new task is submitted, invoked, or scheduled.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>The following example shows how to pass tracing information with <code>TraceableExecutorService</code> when working with <code>CompletableFuture</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(() -> {
|
|
// perform some logic
|
|
return 1_000_000L;
|
|
}, new TraceableExecutorService(beanFactory, executorService,
|
|
// 'calculateTax' explicitly names the span - this param is optional
|
|
"calculateTax"));</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
Sleuth does not work with <code>parallelStream()</code> out of the box.
|
|
If you want to have the tracing information propagated through the stream, you have to use the approach with <code>supplyAsync(...)</code>, as shown earlier.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If there are beans that implement the <code>Executor</code> interface that you would like
|
|
to exclude from span creation, you can use the <code>spring.sleuth.async.ignored-beans</code>
|
|
property where you can provide a list of bean names.</p>
|
|
</div>
|
|
<div class="sect4">
|
|
<h5 id="customization-of-executors"><a class="anchor" href="#customization-of-executors"></a><a class="link" href="#customization-of-executors">Customization of Executors</a></h5>
|
|
<div class="paragraph">
|
|
<p>Sometimes, you need to set up a custom instance of the <code>AsyncExecutor</code>.
|
|
The following example shows how to set up such a custom <code>Executor</code>:</p>
|
|
</div>
|
|
<div class="listingblock">
|
|
<div class="content">
|
|
<pre class="highlightjs highlight"><code class="language-java hljs" data-lang="java">@Configuration
|
|
@EnableAutoConfiguration
|
|
@EnableAsync
|
|
// add the infrastructure role to ensure that the bean gets auto-proxied
|
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
|
static class CustomExecutorConfig extends AsyncConfigurerSupport {
|
|
|
|
@Autowired
|
|
BeanFactory beanFactory;
|
|
|
|
@Override
|
|
public Executor getAsyncExecutor() {
|
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|
// CUSTOMIZE HERE
|
|
executor.setCorePoolSize(7);
|
|
executor.setMaxPoolSize(42);
|
|
executor.setQueueCapacity(11);
|
|
executor.setThreadNamePrefix("MyExecutor-");
|
|
// DON'T FORGET TO INITIALIZE
|
|
executor.initialize();
|
|
return new LazyTraceExecutor(this.beanFactory, executor);
|
|
}
|
|
|
|
}</code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="admonitionblock tip">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-tip" title="Tip"></i>
|
|
</td>
|
|
<td class="content">
|
|
To ensure that your configuration gets post processed, remember
|
|
to add the <code>@Role(BeanDefinition.ROLE_INFRASTRUCTURE)</code> on your
|
|
<code>@Configuration</code> class
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="messaging-2"><a class="anchor" href="#messaging-2"></a><a class="link" href="#messaging-2">14.10. Messaging</a></h3>
|
|
<div class="paragraph">
|
|
<p>Features from this section can be disabled by setting the <code>spring.sleuth.messaging.enabled</code> property with value equal to <code>false</code>.</p>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-integration-and-spring-cloud-stream"><a class="anchor" href="#spring-integration-and-spring-cloud-stream"></a><a class="link" href="#spring-integration-and-spring-cloud-stream">14.10.1. Spring Integration and Spring Cloud Stream</a></h4>
|
|
<div class="paragraph">
|
|
<p>Spring Cloud Sleuth integrates with <a href="https://projects.spring.io/spring-integration/">Spring Integration</a>.
|
|
It creates spans for publish and subscribe events.
|
|
To disable Spring Integration instrumentation, set <code>spring.sleuth.integration.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>You can provide the <code>spring.sleuth.integration.patterns</code> pattern to explicitly provide the names of channels that you want to include for tracing.
|
|
By default, all channels but <code>hystrixStreamOutput</code> channel are included.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
When using the <code>Executor</code> to build a Spring Integration <code>IntegrationFlow</code>, you must use the untraced version of the <code>Executor</code>.
|
|
Decorating the Spring Integration Executor Channel with <code>TraceableExecutorService</code> causes the spans to be improperly closed.
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>If you want to customize the way tracing context is read from and written to message headers,
|
|
it’s enough for you to register beans of types:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><code>Propagation.Setter<MessageHeaderAccessor, String></code> - for writing headers to the message</p>
|
|
</li>
|
|
<li>
|
|
<p><code>Propagation.Getter<MessageHeaderAccessor, String></code> - for reading headers from the message</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-rabbitmq"><a class="anchor" href="#spring-rabbitmq"></a><a class="link" href="#spring-rabbitmq">14.10.2. Spring RabbitMq</a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the <code>RabbitTemplate</code> so that tracing headers get injected
|
|
into the message.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.messaging.rabbit.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-kafka"><a class="anchor" href="#spring-kafka"></a><a class="link" href="#spring-kafka">14.10.3. Spring Kafka</a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the Spring Kafka’s <code>ProducerFactory</code> and <code>ConsumerFactory</code>
|
|
so that tracing headers get injected into the created Spring Kafka’s
|
|
<code>Producer</code> and <code>Consumer</code>.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.messaging.kafka.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-kafka-streams"><a class="anchor" href="#spring-kafka-streams"></a><a class="link" href="#spring-kafka-streams">14.10.4. Spring Kafka Streams</a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the <code>KafkaStreams</code> <code>KafkaClientSupplier</code> so that tracing headers
|
|
get injected into the <code>Producer</code> and <code>Consumer`s. A `KafkaStreamsTracing</code> bean
|
|
allows for further instrumentation through additional <code>TransformerSupplier</code> and
|
|
<code>ProcessorSupplier</code> methods.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.messaging.kafka.streams.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-jms"><a class="anchor" href="#spring-jms"></a><a class="link" href="#spring-jms">14.10.5. Spring JMS</a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument the <code>JmsTemplate</code> so that tracing headers get injected
|
|
into the message. We also support <code>@JmsListener</code> annotated methods on the consumer side.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.messaging.jms.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
<div class="admonitionblock important">
|
|
<table>
|
|
<tr>
|
|
<td class="icon">
|
|
<i class="fa icon-important" title="Important"></i>
|
|
</td>
|
|
<td class="content">
|
|
We don’t support baggage propagation for JMS
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
<div class="sect3">
|
|
<h4 id="spring-cloud-aws-messaging-sqs"><a class="anchor" href="#spring-cloud-aws-messaging-sqs"></a><a class="link" href="#spring-cloud-aws-messaging-sqs">14.10.6. Spring Cloud AWS Messaging SQS</a></h4>
|
|
<div class="paragraph">
|
|
<p>We instrument <code>@SqsListener</code> which is provided by <code>org.springframework.cloud:spring-cloud-aws-messaging</code>
|
|
so that tracing headers get extracted from the message and a trace gets put into the context.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To block this feature, set <code>spring.sleuth.messaging.sqs.enabled</code> to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="redis"><a class="anchor" href="#redis"></a><a class="link" href="#redis">14.11. Redis</a></h3>
|
|
<div class="paragraph">
|
|
<p>We set <code>tracing</code> property to Lettcue <code>ClientResources</code> instance to enable Brave tracing built in Lettuce .
|
|
To disable Redis support, set the <code>spring.sleuth.redis.enabled</code> property to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="quartz"><a class="anchor" href="#quartz"></a><a class="link" href="#quartz">14.12. Quartz</a></h3>
|
|
<div class="paragraph">
|
|
<p>We instrument quartz jobs by adding Job/Trigger listeners to the Quartz Scheduler.</p>
|
|
</div>
|
|
<div class="paragraph">
|
|
<p>To turn off this feature, set the <code>spring.sleuth.quartz.enabled</code> property to <code>false</code>.</p>
|
|
</div>
|
|
</div>
|
|
<div class="sect2">
|
|
<h3 id="project-reactor"><a class="anchor" href="#project-reactor"></a><a class="link" href="#project-reactor">14.13. Project Reactor</a></h3>
|
|
<div class="paragraph">
|
|
<p>For projects depending on Project Reactor such as Spring Cloud Gateway, we suggest turning the <code>spring.sleuth.reactor.decorate-on-each</code> option to <code>false</code>. That way an increased performance gain should be observed in comparison to the standard instrumentation mechanism. What this option does is it will wrap decorate <code>onLast</code> operator instead of <code>onEach</code> which will result in creation of far fewer objects. The downside of this is that when Project Reactor will change threads, the trace propagation will continue without issues, however anything relying on the <code>ThreadLocal</code> such as e.g. MDC entries can be buggy.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="configuration-properties"><a class="anchor" href="#configuration-properties"></a><a class="link" href="#configuration-properties">15. Configuration properties</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>To see the list of all Sleuth related configuration properties please check <a href="appendix.html">the Appendix page</a>.</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="sect1">
|
|
<h2 id="running-examples"><a class="anchor" href="#running-examples"></a><a class="link" href="#running-examples">16. Running examples</a></h2>
|
|
<div class="sectionbody">
|
|
<div class="paragraph">
|
|
<p>You can see the running examples deployed in the <a href="https://run.pivotal.io/">Pivotal Web Services</a>.
|
|
Check them out at the following links:</p>
|
|
</div>
|
|
<div class="ulist">
|
|
<ul>
|
|
<li>
|
|
<p><a href="https://docssleuth-zipkin-server.cfapps.io/">Zipkin for apps presented in the samples to the top</a>. First make
|
|
a request to <a href="https://docssleuth-service1.cfapps.io/start">Service 1</a> and then check out the trace in Zipkin.</p>
|
|
</li>
|
|
<li>
|
|
<p><a href="https://docsbrewing-zipkin-server.cfapps.io/">Zipkin for Brewery on PWS</a>, its <a href="https://github.com/spring-cloud-samples/brewery">Github Code</a>.
|
|
Ensure that you’ve picked the lookback period of 7 days. If there are no traces, go to <a href="https://docsbrewing-presenting.cfapps.io/">Presenting application</a>
|
|
and order some beers. Then check Zipkin for traces.</p>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<script type="text/javascript" src="js/tocbot/tocbot.min.js"></script>
|
|
<script type="text/javascript" src="js/toc.js"></script>
|
|
<link rel="stylesheet" href="js/highlight/styles/github.min.css">
|
|
<script src="js/highlight/highlight.min.js"></script>
|
|
<script>hljs.initHighlighting()</script>
|
|
</body>
|
|
</html> |