Added baggage support (#440)
without this change there is no support for context propagation with this change whenever you pass the `baggage-...` for http or `baggage_` for messaging headers then such a value will be propagated through your system fixes #237
This commit is contained in:
committed by
GitHub
parent
9ab37c34fa
commit
96df523557
99
README.adoc
99
README.adoc
@@ -1,6 +1,10 @@
|
||||
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
||||
|
||||
:jdkversion: 1.8
|
||||
:github-tag: master
|
||||
:github-repo: spring-cloud/spring-cloud-sleuth
|
||||
:github-raw: http://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: http://github.com/{github-repo}/tree/{github-tag}
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
@@ -72,7 +76,7 @@ the start and stop of a request are:
|
||||
|
||||
Visualization of what *Span* and *Trace* will look in a system together with the Zipkin annotations:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/trace-id.png[Trace Info propagation]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/trace-id.png[Trace Info propagation]
|
||||
|
||||
Each color of a note signifies a span (7 spans - from *A* to *G*). If you have such information in the note:
|
||||
|
||||
@@ -86,7 +90,7 @@ That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*
|
||||
|
||||
This is how the visualization of the parent / child relationship of spans would look like:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/parents.png[Parent child relationship]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/parents.png[Parent child relationship]
|
||||
|
||||
=== Purpose
|
||||
|
||||
@@ -96,11 +100,11 @@ In the following sections the example from the image above will be taken into co
|
||||
|
||||
Altogether there are *10 spans* . If you go to traces in Zipkin you will see this number:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-traces.png[Traces]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/zipkin-traces.png[Traces]
|
||||
|
||||
However if you pick a particular trace then you will see *7 spans*:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-ui.png[Traces Info propagation]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/zipkin-ui.png[Traces Info propagation]
|
||||
|
||||
NOTE: 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
|
||||
@@ -128,15 +132,15 @@ Altogether *10* spans.
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
|
||||
The dependency graph in Zipkin would look like this:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/dependencies.png[Dependencies]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/dependencies.png[Dependencies]
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
|
||||
|
||||
==== Log correlation
|
||||
@@ -156,7 +160,7 @@ If you're using a log aggregating tool like https://www.elastic.co/products/kiba
|
||||
http://www.splunk.com/[Splunk] etc. you can order the events that took place. An example of
|
||||
Kibana would look like this:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
|
||||
|
||||
If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash] here is the Grok pattern for Logstash:
|
||||
|
||||
@@ -275,6 +279,49 @@ Below you can find an example of a Logback configuration (file named `https://gi
|
||||
NOTE: If you're using a custom `logback-spring.xml` then you have to pass the `spring.application.name` in
|
||||
`bootstrap` instead of `application` property file. Otherwise your custom logback file won't read the property properly.
|
||||
|
||||
===== Propagating Span Context
|
||||
|
||||
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.
|
||||
|
||||
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 will understand that a header is baggage related if the HTTP
|
||||
header is prefixed with `baggage-` and for messaging it starts with `baggage_`.
|
||||
|
||||
IMPORTANT: There's 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, it could crash the app due
|
||||
to exceeding transport-level message or header capacity.
|
||||
|
||||
Example of setting span:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
Unresolved directive in intro.adoc - include::http://raw.github.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
----
|
||||
|
||||
====== Baggage vs. Span Tags
|
||||
|
||||
Baggage travels with the trace (i.e. every child span contains the baggage of its parent). Zipkin has no knowledge of
|
||||
baggage and will not even receive that information.
|
||||
|
||||
Tags are attached to a specific span - they are presented for that particular span only. However you
|
||||
can search by tag to find the trace, where there exists a span having the searched tag value.
|
||||
|
||||
If you want to be able to lookup a span based on baggage, you should add corresponding entry as a tag in the root span.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
Span span = tracer.getCurrentSpan();
|
||||
String baggageKey = "key";
|
||||
String baggageValue = "foo";
|
||||
span.setBaggageItem(baggageKey, baggageValue);
|
||||
tracer.addTag(baggageKey, baggageValue);
|
||||
----
|
||||
|
||||
|
||||
=== Adding to the project
|
||||
|
||||
==== Only Sleuth (log correlation)
|
||||
@@ -290,7 +337,7 @@ the `spring-cloud-starter-sleuth` module to your project.
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -311,7 +358,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,7 +382,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` de
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -356,7 +403,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,7 +427,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-sleuth-stream` dep
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -412,7 +459,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,7 +489,7 @@ dependency
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -474,7 +521,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,22 +542,7 @@ and then just annotate your main class with `@EnableZipkinStreamServer` annotati
|
||||
|
||||
[source,java]
|
||||
----
|
||||
package example;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableZipkinStreamServer
|
||||
public class ZipkinStreamServerApplication {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
SpringApplication.run(ZipkinStreamServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
----
|
||||
Unresolved directive in intro.adoc - include::http://raw.github.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin-stream/src/main/java/example/ZipkinStreamServerApplication.java[]
|
||||
|
||||
== Additional resources
|
||||
|
||||
@@ -556,6 +588,9 @@ rest template, scheduled actions, message channels, zuul filters, feign client).
|
||||
works via Zipkin-compatible request headers. This propagation logic is defined and customized via
|
||||
`SpanInjector` and `SpanExtractor` implementations.
|
||||
|
||||
* Sleuth gives you the possibility to propagate context (also known as baggage) between processes. That means that if you set on a Span
|
||||
a baggage element then it will be sent downstream either via HTTP or messaging to other processes.
|
||||
|
||||
* Provides simple metrics of accepted / dropped spans.
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces.
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
:jdkversion: 1.8
|
||||
:github-tag: master
|
||||
:github-repo: spring-cloud/spring-cloud-sleuth
|
||||
:github-raw: http://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: http://github.com/{github-repo}/tree/{github-tag}
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-sleuth.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-sleuth/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-sleuth"]
|
||||
|
||||
@@ -34,6 +34,9 @@ rest template, scheduled actions, message channels, zuul filters, feign client).
|
||||
works via Zipkin-compatible request headers. This propagation logic is defined and customized via
|
||||
`SpanInjector` and `SpanExtractor` implementations.
|
||||
|
||||
* Sleuth gives you the possibility to propagate context (also known as baggage) between processes. That means that if you set on a Span
|
||||
a baggage element then it will be sent downstream either via HTTP or messaging to other processes.
|
||||
|
||||
* Provides simple metrics of accepted / dropped spans.
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces.
|
||||
|
||||
@@ -33,7 +33,7 @@ the start and stop of a request are:
|
||||
|
||||
Visualization of what *Span* and *Trace* will look in a system together with the Zipkin annotations:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/trace-id.png[Trace Info propagation]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/trace-id.png[Trace Info propagation]
|
||||
|
||||
Each color of a note signifies a span (7 spans - from *A* to *G*). If you have such information in the note:
|
||||
|
||||
@@ -47,7 +47,7 @@ That means that the current span has *Trace-Id* set to *X*, *Span-Id* set to *D*
|
||||
|
||||
This is how the visualization of the parent / child relationship of spans would look like:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/parents.png[Parent child relationship]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/parents.png[Parent child relationship]
|
||||
|
||||
=== Purpose
|
||||
|
||||
@@ -57,11 +57,11 @@ In the following sections the example from the image above will be taken into co
|
||||
|
||||
Altogether there are *10 spans* . If you go to traces in Zipkin you will see this number:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-traces.png[Traces]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/zipkin-traces.png[Traces]
|
||||
|
||||
However if you pick a particular trace then you will see *7 spans*:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-ui.png[Traces Info propagation]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/zipkin-ui.png[Traces Info propagation]
|
||||
|
||||
NOTE: 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
|
||||
@@ -89,15 +89,15 @@ Altogether *10* spans.
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/", width=150, height=74]
|
||||
|
||||
The dependency graph in Zipkin would look like this:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/dependencies.png[Dependencies]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/dependencies.png[Dependencies]
|
||||
|
||||
.Click Pivotal Web Services icon to see it live!
|
||||
[caption="Click Pivotal Web Services icon to see it live!"]
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/pws.png["Zipkin deployed on Pivotal Web Services", link="http://docssleuth-zipkin-server.cfapps.io/dependency", width=150, height=74]
|
||||
|
||||
|
||||
==== Log correlation
|
||||
@@ -117,7 +117,7 @@ If you're using a log aggregating tool like https://www.elastic.co/products/kiba
|
||||
http://www.splunk.com/[Splunk] etc. you can order the events that took place. An example of
|
||||
Kibana would look like this:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/kibana.png[Log correlation with Kibana]
|
||||
|
||||
If you want to use https://www.elastic.co/guide/en/logstash/current/index.html[Logstash] here is the Grok pattern for Logstash:
|
||||
|
||||
@@ -164,6 +164,49 @@ include::https://raw.githubusercontent.com/spring-cloud-samples/sleuth-documenta
|
||||
NOTE: If you're using a custom `logback-spring.xml` then you have to pass the `spring.application.name` in
|
||||
`bootstrap` instead of `application` property file. Otherwise your custom logback file won't read the property properly.
|
||||
|
||||
===== Propagating Span Context
|
||||
|
||||
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.
|
||||
|
||||
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 will understand that a header is baggage related if the HTTP
|
||||
header is prefixed with `baggage-` and for messaging it starts with `baggage_`.
|
||||
|
||||
IMPORTANT: There's 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, it could crash the app due
|
||||
to exceeding transport-level message or header capacity.
|
||||
|
||||
Example of setting span:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::{github-raw}/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java[tags=baggage,indent=0]
|
||||
----
|
||||
|
||||
====== Baggage vs. Span Tags
|
||||
|
||||
Baggage travels with the trace (i.e. every child span contains the baggage of its parent). Zipkin has no knowledge of
|
||||
baggage and will not even receive that information.
|
||||
|
||||
Tags are attached to a specific span - they are presented for that particular span only. However you
|
||||
can search by tag to find the trace, where there exists a span having the searched tag value.
|
||||
|
||||
If you want to be able to lookup a span based on baggage, you should add corresponding entry as a tag in the root span.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
Span span = tracer.getCurrentSpan();
|
||||
String baggageKey = "key";
|
||||
String baggageValue = "foo";
|
||||
span.setBaggageItem(baggageKey, baggageValue);
|
||||
tracer.addTag(baggageKey, baggageValue);
|
||||
----
|
||||
|
||||
|
||||
=== Adding to the project
|
||||
|
||||
==== Only Sleuth (log correlation)
|
||||
@@ -179,7 +222,7 @@ the `spring-cloud-starter-sleuth` module to your project.
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -200,7 +243,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +267,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-starter-zipkin` de
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -245,7 +288,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,7 +312,7 @@ If you want both Sleuth and Zipkin just add the `spring-cloud-sleuth-stream` dep
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -301,7 +344,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -331,7 +374,7 @@ dependency
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>Brixton.RELEASE</version>
|
||||
<version>Camden.RELEASE</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
@@ -363,7 +406,7 @@ the Spring BOM
|
||||
----
|
||||
dependencyManagement { <1>
|
||||
imports {
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Brixton.RELEASE"
|
||||
mavenBom "org.springframework.cloud:spring-cloud-dependencies:Camden.RELEASE"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,7 +427,7 @@ and then just annotate your main class with `@EnableZipkinStreamServer` annotati
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin-stream/src/main/java/example/ZipkinStreamServerApplication.java[]
|
||||
include::{github-raw}/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin-stream/src/main/java/example/ZipkinStreamServerApplication.java[]
|
||||
----
|
||||
|
||||
== Additional resources
|
||||
|
||||
@@ -28,13 +28,13 @@ import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Class for gathering and reporting statistics about a block of execution.
|
||||
* <p>
|
||||
@@ -73,7 +73,7 @@ import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
*/
|
||||
@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
|
||||
@JsonInclude(JsonInclude.Include.NON_DEFAULT)
|
||||
public class Span {
|
||||
public class Span implements SpanContext {
|
||||
|
||||
public static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
public static final String PROCESS_ID_NAME = "X-Process-Id";
|
||||
@@ -82,6 +82,7 @@ public class Span {
|
||||
public static final String SPAN_NAME_NAME = "X-Span-Name";
|
||||
public static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
public static final String SPAN_EXPORT_NAME = "X-Span-Export";
|
||||
public static final String SPAN_BAGGAGE_HEADER_PREFIX = "baggage";
|
||||
public static final Set<String> SPAN_HEADERS = new HashSet<>(
|
||||
Arrays.asList(SAMPLED_NAME, PROCESS_ID_NAME, PARENT_ID_NAME, TRACE_ID_NAME,
|
||||
SPAN_ID_NAME, SPAN_NAME_NAME, SPAN_EXPORT_NAME));
|
||||
@@ -146,6 +147,8 @@ public class Span {
|
||||
private final String processId;
|
||||
private final Collection<Log> logs;
|
||||
private final Span savedSpan;
|
||||
@JsonIgnore
|
||||
private final Map<String,String> baggage;
|
||||
|
||||
// Null means we don't know the start tick, so fallback to time
|
||||
@JsonIgnore
|
||||
@@ -176,6 +179,7 @@ public class Span {
|
||||
this.logs = current.logs;
|
||||
this.startNanos = current.startNanos;
|
||||
this.durationMicros = current.durationMicros;
|
||||
this.baggage = current.baggage;
|
||||
this.savedSpan = savedSpan;
|
||||
}
|
||||
|
||||
@@ -209,6 +213,7 @@ public class Span {
|
||||
this.savedSpan = savedSpan;
|
||||
this.tags = new ConcurrentHashMap<>();
|
||||
this.logs = new ConcurrentLinkedQueue<>();
|
||||
this.baggage = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
public static SpanBuilder builder() {
|
||||
@@ -304,6 +309,40 @@ public class Span {
|
||||
this.logs.add(new Log(System.currentTimeMillis(), event));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a baggage item in the Span (and its SpanContext) as a key/value pair.
|
||||
*
|
||||
* Baggage enables powerful distributed context propagation functionality where arbitrary application data can be
|
||||
* carried along the full path of request execution throughout the system.
|
||||
*
|
||||
* Note 1: Baggage is only propagated to the future (recursive) children of this SpanContext.
|
||||
*
|
||||
* Note 2: Baggage is sent in-band with every subsequent local and remote calls, so this feature must be used with
|
||||
* care.
|
||||
*
|
||||
* @return this Span instance, for chaining
|
||||
*/
|
||||
public Span setBaggageItem(String key, String value) {
|
||||
this.baggage.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the value of the baggage item identified by the given key, or null if no such item could be found
|
||||
*/
|
||||
public String getBaggageItem(String key) {
|
||||
return this.baggage.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterable<Map.Entry<String,String>> baggageItems() {
|
||||
return this.baggage.entrySet();
|
||||
}
|
||||
|
||||
public final Map<String,String> getBaggage() {
|
||||
return Collections.unmodifiableMap(this.baggage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tag data associated with this span (read only)
|
||||
* <p/>
|
||||
@@ -493,6 +532,7 @@ public class Span {
|
||||
private Span savedSpan;
|
||||
private List<Log> logs = new ArrayList<>();
|
||||
private Map<String, String> tags = new LinkedHashMap<>();
|
||||
private Map<String, String> baggage = new LinkedHashMap<>();
|
||||
|
||||
SpanBuilder() {
|
||||
}
|
||||
@@ -554,6 +594,16 @@ public class Span {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder baggage(String baggageKey, String baggageValue) {
|
||||
this.baggage.put(baggageKey, baggageValue);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder baggage(Map<String, String> baggage) {
|
||||
this.baggage.putAll(baggage);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Span.SpanBuilder spanId(long spanId) {
|
||||
this.spanId = spanId;
|
||||
return this;
|
||||
@@ -585,6 +635,7 @@ public class Span {
|
||||
this.processId, this.savedSpan);
|
||||
span.logs.addAll(this.logs);
|
||||
span.tags.putAll(this.tags);
|
||||
span.baggage.putAll(this.baggage);
|
||||
return span;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/0.16.0/opentracing-api/src/main/java/io/opentracing/SpanContext.java
|
||||
*
|
||||
* SpanContext represents Span state that must propagate to descendant Spans and across process boundaries.
|
||||
*
|
||||
* SpanContext is logically divided into two pieces: (1) the user-level "Baggage" that propagates across Span
|
||||
* boundaries and (2) any Tracer-implementation-specific fields that are needed to identify or otherwise contextualize
|
||||
* the associated Span instance (e.g., a <trace_id, span_id, sampled> tuple).
|
||||
*
|
||||
* The {@link SpanContext#baggageItems()} returns the map of user-level "Baggage".
|
||||
*
|
||||
* @see Span#setBaggageItem(String, String)
|
||||
* @see Span#getBaggageItem(String)
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.2.0
|
||||
*/
|
||||
public interface SpanContext {
|
||||
|
||||
/**
|
||||
* @return all zero or more baggage items propagating along with the associated Span
|
||||
*
|
||||
* @see Span#setBaggageItem(String, String)
|
||||
* @see Span#getBaggageItem(String)
|
||||
*/
|
||||
Iterable<Map.Entry<String, String>> baggageItems();
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/master/opentracing-api/src/main/java/io/opentracing/propagation/TextMap.java
|
||||
* Adopted from: https://github.com/opentracing/opentracing-java/blob/0.16.0/opentracing-api/src/main/java/io/opentracing/propagation/TextMap.java
|
||||
*
|
||||
* TextMap is a built-in carrier for {@link SpanInjector} and {@link SpanExtractor}. TextMap implementations allows Tracers to
|
||||
* read and write key:value String pairs from arbitrary underlying sources of data.
|
||||
|
||||
@@ -43,6 +43,11 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac
|
||||
}
|
||||
setParentIdIfApplicable(carrier, spanBuilder, TraceMessageHeaders.PARENT_ID_NAME);
|
||||
spanBuilder.remote(true);
|
||||
for (Map.Entry<String, String> entry : carrier.entrySet()) {
|
||||
if (entry.getKey().startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER)) {
|
||||
spanBuilder.baggage(unprefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return spanBuilder.build();
|
||||
}
|
||||
|
||||
@@ -58,4 +63,8 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac
|
||||
}
|
||||
}
|
||||
|
||||
private String unprefixedKey(String key) {
|
||||
return key.substring(key.indexOf(TraceMessageHeaders.HEADER_DELIMITER) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,6 +56,9 @@ public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjecto
|
||||
else {
|
||||
addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : span.baggageItems()) {
|
||||
textMap.put(prefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void addAnnotations(TraceKeys traceKeys, SpanTextMap spanTextMap, Span span) {
|
||||
@@ -99,4 +102,11 @@ public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjecto
|
||||
return parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
private String prefixedKey(String key) {
|
||||
if (key.startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER )) {
|
||||
return key;
|
||||
}
|
||||
return Span.SPAN_BAGGAGE_HEADER_PREFIX + TraceMessageHeaders.HEADER_DELIMITER + key;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ public class TraceMessageHeaders {
|
||||
public static final String SPAN_NAME_NAME = "spanName";
|
||||
|
||||
static final String MESSAGE_SENT_FROM_CLIENT = "messageSent";
|
||||
static final String HEADER_DELIMITER = "_";
|
||||
|
||||
private TraceMessageHeaders() {}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ public class ZipkinHttpSpanExtractor implements HttpSpanExtractor {
|
||||
|
||||
private static final org.apache.commons.logging.Log log = LogFactory.getLog(
|
||||
MethodHandles.lookup().lookupClass());
|
||||
private static final String HEADER_DELIMITER = "-";
|
||||
static final String URI_HEADER = "X-Span-Uri";
|
||||
private static final String HTTP_COMPONENT = "http";
|
||||
|
||||
@@ -83,7 +84,16 @@ public class ZipkinHttpSpanExtractor implements HttpSpanExtractor {
|
||||
if (skip) {
|
||||
span.exportable(false);
|
||||
}
|
||||
for (Map.Entry<String, String> entry : carrier.entrySet()) {
|
||||
if (entry.getKey().startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + HEADER_DELIMITER)) {
|
||||
span.baggage(unprefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
return span.build();
|
||||
}
|
||||
|
||||
private String unprefixedKey(String key) {
|
||||
return key.substring(key.indexOf(HEADER_DELIMITER) + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanTextMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -12,6 +14,8 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
public class ZipkinHttpSpanInjector implements HttpSpanInjector {
|
||||
|
||||
private static final String HEADER_DELIMITER = "-";
|
||||
|
||||
@Override
|
||||
public void inject(Span span, SpanTextMap carrier) {
|
||||
setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
@@ -20,22 +24,32 @@ public class ZipkinHttpSpanInjector implements HttpSpanInjector {
|
||||
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
|
||||
setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
for (Map.Entry<String, String> entry : span.baggageItems()) {
|
||||
carrier.put(prefixedKey(entry.getKey()), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private String prefixedKey(String key) {
|
||||
if (key.startsWith(Span.SPAN_BAGGAGE_HEADER_PREFIX + HEADER_DELIMITER)) {
|
||||
return key;
|
||||
}
|
||||
return Span.SPAN_BAGGAGE_HEADER_PREFIX + HEADER_DELIMITER + key;
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
private void setHeader(SpanTextMap carrier, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
carrier.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void setIdHeader(SpanTextMap carrier, String name, Long value) {
|
||||
if (value != null) {
|
||||
setHeader(carrier, name, Span.idToHex(value));
|
||||
}
|
||||
}
|
||||
|
||||
private void setHeader(SpanTextMap carrier, String name, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
carrier.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ public class DefaultTracer implements Tracer {
|
||||
return savedSpan;
|
||||
}
|
||||
|
||||
protected Span createChild(Span parent, String name) {
|
||||
Span createChild(Span parent, String name) {
|
||||
long id = createId();
|
||||
if (parent == null) {
|
||||
Span span = Span.builder().name(name)
|
||||
@@ -154,7 +154,9 @@ public class DefaultTracer implements Tracer {
|
||||
Span span = Span.builder().name(name)
|
||||
.traceId(parent.getTraceId()).parent(parent.getSpanId()).spanId(id)
|
||||
.processId(parent.getProcessId()).savedSpan(parent)
|
||||
.exportable(parent.isExportable()).build();
|
||||
.exportable(parent.isExportable())
|
||||
.baggage(parent.getBaggage())
|
||||
.build();
|
||||
this.spanLogger.logStartedSpan(parent, span);
|
||||
return span;
|
||||
}
|
||||
|
||||
@@ -51,4 +51,10 @@ public class ArrayListSpanAccumulator implements SpanReporter {
|
||||
this.spans.add(span);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
synchronized (this.spans) {
|
||||
this.spans.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.assertj.core.api.AbstractAssert;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -89,6 +88,26 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListOfSpansAssert everySpanHasABaggage(String baggageKey, String baggageValue) {
|
||||
isNotNull();
|
||||
printSpans();
|
||||
if (!everySpanHasBaggage(baggageKey, baggageValue)) {
|
||||
failWithMessage("Expected spans \n <%s> \nto ALL contain baggage with key "
|
||||
+ "equal to <%s>, and value equal to <%s>", spansToString(), baggageKey, baggageValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ListOfSpansAssert anySpanHasABaggage(String baggageKey, String baggageValue) {
|
||||
isNotNull();
|
||||
printSpans();
|
||||
if (!hasBaggage(baggageKey, baggageValue)) {
|
||||
failWithMessage("Expected spans \n <%s> \nto contain at least one span with baggage key "
|
||||
+ "equal to <%s>, and value equal to <%s>", spansToString(), baggageKey, baggageValue);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private boolean spanWithKeyTagExists(String tagKey) {
|
||||
for (Span span : this.actual.spans) {
|
||||
if (span.tags().containsKey(tagKey)) {
|
||||
@@ -98,6 +117,37 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean everySpanHasBaggage(String baggageKey, String baggageValue) {
|
||||
boolean exists = false;
|
||||
for (Span span : this.actual.spans) {
|
||||
for (Map.Entry<String, String> baggage : span.baggageItems()) {
|
||||
if (baggage.getKey().equals(baggageKey)) {
|
||||
if (baggage.getValue().equals(baggageValue)) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return exists;
|
||||
}
|
||||
|
||||
private boolean hasBaggage(String baggageKey, String baggageValue) {
|
||||
for (Span span : this.actual.spans) {
|
||||
for (Map.Entry<String, String> baggage : span.baggageItems()) {
|
||||
if (baggage.getKey().equals(baggageKey)) {
|
||||
if (baggage.getValue().equals(baggageValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public ListOfSpansAssert hasASpanWithTagEqualTo(String tagKey, String tagValue) {
|
||||
isNotNull();
|
||||
printSpans();
|
||||
@@ -113,7 +163,8 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
|
||||
private String spansToString() {
|
||||
return this.actual.spans.stream().map(span -> "\nSPAN: " + span.toString() + " with name [" + span.getName() + "] " +
|
||||
"\nwith tags " + span.tags() + "\nwith logs " + span.logs()).collect(joining("\n"));
|
||||
"\nwith tags " + span.tags() + "\nwith logs " + span.logs() +
|
||||
"\nwith baggage " + span.getBaggage()).collect(joining("\n"));
|
||||
}
|
||||
|
||||
public ListOfSpansAssert doesNotHaveASpanWithName(String name) {
|
||||
@@ -143,16 +194,12 @@ public class ListOfSpansAssert extends AbstractAssert<ListOfSpansAssert, ListOfS
|
||||
}
|
||||
|
||||
private void printSpans() {
|
||||
try {
|
||||
log.info("Stored spans " + this.objectMapper.writeValueAsString(new ArrayList<>(this.actual.spans)));
|
||||
}
|
||||
catch (JsonProcessingException e) {
|
||||
}
|
||||
log.info("Stored spans " + spansToString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void failWithMessage(String errorMessage, Object... arguments) {
|
||||
log.error(errorMessage);
|
||||
log.error(String.format(errorMessage, arguments));
|
||||
super.failWithMessage(errorMessage, arguments);
|
||||
}
|
||||
}
|
||||
@@ -99,6 +99,19 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanAssert hasBaggageItem(String baggageKey, String baggageValue) {
|
||||
isNotNull();
|
||||
assertThatBaggageContainsKey(baggageKey);
|
||||
String foundValue = this.actual.getBaggageItem(baggageKey);
|
||||
if (!foundValue.equals(baggageValue)) {
|
||||
String message = String.format("Expected span to have the baggage with key <%s> and value <%s>. "
|
||||
+ "Found value for that baggage is <%s>", baggageKey, baggageValue, foundValue);
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanAssert hasATagWithKey(String tagKey) {
|
||||
isNotNull();
|
||||
assertThatTagIsPresent(tagKey);
|
||||
@@ -134,6 +147,15 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertThatBaggageContainsKey(String baggageKey) {
|
||||
if (!this.actual.getBaggage().containsKey(baggageKey)) {
|
||||
String message = String.format("Expected span to have the baggage with key <%s>. "
|
||||
+ "Found baggage are <%s>", baggageKey, this.actual.getBaggage());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public SpanAssert hasLoggedAnEvent(String event) {
|
||||
isNotNull();
|
||||
if (!this.actual.logs().stream().map(org.springframework.cloud.sleuth.Log::getEvent)
|
||||
|
||||
@@ -1,51 +1,60 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.multiple;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceFilter;
|
||||
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static com.jayway.awaitility.Awaitility.await;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration(classes = MultipleHopsIntegrationTests.Config.class)
|
||||
public class MultipleHopsIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
@SpringBootTest(classes = MultipleHopsIntegrationTests.Config.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class MultipleHopsIntegrationTests {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired TraceKeys traceKeys;
|
||||
@Autowired TraceFilter traceFilter;
|
||||
@Autowired ArrayListSpanAccumulator arrayListSpanAccumulator;
|
||||
@Autowired SpanReporter spanReporter;
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(this.traceFilter);
|
||||
@Before
|
||||
public void setup() {
|
||||
this.arrayListSpanAccumulator.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_prepare_spans_for_export() throws Exception {
|
||||
this.mockMvc.perform(get("/greeting")).andExpect(
|
||||
MockMvcResultMatchers.status().isOk());
|
||||
this.restTemplate.getForObject("http://localhost:" + this.config.port + "/greeting", String.class);
|
||||
|
||||
await().atMost(5, SECONDS).until(() -> {
|
||||
then(this.arrayListSpanAccumulator.getSpans().stream().map(Span::getName)
|
||||
@@ -55,9 +64,46 @@ public class MultipleHopsIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
});
|
||||
}
|
||||
|
||||
// issue #237 - baggage
|
||||
@Test
|
||||
public void should_propagate_the_baggage() throws Exception {
|
||||
//tag::baggage[]
|
||||
Span initialSpan = this.tracer.createSpan("span");
|
||||
initialSpan.setBaggageItem("foo", "bar");
|
||||
//end::baggage[]
|
||||
|
||||
try {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.put("baggage-baz", Collections.singletonList("baz"));
|
||||
RequestEntity requestEntity = new RequestEntity(headers, HttpMethod.GET,
|
||||
URI.create("http://localhost:" + this.config.port + "/greeting"));
|
||||
this.restTemplate.exchange(requestEntity, String.class);
|
||||
|
||||
await().atMost(5, SECONDS).until(() -> {
|
||||
then(new ListOfSpans(this.arrayListSpanAccumulator.getSpans()))
|
||||
.everySpanHasABaggage("foo", "bar")
|
||||
.anySpanHasABaggage("baz", "baz");
|
||||
});
|
||||
} finally {
|
||||
this.tracer.close(initialSpan);
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@SpringBootApplication
|
||||
public static class Config {
|
||||
public static class Config implements
|
||||
ApplicationListener<EmbeddedServletContainerInitializedEvent> {
|
||||
int port;
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
|
||||
this.port = event.getEmbeddedServletContainer().getPort();
|
||||
}
|
||||
|
||||
@Bean
|
||||
RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean ArrayListSpanAccumulator arrayListSpanAccumulator() {
|
||||
return new ArrayListSpanAccumulator();
|
||||
|
||||
@@ -185,6 +185,38 @@ public class DefaultTracerTests {
|
||||
then(span).isEqualTo(continuedSpan);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPropagateBaggageFromParentToChild() {
|
||||
DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
this.spanNamer, this.spanLogger, this.spanReporter);
|
||||
Span parent = Span.builder().name(IMPORTANT_WORK_1).traceId(1L).spanId(1L)
|
||||
.baggage("foo", "bar").build();
|
||||
Span child = tracer.createSpan("child", parent);
|
||||
|
||||
then(parent).hasBaggageItem("foo", "bar");
|
||||
then(child).hasBaggageItem("foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPropagateBaggageToContinuedSpan() {
|
||||
DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
this.spanNamer, this.spanLogger, this.spanReporter);
|
||||
Span parent = Span.builder().name(IMPORTANT_WORK_1).traceId(1L).spanId(1L)
|
||||
.baggage("foo", "bar").build();
|
||||
Span continuedSpan = tracer.continueSpan(parent);
|
||||
|
||||
parent.setBaggageItem("baz1", "baz1");
|
||||
continuedSpan.setBaggageItem("baz2", "baz2");
|
||||
|
||||
then(parent).hasBaggageItem("foo", "bar")
|
||||
.hasBaggageItem("baz1", "baz1")
|
||||
.hasBaggageItem("baz2", "baz2");
|
||||
then(continuedSpan).hasBaggageItem("foo", "bar")
|
||||
.hasBaggageItem("baz1", "baz1")
|
||||
.hasBaggageItem("baz2", "baz2");
|
||||
then(parent).isEqualTo(continuedSpan);
|
||||
}
|
||||
|
||||
private Span assertSpan(List<Span> spans, Long parentId, String name) {
|
||||
List<Span> found = findSpans(spans, parentId);
|
||||
assertThat(found).as("More than one span with parentId %s", parentId).hasSize(1);
|
||||
|
||||
@@ -27,7 +27,7 @@ The Ribbon sample makes an interesting demo or playground for learning about zip
|
||||
|
||||
NOTE: You can see the zipkin spans without the UI (in logs) if you run the sample with `sample.zipkin.enabled=false`.
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-trace-screenshot.png[Sample Zipkin Screenshot]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/zipkin-trace-screenshot.png[Sample Zipkin Screenshot]
|
||||
|
||||
> The fact that the first trace in says "testSleuthMessaging" seems to be a bug in the UI (it has some annotations from that service, but it originates in the "testSleuthRibbon" service).
|
||||
|
||||
@@ -42,4 +42,4 @@ Instead of POSTing trace data directly to a Zipkin server, you can export them o
|
||||
|
||||
The UI should look more or less like this:
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-traces.png[Zipkin Web Screenshot]
|
||||
image::{github-raw}/docs/src/main/asciidoc/images/zipkin-traces.png[Zipkin Web Screenshot]
|
||||
|
||||
Reference in New Issue
Block a user