diff --git a/README.adoc b/README.adoc
index 964988f2e..556fce901 100644
--- a/README.adoc
+++ b/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.
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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.
diff --git a/docs/src/main/asciidoc/README.adoc b/docs/src/main/asciidoc/README.adoc
index 388990e86..f0f404eba 100644
--- a/docs/src/main/asciidoc/README.adoc
+++ b/docs/src/main/asciidoc/README.adoc
@@ -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"]
diff --git a/docs/src/main/asciidoc/features.adoc b/docs/src/main/asciidoc/features.adoc
index f702a5058..44f81daa2 100644
--- a/docs/src/main/asciidoc/features.adoc
+++ b/docs/src/main/asciidoc/features.adoc
@@ -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.
diff --git a/docs/src/main/asciidoc/intro.adoc b/docs/src/main/asciidoc/intro.adoc
index c11a21da8..8c118fcea 100644
--- a/docs/src/main/asciidoc/intro.adoc
+++ b/docs/src/main/asciidoc/intro.adoc
@@ -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.
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
org.springframework.cloud
spring-cloud-dependencies
- Brixton.RELEASE
+ Camden.RELEASE
pom
import
@@ -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
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java
index e44e6d44a..25fdbd1e5 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java
@@ -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.
*
@@ -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 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 logs;
private final Span savedSpan;
+ @JsonIgnore
+ private final Map 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> baggageItems() {
+ return this.baggage.entrySet();
+ }
+
+ public final Map getBaggage() {
+ return Collections.unmodifiableMap(this.baggage);
+ }
+
/**
* Get tag data associated with this span (read only)
*
@@ -493,6 +532,7 @@ public class Span {
private Span savedSpan;
private List logs = new ArrayList<>();
private Map tags = new LinkedHashMap<>();
+ private Map 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 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;
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanContext.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanContext.java
new file mode 100644
index 000000000..9752d0b00
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanContext.java
@@ -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 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> baggageItems();
+}
\ No newline at end of file
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java
index 54e77e2d2..3fed538fb 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java
@@ -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.
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java
index 7d1349af1..a2c0d14c0 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java
@@ -43,6 +43,11 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac
}
setParentIdIfApplicable(carrier, spanBuilder, TraceMessageHeaders.PARENT_ID_NAME);
spanBuilder.remote(true);
+ for (Map.Entry 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);
+ }
+
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java
index f5e840d8a..b74a8e4b2 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java
@@ -56,6 +56,9 @@ public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjecto
else {
addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
}
+ for (Map.Entry 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;
+ }
+
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java
index 4ed97a6b1..b5e123266 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java
@@ -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() {}
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java
index d4e04865c..5f8aceffe 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java
@@ -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 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);
+ }
+
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java
index 5313950fd..9b05ca17d 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java
@@ -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 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);
+ }
+ }
+
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java
index e1bc938b6..56bb4cefe 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java
@@ -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;
}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ArrayListSpanAccumulator.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ArrayListSpanAccumulator.java
index 2a412d167..6636b9f58 100644
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ArrayListSpanAccumulator.java
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/ArrayListSpanAccumulator.java
@@ -51,4 +51,10 @@ public class ArrayListSpanAccumulator implements SpanReporter {
this.spans.add(span);
}
}
+
+ public void clear() {
+ synchronized (this.spans) {
+ this.spans.clear();
+ }
+ }
}
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/ListOfSpansAssert.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/ListOfSpansAssert.java
index 06fb23942..a81a5cd14 100644
--- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/ListOfSpansAssert.java
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/ListOfSpansAssert.java
@@ -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 \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 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 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 "\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(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);
}
}
\ No newline at end of file
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java
index f0f5bd5ef..e0d942cea 100644
--- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/assertions/SpanAssert.java
@@ -99,6 +99,19 @@ public class SpanAssert extends AbstractAssert {
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 {
}
}
+ 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)
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java
index 6ce92052e..70e2b042f 100644
--- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java
@@ -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 {
+ 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();
diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java
index 74058665e..207b7564c 100644
--- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java
+++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java
@@ -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 spans, Long parentId, String name) {
List found = findSpans(spans, parentId);
assertThat(found).as("More than one span with parentId %s", parentId).hasSize(1);
diff --git a/spring-cloud-sleuth-samples/README.adoc b/spring-cloud-sleuth-samples/README.adoc
index 3dc8573a0..936904d5c 100644
--- a/spring-cloud-sleuth-samples/README.adoc
+++ b/spring-cloud-sleuth-samples/README.adoc
@@ -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]