958 lines
199 KiB
HTML
958 lines
199 KiB
HTML
<html><head>
|
|
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
|
<title>Spring Cloud Sleuth</title><link rel="stylesheet" type="text/css" href="css/manual-singlepage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div lang="en" class="book"><div class="titlepage"><div><div><h1 class="title"><a name="d0e3"></a>Spring Cloud Sleuth</h1></div><div><span xmlns:d="http://docbook.org/ns/docbook" class="author"><span class="firstname">Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant</span></span></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="preface"><a href="#d0e17"></a></span></dt><dt><span class="chapter"><a href="#_introduction">1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#_terminology">1.1. Terminology</a></span></dt><dt><span class="section"><a href="#_purpose">1.2. Purpose</a></span></dt><dd><dl><dt><span class="section"><a href="#_distributed_tracing_with_zipkin">1.2.1. Distributed Tracing with Zipkin</a></span></dt><dt><span class="section"><a href="#_visualizing_errors">1.2.2. Visualizing errors</a></span></dt><dt><span class="section"><a href="#_distributed_tracing_with_brave">1.2.3. Distributed Tracing with Brave</a></span></dt><dt><span class="section"><a href="#_live_examples">1.2.4. Live examples</a></span></dt><dt><span class="section"><a href="#_log_correlation">1.2.5. Log correlation</a></span></dt><dd><dl><dt><span class="section"><a href="#_json_logback_with_logstash">JSON Logback with Logstash</a></span></dt></dl></dd><dt><span class="section"><a href="#_propagating_span_context">1.2.6. Propagating Span Context</a></span></dt><dd><dl><dt><span class="section"><a href="#_baggage_versus_span_tags">Baggage versus Span Tags</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#sleuth-adding-project">1.3. Adding Sleuth to the Project</a></span></dt><dd><dl><dt><span class="section"><a href="#_only_sleuth_log_correlation">1.3.1. Only Sleuth (log correlation)</a></span></dt><dt><span class="section"><a href="#_sleuth_with_zipkin_via_http">1.3.2. Sleuth with Zipkin via HTTP</a></span></dt><dt><span class="section"><a href="#_sleuth_with_zipkin_over_rabbitmq_or_kafka">1.3.3. Sleuth with Zipkin over RabbitMQ or Kafka</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_additional_resources">2. Additional Resources</a></span></dt><dt><span class="chapter"><a href="#_features">3. Features</a></span></dt><dd><dl><dt><span class="section"><a href="#_introduction_to_brave">3.1. Introduction to Brave</a></span></dt><dd><dl><dt><span class="section"><a href="#_tracing">3.1.1. Tracing</a></span></dt><dt><span class="section"><a href="#_local_tracing">3.1.2. Local Tracing</a></span></dt><dt><span class="section"><a href="#_customizing_spans">3.1.3. Customizing Spans</a></span></dt><dt><span class="section"><a href="#_implicitly_looking_up_the_current_span">3.1.4. Implicitly Looking up the Current Span</a></span></dt><dt><span class="section"><a href="#_rpc_tracing">3.1.5. RPC tracing</a></span></dt><dd><dl><dt><span class="section"><a href="#_one_way_tracing">One-Way tracing</a></span></dt></dl></dd></dl></dd></dl></dd><dt><span class="chapter"><a href="#_sampling">4. Sampling</a></span></dt><dd><dl><dt><span class="section"><a href="#_declarative_sampling">4.1. Declarative sampling</a></span></dt><dt><span class="section"><a href="#_custom_sampling">4.2. Custom sampling</a></span></dt><dt><span class="section"><a href="#_sampling_in_spring_cloud_sleuth">4.3. Sampling in Spring Cloud Sleuth</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_propagation">5. Propagation</a></span></dt><dd><dl><dt><span class="section"><a href="#_propagating_extra_fields">5.1. Propagating extra fields</a></span></dt><dd><dl><dt><span class="section"><a href="#prefixed-fields">5.1.1. Prefixed fields</a></span></dt><dt><span class="section"><a href="#_extracting_a_propagated_context">5.1.2. Extracting a Propagated Context</a></span></dt><dt><span class="section"><a href="#_sharing_span_ids_between_client_and_server">5.1.3. Sharing span IDs between Client and Server</a></span></dt><dt><span class="section"><a href="#_implementing_propagation">5.1.4. Implementing Propagation</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_current_tracing_component">6. Current Tracing Component</a></span></dt><dt><span class="chapter"><a href="#_current_span">7. Current Span</a></span></dt><dd><dl><dt><span class="section"><a href="#_setting_a_span_in_scope_manually">7.1. Setting a span in scope manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_instrumentation">8. Instrumentation</a></span></dt><dt><span class="chapter"><a href="#_span_lifecycle">9. Span lifecycle</a></span></dt><dd><dl><dt><span class="section"><a href="#creating-and-finishing-spans">9.1. Creating and finishing spans</a></span></dt><dt><span class="section"><a href="#continuing-spans">9.2. Continuing Spans</a></span></dt><dt><span class="section"><a href="#creating-spans-with-explicit-parent">9.3. Creating a Span with an explicit Parent</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_naming_spans">10. Naming spans</a></span></dt><dd><dl><dt><span class="section"><a href="#_spanname_annotation">10.1. <code class="literal">@SpanName</code> Annotation</a></span></dt><dt><span class="section"><a href="#_tostring_method">10.2. <code class="literal">toString()</code> method</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_managing_spans_with_annotations">11. Managing Spans with Annotations</a></span></dt><dd><dl><dt><span class="section"><a href="#_rationale">11.1. Rationale</a></span></dt><dt><span class="section"><a href="#_creating_new_spans">11.2. Creating New Spans</a></span></dt><dt><span class="section"><a href="#_continuing_spans">11.3. Continuing Spans</a></span></dt><dt><span class="section"><a href="#_advanced_tag_setting">11.4. Advanced Tag Setting</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_extractor">11.4.1. Custom extractor</a></span></dt><dt><span class="section"><a href="#_resolving_expressions_for_a_value">11.4.2. Resolving Expressions for a Value</a></span></dt><dt><span class="section"><a href="#_using_the_tostring_method">11.4.3. Using the <code class="literal">toString()</code> method</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_customizations">12. Customizations</a></span></dt><dd><dl><dt><span class="section"><a href="#_http">12.1. HTTP</a></span></dt><dt><span class="section"><a href="#_tracingfilter">12.2. <code class="literal">TracingFilter</code></a></span></dt><dt><span class="section"><a href="#_custom_service_name">12.3. Custom service name</a></span></dt><dt><span class="section"><a href="#_customization_of_reported_spans">12.4. Customization of Reported Spans</a></span></dt><dt><span class="section"><a href="#_host_locator">12.5. Host Locator</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_sending_spans_to_zipkin">13. Sending Spans to Zipkin</a></span></dt><dt><span class="chapter"><a href="#_zipkin_stream_span_consumer">14. Zipkin Stream Span Consumer</a></span></dt><dt><span class="chapter"><a href="#_integrations">15. Integrations</a></span></dt><dd><dl><dt><span class="section"><a href="#_opentracing">15.1. OpenTracing</a></span></dt><dt><span class="section"><a href="#_runnable_and_callable">15.2. Runnable and Callable</a></span></dt><dt><span class="section"><a href="#_hystrix">15.3. Hystrix</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_concurrency_strategy">15.3.1. Custom Concurrency Strategy</a></span></dt><dt><span class="section"><a href="#_manual_command_setting">15.3.2. Manual Command setting</a></span></dt></dl></dd><dt><span class="section"><a href="#_rxjava">15.4. RxJava</a></span></dt><dt><span class="section"><a href="#_http_integration">15.5. HTTP integration</a></span></dt><dd><dl><dt><span class="section"><a href="#_http_filter">15.5.1. HTTP Filter</a></span></dt><dt><span class="section"><a href="#_handlerinterceptor">15.5.2. HandlerInterceptor</a></span></dt><dt><span class="section"><a href="#_async_servlet_support">15.5.3. Async Servlet support</a></span></dt><dt><span class="section"><a href="#_webflux_support">15.5.4. WebFlux support</a></span></dt><dt><span class="section"><a href="#_dubbo_rpc_support">15.5.5. Dubbo RPC support</a></span></dt></dl></dd><dt><span class="section"><a href="#_http_client_integration">15.6. HTTP Client Integration</a></span></dt><dd><dl><dt><span class="section"><a href="#_synchronous_rest_template">15.6.1. Synchronous Rest Template</a></span></dt><dt><span class="section"><a href="#_asynchronous_rest_template">15.6.2. Asynchronous Rest Template</a></span></dt><dd><dl><dt><span class="section"><a href="#_multiple_asynchronous_rest_templates">Multiple Asynchronous Rest Templates</a></span></dt></dl></dd><dt><span class="section"><a href="#_webclient">15.6.3. <code class="literal">WebClient</code></a></span></dt><dt><span class="section"><a href="#_traverson">15.6.4. Traverson</a></span></dt><dt><span class="section"><a href="#_apache_httpclientbuilder_and_httpasyncclientbuilder">15.6.5. Apache <code class="literal">HttpClientBuilder</code> and <code class="literal">HttpAsyncClientBuilder</code></a></span></dt><dt><span class="section"><a href="#_netty_httpclient">15.6.6. Netty <code class="literal">HttpClient</code></a></span></dt><dt><span class="section"><a href="#_userinforesttemplatecustomizer">15.6.7. <code class="literal">UserInfoRestTemplateCustomizer</code></a></span></dt></dl></dd><dt><span class="section"><a href="#_feign">15.7. Feign</a></span></dt><dt><span class="section"><a href="#_asynchronous_communication">15.8. Asynchronous Communication</a></span></dt><dd><dl><dt><span class="section"><a href="#_async_annotated_methods">15.8.1. <code class="literal">@Async</code> Annotated methods</a></span></dt><dt><span class="section"><a href="#_scheduled_annotated_methods">15.8.2. <code class="literal">@Scheduled</code> Annotated Methods</a></span></dt><dt><span class="section"><a href="#_executor_executorservice_and_scheduledexecutorservice">15.8.3. Executor, ExecutorService, and ScheduledExecutorService</a></span></dt><dd><dl><dt><span class="section"><a href="#_customization_of_executors">Customization of Executors</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_messaging">15.9. Messaging</a></span></dt><dd><dl><dt><span class="section"><a href="#_spring_integration_and_spring_cloud_stream">15.9.1. Spring Integration and Spring Cloud Stream</a></span></dt><dt><span class="section"><a href="#_spring_rabbitmq">15.9.2. Spring RabbitMq</a></span></dt><dt><span class="section"><a href="#_spring_kafka">15.9.3. Spring Kafka</a></span></dt><dt><span class="section"><a href="#_spring_jms">15.9.4. Spring JMS</a></span></dt></dl></dd><dt><span class="section"><a href="#_zuul">15.10. Zuul</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_running_examples">16. Running examples</a></span></dt></dl></div><div class="preface"><div class="titlepage"><div><div><h1 class="title"><a name="d0e17" href="#d0e17"></a></h1></div></div></div><p><span class="strong"><strong>2.1.0.M2</strong></span></p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_introduction" href="#_introduction"></a>1. Introduction</h1></div></div></div><p>Spring Cloud Sleuth implements a distributed tracing solution for <a class="link" href="http://cloud.spring.io" target="_top">Spring Cloud</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_terminology" href="#_terminology"></a>1.1 Terminology</h2></div></div></div><p>Spring Cloud Sleuth borrows <a class="link" href="http://research.google.com/pubs/pub36356.html" target="_top">Dapper’s</a> terminology.</p><p><span class="strong"><strong>Span</strong></span>: The basic unit of work. For example, sending an RPC is a new span, as is sending a response to an RPC.
|
|
Spans are identified by a unique 64-bit ID for the span and another 64-bit ID for the trace the span is a part of.
|
|
Spans also have other data, such as descriptions, timestamped events, key-value annotations (tags), the ID of the span that caused them, and process IDs (normally IP addresses).</p><p>Spans can be started and stopped, and they keep track of their timing information.
|
|
Once you create a span, you must stop it at some point in the future.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>The initial span that starts a trace is called a <code class="literal">root span</code>. The value of the ID
|
|
of that span is equal to the trace ID.</p></td></tr></table></div><p><span class="strong"><strong>Trace:</strong></span> A set of spans forming a tree-like structure.
|
|
For example, if you run a distributed big-data store, a trace might be formed by a <code class="literal">PUT</code> request.</p><p><span class="strong"><strong>Annotation:</strong></span> Used to record the existence of an event in time. With
|
|
<a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> instrumentation, we no longer need to set special events
|
|
for <a class="link" href="https://zipkin.io/" target="_top">Zipkin</a> to understand who the client and server are, where
|
|
the request started, and where it ended. For learning purposes,
|
|
however, we mark these events to highlight what kind
|
|
of an action took place.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>cs</strong></span>: Client Sent. The client has made a request. This annotation indicates the start of the span.</li><li class="listitem"><span class="strong"><strong>sr</strong></span>: Server Received: The server side got the request and started processing it.
|
|
Subtracting the <code class="literal">cs</code> timestamp from this timestamp reveals the network latency.</li><li class="listitem"><span class="strong"><strong>ss</strong></span>: Server Sent. Annotated upon completion of request processing (when the response got sent back to the client).
|
|
Subtracting the <code class="literal">sr</code> timestamp from this timestamp reveals the time needed by the server side to process the request.</li><li class="listitem"><span class="strong"><strong>cr</strong></span>: Client Received. Signifies the end of the span.
|
|
The client has successfully received the response from the server side.
|
|
Subtracting the <code class="literal">cs</code> timestamp from this timestamp reveals the whole time needed by the client to receive the response from the server.</li></ul></div><p>The following image shows how <span class="strong"><strong>Span</strong></span> and <span class="strong"><strong>Trace</strong></span> look in a system, together with the Zipkin annotations:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/trace-id.png" alt="Trace Info propagation"></div></div><p>Each color of a note signifies a span (there are seven spans - from <span class="strong"><strong>A</strong></span> to <span class="strong"><strong>G</strong></span>).
|
|
Consider the following note:</p><pre class="screen">Trace Id = X
|
|
Span Id = D
|
|
Client Sent</pre><p>This note indicates that the current span has <span class="strong"><strong>Trace Id</strong></span> set to <span class="strong"><strong>X</strong></span> and <span class="strong"><strong>Span Id</strong></span> set to <span class="strong"><strong>D</strong></span>.
|
|
Also, the <code class="literal">Client Sent</code> event took place.</p><p>The following image shows how parent-child relationships of spans look:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/parents.png" alt="Parent child relationship"></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_purpose" href="#_purpose"></a>1.2 Purpose</h2></div></div></div><p>The following sections refer to the example shown in the preceding image.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_distributed_tracing_with_zipkin" href="#_distributed_tracing_with_zipkin"></a>1.2.1 Distributed Tracing with Zipkin</h3></div></div></div><p>This example has seven spans.
|
|
If you go to traces in Zipkin, you can see this number in the second trace, as shown in the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-traces.png" alt="Traces"></div></div><p>However, if you pick a particular trace, you can see four spans, as shown in the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-ui.png" alt="Traces Info propagation"></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When you pick a particular trace, you see merged spans.
|
|
That means that, if there were two spans sent to Zipkin with Server Received and Server Sent or Client Received and Client Sent annotations, they are presented as a single span.</p></td></tr></table></div><p>Why is there a difference between the seven and four spans in this case?</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Two spans come from the <code class="literal">http:/start</code> span. It has the Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) annotations.</li><li class="listitem">Two spans come from the RPC call from <code class="literal">service1</code> to <code class="literal">service2</code> to the <code class="literal">http:/foo</code> endpoint.
|
|
The Client Sent (<code class="literal">cs</code>) and Client Received (<code class="literal">cr</code>) events took place on the <code class="literal">service1</code> side.
|
|
Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) events took place on the <code class="literal">service2</code> side.
|
|
These two spans form one logical span related to an RPC call.</li><li class="listitem">Two spans come from the RPC call from <code class="literal">service2</code> to <code class="literal">service3</code> to the <code class="literal">http:/bar</code> endpoint.
|
|
The Client Sent (<code class="literal">cs</code>) and Client Received (<code class="literal">cr</code>) events took place on the <code class="literal">service2</code> side.
|
|
The Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) events took place on the <code class="literal">service3</code> side.
|
|
These two spans form one logical span related to an RPC call.</li><li class="listitem">Two spans come from the RPC call from <code class="literal">service2</code> to <code class="literal">service4</code> to the <code class="literal">http:/baz</code> endpoint.
|
|
The Client Sent (<code class="literal">cs</code>) and Client Received (<code class="literal">cr</code>) events took place on the <code class="literal">service2</code> side.
|
|
Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) events took place on the <code class="literal">service4</code> side.
|
|
These two spans form one logical span related to an RPC call.</li></ul></div><p>So, if we count the physical spans, we have one from <code class="literal">http:/start</code>, two from <code class="literal">service1</code> calling <code class="literal">service2</code>, two from <code class="literal">service2</code>
|
|
calling <code class="literal">service3</code>, and two from <code class="literal">service2</code> calling <code class="literal">service4</code>. In sum, we have a total of seven spans.</p><p>Logically, we see the information of four total Spans because we have one span related to the incoming request
|
|
to <code class="literal">service1</code> and three spans related to RPC calls.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_visualizing_errors" href="#_visualizing_errors"></a>1.2.2 Visualizing errors</h3></div></div></div><p>Zipkin lets you visualize errors in your trace.
|
|
When an exception was thrown and was not caught, we set proper tags on the span, which Zipkin can then properly colorize.
|
|
You could see in the list of traces one trace that is red. That appears because an exception was thrown.</p><p>If you click that trace, you see a similar picture, as follows:</p><div class="informalfigure"><div class="mediaobject"><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><p>If you then click on one of the spans, you see the following</p><div class="informalfigure"><div class="mediaobject"><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><p>The span shows the reason for the error and the whole stack trace related to it.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_distributed_tracing_with_brave" href="#_distributed_tracing_with_brave"></a>1.2.3 Distributed Tracing with Brave</h3></div></div></div><p>Starting with version <code class="literal">2.0.0</code>, Spring Cloud Sleuth uses <a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> as the tracing library.
|
|
Consequently, Sleuth no longer takes care of storing the context but delegates that work to Brave.</p><p>Due to the fact that Sleuth had different naming and tagging conventions than Brave, we decided to follow Brave’s conventions from now on.
|
|
However, if you want to use the legacy Sleuth approaches, you can set the <code class="literal">spring.sleuth.http.legacy.enabled</code> property to <code class="literal">true</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_live_examples" href="#_live_examples"></a>1.2.4 Live examples</h3></div></div></div><div class="figure"><a name="d0e357" href="#d0e357"></a><p class="title"><b>Figure 1.1. Click the Pivotal Web Services icon to see it live!</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png" alt="Zipkin deployed on Pivotal Web Services"></div></div></div><br class="figure-break"><p><a class="link" href="http://docssleuth-zipkin-server.cfapps.io/" target="_top">Click here to see it live!</a></p><p>The dependency graph in Zipkin should resemble the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/dependencies.png" alt="Dependencies"></div></div><div class="figure"><a name="d0e378" href="#d0e378"></a><p class="title"><b>Figure 1.2. Click the Pivotal Web Services icon to see it live!</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png" alt="Zipkin deployed on Pivotal Web Services"></div></div></div><br class="figure-break"><p><a class="link" href="http://docssleuth-zipkin-server.cfapps.io/dependency" target="_top">Click here to see it live!</a></p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_log_correlation" href="#_log_correlation"></a>1.2.5 Log correlation</h3></div></div></div><p>When using grep to read the logs of those four applications by scanning for a trace ID equal to (for example) <code class="literal">2485ec27856c56f4</code>, you get output resembling the following:</p><pre class="screen">service1.log:2016-02-26 11:15:47.561 INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application : Hello from service1. Calling service2
|
|
service2.log:2016-02-26 11:15:47.710 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Hello from service2. Calling service3 and then service4
|
|
service3.log:2016-02-26 11:15:47.895 INFO [service3,2485ec27856c56f4,1210be13194bfe5,true] 68060 --- [nio-8083-exec-1] i.s.c.sleuth.docs.service3.Application : Hello from service3
|
|
service2.log:2016-02-26 11:15:47.924 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Got response from service3 [Hello from service3]
|
|
service4.log:2016-02-26 11:15:48.134 INFO [service4,2485ec27856c56f4,1b1845262ffba49d,true] 68061 --- [nio-8084-exec-1] i.s.c.sleuth.docs.service4.Application : Hello from service4
|
|
service2.log:2016-02-26 11:15:48.156 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Got response from service4 [Hello from service4]
|
|
service1.log:2016-02-26 11:15:48.182 INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application : Got response from service2 [Hello from service2, response from service3 [Hello from service3] and from service4 [Hello from service4]]</pre><p>If you use a log aggregating tool (such as <a class="link" href="https://www.elastic.co/products/kibana" target="_top">Kibana</a>, <a class="link" href="http://www.splunk.com/" target="_top">Splunk</a>, and others), you can order the events that took place.
|
|
An example from Kibana would resemble the following image:</p><div class="informalfigure"><div class="mediaobject"><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><p>If you want to use <a class="link" href="https://www.elastic.co/guide/en/logstash/current/index.html" target="_top">Logstash</a>, the following listing shows the Grok pattern for Logstash:</p><pre class="screen">filter {
|
|
# pattern matching logback pattern
|
|
grok {
|
|
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
|
|
}
|
|
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:</p></td></tr></table></div><pre class="screen">filter {
|
|
# pattern matching logback pattern
|
|
grok {
|
|
match => { "message" => "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
|
|
}
|
|
}</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_json_logback_with_logstash" href="#_json_logback_with_logstash"></a>JSON Logback with Logstash</h4></div></div></div><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 class="literal">groupId:artifactId:version</code> notation).</p><p><span class="strong"><strong>Dependencies Setup</strong></span></p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Ensure that Logback is on the classpath (<code class="literal">ch.qos.logback:logback-core</code>).</li><li class="listitem">Add Logstash Logback encode. For example, to use version <code class="literal">4.6</code>, add <code class="literal">net.logstash.logback:logstash-logback-encoder:4.6</code>.</li></ol></div><p><span class="strong"><strong>Logback Setup</strong></span></p><p>Consider the following example of a Logback configuration file (named <a class="link" href="https://github.com/spring-cloud-samples/sleuth-documentation-apps/blob/master/service1/src/main/resources/logback-spring.xml" target="_top">logback-spring.xml</a>).</p><pre class="programlisting"><span class="hl-directive" style="color: maroon"><?xml version="1.0" encoding="UTF-8"?></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><configuration></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><include</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">resource</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"org/springframework/boot/logging/logback/defaults.xml"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/></span>
|
|
​
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><springProperty</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">scope</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"context"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"springAppName"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">source</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"spring.application.name"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- Example for logging into the build folder of your project --></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><property</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"LOG_FILE"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">value</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"${BUILD_FOLDER:-build}/${springAppName}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/></span>​
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- You can override this to have a custom pattern --></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><property</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"CONSOLE_LOG_PATTERN"</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">value</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-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}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/></span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- Appender to log to console --></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"console"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.ConsoleAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><filter</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.classic.filter.ThresholdFilter"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- Minimum logging level to be presented in the console logs--></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><level></span>DEBUG<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></level></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></filter></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><encoder></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><pattern></span>${CONSOLE_LOG_PATTERN}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></pattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><charset></span>utf8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></charset></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></encoder></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></appender></span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- Appender to log to file --></span>​
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"flatfile"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.RollingFileAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><file></span>${LOG_FILE}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></file></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><rollingPolicy</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><fileNamePattern></span>${LOG_FILE}.%d{yyyy-MM-dd}.gz<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></fileNamePattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><maxHistory></span>7<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></maxHistory></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></rollingPolicy></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><encoder></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><pattern></span>${CONSOLE_LOG_PATTERN}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></pattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><charset></span>utf8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></charset></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></encoder></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></appender></span>
|
|
​
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- Appender to log to file in a JSON format --></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"logstash"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.RollingFileAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><file></span>${LOG_FILE}.json<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></file></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><rollingPolicy</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><fileNamePattern></span>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></fileNamePattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><maxHistory></span>7<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></maxHistory></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></rollingPolicy></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><encoder</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><providers></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><timestamp></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><timeZone></span>UTC<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></timeZone></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></timestamp></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><pattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><pattern></span>
|
|
{
|
|
"severity": "%level",
|
|
"service": "${springAppName:-}",
|
|
"trace": "%X{X-B3-TraceId:-}",
|
|
"span": "%X{X-B3-SpanId:-}",
|
|
"parent": "%X{X-B3-ParentSpanId:-}",
|
|
"exportable": "%X{X-Span-Export:-}",
|
|
"pid": "${PID:-}",
|
|
"thread": "%thread",
|
|
"class": "%logger{40}",
|
|
"rest": "%message"
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></pattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></pattern></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></providers></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></encoder></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></appender></span>
|
|
​
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><root</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">level</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"INFO"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><appender-ref</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">ref</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"console"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!-- uncomment this to have also JSON logs --></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!--<appender-ref ref="logstash"/>--></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"><!--<appender-ref ref="flatfile"/>--></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></root></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></configuration></span></pre><p>That Logback configuration file:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Logs information from the application in a JSON format to a <code class="literal">build/${spring.application.name}.json</code> file.</li><li class="listitem">Has commented out two additional appenders: console and standard log file.</li><li class="listitem">Has the same logging pattern as the one presented in the previous section.</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you use a custom <code class="literal">logback-spring.xml</code>, you must pass the <code class="literal">spring.application.name</code> in the <code class="literal">bootstrap</code> rather than the <code class="literal">application</code> property file.
|
|
Otherwise, your custom logback file does not properly read the property.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_propagating_span_context" href="#_propagating_span_context"></a>1.2.6 Propagating Span Context</h3></div></div></div><p>The span context is the state that must get propagated to any child spans across process boundaries.
|
|
Part of the Span Context is the Baggage. The trace and span IDs are a required part of the span context.
|
|
Baggage is an optional part.</p><p>Baggage is a set of key:value pairs stored in the span context.
|
|
Baggage travels together with the trace and is attached to every span.
|
|
Spring Cloud Sleuth understands that a header is baggage-related if the HTTP header is prefixed with <code class="literal">baggage-</code> and, for messaging, it starts with <code class="literal">baggage_</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>There is currently no limitation of the count or size of baggage items.
|
|
However, 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.</p></td></tr></table></div><p>The following example shows setting baggage on a span:</p><pre class="programlisting">Span initialSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"span"</span>).start();
|
|
ExtraFieldPropagation.set(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>);
|
|
ExtraFieldPropagation.set(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UPPER_CASE"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"someValue"</span>);
|
|
}</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_baggage_versus_span_tags" href="#_baggage_versus_span_tags"></a>Baggage versus Span Tags</h4></div></div></div><p>Baggage travels with the trace (every child span contains the baggage of its parent).
|
|
Zipkin has no knowledge of baggage and does not receive that information.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting from Sleuth 2.0.0 you have to pass the baggage key names explicitly
|
|
in your project configuration. Read more about that setup <a class="link" href="#prefixed-fields" title="5.1.1 Prefixed fields">here</a></p></td></tr></table></div><p>Tags are attached to a specific span. In other words, they are presented only for that particular span.
|
|
However, you can search by tag to find the trace, assuming a span having the searched tag value exists.</p><p>If you want to be able to lookup a span based on baggage, you should add a corresponding entry as a tag in the root span.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The span must be in scope.</p></td></tr></table></div><p>The following listing shows integration tests that use baggage:</p><p><b>The setup. </b>
|
|
</p><pre class="programlisting">spring.sleuth:
|
|
baggage-keys:
|
|
- baz
|
|
- bizarrecase
|
|
propagation-keys:
|
|
- foo
|
|
- upper_case</pre><p>
|
|
</p><p><b>The code. </b>
|
|
</p><pre class="programlisting">initialSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,
|
|
ExtraFieldPropagation.get(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>));
|
|
initialSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UPPER_CASE"</span>,
|
|
ExtraFieldPropagation.get(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UPPER_CASE"</span>));</pre><p>
|
|
</p></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sleuth-adding-project" href="#sleuth-adding-project"></a>1.3 Adding Sleuth to the Project</h2></div></div></div><p>This section addresses how to add Sleuth to your project with either Maven or Gradle.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>To ensure that your application name is properly displayed in Zipkin, set the <code class="literal">spring.application.name</code> property in <code class="literal">bootstrap.yml</code>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_only_sleuth_log_correlation" href="#_only_sleuth_log_correlation"></a>1.3.1 Only Sleuth (log correlation)</h3></div></div></div><p>If you want to use only Spring Cloud Sleuth without the Zipkin integration, add the <code class="literal">spring-cloud-starter-sleuth</code> module to your project.</p><p>The following example shows how to add Sleuth with Maven:</p><p class="primary"><b>Maven. </b>
|
|
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependencyManagement></span> <a name="CO1-1" href="#CO1-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependencies></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><version></span>${release.train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></version></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><type></span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></type></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependencies></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependencyManagement></span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span> <a name="CO1-2" href="#CO1-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-sleuth<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p class="primary">
|
|
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO1-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO1-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-sleuth</code>.</p></td></tr></table></div><p>The following example shows how to add Sleuth with Gradle:</p><p class="secondary"><b>Gradle. </b>
|
|
</p><pre class="programlisting">dependencyManagement { <a name="CO2-1" href="#CO2-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
|
|
imports {
|
|
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"</span>
|
|
}
|
|
}
|
|
|
|
dependencies { <a name="CO2-2" href="#CO2-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
|
|
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-sleuth"</span>
|
|
}</pre><p class="secondary">
|
|
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO2-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO2-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-sleuth</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sleuth_with_zipkin_via_http" href="#_sleuth_with_zipkin_via_http"></a>1.3.2 Sleuth with Zipkin via HTTP</h3></div></div></div><p>If you want both Sleuth and Zipkin, add the <code class="literal">spring-cloud-starter-zipkin</code> dependency.</p><p>The following example shows how to do so for Maven:</p><p class="primary"><b>Maven. </b>
|
|
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependencyManagement></span> <a name="CO3-1" href="#CO3-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependencies></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><version></span>${release.train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></version></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><type></span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></type></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependencies></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependencyManagement></span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span> <a name="CO3-2" href="#CO3-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-zipkin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p class="primary">
|
|
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO3-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO3-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>.</p></td></tr></table></div><p>The following example shows how to do so for Gradle:</p><p class="secondary"><b>Gradle. </b>
|
|
</p><pre class="programlisting">dependencyManagement { <a name="CO4-1" href="#CO4-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
|
|
imports {
|
|
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"</span>
|
|
}
|
|
}
|
|
|
|
dependencies { <a name="CO4-2" href="#CO4-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
|
|
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-zipkin"</span>
|
|
}</pre><p class="secondary">
|
|
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO4-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO4-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sleuth_with_zipkin_over_rabbitmq_or_kafka" href="#_sleuth_with_zipkin_over_rabbitmq_or_kafka"></a>1.3.3 Sleuth with Zipkin over RabbitMQ or Kafka</h3></div></div></div><p>If you want to use RabbitMQ or Kafka instead of HTTP, add the <code class="literal">spring-rabbit</code> or <code class="literal">spring-kafka</code> dependency.
|
|
The default destination name is <code class="literal">zipkin</code>.</p><p>If using Kafka, you must set the property <code class="literal">spring.zipkin.sender.type</code> property accordingly:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.sender.type</span>: kafka</pre><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p><code class="literal">spring-cloud-sleuth-stream</code> is deprecated and incompatible with these destinations.</p></td></tr></table></div><p>If you want Sleuth over RabbitMQ, add the <code class="literal">spring-cloud-starter-zipkin</code> and <code class="literal">spring-rabbit</code>
|
|
dependencies.</p><p>The following example shows how to do so for Gradle:</p><p class="primary"><b>Maven. </b>
|
|
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependencyManagement></span> <a name="CO5-1" href="#CO5-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependencies></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><version></span>${release.train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></version></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><type></span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></type></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><scope></span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></scope></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependencies></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependencyManagement></span>
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span> <a name="CO5-2" href="#CO5-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-cloud-starter-zipkin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span> <a name="CO5-3" href="#CO5-3"></a><span><img src="images/callouts/3.png" alt="3" border="0"></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>org.springframework.amqp<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>spring-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p class="primary">
|
|
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO5-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO5-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>. That way, all nested dependencies get downloaded.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO5-3"><span><img src="images/callouts/3.png" alt="3" border="0"></span></a> </p></td><td valign="top" align="left"><p>To automatically configure RabbitMQ, add the <code class="literal">spring-rabbit</code> dependency.</p></td></tr></table></div><p class="secondary"><b>Gradle. </b>
|
|
</p><pre class="programlisting">dependencyManagement { <a name="CO6-1" href="#CO6-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
|
|
imports {
|
|
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"</span>
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-zipkin"</span> <a name="CO6-2" href="#CO6-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
|
|
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.amqp:spring-rabbit"</span> <a name="CO6-3" href="#CO6-3"></a><span><img src="images/callouts/3.png" alt="3" border="0"></span>
|
|
}</pre><p class="secondary">
|
|
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO6-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO6-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>. That way, all nested dependencies get downloaded.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO6-3"><span><img src="images/callouts/3.png" alt="3" border="0"></span></a> </p></td><td valign="top" align="left"><p>To automatically configure RabbitMQ, add the <code class="literal">spring-rabbit</code> dependency.</p></td></tr></table></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_additional_resources" href="#_additional_resources"></a>2. Additional Resources</h1></div></div></div><p>You can watch a video of <a class="link" href="https://twitter.com/reshmi9k" target="_top">Reshmi Krishna</a> and <a class="link" href="https://twitter.com/mgrzejszczak" target="_top">Marcin Grzejszczak</a> talking about Spring Cloud
|
|
Sleuth and Zipkin <a class="link" href="https://content.pivotal.io/springone-platform-2017/distributed-tracing-latency-analysis-for-your-microservices-grzejszczak-krishna" target="_top">by clicking here</a>.</p><p>You can check different setups of Sleuth and Brave <a class="link" href="https://github.com/openzipkin/sleuth-webmvc-example" target="_top">in the openzipkin/sleuth-webmvc-example repository</a>.</p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_features" href="#_features"></a>3. Features</h1></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">Adds trace and span IDs to the Slf4J MDC, so you can extract all the logs from a given trace or span in a log aggregator, as shown in the following example logs:</p><pre class="screen">2016-02-02 15:30:57.902 INFO [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ...
|
|
2016-02-02 15:30:58.372 ERROR [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ...
|
|
2016-02-02 15:31:01.936 INFO [bar,46ab0d418373cbc9,46ab0d418373cbc9,false] 23030 --- [nio-8081-exec-4] ...</pre><p class="simpara">Notice the <code class="literal">[appname,traceId,spanId,exportable]</code> entries from the MDC:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><span class="strong"><strong><code class="literal">spanId</code></strong></span>: The ID of a specific operation that took place.</li><li class="listitem"><span class="strong"><strong><code class="literal">appname</code></strong></span>: The name of the application that logged the span.</li><li class="listitem"><span class="strong"><strong><code class="literal">traceId</code></strong></span>: The ID of the latency graph that contains the span.</li><li class="listitem"><span class="strong"><strong><code class="literal">exportable</code></strong></span>: Whether the log should be exported to Zipkin.
|
|
When would you like the span not to be exportable?
|
|
When you want to wrap some operation in a Span and have it written to the logs only.</li></ul></div></li><li class="listitem">Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations, and key-value annotations.
|
|
Spring Cloud Sleuth is loosely based on HTrace but is compatible with Zipkin (Dapper).</li><li class="listitem">Sleuth records timing information to aid in latency analysis.
|
|
By using sleuth, you can pinpoint causes of latency in your applications.</li><li class="listitem"><p class="simpara">Sleuth is written to not log too much and to not cause your production application to crash.
|
|
To that end, Sleuth:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">Propagates structural data about your call graph in-band and the rest out-of-band.</li><li class="listitem">Includes opinionated instrumentation of layers such as HTTP.</li><li class="listitem">Includes a sampling policy to manage volume.</li><li class="listitem">Can report to a Zipkin system for query and visualization.</li></ul></div></li><li class="listitem">Instruments common ingress and egress points from Spring applications (servlet filter, async endpoints, rest template, scheduled actions, message channels, Zuul filters, and Feign client).</li><li class="listitem">Sleuth includes default logic to join a trace across HTTP or messaging boundaries.
|
|
For example, HTTP propagation works over Zipkin-compatible request headers.</li><li class="listitem">Sleuth can propagate context (also known as baggage) between processes.
|
|
Consequently, if you set a baggage element on a Span, it is sent downstream to other processes over either HTTP or messaging.</li><li class="listitem">Provides a way to create or continue spans and add tags and logs through annotations.</li><li class="listitem"><p class="simpara">If <code class="literal">spring-cloud-sleuth-zipkin</code> is on the classpath, the app generates and collects Zipkin-compatible traces.
|
|
By default, it sends them over HTTP to a Zipkin server on localhost (port 9411).
|
|
You can configure the location of the service by setting <code class="literal">spring.zipkin.baseUrl</code>.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">If you depend on <code class="literal">spring-rabbit</code>, your app sends traces to a RabbitMQ broker instead of HTTP.</li><li class="listitem">If you depend on <code class="literal">spring-kafka</code>, and set <code class="literal">spring.zipkin.sender.type: kafka</code>, your app sends traces to a Kafka broker instead of HTTP.</li></ul></div></li></ul></div><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p><code class="literal">spring-cloud-sleuth-stream</code> is deprecated and should no longer be used.</p></td></tr></table></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Spring Cloud Sleuth is <a class="link" href="http://opentracing.io/" target="_top">OpenTracing</a> compatible.</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you use Zipkin, configure the probability of spans exported by setting <code class="literal">spring.sleuth.sampler.probability</code>
|
|
(default: 0.1, which is 10 percent). Otherwise, you might think that Sleuth is not working be cause it omits some spans.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The SLF4J MDC is always set and logback users immediately see the trace and span IDs in logs per the example
|
|
shown earlier.
|
|
Other logging systems have to configure their own formatter to get the same result.
|
|
The default is as follows:
|
|
<code class="literal">logging.pattern.level</code> set to <code class="literal">%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]</code>
|
|
(this is a Spring Boot feature for logback users).
|
|
If you do not use SLF4J, this pattern is NOT automatically applied.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_introduction_to_brave" href="#_introduction_to_brave"></a>3.1 Introduction to Brave</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting with version <code class="literal">2.0.0</code>, Spring Cloud Sleuth uses
|
|
<a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> as the tracing library.
|
|
For your convenience, we embed part of the Brave’s docs here.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>In the vast majority of cases you need to just use the <code class="literal">Tracer</code>
|
|
or <code class="literal">SpanCustomizer</code> beans from Brave that Sleuth provides. The documentation below contains
|
|
a high overview of what Brave is and how it works.</p></td></tr></table></div><p>Brave is a library used to capture and report latency information about distributed operations to Zipkin.
|
|
Most users do not use Brave directly. They use libraries or frameworks rather than employ Brave on their behalf.</p><p>This module includes a tracer that creates and joins spans that model the latency of potentially distributed work.
|
|
It also includes libraries to propagate the trace context over network boundaries (for example, with HTTP headers).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_tracing" href="#_tracing"></a>3.1.1 Tracing</h3></div></div></div><p>Most importantly, you need a <code class="literal">brave.Tracer</code>, configured to <a class="link" href="https://github.com/openzipkin/zipkin-reporter-java" target="_top">report to Zipkin</a>.</p><p>The following example setup sends trace data (spans) to Zipkin over HTTP (as opposed to Kafka):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Tracer will be autowired</span>
|
|
MyClass(Tracer tracer) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer = tracer;
|
|
}
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doSth() {
|
|
Span span = tracer.newTrace().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>).start();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
}
|
|
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If your span contains a name longer than 50 chars, then that name is truncated to 50 chars.
|
|
Your names have to be explicit and concrete.
|
|
Big names lead to latency issues and sometimes even thrown exceptions.</p></td></tr></table></div><p>The tracer creates and joins spans that model the latency of potentially distributed work.
|
|
It can employ sampling to reduce overhead during the process, to reduce the amount of data sent to Zipkin, or both.</p><p>Spans returned by a tracer report data to Zipkin when finished or do nothing if unsampled.
|
|
After starting a span, you can annotate events of interest or add tags containing details or lookup keys.</p><p>Spans have a context that includes trace identifiers that place the span at the correct spot in the tree representing the distributed operation.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_local_tracing" href="#_local_tracing"></a>3.1.2 Local Tracing</h3></div></div></div><p>When tracing code that never leaves your process, run it inside a scoped span.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a new trace or a span within an existing trace representing an operation</span>
|
|
ScopedSpan span = tracer.startScopedSpan(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// The span is in "scope" meaning downstream code such as loggers can see trace IDs</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> encoder.encode();
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
|
|
span.error(e); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Unless you handle exceptions, you might not know the operation failed!</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
|
|
span.finish(); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// always finish the span</span>
|
|
}</pre><p>When you need more features, or finer control, use the <code class="literal">Span</code> type:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a new trace or a span within an existing trace representing an operation</span>
|
|
Span span = tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>).start();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Put the span in "scope" so that downstream code such as loggers can see trace IDs</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope ws = tracer.withSpanInScope(span)) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> encoder.encode();
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
|
|
span.error(e); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Unless you handle exceptions, you might not know the operation failed!</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
|
|
span.finish(); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// note the scope is independent of the span. Always finish a span.</span>
|
|
}</pre><p>Both of the above examples report the exact same span on finish!</p><p>In the above example, the span will be either a new root span or the
|
|
next child in an existing trace.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_customizing_spans" href="#_customizing_spans"></a>3.1.3 Customizing Spans</h3></div></div></div><p>Once you have a span, you can add tags to it.
|
|
The tags can be used as lookup keys or details.
|
|
For example, you might add a tag with your runtime version, as shown in the following example:</p><pre class="programlisting">span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"clnt/finagle.version"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"6.36.0"</span>);</pre><p>When exposing the ability to customize spans to third parties, prefer <code class="literal">brave.SpanCustomizer</code> as opposed to <code class="literal">brave.Span</code>.
|
|
The former is simpler to understand and test and does not tempt users with span lifecycle hooks.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> MyTraceCallback {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> request(Request request, SpanCustomizer customizer);
|
|
}</pre><p>Since <code class="literal">brave.Span</code> implements <code class="literal">brave.SpanCustomizer</code>, you can pass it to users, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> (MyTraceCallback callback : userCallbacks) {
|
|
callback.request(request, span);
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_implicitly_looking_up_the_current_span" href="#_implicitly_looking_up_the_current_span"></a>3.1.4 Implicitly Looking up the Current Span</h3></div></div></div><p>Sometimes, you do not know if a trace is in progress or not, and you do not want users to do null checks.
|
|
<code class="literal">brave.CurrentSpanCustomizer</code> handles this problem by adding data to any span that’s in progress or drops, as shown in the following example:</p><p>Ex.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// The user code can then inject this without a chance of it being null.</span>
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> SpanCustomizer span;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> userCode() {
|
|
span.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tx.started"</span>);
|
|
...
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_rpc_tracing" href="#_rpc_tracing"></a>3.1.5 RPC tracing</h3></div></div></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Check for <a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation" target="_top">instrumentation written here</a> and <a class="link" href="http://zipkin.io/pages/existing_instrumentations.html" target="_top">Zipkin’s list</a> before rolling your own RPC instrumentation.</p></td></tr></table></div><p>RPC tracing is often done automatically by interceptors. Behind the scenes, they add tags and events that relate to their role in an RPC operation.</p><p>The following example shows how to add a client span:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// before you send a request, add metadata that describes the operation</span>
|
|
span = tracer.nextSpan().name(service + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span> + method).kind(CLIENT);
|
|
span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myrpc.version"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1.0.0"</span>);
|
|
span.remoteServiceName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"backend"</span>);
|
|
span.remoteIpAndPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"172.3.4.1"</span>, <span class="hl-number">8108</span>);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Add the trace context to the request, so it can be propagated in-band</span>
|
|
tracing.propagation().injector(Request::addHeader)
|
|
.inject(span.context(), request);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when the request is scheduled, start the span</span>
|
|
span.start();
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// if there is an error, tag the span</span>
|
|
span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"error"</span>, error.getCode());
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// or if there is an exception</span>
|
|
span.error(exception);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when the response is complete, finish the span</span>
|
|
span.finish();</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_one_way_tracing" href="#_one_way_tracing"></a>One-Way tracing</h4></div></div></div><p>Sometimes, you need to model an asynchronous operation where there is a
|
|
request but no response. In normal RPC tracing, you use <code class="literal">span.finish()</code>
|
|
to indicate that the response was received. In one-way tracing, you use
|
|
<code class="literal">span.flush()</code> instead, as you do not expect a response.</p><p>The following example shows how a client might model a one-way operation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start a new span representing a client request</span>
|
|
oneWaySend = tracer.nextSpan().name(service + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span> + method).kind(CLIENT);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Add the trace context to the request, so it can be propagated in-band</span>
|
|
tracing.propagation().injector(Request::addHeader)
|
|
.inject(oneWaySend.context(), request);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// fire off the request asynchronously, totally dropping any response</span>
|
|
request.execute();
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start the client side and flush instead of finish</span>
|
|
oneWaySend.start().flush();</pre><p>The following example shows how a server might handle a one-way operation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// pull the context out of the incoming request</span>
|
|
extractor = tracing.propagation().extractor(Request::getHeader);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// convert that context to a span which you can name and add tags to</span>
|
|
oneWayReceive = nextSpan(tracer, extractor.extract(request))
|
|
.name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"process-request"</span>)
|
|
.kind(SERVER)
|
|
... add tags etc.
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start the server side and flush instead of finish</span>
|
|
oneWayReceive.start().flush();
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you should not modify this span anymore as it is complete. However,</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you can create children to represent follow-up work.</span>
|
|
next = tracer.newSpan(oneWayReceive.context()).name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"step2"</span>).start();</pre></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_sampling" href="#_sampling"></a>4. Sampling</h1></div></div></div><p>Sampling may be employed to reduce the data collected and reported out of process.
|
|
When a span is not sampled, it adds no overhead (a noop).</p><p>Sampling is an up-front decision, meaning that the decision to report data is made at the first operation in a trace and that decision is propagated downstream.</p><p>By default, a global sampler applies a single rate to all traced operations.
|
|
<code class="literal">Tracer.Builder.sampler</code> controls this setting, and it defaults to tracing every request.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_declarative_sampling" href="#_declarative_sampling"></a>4.1 Declarative sampling</h2></div></div></div><p>Some applications need to sample based on the type or annotations of a java method.</p><p>Most users use a framework interceptor to automate this sort of policy.
|
|
The following example shows how that might work internally:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// derives a sample rate from an annotation on a java method</span>
|
|
DeclarativeSampler<Traced> sampler = DeclarativeSampler.create(Traced::sampleRate);
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Around("@annotation(traced)")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Object traceThing(ProceedingJoinPoint pjp, Traced traced) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Throwable {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// When there is no trace in progress, this decides using an annotation</span>
|
|
Sampler decideUsingAnnotation = declarativeSampler.toSampler(traced);
|
|
Tracer tracer = tracer.withSampler(decideUsingAnnotation);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// This code looks the same as if there was no declarative override</span>
|
|
ScopedSpan span = tracer.startScopedSpan(spanName(pjp));
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> pjp.proceed();
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
|
|
span.error(e);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
|
|
span.finish();
|
|
}
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_custom_sampling" href="#_custom_sampling"></a>4.2 Custom sampling</h2></div></div></div><p>Depending on what the operation is, you may want to apply different policies.
|
|
For example, you might not want to trace requests to static resources such as images, or you might want to trace all requests to a new api.</p><p>Most users use a framework interceptor to automate this sort of policy.
|
|
The following example shows how that might work internally:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Sampler fallback;
|
|
|
|
Span nextSpan(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Request input) {
|
|
Sampler requestBased = Sampler() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isSampled(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> traceId) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (input.url().startsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/experimental"</span>)) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true;
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (input.url().startsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/static"</span>)) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> false;
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> fallback.isSampled(traceId);
|
|
}
|
|
};
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> tracer.withSampler(requestBased).nextSpan();
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_sampling_in_spring_cloud_sleuth" href="#_sampling_in_spring_cloud_sleuth"></a>4.3 Sampling in Spring Cloud Sleuth</h2></div></div></div><p>By default Spring Cloud Sleuth sets all spans to non-exportable.
|
|
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 class="literal">Sampler.ALWAYS_SAMPLE</code> setting that exports everything and a <code class="literal">ProbabilityBasedSampler</code> setting that samples a fixed fraction of spans.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">ProbabilityBasedSampler</code> is the default if you use <code class="literal">spring-cloud-sleuth-zipkin</code>.
|
|
You can configure the exports by setting <code class="literal">spring.sleuth.sampler.probability</code>.
|
|
The passed value needs to be a double from <code class="literal">0.0</code> to <code class="literal">1.0</code>.</p></td></tr></table></div><p>A sampler can be installed by creating a bean definition, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Sampler defaultSampler() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Sampler.ALWAYS_SAMPLE;
|
|
}</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You can set the HTTP header <code class="literal">X-B3-Flags</code> to <code class="literal">1</code>, or, when doing messaging, you can set the <code class="literal">spanFlags</code> header to <code class="literal">1</code>.
|
|
Doing so forces the current span to be exportable regardless of the sampling decision.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_propagation" href="#_propagation"></a>5. Propagation</h1></div></div></div><p>Propagation is needed to ensure activities originating from the same root are collected together in the same trace.
|
|
The most common propagation approach is to copy a trace context from a client by sending an RPC request to a server receiving it.</p><p>For example, when a downstream HTTP call is made, its trace context is encoded as request headers and sent along with it, as shown in the following image:</p><pre class="screen"> Client Span Server Span
|
|
┌──────────────────┐ ┌──────────────────┐
|
|
│ │ │ │
|
|
│ TraceContext │ Http Request Headers │ TraceContext │
|
|
│ ┌──────────────┐ │ ┌───────────────────┐ │ ┌──────────────┐ │
|
|
│ │ TraceId │ │ │ X─B3─TraceId │ │ │ TraceId │ │
|
|
│ │ │ │ │ │ │ │ │ │
|
|
│ │ ParentSpanId │ │ Extract │ X─B3─ParentSpanId │ Inject │ │ ParentSpanId │ │
|
|
│ │ ├─┼─────────>│ ├────────┼>│ │ │
|
|
│ │ SpanId │ │ │ X─B3─SpanId │ │ │ SpanId │ │
|
|
│ │ │ │ │ │ │ │ │ │
|
|
│ │ Sampled │ │ │ X─B3─Sampled │ │ │ Sampled │ │
|
|
│ └──────────────┘ │ └───────────────────┘ │ └──────────────┘ │
|
|
│ │ │ │
|
|
└──────────────────┘ └──────────────────┘</pre><p>The names above are from <a class="link" href="https://github.com/openzipkin/b3-propagation" target="_top">B3 Propagation</a>, which is built-in to Brave and has implementations in many languages and frameworks.</p><p>Most users use a framework interceptor to automate propagation.
|
|
The next two examples show how that might work for a client and a server.</p><p>The following example shows how client-side propagation might work:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// configure a function that injects a trace context into a request</span>
|
|
injector = tracing.propagation().injector(Request.Builder::addHeader);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// before a request is sent, add the current span's context to it</span>
|
|
injector.inject(span.context(), request);</pre><p>The following example shows how server-side propagation might work:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// configure a function that extracts the trace context from a request</span>
|
|
extractor = tracing.propagation().extractor(Request::getHeader);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when a server receives a request, it joins or starts a new trace</span>
|
|
span = tracer.nextSpan(extractor.extract(request));</pre><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_propagating_extra_fields" href="#_propagating_extra_fields"></a>5.1 Propagating extra fields</h2></div></div></div><p>Sometimes you need to propagate extra fields, such as a request ID or an alternate trace context.
|
|
For example, if you are in a Cloud Foundry environment, you might want to pass the request ID, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when you initialize the builder, define the extra field you want to propagate</span>
|
|
Tracing.newBuilder().propagationFactory(
|
|
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-vcap-request-id"</span>)
|
|
);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// later, you can tag that request ID or use it in log correlation</span>
|
|
requestId = ExtraFieldPropagation.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-vcap-request-id"</span>);</pre><p>You may also need to propagate a trace context that you are not using.
|
|
For example, you may be in an Amazon Web Services environment but not be reporting data to X-Ray.
|
|
To ensure X-Ray can co-exist correctly, pass-through its tracing header, as shown in the following example:</p><pre class="programlisting">tracingBuilder.propagationFactory(
|
|
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-amzn-trace-id"</span>)
|
|
);</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>In Spring Cloud Sleuth all elements of the tracing builder <code class="literal">Tracing.newBuilder()</code>
|
|
are defined as beans. So if you want to pass a custom <code class="literal">PropagationFactory</code>, it’s enough
|
|
for you to create a bean of that type and we will set it in the <code class="literal">Tracing</code> bean.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="prefixed-fields" href="#prefixed-fields"></a>5.1.1 Prefixed fields</h3></div></div></div><p>If they follow a common pattern, you can also prefix fields.
|
|
The following example shows how to propagate <code class="literal">x-vcap-request-id</code> the field as-is but send the <code class="literal">country-code</code> and <code class="literal">user-id</code> fields on the wire as <code class="literal">x-baggage-country-code</code> and <code class="literal">x-baggage-user-id</code>, respectively:</p><pre class="programlisting">Tracing.newBuilder().propagationFactory(
|
|
ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
|
|
.addField(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-vcap-request-id"</span>)
|
|
.addPrefixedFields(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-baggage-"</span>, Arrays.asList(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"country-code"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user-id"</span>))
|
|
.build()
|
|
);</pre><p>Later, you can call the following code to affect the country code of the current trace context:</p><pre class="programlisting">ExtraFieldPropagation.set(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FO"</span>);
|
|
String countryCode = ExtraFieldPropagation.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>);</pre><p>Alternatively, if you have a reference to a trace context, you can use it explicitly, as shown in the following example:</p><pre class="programlisting">ExtraFieldPropagation.set(span.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FO"</span>);
|
|
String countryCode = ExtraFieldPropagation.get(span.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>);</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>A difference from previous versions of Sleuth is that, with Brave, you must pass the list of baggage keys.
|
|
There are two properties to achieve this.
|
|
With the <code class="literal">spring.sleuth.baggage-keys</code>, you set keys that get prefixed with <code class="literal">baggage-</code> for HTTP calls and <code class="literal">baggage_</code> for messaging.
|
|
You can also use the <code class="literal">spring.sleuth.propagation-keys</code> property to pass a list of prefixed keys that are whitelisted without any prefix.
|
|
Notice that there’s no <code class="literal">x-</code> in front of the header keys.</p></td></tr></table></div><p>In order to automatically set the baggage values to Slf4j’s MDC, you have to set
|
|
the <code class="literal">spring.sleuth.log.slf4j.whitelisted-mdc-keys</code> property with a list of whitelisted
|
|
baggage and propagation keys. E.g. <code class="literal">spring.sleuth.log.slf4j.whitelisted-mdc-keys=foo</code> will set the value of the <code class="literal">foo</code> baggage into MDC.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember that adding entries to MDC can drastically decrease the performance of your application!</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_extracting_a_propagated_context" href="#_extracting_a_propagated_context"></a>5.1.2 Extracting a Propagated Context</h3></div></div></div><p>The <code class="literal">TraceContext.Extractor<C></code> reads trace identifiers and sampling status from an incoming request or message.
|
|
The carrier is usually a request object or headers.</p><p>This utility is used in standard instrumentation (such as <code class="literal">HttpServerHandler`</code>) but can also be used for custom RPC or messaging code.</p><p><code class="literal">TraceContextOrSamplingFlags</code> is usually used only with <code class="literal">Tracer.nextSpan(extracted)</code>, unless you are
|
|
sharing span IDs between a client and a server.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sharing_span_ids_between_client_and_server" href="#_sharing_span_ids_between_client_and_server"></a>5.1.3 Sharing span IDs between Client and Server</h3></div></div></div><p>A normal instrumentation pattern is to create a span representing the server side of an RPC.
|
|
<code class="literal">Extractor.extract</code> might return a complete trace context when applied to an incoming client request.
|
|
<code class="literal">Tracer.joinSpan</code> attempts to continue this trace, using the same span ID if supported or creating a child span
|
|
if not. When the span ID is shared, the reported data includes a flag saying so.</p><p>The following image shows an example of B3 propagation:</p><pre class="screen"> ┌───────────────────┐ ┌───────────────────┐
|
|
Incoming Headers │ TraceContext │ │ TraceContext │
|
|
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
|
|
│ X─B3-TraceId │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
|
|
│ │ │ │ │ │ │ │ │ │
|
|
│ X─B3-ParentSpanId │─────────┼─┼> ParentSpanId │ │──────┼─┼> ParentSpanId │ │
|
|
│ │ │ │ │ │ │ │ │ │
|
|
│ X─B3-SpanId │─────────┼─┼> SpanId │ │──────┼─┼> SpanId │ │
|
|
└───────────────────┘ │ │ │ │ │ │ │ │
|
|
│ │ │ │ │ │ Shared: true │ │
|
|
│ └───────────────┘ │ │ └───────────────┘ │
|
|
└───────────────────┘ └───────────────────┘</pre><p>Some propagation systems forward only the parent span ID, detected when <code class="literal">Propagation.Factory.supportsJoin() == false</code>.
|
|
In this case, a new span ID is always provisioned, and the incoming context determines the parent ID.</p><p>The following image shows an example of AWS propagation:</p><pre class="screen"> ┌───────────────────┐ ┌───────────────────┐
|
|
x-amzn-trace-id │ TraceContext │ │ TraceContext │
|
|
┌───────────────────┐(extract)│ ┌───────────────┐ │(join)│ ┌───────────────┐ │
|
|
│ Root │─────────┼─┼> TraceId │ │──────┼─┼> TraceId │ │
|
|
│ │ │ │ │ │ │ │ │ │
|
|
│ Parent │─────────┼─┼> SpanId │ │──────┼─┼> ParentSpanId │ │
|
|
└───────────────────┘ │ └───────────────┘ │ │ │ │ │
|
|
└───────────────────┘ │ │ SpanId: New │ │
|
|
│ └───────────────┘ │
|
|
└───────────────────┘</pre><p>Note: Some span reporters do not support sharing span IDs.
|
|
For example, if you set <code class="literal">Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)</code>, you should disable join by setting <code class="literal">Tracing.Builder.supportsJoin(false)</code>.
|
|
Doing so forces a new child span on <code class="literal">Tracer.joinSpan()</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_implementing_propagation" href="#_implementing_propagation"></a>5.1.4 Implementing Propagation</h3></div></div></div><p><code class="literal">TraceContext.Extractor<C></code> is implemented by a <code class="literal">Propagation.Factory</code> plugin.
|
|
Internally, this code creates the union type, <code class="literal">TraceContextOrSamplingFlags</code>, with one of the following:
|
|
* <code class="literal">TraceContext</code> if trace and span IDs were present.
|
|
* <code class="literal">TraceIdContext</code> if a trace ID was present but span IDs were not present.
|
|
* <code class="literal">SamplingFlags</code> if no identifiers were present.</p><p>Some <code class="literal">Propagation</code> implementations carry extra data from the point of extraction (for example, reading incoming headers) to injection (for example, writing outgoing headers).
|
|
For example, it might carry a request ID.
|
|
When implementations have extra data, they handle it as follows:
|
|
* If a <code class="literal">TraceContext</code> were extracted, add the extra data as <code class="literal">TraceContext.extra()</code>.
|
|
* Otherwise, add it as <code class="literal">TraceContextOrSamplingFlags.extra()</code>, which <code class="literal">Tracer.nextSpan</code> handles.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_current_tracing_component" href="#_current_tracing_component"></a>6. Current Tracing Component</h1></div></div></div><p>Brave supports a "<code class="literal">current tracing component</code>" concept, which should only be used when you have no other way to get a reference.
|
|
This was made for JDBC connections, as they often initialize prior to the tracing component.</p><p>The most recent tracing component instantiated is available through <code class="literal">Tracing.current()</code>.
|
|
You can also use <code class="literal">Tracing.currentTracer()</code> to get only the tracer.
|
|
If you use either of these methods, do not cache the result.
|
|
Instead, look them up each time you need them.</p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_current_span" href="#_current_span"></a>7. Current Span</h1></div></div></div><p>Brave supports a "<code class="literal">current span</code>" concept which represents the in-flight operation.
|
|
You can use <code class="literal">Tracer.currentSpan()</code> to add custom tags to a span and <code class="literal">Tracer.nextSpan()</code> to create a child of whatever is in-flight.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>In Sleuth, you can autowire the <code class="literal">Tracer</code> bean to retrieve the current span via
|
|
<code class="literal">tracer.currentSpan()</code> method. To retrieve the current context just call
|
|
<code class="literal">tracer.currentSpan().context()</code>. To get the current trace id as String
|
|
you can use the <code class="literal">traceIdString()</code> method like this: <code class="literal">tracer.currentSpan().context().traceIdString()</code>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_setting_a_span_in_scope_manually" href="#_setting_a_span_in_scope_manually"></a>7.1 Setting a span in scope manually</h2></div></div></div><p>When writing new instrumentation, it is important to place a span you created in scope as the current span.
|
|
Not only does doing so let users access it with <code class="literal">Tracer.currentSpan()</code>, but it also allows customizations such as SLF4J MDC to see the current trace IDs.</p><p><code class="literal">Tracer.withSpanInScope(Span)</code> facilitates this and is most conveniently employed by using the try-with-resources idiom.
|
|
Whenever external code might be invoked (such as proceeding an interceptor or otherwise), place the span in scope, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope ws = tracer.withSpanInScope(span)) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> inboundRequest.invoke();
|
|
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// note the scope is independent of the span</span>
|
|
span.finish();
|
|
}</pre><p>In edge cases, you may need to clear the current span temporarily (for example, launching a task that should not be associated with the current request). To do tso, pass null to <code class="literal">withSpanInScope</code>, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope cleared = tracer.withSpanInScope(null)) {
|
|
startBackgroundThread();
|
|
}</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_instrumentation" href="#_instrumentation"></a>8. Instrumentation</h1></div></div></div><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 class="literal">Filter</code>, and, for Spring Integration, we use <code class="literal">ChannelInterceptors</code>.</p><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 class="literal">spring.sleuth.keys.http.headers</code> (a list of header names).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Tags are collected and exported only if there is a <code class="literal">Sampler</code> that allows it. By default, there is no such <code class="literal">Sampler</code>, to ensure that there is no danger of accidentally collecting too much data without configuring something).</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_span_lifecycle" href="#_span_lifecycle"></a>9. Span lifecycle</h1></div></div></div><p>You can do the following operations on the Span by means of <code class="literal">brave.Tracer</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="#creating-and-finishing-spans" title="9.1 Creating and finishing spans">start</a>: When you start a span, its name is assigned and the start timestamp is recorded.</li><li class="listitem"><a class="link" href="#creating-and-finishing-spans" title="9.1 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).</li><li class="listitem"><a class="link" href="#continuing-spans" title="9.2 Continuing Spans">continue</a>: A new instance of span is created.
|
|
It is a copy of the one that it continues.</li><li class="listitem"><a class="link" href="#continuing-spans" title="9.2 Continuing Spans">detach</a>: The span does not get stopped or closed.
|
|
It only gets removed from the current thread.</li><li class="listitem"><a class="link" href="#creating-spans-with-explicit-parent" title="9.3 Creating a Span with an explicit Parent">create with explicit parent</a>: You can create a new span and set an explicit parent for it.</li></ul></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Sleuth creates an instance of <code class="literal">Tracer</code> for you. In order to use it, you can autowire it.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="creating-and-finishing-spans" href="#creating-and-finishing-spans"></a>9.1 Creating and finishing spans</h2></div></div></div><p>You can manually create spans by using the <code class="literal">Tracer</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a span. If there was a span present in this thread it will become</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the `newSpan`'s parent.</span>
|
|
Span newSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (Tracer.SpanInScope ws = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.withSpanInScope(newSpan.start())) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can tag a span</span>
|
|
newSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxValue"</span>, taxValue);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can log an event on a span</span>
|
|
newSpan.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxCalculated"</span>);
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Once done remember to finish the span. This will allow collecting</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the span to send it to Zipkin</span>
|
|
newSpan.finish();
|
|
}</pre><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 class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Always clean after you create a span. Also, always finish any span that you want to send to Zipkin.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>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.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="continuing-spans" href="#continuing-spans"></a>9.2 Continuing Spans</h2></div></div></div><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 class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>AOP</strong></span>: If there was already a span created before an aspect was reached, you might not want to create a new span.</li><li class="listitem"><span class="strong"><strong>Hystrix</strong></span>: Executing a Hystrix command is most likely a logical part of the current processing.
|
|
It is in fact merely a technical implementation detail that you would not necessarily want to reflect in tracing as a separate being.</li></ul></div><p>To continue a span, you can use <code class="literal">brave.Tracer</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// let's assume that we're in a thread Y and we've received</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the `initialSpan` from thread X</span>
|
|
Span continuedSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.toSpan(newSpan.context());
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can tag a span</span>
|
|
continuedSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxValue"</span>, taxValue);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can log an event on a span</span>
|
|
continuedSpan.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxCalculated"</span>);
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Once done remember to flush the span. That means that</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it will get reported but the span itself is not yet finished</span>
|
|
continuedSpan.flush();
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="creating-spans-with-explicit-parent" href="#creating-spans-with-explicit-parent"></a>9.3 Creating a Span with an explicit Parent</h2></div></div></div><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 class="literal">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 class="literal">nextSpan()</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// let's assume that we're in a thread Y and we've received</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the `initialSpan` from thread X. `initialSpan` will be the parent</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// of the `newSpan`</span>
|
|
Span newSpan = null;
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (Tracer.SpanInScope ws = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.withSpanInScope(initialSpan)) {
|
|
newSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateCommission"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can tag a span</span>
|
|
newSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"commissionValue"</span>, commissionValue);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can log an event on a span</span>
|
|
newSpan.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"commissionCalculated"</span>);
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Once done remember to finish the span. This will allow collecting</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the span to send it to Zipkin. The tags and events set on the</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// newSpan will not be present on the parent</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (newSpan != null) {
|
|
newSpan.finish();
|
|
}
|
|
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>After creating such a span, you must finish it. Otherwise it is not reported (for example, to Zipkin).</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_naming_spans" href="#_naming_spans"></a>10. Naming spans</h1></div></div></div><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><p>Since there is a lot of instrumentation going on, some span names are artificial:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">controller-method-name</code> when received by a Controller with a method name of <code class="literal">controllerMethodName</code></li><li class="listitem"><code class="literal">async</code> for asynchronous operations done with wrapped <code class="literal">Callable</code> and <code class="literal">Runnable</code> interfaces.</li><li class="listitem">Methods annotated with <code class="literal">@Scheduled</code> return the simple name of the class.</li></ul></div><p>Fortunately, for asynchronous processing, you can provide explicit naming.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spanname_annotation" href="#_spanname_annotation"></a>10.1 <code class="literal">@SpanName</code> Annotation</h2></div></div></div><p>You can name the span explicitly by using the <code class="literal">@SpanName</code> annotation, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpanName("calculateTax")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TaxCountingRunnable <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> Runnable {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform logic</span>
|
|
}
|
|
|
|
}</pre><p>In this case, when processed in the following manner, the span is named <code class="literal">calculateTax</code>:</p><pre class="programlisting">Runnable runnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceRunnable(tracing, spanNamer,
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TaxCountingRunnable());
|
|
Future<?> future = executorService.submit(runnable);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ... some additional logic ...</span>
|
|
future.get();</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_tostring_method" href="#_tostring_method"></a>10.2 <code class="literal">toString()</code> method</h2></div></div></div><p>It is pretty rare to create separate classes for <code class="literal">Runnable</code> or <code class="literal">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 class="literal">@SpanName</code> annotation present, we check whether the class has a custom implementation of the <code class="literal">toString()</code> method.</p><p>Running such code leads to creating a span named <code class="literal">calculateTax</code>, as shown in the following example:</p><pre class="programlisting">Runnable runnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceRunnable(tracing, spanNamer, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Runnable() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform logic</span>
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>;
|
|
}
|
|
});
|
|
Future<?> future = executorService.submit(runnable);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ... some additional logic ...</span>
|
|
future.get();</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_managing_spans_with_annotations" href="#_managing_spans_with_annotations"></a>11. Managing Spans with Annotations</h1></div></div></div><p>You can manage spans with a variety of annotations.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rationale" href="#_rationale"></a>11.1 Rationale</h2></div></div></div><p>There are a number of good reasons to manage spans with annotations, including:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">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.</li><li class="listitem">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.</li><li class="listitem">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.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_creating_new_spans" href="#_creating_new_spans"></a>11.2 Creating New Spans</h2></div></div></div><p>If you do not want to create local spans manually, you can use the <code class="literal">@NewSpan</code> annotation.
|
|
Also, we provide the <code class="literal">@SpanTag</code> annotation to add tags in an automated fashion.</p><p>Now we can consider some examples of usage.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod();</pre><p>Annotating the method without any parameter leads to creating a new span whose name equals the annotated method name.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan("customNameOnTestMethod4")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod4();</pre><p>If you provide the value in the annotation (either directly or by setting the <code class="literal">name</code> parameter), the created span has the provided value as the name.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// method declaration</span>
|
|
<em><span class="hl-annotation" style="color: gray">@NewSpan(name = "customNameOnTestMethod5")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod5(<em><span class="hl-annotation" style="color: gray">@SpanTag("testTag")</span></em> String param);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and method execution</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.testBean.testMethod5(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test"</span>);</pre><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 class="literal">testTag</code>, and the tag value is <code class="literal">test</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan(name = "customNameOnTestMethod3")</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod3() {
|
|
}</pre><p>You can place the <code class="literal">@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 class="literal">@NewSpan</code> annotation, the most
|
|
concrete one wins (in this case <code class="literal">customNameOnTestMethod3</code> is set).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_continuing_spans" href="#_continuing_spans"></a>11.3 Continuing Spans</h2></div></div></div><p>If you want to add tags and annotations to an existing span, you can use the <code class="literal">@ContinueSpan</code> annotation, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// method declaration</span>
|
|
<em><span class="hl-annotation" style="color: gray">@ContinueSpan(log = "testMethod11")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod11(<em><span class="hl-annotation" style="color: gray">@SpanTag("testTag11")</span></em> String param);
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// method execution</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.testBean.testMethod11(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.testBean.testMethod13();</pre><p>(Note that, in contrast with the <code class="literal">@NewSpan</code> annotation ,you can also add logs with the <code class="literal">log</code> parameter.)</p><p>That way, the span gets continued and:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Log entries named <code class="literal">testMethod11.before</code> and <code class="literal">testMethod11.after</code> are created.</li><li class="listitem">If an exception is thrown, a log entry named <code class="literal">testMethod11.afterFailure</code> is also created.</li><li class="listitem">A tag with a key of <code class="literal">testTag11</code> and a value of <code class="literal">test</code> is created.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_advanced_tag_setting" href="#_advanced_tag_setting"></a>11.4 Advanced Tag Setting</h2></div></div></div><p>There are 3 different ways to add tags to a span. All of them are controlled by the <code class="literal">SpanTag</code> annotation.
|
|
The precedence is as follows:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Try with a bean of <code class="literal">TagValueResolver</code> type and a provided name.</li><li class="listitem">If the bean name has not been provided, try to evaluate an expression.
|
|
We search for a <code class="literal">TagValueExpressionResolver</code> bean.
|
|
The default implementation uses SPEL expression resolution.
|
|
<span class="strong"><strong>IMPORTANT</strong></span> You can only reference properties from the SPEL expression. Method execution is not allowed due to security constraints.</li><li class="listitem">If we do not find any expression to evaluate, return the <code class="literal">toString()</code> value of the parameter.</li></ol></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_extractor" href="#_custom_extractor"></a>11.4.1 Custom extractor</h3></div></div></div><p>The value of the tag for the following method is computed by an implementation of <code class="literal">TagValueResolver</code> interface.
|
|
Its class name has to be passed as the value of the <code class="literal">resolver</code> attribute.</p><p>Consider the following annotated method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> getAnnotationForTagValueResolver(
|
|
<em><span class="hl-annotation" style="color: gray">@SpanTag(key = "test", resolver = TagValueResolver.class)</span></em> String test) {
|
|
}</pre><p>Now further consider the following <code class="literal">TagValueResolver</code> bean implementation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean(name = "myCustomTagValueResolver")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> TagValueResolver tagValueResolver() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> parameter -> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Value from myCustomTagValueResolver"</span>;
|
|
}</pre><p>The two preceding examples lead to setting a tag value equal to <code class="literal">Value from myCustomTagValueResolver</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_resolving_expressions_for_a_value" href="#_resolving_expressions_for_a_value"></a>11.4.2 Resolving Expressions for a Value</h3></div></div></div><p>Consider the following annotated method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> getAnnotationForTagValueExpression(
|
|
<em><span class="hl-annotation" style="color: gray">@SpanTag(key = "test", expression = "'hello' + ' characters'")</span></em> String test) {
|
|
}</pre><p>No custom implementation of a <code class="literal">TagValueExpressionResolver</code> leads to evaluation of the SPEL expression, and a tag with a value of <code class="literal">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 class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_the_tostring_method" href="#_using_the_tostring_method"></a>11.4.3 Using the <code class="literal">toString()</code> method</h3></div></div></div><p>Consider the following annotated method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> getAnnotationForArgumentToString(<em><span class="hl-annotation" style="color: gray">@SpanTag("test")</span></em> Long param) {
|
|
}</pre><p>Running the preceding method with a value of <code class="literal">15</code> leads to setting a tag with a String value of <code class="literal">"15"</code>.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_customizations" href="#_customizations"></a>12. Customizations</h1></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http" href="#_http"></a>12.1 HTTP</h2></div></div></div><p>If a customization of client / server parsing of the HTTP related spans is required,
|
|
just register a bean of type <code class="literal">brave.http.HttpClientParser</code> or
|
|
<code class="literal">brave.http.HttpServerParser</code>. If client /server sampling is required, just
|
|
register a bean of type <code class="literal">brave.http.HttpSampler</code> and name the bean
|
|
<code class="literal">sleuthClientSampler</code> for client sampler and <code class="literal">sleuthServerSampler</code> for server sampler.
|
|
For your convenience the <code class="literal">@ClientSampler</code> and <code class="literal">@ServerSampler</code>
|
|
annotations can be used to inject the proper beans or to
|
|
reference the bean names via their static String <code class="literal">NAME</code> fields.</p><p>Check out Brave’s code to see an example of how to make a path-based sampler
|
|
<a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy" target="_top">https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy</a></p><p>If you want to completely rewrite the <code class="literal">HttpTracing</code> bean you can use the <code class="literal">SkipPatternProvider</code>
|
|
interface to retrieve the URL <code class="literal">Pattern</code> for spans that should be not sampled. Below you can see
|
|
an example of usage of <code class="literal">SkipPatternProvider</code> inside a server side, <code class="literal">HttpSampler</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
|
|
<em><span class="hl-annotation" style="color: gray">@Bean(name = ServerSampler.NAME)</span></em>
|
|
HttpSampler myHttpSampler(SkipPatternProvider provider) {
|
|
Pattern pattern = provider.skipPattern();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpSampler() {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <Req> Boolean trySample(HttpAdapter<Req, ?> adapter, Req request) {
|
|
String url = adapter.path(request);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> shouldSkip = pattern.matcher(url).matches();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (shouldSkip) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> false;
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
|
|
}
|
|
};
|
|
}
|
|
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_tracingfilter" href="#_tracingfilter"></a>12.2 <code class="literal">TracingFilter</code></h2></div></div></div><p>You can also modify the behavior of the <code class="literal">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 class="literal">TracingFilter</code> bean.</p><p>In the following example, we register the <code class="literal">TracingFilter</code> bean, add the <code class="literal">ZIPKIN-TRACE-ID</code> response header containing the current Span’s trace id, and add a tag with key <code class="literal">custom</code> and a value <code class="literal">tag</code> to the span.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Component</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyFilter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> GenericFilterBean {
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Tracer tracer;
|
|
|
|
MyFilter(Tracer tracer) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer = tracer;
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doFilter(ServletRequest request, ServletResponse response,
|
|
FilterChain chain) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException, ServletException {
|
|
Span currentSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.currentSpan();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (currentSpan == null) {
|
|
chain.doFilter(request, response);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span>;
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// for readability we're returning trace id in a hex form</span>
|
|
((HttpServletResponse) response).addHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"ZIPKIN-TRACE-ID"</span>,
|
|
currentSpan.context().traceIdString());
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// we can also add some custom tags</span>
|
|
currentSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"custom"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tag"</span>);
|
|
chain.doFilter(request, response);
|
|
}
|
|
|
|
}
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// end::response_headers[]</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_custom_service_name" href="#_custom_service_name"></a>12.3 Custom service name</h2></div></div></div><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 class="literal">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 class="literal">myService</code>):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.service.name</span>: myService</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customization_of_reported_spans" href="#_customization_of_reported_spans"></a>12.4 Customization of Reported Spans</h2></div></div></div><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 class="literal">FinishedSpanHandler</code> interface.</p><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 class="literal">FinishedSpanHandler</code> interface to alter that name.</p><p>The following example shows how to register two beans that implement <code class="literal">FinishedSpanHandler</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
FinishedSpanHandler handlerOne() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FinishedSpanHandler() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> handle(TraceContext traceContext, MutableSpan span) {
|
|
span.name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// keep this span</span>
|
|
}
|
|
};
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
|
|
FinishedSpanHandler handlerTwo() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FinishedSpanHandler() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> handle(TraceContext traceContext, MutableSpan span) {
|
|
span.name(span.name() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" bar"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// keep this span</span>
|
|
}
|
|
};
|
|
}</pre><p>The preceding example results in changing the name of the reported span to <code class="literal">foo bar</code>, just before it gets reported (for example, to Zipkin).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_host_locator" href="#_host_locator"></a>12.5 Host Locator</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is about defining <span class="strong"><strong>host</strong></span> from service discovery.
|
|
It is <span class="strong"><strong>NOT</strong></span> about finding Zipkin through service discovery.</p></td></tr></table></div><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><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 class="literal">spring.zipkin.locator.discovery.enabled</code> property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.locator.discovery.enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_sending_spans_to_zipkin" href="#_sending_spans_to_zipkin"></a>13. Sending Spans to Zipkin</h1></div></div></div><p>By default, if you add <code class="literal">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 class="literal">spring.zipkin.baseUrl</code> property, as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.baseUrl</span>: http://<span class="hl-number">192.168</span>.<span class="hl-number">99.100</span>:<span class="hl-number">9411</span>/</pre><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 class="literal">zipkinserver</code> service ID:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.baseUrl</span>: http://zipkinserver/</pre><p>To disable this feature just set <code class="literal">spring.zipkin.discoveryClientEnabled</code> to `false.</p><p>When the Discovery Client feature is enabled, Sleuth uses
|
|
<code class="literal">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><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">zipkinserver</span>:
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ribbon</span>:
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ListOfServers</span>: host1,host2</pre><p>If you have web, rabbit, 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 class="literal">web</code>, <code class="literal">rabbit</code>, or <code class="literal">kafka</code> to the <code class="literal">spring.zipkin.sender.type</code> property.
|
|
The following example shows setting the sender type for <code class="literal">web</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.sender.type</span>: web</pre><p>To customize the <code class="literal">RestTemplate</code> that sends spans to Zipkin via HTTP, you can register
|
|
the <code class="literal">ZipkinRestTemplateCustomizer</code> bean.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfig {
|
|
<em><span class="hl-annotation" style="color: gray">@Bean</span></em> ZipkinRestTemplateCustomizer myCustomizer() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ZipkinRestTemplateCustomizer() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> customize(RestTemplate restTemplate) {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// customize the RestTemplate</span>
|
|
}
|
|
};
|
|
}
|
|
}</pre><p>If, however, you would like to control the full process of creating the <code class="literal">RestTemplate</code>
|
|
object, you will have to create a bean of <code class="literal">zipkin2.reporter.Sender</code> type.</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@Bean</span></em> Sender myRestTemplateSender(ZipkinProperties zipkin,
|
|
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
|
|
RestTemplate restTemplate = mySuperCustomRestTemplate();
|
|
zipkinRestTemplateCustomizer.customize(restTemplate);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> myCustomSender(zipkin, restTemplate);
|
|
}</pre></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_zipkin_stream_span_consumer" href="#_zipkin_stream_span_consumer"></a>14. Zipkin Stream Span Consumer</h1></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>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.</p></td></tr></table></div><p>If for some reason you need to create the deprecated Stream Zipkin server, see the <a class="link" href="http://cloud.spring.io/spring-cloud-static/Dalston.SR4/multi/multi__span_data_as_messages.html#_zipkin_consumer" target="_top">Dalston Documentation</a>.</p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_integrations" href="#_integrations"></a>15. Integrations</h1></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_opentracing" href="#_opentracing"></a>15.1 OpenTracing</h2></div></div></div><p>Spring Cloud Sleuth is compatible with <a class="link" href="http://opentracing.io/" target="_top">OpenTracing</a>.
|
|
If you have OpenTracing on the classpath, we automatically register the OpenTracing <code class="literal">Tracer</code> bean.
|
|
If you wish to disable this, set <code class="literal">spring.sleuth.opentracing.enabled</code> to <code class="literal">false</code></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_runnable_and_callable" href="#_runnable_and_callable"></a>15.2 Runnable and Callable</h2></div></div></div><p>If you wrap your logic in <code class="literal">Runnable</code> or <code class="literal">Callable</code>, you can wrap those classes in their Sleuth representative, as shown in the following example for <code class="literal">Runnable</code>:</p><pre class="programlisting">Runnable runnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Runnable() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// do some work</span>
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spanNameFromToStringMethod"</span>;
|
|
}
|
|
};
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Manual `TraceRunnable` creation with explicit "calculateTax" Span name</span>
|
|
Runnable traceRunnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceRunnable(tracing, spanNamer, runnable,
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Wrapping `Runnable` with `Tracing`. That way the current span will be available</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in the thread of `Runnable`</span>
|
|
Runnable traceRunnableFromTracer = tracing.currentTraceContext().wrap(runnable);</pre><p>The following example shows how to do so for <code class="literal">Callable</code>:</p><pre class="programlisting">Callable<String> callable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Callable<String>() {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String call() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> someLogic();
|
|
}
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spanNameFromToStringMethod"</span>;
|
|
}
|
|
};
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Manual `TraceCallable` creation with explicit "calculateTax" Span name</span>
|
|
Callable<String> traceCallable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceCallable<>(tracing, spanNamer, callable,
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Wrapping `Callable` with `Tracing`. That way the current span will be available</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in the thread of `Callable`</span>
|
|
Callable<String> traceCallableFromTracer = tracing.currentTraceContext()
|
|
.wrap(callable);</pre><p>That way, you ensure that a new span is created and closed for each execution.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_hystrix" href="#_hystrix"></a>15.3 Hystrix</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_concurrency_strategy" href="#_custom_concurrency_strategy"></a>15.3.1 Custom Concurrency Strategy</h3></div></div></div><p>We register a custom <a class="link" href="https://github.com/Netflix/Hystrix/wiki/Plugins#concurrencystrategy" target="_top"><code class="literal">HystrixConcurrencyStrategy</code></a> called <code class="literal">TraceCallable</code> that wraps all <code class="literal">Callable</code> instances in their Sleuth representative.
|
|
The strategy either starts or continues a span, depending on whether tracing was already going on before the Hystrix command was called.
|
|
To disable the custom Hystrix Concurrency Strategy, set the <code class="literal">spring.sleuth.hystrix.strategy.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_manual_command_setting" href="#_manual_command_setting"></a>15.3.2 Manual Command setting</h3></div></div></div><p>Assume that you have the following <code class="literal">HystrixCommand</code>:</p><pre class="programlisting">HystrixCommand<String> hystrixCommand = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HystrixCommand<String>(setter) {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> String run() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> someLogic();
|
|
}
|
|
};</pre><p>To pass the tracing information, you have to wrap the same logic in the Sleuth version of the <code class="literal">HystrixCommand</code>, which is called
|
|
<code class="literal">TraceCommand</code>, as shown in the following example:</p><pre class="programlisting">TraceCommand<String> traceCommand = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceCommand<String>(tracer, setter) {
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doRun() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> someLogic();
|
|
}
|
|
};</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rxjava" href="#_rxjava"></a>15.4 RxJava</h2></div></div></div><p>We registering a custom <a class="link" href="https://github.com/ReactiveX/RxJava/wiki/Plugins#rxjavaschedulershook" target="_top"><code class="literal">RxJavaSchedulersHook</code></a> that wraps all <code class="literal">Action0</code> instances in their Sleuth representative, which is called <code class="literal">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 class="literal">RxJavaSchedulersHook</code>, set the <code class="literal">spring.sleuth.rxjava.schedulers.hook.enabled</code> to <code class="literal">false</code>.</p><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 class="literal">spring.sleuth.rxjava.schedulers.ignoredthreads</code> property.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The suggest approach to reactive programming and Sleuth is to use
|
|
the Reactor support.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http_integration" href="#_http_integration"></a>15.5 HTTP integration</h2></div></div></div><p>Features from this section can be disabled by setting the <code class="literal">spring.sleuth.web.enabled</code> property with value equal to <code class="literal">false</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_http_filter" href="#_http_filter"></a>15.5.1 HTTP Filter</h3></div></div></div><p>Through the <code class="literal">TracingFilter</code>, all sampled incoming requests result in creation of a Span.
|
|
That Span’s name is <code class="literal">http:</code> + the path to which the request was sent.
|
|
For example, if the request was sent to <code class="literal">/this/that</code> then the name will be <code class="literal">http:/this/that</code>.
|
|
You can configure which URIs you would like to skip by setting the <code class="literal">spring.sleuth.web.skipPattern</code> property.
|
|
If you have <code class="literal">ManagementServerProperties</code> on classpath, its value of <code class="literal">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 class="literal">spring.sleuth.web.additionalSkipPattern</code>.</p><p>To change the order of tracing filter registration, please set the
|
|
<code class="literal">spring.sleuth.web.filter-order</code> property.</p><p>To disable the filter that logs uncaught exceptions you can disable the
|
|
<code class="literal">spring.sleuth.web.exception-throwing-filter-enabled</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_handlerinterceptor" href="#_handlerinterceptor"></a>15.5.2 HandlerInterceptor</h3></div></div></div><p>Since we want the span names to be precise, we use a <code class="literal">TraceHandlerInterceptor</code> that either wraps an existing <code class="literal">HandlerInterceptor</code> or is added directly to the list of existing <code class="literal">HandlerInterceptors</code>.
|
|
The <code class="literal">TraceHandlerInterceptor</code> adds a special request attribute to the given <code class="literal">HttpServletRequest</code>.
|
|
If the the <code class="literal">TracingFilter</code> does not see this attribute, it creates a "<code class="literal">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 class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_async_servlet_support" href="#_async_servlet_support"></a>15.5.3 Async Servlet support</h3></div></div></div><p>If your controller returns a <code class="literal">Callable</code> or a <code class="literal">WebAsyncTask</code>, Spring Cloud Sleuth continues the existing span instead of creating a new one.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_webflux_support" href="#_webflux_support"></a>15.5.4 WebFlux support</h3></div></div></div><p>Through <code class="literal">TraceWebFilter</code>, all sampled incoming requests result in creation of a Span.
|
|
That Span’s name is <code class="literal">http:</code> + the path to which the request was sent.
|
|
For example, if the request was sent to <code class="literal">/this/that</code>, the name is <code class="literal">http:/this/that</code>.
|
|
You can configure which URIs you would like to skip by using the <code class="literal">spring.sleuth.web.skipPattern</code> property.
|
|
If you have <code class="literal">ManagementServerProperties</code> on the classpath, its value of <code class="literal">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 class="literal">spring.sleuth.web.additionalSkipPattern</code>.</p><p>To change the order of tracing filter registration, please set the
|
|
<code class="literal">spring.sleuth.web.filter-order</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_dubbo_rpc_support" href="#_dubbo_rpc_support"></a>15.5.5 Dubbo RPC support</h3></div></div></div><p>Via the integration with Brave, Spring Cloud Sleuth supports <a class="link" href="http://dubbo.io/" target="_top">Dubbo</a>.
|
|
It’s enough to add the <code class="literal">brave-instrumentation-dubbo-rpc</code> dependency:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><dependency></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><groupId></span>io.zipkin.brave<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></groupId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"><artifactId></span>brave-instrumentation-dubbo-rpc<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></artifactId></span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"></dependency></span></pre><p>You need to also set a <code class="literal">dubbo.properties</code> file with the following contents:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">dubbo.provider.filter</span>=tracing
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">dubbo.consumer.filter</span>=tracing</pre><p>You can read more about Brave - Dubbo integration <a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc" target="_top">here</a>.
|
|
An example of Spring Cloud Sleuth and Dubbo can be found <a class="link" href="https://github.com/openzipkin/sleuth-webmvc-example/compare/add-dubbo-tracing" target="_top">here</a>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http_client_integration" href="#_http_client_integration"></a>15.6 HTTP Client Integration</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_synchronous_rest_template" href="#_synchronous_rest_template"></a>15.6.1 Synchronous Rest Template</h3></div></div></div><p>We inject a <code class="literal">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 class="literal">RestTemplate</code> features, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to register <code class="literal">RestTemplate</code> as a bean so that the interceptors get injected.
|
|
If you create a <code class="literal">RestTemplate</code> instance with a <code class="literal">new</code> keyword, the instrumentation does NOT work.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_asynchronous_rest_template" href="#_asynchronous_rest_template"></a>15.6.2 Asynchronous Rest Template</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting with Sleuth <code class="literal">2.0.0</code>, we no longer register a bean of <code class="literal">AsyncRestTemplate</code> type.
|
|
It is up to you to create such a bean.
|
|
Then we instrument it.</p></td></tr></table></div><p>To block the <code class="literal">AsyncRestTemplate</code> features, set <code class="literal">spring.sleuth.web.async.client.enabled</code> to <code class="literal">false</code>.
|
|
To disable creation of the default <code class="literal">TraceAsyncClientHttpRequestFactoryWrapper</code>, set <code class="literal">spring.sleuth.web.async.client.factory.enabled</code>
|
|
to <code class="literal">false</code>.
|
|
If you do not want to create <code class="literal">AsyncRestClient</code> at all, set <code class="literal">spring.sleuth.web.async.client.template.enabled</code> to <code class="literal">false</code>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_multiple_asynchronous_rest_templates" href="#_multiple_asynchronous_rest_templates"></a>Multiple Asynchronous Rest Templates</h4></div></div></div><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 class="literal">AsyncRestTemplate</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Bean(name = "customAsyncRestTemplate")</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> AsyncRestTemplate traceAsyncRestTemplate() {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AsyncRestTemplate(asyncClientFactory(),
|
|
clientHttpRequestFactory());
|
|
}
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> ClientHttpRequestFactory clientHttpRequestFactory() {
|
|
ClientHttpRequestFactory clientHttpRequestFactory = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> CustomClientHttpRequestFactory();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// CUSTOMIZE HERE</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> clientHttpRequestFactory;
|
|
}
|
|
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> AsyncClientHttpRequestFactory asyncClientFactory() {
|
|
AsyncClientHttpRequestFactory factory = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> CustomAsyncClientHttpRequestFactory();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// CUSTOMIZE HERE</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> factory;
|
|
}
|
|
|
|
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_webclient" href="#_webclient"></a>15.6.3 <code class="literal">WebClient</code></h3></div></div></div><p>We inject a <code class="literal">ExchangeFilterFunction</code> implementation that creates a span and, through on-success and on-error callbacks, takes care of closing client-side spans.</p><p>To block this feature, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to register <code class="literal">WebClient</code> as a bean so that the tracing instrumentation gets applied.
|
|
If you create a <code class="literal">WebClient</code> instance with a <code class="literal">new</code> keyword, the instrumentation does NOT work.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_traverson" href="#_traverson"></a>15.6.4 Traverson</h3></div></div></div><p>If you use the <a class="link" href="http://docs.spring.io/spring-hateoas/docs/current/reference/html/#client.traverson" target="_top">Traverson</a> library, you can inject a <code class="literal">RestTemplate</code> as a bean into your Traverson object.
|
|
Since <code class="literal">RestTemplate</code> is already intercepted, you get full support for tracing in your client. The following pseudo code
|
|
shows how to do that:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> RestTemplate restTemplate;
|
|
|
|
Traverson traverson = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Traverson(URI.create(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://some/address"</span>),
|
|
MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8).setRestOperations(restTemplate);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// use Traverson</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_apache_httpclientbuilder_and_httpasyncclientbuilder" href="#_apache_httpclientbuilder_and_httpasyncclientbuilder"></a>15.6.5 Apache <code class="literal">HttpClientBuilder</code> and <code class="literal">HttpAsyncClientBuilder</code></h3></div></div></div><p>We instrument the <code class="literal">HttpClientBuilder</code> and <code class="literal">HttpAsyncClientBuilder</code> so that
|
|
tracing context gets injected to the sent requests.</p><p>To block these features, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_netty_httpclient" href="#_netty_httpclient"></a>15.6.6 Netty <code class="literal">HttpClient</code></h3></div></div></div><p>We instrument the Netty’s <code class="literal">HttpClient</code>.</p><p>To block this feature, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to register <code class="literal">HttpClient</code> as a bean so that the instrumentation happens.
|
|
If you create a <code class="literal">HttpClient</code> instance with a <code class="literal">new</code> keyword, the instrumentation does NOT work.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_userinforesttemplatecustomizer" href="#_userinforesttemplatecustomizer"></a>15.6.7 <code class="literal">UserInfoRestTemplateCustomizer</code></h3></div></div></div><p>We instrument the Spring Security’s <code class="literal">UserInfoRestTemplateCustomizer</code>.</p><p>To block this feature, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_feign" href="#_feign"></a>15.7 Feign</h2></div></div></div><p>By default, Spring Cloud Sleuth provides integration with Feign through <code class="literal">TraceFeignClientAutoConfiguration</code>.
|
|
You can disable it entirely by setting <code class="literal">spring.sleuth.feign.enabled</code> to <code class="literal">false</code>.
|
|
If you do so, no Feign-related instrumentation take place.</p><p>Part of Feign instrumentation is done through a <code class="literal">FeignBeanPostProcessor</code>.
|
|
You can disable it by setting <code class="literal">spring.sleuth.feign.processor.enabled</code> to <code class="literal">false</code>.
|
|
If you set it to <code class="literal">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 class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_asynchronous_communication" href="#_asynchronous_communication"></a>15.8 Asynchronous Communication</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_async_annotated_methods" href="#_async_annotated_methods"></a>15.8.1 <code class="literal">@Async</code> Annotated methods</h3></div></div></div><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 class="literal">spring.sleuth.async.enabled</code> to <code class="literal">false</code>.</p><p>If you annotate your method with <code class="literal">@Async</code>, we automatically create a new Span with the following characteristics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">If the method is annotated with <code class="literal">@SpanName</code>, the value of the annotation is the Span’s name.</li><li class="listitem">If the method is not annotated with <code class="literal">@SpanName</code>, the Span name is the annotated method name.</li><li class="listitem">The span is tagged with the method’s class name and method name.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_scheduled_annotated_methods" href="#_scheduled_annotated_methods"></a>15.8.2 <code class="literal">@Scheduled</code> Annotated Methods</h3></div></div></div><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 class="literal">spring.sleuth.scheduled.enabled</code> to <code class="literal">false</code>.</p><p>If you annotate your method with <code class="literal">@Scheduled</code>, we automatically create a new span with the following characteristics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The span name is the annotated method name.</li><li class="listitem">The span is tagged with the method’s class name and method name.</li></ul></div><p>If you want to skip span creation for some <code class="literal">@Scheduled</code> annotated classes, you can set the <code class="literal">spring.sleuth.scheduled.skipPattern</code> with a regular expression that matches the fully qualified name of the <code class="literal">@Scheduled</code> annotated class.
|
|
If you use <code class="literal">spring-cloud-sleuth-stream</code> and <code class="literal">spring-cloud-netflix-hystrix-stream</code> together, a span is created for each Hystrix metrics and sent to Zipkin.
|
|
This behavior may be annoying. That’s why, by default, <code class="literal">spring.sleuth.scheduled.skipPattern=org.springframework.cloud.netflix.hystrix.stream.HystrixStreamTask</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_executor_executorservice_and_scheduledexecutorservice" href="#_executor_executorservice_and_scheduledexecutorservice"></a>15.8.3 Executor, ExecutorService, and ScheduledExecutorService</h3></div></div></div><p>We provide <code class="literal">LazyTraceExecutor</code>, <code class="literal">TraceableExecutorService</code>, and <code class="literal">TraceableScheduledExecutorService</code>. Those implementations create spans each time a new task is submitted, invoked, or scheduled.</p><p>The following example shows how to pass tracing information with <code class="literal">TraceableExecutorService</code> when working with <code class="literal">CompletableFuture</code>:</p><pre class="programlisting">CompletableFuture<Long> completableFuture = CompletableFuture.supplyAsync(() -> {
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform some logic</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">1</span>_<span class="hl-number">000</span>_<span class="hl-number">000L</span>;
|
|
}, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceableExecutorService(beanFactory, executorService,
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// 'calculateTax' explicitly names the span - this param is optional</span>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>));</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Sleuth does not work with <code class="literal">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 class="literal">supplyAsync(...)</code>, as shown earlier.</p></td></tr></table></div><p>If there are beans that implement the <code class="literal">Executor</code> interface that you would like
|
|
to exclude from span creation, you can use the <code class="literal">spring.sleuth.async.ignored-beans</code>
|
|
property where you can provide a list of bean names.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_customization_of_executors" href="#_customization_of_executors"></a>Customization of Executors</h4></div></div></div><p>Sometimes, you need to set up a custom instance of the <code class="literal">AsyncExecutor</code>.
|
|
The following example shows how to set up such a custom <code class="literal">Executor</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
|
|
<em><span class="hl-annotation" style="color: gray">@EnableAsync</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomExecutorConfig <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AsyncConfigurerSupport {
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
|
|
BeanFactory beanFactory;
|
|
|
|
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Executor getAsyncExecutor() {
|
|
ThreadPoolTaskExecutor executor = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ThreadPoolTaskExecutor();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// CUSTOMIZE HERE</span>
|
|
executor.setCorePoolSize(<span class="hl-number">7</span>);
|
|
executor.setMaxPoolSize(<span class="hl-number">42</span>);
|
|
executor.setQueueCapacity(<span class="hl-number">11</span>);
|
|
executor.setThreadNamePrefix(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"MyExecutor-"</span>);
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// DON'T FORGET TO INITIALIZE</span>
|
|
executor.initialize();
|
|
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LazyTraceExecutor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.beanFactory, executor);
|
|
}
|
|
|
|
}</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_messaging" href="#_messaging"></a>15.9 Messaging</h2></div></div></div><p>Features from this section can be disabled by setting the <code class="literal">spring.sleuth.messaging.enabled</code> property with value equal to <code class="literal">false</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_integration_and_spring_cloud_stream" href="#_spring_integration_and_spring_cloud_stream"></a>15.9.1 Spring Integration and Spring Cloud Stream</h3></div></div></div><p>Spring Cloud Sleuth integrates with <a class="link" href="http://projects.spring.io/spring-integration/" target="_top">Spring Integration</a>.
|
|
It creates spans for publish and subscribe events.
|
|
To disable Spring Integration instrumentation, set <code class="literal">spring.sleuth.integration.enabled</code> to <code class="literal">false</code>.</p><p>You can provide the <code class="literal">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 class="literal">hystrixStreamOutput</code> channel are included.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>When using the <code class="literal">Executor</code> to build a Spring Integration <code class="literal">IntegrationFlow</code>, you must use the untraced version of the <code class="literal">Executor</code>.
|
|
Decorating the Spring Integration Executor Channel with <code class="literal">TraceableExecutorService</code> causes the spans to be improperly closed.</p></td></tr></table></div><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 class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">Propagation.Setter<MessageHeaderAccessor, String></code> - for writing headers to the message</li><li class="listitem"><code class="literal">Propagation.Getter<MessageHeaderAccessor, String></code> - for reading headers from the message</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_rabbitmq" href="#_spring_rabbitmq"></a>15.9.2 Spring RabbitMq</h3></div></div></div><p>We instrument the <code class="literal">RabbitTemplate</code> so that tracing headers get injected
|
|
into the message.</p><p>To block this feature, set <code class="literal">spring.sleuth.messaging.rabbit.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_kafka" href="#_spring_kafka"></a>15.9.3 Spring Kafka</h3></div></div></div><p>We instrument the Spring Kafka’s <code class="literal">ProducerFactory</code> and <code class="literal">ConsumerFactory</code>
|
|
so that tracing headers get injected into the created Spring Kafka’s
|
|
<code class="literal">Producer</code> and <code class="literal">Consumer</code>.</p><p>To block this feature, set <code class="literal">spring.sleuth.messaging.kafka.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_jms" href="#_spring_jms"></a>15.9.4 Spring JMS</h3></div></div></div><p>We instrument the <code class="literal">JmsTemplate</code> so that tracing headers get injected
|
|
into the message. We also support <code class="literal">@JmsListener</code> annotated methods on the consumer side.</p><p>To block this feature, set <code class="literal">spring.sleuth.messaging.jms.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>We don’t support baggage propagation for JMS</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_zuul" href="#_zuul"></a>15.10 Zuul</h2></div></div></div><p>We instrument the Zuul Ribbon integration by enriching the Ribbon requests with tracing information.
|
|
To disable Zuul support, set the <code class="literal">spring.sleuth.zuul.enabled</code> property to <code class="literal">false</code>.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_running_examples" href="#_running_examples"></a>16. Running examples</h1></div></div></div><p>You can see the running examples deployed in the <a class="link" href="https://run.pivotal.io/" target="_top">Pivotal Web Services</a>.
|
|
Check them out at the following links:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="http://docssleuth-zipkin-server.cfapps.io/" target="_top">Zipkin for apps presented in the samples to the top</a>. First make
|
|
a request to <a class="link" href="http://docssleuth-service1.cfapps.io/start" target="_top">Service 1</a> and then check out the trace in Zipkin.</li><li class="listitem"><a class="link" href="http://docsbrewing-zipkin-server.cfapps.io/" target="_top">Zipkin for Brewery on PWS</a>, its <a class="link" href="https://github.com/spring-cloud-samples/brewery" target="_top">Github Code</a>.
|
|
Ensure that you’ve picked the lookback period of 7 days. If there are no traces, go to <a class="link" href="https://docsbrewing-presenting.cfapps.io/" target="_top">Presenting application</a>
|
|
and order some beers. Then check Zipkin for traces.</li></ul></div></div></div></body></html> |