Sync docs from master to gh-pages

This commit is contained in:
Marcin Grzejszczak
2016-03-03 16:26:42 +01:00
parent 5bb5e8fcf1
commit c108636c74
6 changed files with 571 additions and 54 deletions

BIN
images/parents.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
images/trace-id.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 KiB

After

Width:  |  Height:  |  Size: 218 KiB

BIN
images/zipkin-traces.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
images/zipkin-ui.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@@ -4,13 +4,13 @@
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.2">
<meta name="generator" content="Asciidoctor 1.5.3">
<title>Spring Cloud Sleuth</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Remove the comments around the @import statement below when using this as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400";*/
/* Remove comment around @import statement below when using as a custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
@@ -57,7 +57,6 @@ a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
@@ -65,10 +64,12 @@ img{-ms-interpolation-mode:bicubic}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
.antialiased,body{-webkit-font-smoothing:antialiased}
img{display:inline-block;vertical-align:middle}
body{-webkit-font-smoothing:antialiased}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.spread{width:100%}
p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
@@ -111,12 +112,14 @@ blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7;font-weight:bold}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
body{tab-size:4}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table}
@@ -124,7 +127,7 @@ h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title str
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
.keyseq{color:rgba(51,51,51,.8)}
kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menu{color:rgba(0,0,0,.8)}
@@ -155,29 +158,33 @@ p a>code:hover{color:rgba(0,0,0,.9)}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media only screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;font-size:1.2em}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}
@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
.sect1{padding-bottom:.625em}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed}
@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}
.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
@@ -206,7 +213,9 @@ table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inh
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
@@ -216,7 +225,7 @@ table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inh
.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
@@ -237,13 +246,12 @@ table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 0 1.25em 0;display:block}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0}
.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0}
table.spread{width:100%}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0}
table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0}
@@ -289,8 +297,8 @@ ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1{padding-right:.75em;font-weight:bold}
td.hdlist1,td.hdlist2{vertical-align:top}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1}
.colist>table tr>td:last-of-type{padding:.25em 0}
@@ -303,13 +311,14 @@ td.hdlist1,td.hdlist2{vertical-align:top}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none}
span.footnote,span.footnoteref{vertical-align:super;font-size:.875em}
span.footnote a,span.footnoteref a{text-decoration:none}
span.footnote a:active,span.footnoteref a:active{text-decoration:underline}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0}
#footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;text-indent:-1.05em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
@@ -367,11 +376,10 @@ span.icon>.fa{cursor:default}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
h1,h2{letter-spacing:-.01em}
dt,th.tableblock,td.content{text-rendering:optimizeLegibility}
p,td.content{letter-spacing:-.01em}
p strong,td.content strong{letter-spacing:-.005em}
p,blockquote,dt,td.content{font-size:1.0625rem}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
@@ -382,9 +390,9 @@ a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]:after{content:" (" attr(title) ")"}
pre,blockquote,tr,img{page-break-inside:avoid}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
img{max-width:100%!important}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
@@ -426,13 +434,171 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
<p>Spring Cloud Sleuth borrows <a href="http://research.google.com/pubs/pub36356.html">Dapper&#8217;s</a> terminology.</p>
</div>
<div class="paragraph">
<p><strong>Span:</strong> The basic unit of work. For example, sending an RPC is a new span, as is sending a response to an RPC. Span&#8217;s 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 ID&#8217;s (normally IP address).</p>
<p><strong>Span:</strong> The basic unit of work. For example, sending an RPC is a new span, as is sending a response to an
RPC. Span&#8217;s 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 ID&#8217;s (normally IP address).</p>
</div>
<div class="paragraph">
<p>Spans are 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>
<p>Spans are 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>
<div class="paragraph">
<p><strong>Trace:</strong> A set of spans forming a tree-like structure. For example, if you are running a distributed big-data store, a trace might be formed by a put request.</p>
<p><strong>Trace:</strong> A set of spans forming a tree-like structure. For example, if you are running a distributed
big-data store, a trace might be formed by a put request.</p>
</div>
<div class="paragraph">
<p><strong>Annotation:</strong> is used to record existence of an event in time. Some of the core annotations used to define
the start and stop of a request are:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>cs</strong> - Client Sent - The client has made a request. This annotation depicts the start of the span.</p>
</li>
<li>
<p><strong>sr</strong> - Server Received - The server side got the request and will start processing it.
If one subtracts the cs timestamp from this timestamp one will receive the network latency.</p>
</li>
<li>
<p><strong>ss</strong> - Server Sent - Annotated upon completion of request processing (when the response
got sent back to the client). If one subtracts the sr timestamp from this timestamp one
will receive the time needed by the server side to process the request.</p>
</li>
<li>
<p><strong>cr</strong> - Client Received - Signifies the end of the span. The client has successfully received the
response from the server side. If one subtracts the cs timestamp from this timestamp one
will receive the whole time needed by the client to receive the response from the server.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Visualization of what <strong>Span</strong> and <strong>Trace</strong> will look in a system together with the Zipkin annotations:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/trace-id.png" alt="Trace Info propagation">
</div>
</div>
<div class="paragraph">
<p>Each color of a note signifies a span (7 spans - from <strong>A</strong> to <strong>G</strong>). If you have such information in the note:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>Trace Id = X
Span Id = D
Client Sent</code></pre>
</div>
</div>
<div class="paragraph">
<p>That means that the current span has <strong>Trace-Id</strong> set to <strong>X</strong>, <strong>Span-Id</strong> set to <strong>D</strong>. It also has emitted
<strong>Client Sent</strong> event.</p>
</div>
<div class="paragraph">
<p>This is how the visualization of the parent / child relationship of spans would look like:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/parents.png" alt="Parent child relationship">
</div>
</div>
</div>
<div class="sect2">
<h3 id="_purpose">Purpose</h3>
<div class="paragraph">
<p>In the following sections the example from the image above will be taken into consideration.</p>
</div>
<div class="sect3">
<h4 id="_distributed_tracing_with_zipkin">Distributed tracing with Zipkin</h4>
<div class="paragraph">
<p>Altogether there are <strong>10 spans</strong> . If you go to traces in Zipkin you will see this number:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/zipkin-traces.png" alt="Traces">
</div>
</div>
<div class="paragraph">
<p>However if you pick a particular trace then you will see <strong>7 spans</strong>:</p>
</div>
<div class="imageblock">
<div class="content">
<img src="images/zipkin-ui.png" alt="Traces Info propagation">
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
When picking a particular trace you will see merged spans. That means that if there were 2 spans sent to
Zipkin with Server Received and Server Sent / Client Received and Client Sent
annotations then they will presented as a single span.
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>In the image depicting the visualization of what <strong>Span</strong> and <strong>Trace</strong> is you can see 20
colorful labels. How does it happen that in Zipkin 10 spans are received?</p>
</div>
<div class="ulist">
<ul>
<li>
<p>2 span <strong>A</strong> labels signify span started and closed. Upon closing a single span is sent to Zipkin.</p>
</li>
<li>
<p>4 span <strong>B</strong> labels are in fact are single span with 4 annotations. However this span is composed of
two separate instances. One sent from service 1 and one from service 2. So in fact two span instances will be sent
to Zipkin and merged there.</p>
</li>
<li>
<p>2 span <strong>C</strong> labels signify span started and closed. Upon closing a single span is sent to Zipkin.</p>
</li>
<li>
<p>4 span <strong>B</strong> labels are in fact are single span with 4 annotations. However this span is composed of
two separate instances. One sent from service 2 and one from service 3. So in fact two span instances will be sent
to Zipkin and merged there.</p>
</li>
<li>
<p>2 span <strong>E</strong> labels signify span started and closed. Upon closing a single span is sent to Zipkin.</p>
</li>
<li>
<p>4 span <strong>B</strong> labels are in fact are single span with 4 annotations. However this span is composed of
two separate instances. One sent from service 2 and one from service 4. So in fact two span instances will be sent
to Zipkin and merged there.</p>
</li>
<li>
<p>2 span <strong>G</strong> labels signify span started and closed. Upon closing a single span is sent to Zipkin.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>So 1 span from <strong>A</strong>, 2 spans from <strong>B</strong>, 1 span from <strong>C</strong>, 2 spans from <strong>D</strong>, 1 span from <strong>E</strong>, 2 spans from <strong>F</strong> and 1 from <strong>G</strong>.
Altogether <strong>10</strong> spans.</p>
</div>
</div>
<div class="sect3">
<h4 id="_log_correlation">Log correlation</h4>
<div class="paragraph">
<p>When grepping the logs of those four applications by trace id equal to e.g. <code>2485ec27856c56f4</code> one would get the following:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code>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]]</code></pre>
</div>
</div>
<div class="paragraph">
<p>If you&#8217;re using a log aggregating tool like Kibana, Splunk etc. you can order the events that took place</p>
</div>
</div>
</div>
</div>
@@ -452,28 +618,80 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
</div>
</div>
<div class="paragraph">
<p>(notice the <code>[appname,traceId,spanId,exportable]</code> entries from the MDC).</p>
<p>notice the <code>[appname,traceId,spanId,exportable]</code> entries from the MDC:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>spanId</strong> - the id of a specific operation that took place</p>
</li>
<li>
<p>Optionally log span data in JSON format for harvesting in a log aggregator (set <code>spring.sleuth.log.json.enabled=true</code>).</p>
<p><strong>appname</strong> - the name of the application that logged the span</p>
</li>
<li>
<p>Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations, key-value annotations. Loosely based on HTrace, but Zipkin (Dapper) compatible.</p>
<p><strong>traceId</strong> - the id of the latency graph that contains the span</p>
</li>
<li>
<p>Instruments common ingress and egress points from Spring applications (servlet filter, rest template, scheduled actions, message channels, zuul filters, feign client).</p>
</li>
<li>
<p>If <code>spring-cloud-sleuth-zipkin</code> then the app will generate and collect Zipkin-compatible traces (using Brave). By default it sends them via HTTP to a Zipkin server on localhost (port 9411). Configure the location of the service using <code>spring.zipkin.[host,port]</code>.</p>
</li>
<li>
<p>If <code>spring-cloud-sleuth-stream</code> then the app will generate and collect traces via Spring Cloud Stream. Your app automatically becomes a producer of tracer messages that are sent over your broker of choice (e.g. RabbitMQ, Apache Kafka, Redis).</p>
<p><strong>exportable</strong> - whether the log should be exported to Zipkin or not</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>If using Zipkin or Stream, configure the percentage of spans exported using <code>spring.sleuth.sampler.percentage</code> (default 0.1, i.e. 10%).</p>
</li>
<li>
<p>Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations,
key-value annotations. Loosely based on HTrace, but Zipkin (Dapper) compatible.</p>
</li>
<li>
<p>Sleuth records timing information to aid in latency analysis. Using sleuth, you can pinpoint causes of
latency in your applications. Sleuth is written to not log too much, and to not cause your production application to crash.</p>
<div class="ulist">
<ul>
<li>
<p>propagates structural data about your call-graph in-band, and the rest out-of-band.</p>
</li>
<li>
<p>includes opinionated instrumentation of layers such as HTTP</p>
</li>
<li>
<p>includes sampling policy to manage volume</p>
</li>
<li>
<p>can report to a Zipkin system for query and visualization</p>
</li>
</ul>
</div>
</li>
<li>
<p>Instruments common ingress and egress points from Spring applications (servlet filter, async endpoints,
rest template, scheduled actions, message channels, zuul filters, feign client).</p>
</li>
<li>
<p>Provides simple metrics of accepted / dropped spans.</p>
</li>
<li>
<p>If <code>spring-cloud-sleuth-zipkin</code> then the app will generate and collect Zipkin-compatible traces.
By default it sends them via HTTP to a Zipkin server on localhost (port 9411).
Configure the location of the service using <code>spring.zipkin.baseUrl</code>.</p>
</li>
<li>
<p>If <code>spring-cloud-sleuth-stream</code> then the app will generate and collect traces via <a href="https://github.com/spring-cloud/spring-cloud-stream">Spring Cloud Stream</a>.
Your app automatically becomes a producer of tracer messages that are sent over your broker of choice
(e.g. RabbitMQ, Apache Kafka, Redis).</p>
</li>
</ul>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<div class="title">Important</div>
</td>
<td class="content">
If using Zipkin or Stream, configure the percentage of spans exported using <code>spring.sleuth.sampler.percentage</code>
(default 0.1, i.e. 10%). <strong>Otherwise you might think that Sleuth is not working cause it&#8217;s omitting some spans.</strong>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
@@ -482,7 +700,11 @@ body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-b
<div class="title">Note</div>
</td>
<td class="content">
the SLF4J MDC is always set and logback users will immediately see the trace and span ids in logs per the example above. Other logging systems have to configure their own formatter to get the same result. The default is <code>logging.pattern.level</code> set to <code>%clr(%5p) %clr([${spring.application.name:},%X{X-Trace-Id:-},%X{X-Span-Id:-},%X{X-Span-Export:-}]){yellow}</code> (this is a Spring Boot feature for logback users).
the SLF4J MDC is always set and logback users will immediately see the trace and span ids in logs per the example
above. Other logging systems have to configure their own formatter to get the same result. The default is
<code>logging.pattern.level</code> set to <code>%clr(%5p) %clr([${spring.application.name:},%X{X-Trace-Id:-},%X{X-Span-Id:-},%X{X-Span-Export:-}]){yellow}</code>
(this is a Spring Boot feature for logback users).
<strong>This means that if you&#8217;re not using SLF4J this pattern WILL NOT be automatically applied</strong>.
</td>
</tr>
</table>
@@ -529,8 +751,8 @@ configure the exports using <code>spring.sleuth.sampler.percentage</code>.
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@Bean
public Sampler&lt;?&gt; defaultSampler() {
return new AlwaysSampler();
public Sampler defaultSampler() {
return new AlwaysSampler();
}</code></pre>
</div>
</div>
@@ -569,6 +791,289 @@ something).
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
Currently the instrumentation in Spring Cloud Sleuth is eager - it means that
we&#8217;re actively trying to pass the tracing context between threads. Also timing events
are captured even when sleuth isn&#8217;t exporting data to a tracing system.
This approach may change in the future towards being lazy on this matter.
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_span_lifecycle">Span lifecycle</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You can do the following operations on the Span by means of <strong>Tracer</strong> interface:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="#creating-and-closing-spans">start</a> - when you start a span its name is assigned and start timestamp is recorded.</p>
</li>
<li>
<p><a href="#creating-and-closing-spans">close</a> - the span gets finished (the end time of the span is recorded) and if
the span is <strong>exportable</strong> then it will be eligible for collection to Zipkin.
The span is also removed from the current thread.</p>
</li>
<li>
<p><a href="#continuing-spans">continue</a> - a new instance of span will be created whereas it will be a copy of the
one that it continues.</p>
</li>
<li>
<p><a href="#continuing-spans">detach</a> - the span doesn&#8217;t get stopped or closed. It only gets removed from the current thread.</p>
</li>
<li>
<p><a href="#joining-spans">join</a> - you can create a new span and set an explicit parent to it</p>
</li>
</ul>
</div>
<div class="sect2">
<h3 id="creating-and-closing-spans">Creating and closing spans</h3>
<div class="paragraph">
<p>You can manually create spans by using the <strong>Tracer</strong> interface.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">// Start a span. If there was a span present in this thread it will become
// the `newSpan`'s parent.
Span newSpan = this.tracer.startTrace("calculateTax");
try {
// ...
// You can tag a span
this.tracer.addTag("taxValue", taxValue);
// ...
// You can log an event on a span
newSpan.logEvent("taxCalculated");
} finally {
// Once done remember to close the span. This will allow collecting
// the span to send it to Zipkin
this.tracer.close(newSpan);
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this example we could see how to create a new instance of span. Assuming that there already
was a span present in this thread then it would become the parent of that span.</p>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<div class="title">Important</div>
</td>
<td class="content">
Always clean after you create a span! Don&#8217;t forget to close a span if you want to send it to Zipkin.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="continuing-spans">Continuing spans</h3>
<div class="paragraph">
<p>Sometimes you don&#8217;t want to create a new span but you want to continue one. Example of such a
situation might be (of course it all depends on the use-case):</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>AOP</strong> - If there was already a span created before an aspect was reached then you might not want to create a new span.</p>
</li>
<li>
<p><strong>Hystrix</strong> - executing a Hystrix command is most likely a logical part of the current processing. It&#8217;s in fact
only a technical implementation detail that you wouldn&#8217;t necessarily want to reflect in tracing as a separate being.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>The continued instance of span is equal to the one that it continues:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">Span continuedSpan = this.tracer.continueSpan(spanToContinue);
assertThat(continuedSpan).isEqualTo(spanToContinue);</code></pre>
</div>
</div>
<div class="paragraph">
<p>To continue a span you can use the <strong>Tracer</strong> interface.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">// let's assume that we're in a thread Y and we've received
// the `initialSpan` from thread X
Span continuedSpan = this.tracer.continueSpan(initialSpan);
try {
// ...
// You can tag a span
this.tracer.addTag("taxValue", taxValue);
// ...
// You can log an event on a span
continuedSpan.logEvent("taxCalculated");
} finally {
// Once done remember to detach the span. That way you'll
// safely remove it from the current thread without closing it
this.tracer.detach(continuedSpan);
}</code></pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<div class="title">Important</div>
</td>
<td class="content">
Always clean after you create a span! Don&#8217;t forget to detach a span if some work was done started in one
thread (e.g. thread X) and it&#8217;s waiting for other threads (e.g. Y, Z) to finish.
Then the spans in the threads Y, Z should be detached at the end of their work. When the results are collected
the span in thread X should be closed.
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="joining-spans">Joining spans</h3>
<div class="paragraph">
<p>There is a possibility that you want to start a new span and provide an explicit parent of that span.
Let&#8217;s assume that the parent of a span is in one thread and you want to start a new span in another thread. The
<code>joinTrace</code> method of the <code>Tracer</code> interface is the method you are looking for.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">// let's assume that we're in a thread Y and we've received
// the `initialSpan` from thread X. `initialSpan` will be the parent
// of the `joinedSpan`
Span joinedSpan = this.tracer.joinTrace("calculateCommission", initialSpan);
try {
// ...
// You can tag a span
this.tracer.addTag("commissionValue", commissionValue);
// ...
// You can log an event on a span
joinedSpan.logEvent("commissionCalculated");
} finally {
// Once done remember to close the span. This will allow collecting
// the span to send it to Zipkin. The tags and events set on the
// joinedSpan will not be present on the parent
this.tracer.close(joinedSpan);
}</code></pre>
</div>
</div>
<div class="admonitionblock important">
<table>
<tr>
<td class="icon">
<div class="title">Important</div>
</td>
<td class="content">
After having joined the span remember to close it. Otherwise you will see a lot of warnings in your logs
related to the fact that you have a span present in the current thread other than the one you&#8217;re trying to close.
What&#8217;s worse your spans won&#8217;t get closed properly thus will not get collected to Zipkin.
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_naming_spans">Naming spans</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Picking a span name is not a trivial task. Span name should depict an operation name. The name should
be low cardinality (e.g. not include identifiers).</p>
</div>
<div class="paragraph">
<p>Since there is a lot of instrumentation going on some of the span names will be
artificial like:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>http:path</code> when received an http request on a given path</p>
</li>
<li>
<p><code>async</code> for asynchronous operations done via wrapped <code>Callable</code> and <code>Runnable</code>.</p>
</li>
<li>
<p><code>@Scheduled</code> annotated methods will return the simple name of the class.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Fortunately, for the asynchronous processing you can provide explicit naming.</p>
</div>
<div class="sect3">
<h4 id="__spanname_annotation">@SpanName annotation</h4>
<div class="paragraph">
<p>You can do name the span explicitly via the <code>@SpanName</code> annotation.</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">@SpanName("calculateTax")
class TaxCountingRunnable implements Runnable {
@Override public void run() {
// perform logic
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>In this case, when processed in the following manner:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">Runnable runnable = new TraceRunnable(tracer, spanNamer, new TaxCountingRunnable());
Future&lt;?&gt; future = executorService.submit(runnable);
// ... some additional logic ...
future.get();</code></pre>
</div>
</div>
<div class="paragraph">
<p>The span will be named <code>calculateTax</code>.</p>
</div>
</div>
<div class="sect3">
<h4 id="_tostring_method">toString() method</h4>
<div class="paragraph">
<p>It&#8217;s pretty rare to create separate classes for <code>Runnable</code> or <code>Callable</code>. Typically one creates an anonymous
instance of those classes. You can&#8217;t annotate such classes thus to override that, if there is no <code>@SpanName</code> annotation present,
we&#8217;re checking if the class has a custom implementation of the <code>toString()</code> method.</p>
</div>
<div class="paragraph">
<p>So executing such code:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlight"><code class="language-java" data-lang="java">Runnable runnable = new TraceRunnable(tracer, spanNamer, new Runnable() {
@Override public void run() {
// perform logic
}
@Override public String toString() {
return "calculateTax";
}
});
Future&lt;?&gt; future = executorService.submit(runnable);
// ... some additional logic ...
future.get();</code></pre>
</div>
</div>
<div class="paragraph">
<p>will lead in creating a span named <code>calculateTax</code>.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
@@ -595,9 +1100,9 @@ for the Span data and pushing it into a Zipkin <code>SpanStore</code>. This appl
<pre class="highlight"><code class="language-java" data-lang="java">@SpringBootApplication
@EnableZipkinStreamServer
public class Consumer {
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
public static void main(String[] args) {
SpringApplication.run(Consumer.class, args);
}
}</code></pre>
</div>
</div>
@@ -612,7 +1117,7 @@ port 9411 if you want the query server on the same host and the
default configuration).</p>
</div>
<div class="paragraph">
<p>The deafult <code>SpanStore</code> is in-memory (good for demos and getting
<p>The default <code>SpanStore</code> is in-memory (good for demos and getting
started quickly). For a more robust solution you can add MySQL and
<code>spring-boot-starter-jdbc</code> to your classpath and enable the JDBC
<code>SpanStore</code> via configuration, e.g.:</p>
@@ -691,10 +1196,22 @@ the consumer app).
</div>
</div>
</div>
<div class="sect1">
<h2 id="_metrics">Metrics</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Currently Spring Cloud Sleuth registers very simple metrics related to spans.
It&#8217;s using the <a href="http://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-metrics.html#production-ready-recording-metrics">Spring Boot&#8217;s metrics support</a>
to calculate the number of accepted and dropped spans. Each time a span gets
sent to Zipkin the number of accepted spans will increase. If there&#8217;s an error then
the number of dropped spans will get increased.</p>
</div>
</div>
</div>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2016-02-18 23:05:40 +00:00
Last updated 2016-03-03 11:19:57 CET
</div>
</div>
</body>