diff --git a/docs/src/main/asciidoc/README.adoc b/docs/src/main/asciidoc/README.adoc
index 41f433d4e..ce1efe02d 100644
--- a/docs/src/main/asciidoc/README.adoc
+++ b/docs/src/main/asciidoc/README.adoc
@@ -6,20 +6,15 @@ include::features.adoc[]
== Running the samples
-1. Optionally run https://github.com/openzipkin/zipkin[Zipkin], e.g. via docker compose (there's a `docker-compose.yml` in https://github.com/spring-cloud-incubator/spring-cloud-sleuth[Spring Cloud Sleuth], or in https://github.com/openzipkin/docker-zipkin[Docker Zipkin]
-7. Run the zipkin sample application (and set `sample.zipkin.enabled=false` if you have no collector running)
-8. Hit `http://localhost:3380`, `http://localhost:3380/call`, `http://localhost:3380/async` for some interesting sample traces (the app callas back to itself).
-9. Goto `http://localhost:8082` for zipkin web (8080 if running locally from source, the host is the docker host, so if you are using boot2docker it will be different)
-
-WARNING: The docker images for zipkin are old and don't work very well (the UI in particular). Zipkin is in a state of flux, but it should settle down soon when there is an actual release. Best results actually come from building from source and running the jar files (the query and collector services need command line arguments, so check the zipkin README for updates).
-
-NOTE: You can see the zipkin spans without the UI (in logs) if you run the sample with `sample.zipkin.enabled=false`.
-
-There are a few samples with slightly different features:
+There are a few samples with slightly different features. You can run all of them from an IDE via the main method, or on the command line with `mvn spring-boot:run`. They all log trace and span data on the console by default. Here's a list:
* `spring-cloud-sleuth-sample`: vanilla (no zipkin) web app that calls back to itself on various endpoints ("/", "/call", "/async")
-* `spring-cloud-sleuth-sample-zipkin`: same as vanilla sample but with zipkin
+* `spring-cloud-sleuth-sample-zipkin`: same as vanilla sample but with zipkin (set `sample.zipkin.enabled=true` if you have a collector running)
+
+* `spring-cloud-sleuth-sample-stream`: same as vanilla sample, but exports span data to RabbitMQ using Spring Cloud Stream
+
+* `spring-cloud-sleuth-sample-stream-zipkin`: a consumer for the span data on RabbitMQ that pushes it into a Zipkin span store, so it can be queried and visualized using the standard Zipkin UI.
* `spring-cloud-sleuth-sample-messaging`: a Spring Integration application with two HTTP endpoints ("/" and "/xform")
@@ -27,10 +22,29 @@ There are a few samples with slightly different features:
The Ribbon sample makes an interesting demo or playground for learning about zipkin. In the screenshot below you can see a trace with 3 spans - it starts in the "testSleuthRibbon" app and crosses to "testSleuthMessaging" for the next 2 spans.
+=== Running samples with Zipkin
+
+1. Optionally run the https://github.com/openzipkin/zipkin[Zipkin] UI, e.g. via docker compose (there's a `docker-compose.yml` in https://github.com/spring-cloud/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin[Spring Cloud Sleuth], or in https://github.com/openzipkin/docker-zipkin[Docker Zipkin]
+7. Run the zipkin sample application (set `sample.zipkin.enabled=false` if you have no Zipkin running). If you are using a VM to run docker you might need to tunnel port 9410 to localhost, or change the `spring.zipkin.host`.
+8. Hit `http://localhost:3380`, `http://localhost:3380/call`, `http://localhost:3380/async` for some interesting sample traces (the app callas back to itself).
+9. Goto `http://localhost:8080` for zipkin web (if you are using boot2docker the host will be different)
+
+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[Eample 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).
+=== Running samples with Zipkin Stream
+
+Instead of using the native (Scala) Zipkin collector server you can export your span data over https://raw.githubusercontent.com/spring-cloud/spring-cloud-stream[Spring Cloud Stream].
+
+1. Build the Zipkin Stream sample with Maven and run it via its `docker-compose.yml` (which also starts the required middleware and the Zipkin UI).
+7. Run the `spring-cloud-sleuth-sample-stream` app and interact with it in a browser, just like the vanilla sample. If you are using a VM to run docker you might need to tunnel port 5672 to localhost, or change the `spring.rabbbitmq.host`.
+9. Goto `http://localhost:8080` for zipkin web (if you are using a VM to run docker the host will be different).
+
+The UI should look like the screenshot above.
+
== Building
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building.adoc[]
diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc
index 89c5c068a..09ccab7a5 100644
--- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc
+++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc
@@ -11,14 +11,19 @@ include::features.adoc[]
== Sampling
In distributed tracing the data volumes can be very high so sampling
-is important (you usually don't need to trace all requests to get a
+can be important (you usually don't need to export all spans to get a
good picture of what is happening). Spring Cloud Sleuth has a
`Sampler` strategy that you can implement to take control of the
sampling algorithm. By default you get a strategy that continues to
-trace if a span is already active, but never starts a new one. If all
-your apps run with this sampler you will see no traces, so it's best
-to install your own strategy. For testing there is an `AlwaysSampler`
-that traces everything, which can be installed just by creating a bean definition:
+trace if a span is already active, but never starts a new one with the
+exportable flag set. If all your apps run with this sampler you will
+see traces in logs, but not in any remote store. For testing the
+default is often enough, and it probably is all you need if you are
+only using the logs (e.g. with an ELK aggregator). If you are
+exporting span data to Zipkin or Spring Cloud Stream, there is also an
+`AlwaysSampler` that exports everything.
+
+A sampler can be installed just by creating a bean definition, e.g:
[source,java]
----
@@ -28,7 +33,7 @@ public Sampler> defaultSampler() {
}
----
-== Spans Data as Messages
+== Span Data as Messages
You can accumulate and send span data over
http://cloud.spring.io/spring-cloud-stream[Spring Cloud Stream] by
diff --git a/spring-cloud-sleuth-samples/pom.xml b/spring-cloud-sleuth-samples/pom.xml
index a0cdb690f..2d4a635c3 100644
--- a/spring-cloud-sleuth-samples/pom.xml
+++ b/spring-cloud-sleuth-samples/pom.xml
@@ -21,6 +21,7 @@
spring-cloud-sleuth-sample-messaging
spring-cloud-sleuth-sample-ribbon
spring-cloud-sleuth-sample-zipkin
+ spring-cloud-sleuth-sample-stream
spring-cloud-sleuth-sample-zipkin-stream
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/pom.xml b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/pom.xml
new file mode 100644
index 000000000..1f7a0a7c6
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/pom.xml
@@ -0,0 +1,78 @@
+
+
+ 4.0.0
+
+ spring-cloud-sleuth-sample-stream
+ jar
+ spring-cloud-sleuth-sample-stream
+ Spring Cloud Sleuth Sample
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-samples
+ 1.0.0.BUILD-SNAPSHOT
+ ..
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+
+ maven-deploy-plugin
+
+ true
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.cloud
+ spring-cloud-sleuth-stream
+
+
+ org.springframework.integration
+ spring-integration-jmx
+
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-rabbit
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+ org.projectlombok
+ lombok
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleBackground.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleBackground.java
new file mode 100644
index 000000000..d7d1fa6d7
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleBackground.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample;
+
+import java.util.Random;
+
+import lombok.SneakyThrows;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.sleuth.TraceManager;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author Spencer Gibb
+ */
+@Component
+public class SampleBackground {
+
+ @Autowired
+ private TraceManager traceManager;
+
+ @SneakyThrows
+ @Async
+ public void background() {
+ final Random random = new Random();
+ int millis = random.nextInt(1000);
+ Thread.sleep(millis);
+ this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));
+ }
+
+}
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleController.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleController.java
new file mode 100644
index 000000000..3183d56ec
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleController.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2013-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample;
+
+import java.util.Random;
+import java.util.concurrent.Callable;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceAccessor;
+import org.springframework.cloud.sleuth.TraceManager;
+import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
+import org.springframework.context.ApplicationListener;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author Spencer Gibb
+ */
+@Slf4j
+@RestController
+public class SampleController implements
+ApplicationListener {
+ @Autowired
+ private RestTemplate restTemplate;
+ @Autowired
+ private TraceManager traceManager;
+ @Autowired
+ private TraceAccessor accessor;
+ @Autowired
+ private SampleBackground controller;
+ private int port;
+
+ @SneakyThrows
+ @RequestMapping("/")
+ public String hi() {
+ final Random random = new Random();
+ Thread.sleep(random.nextInt(1000));
+
+ String s = this.restTemplate.getForObject("http://localhost:" + this.port
+ + "/hi2", String.class);
+ return "hi/" + s;
+ }
+
+ @RequestMapping("/call")
+ public Callable call() {
+ return new Callable() {
+ @Override
+ public String call() throws Exception {
+ final Random random = new Random();
+ int millis = random.nextInt(1000);
+ Thread.sleep(millis);
+ SampleController.this.traceManager.addAnnotation("callable-sleep-millis", String.valueOf(millis));
+ Span currentSpan = SampleController.this.accessor.getCurrentSpan();
+ return "async hi: " + currentSpan;
+ }
+ };
+ }
+
+ @RequestMapping("/async")
+ public String async() {
+ this.controller.background();
+ return "ho";
+ }
+
+ @SneakyThrows
+ @RequestMapping("/hi2")
+ public String hi2() {
+ final Random random = new Random();
+ int millis = random.nextInt(1000);
+ Thread.sleep(millis);
+ this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
+ return "hi2";
+ }
+
+ @SneakyThrows
+ @RequestMapping("/traced")
+ public String traced() {
+ Trace trace = this.traceManager.startSpan("customTraceEndpoint",
+ new AlwaysSampler(), null);
+ final Random random = new Random();
+ int millis = random.nextInt(1000);
+ log.info("Sleeping for {} millis", millis);
+ Thread.sleep(millis);
+ this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
+
+ String s = this.restTemplate.getForObject("http://localhost:" + this.port
+ + "/call", String.class);
+ this.traceManager.close(trace);
+ return "traced/" + s;
+ }
+
+ @SneakyThrows
+ @RequestMapping("/start")
+ public String start() {
+ final Random random = new Random();
+ int millis = random.nextInt(1000);
+ log.info("Sleeping for {} millis", millis);
+ Thread.sleep(millis);
+ this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
+
+ String s = this.restTemplate.getForObject("http://localhost:" + this.port
+ + "/call", String.class);
+ return "start/" + s;
+ }
+
+ @Override
+ public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
+ this.port = event.getEmbeddedServletContainer().getPort();
+ }
+}
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleSleuthApplication.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleSleuthApplication.java
new file mode 100644
index 000000000..8312ad842
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/java/sample/SampleSleuthApplication.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2013-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package sample;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.sleuth.Sampler;
+import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableAsync;
+
+/**
+ * @author Spencer Gibb
+ */
+@SpringBootApplication
+@EnableAspectJAutoProxy(proxyTargetClass = true)
+@EnableAsync
+public class SampleSleuthApplication {
+
+ public static final String CLIENT_NAME = "testApp";
+
+ @Bean
+ public Sampler> defaultSampler() {
+ return new AlwaysSampler();
+ }
+
+ @Bean
+ public SampleController sampleController() {
+ return new SampleController();
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(SampleSleuthApplication.class, args);
+ }
+
+}
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/resources/application.yml b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/resources/application.yml
new file mode 100644
index 000000000..4fa8dc06f
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/main/resources/application.yml
@@ -0,0 +1,14 @@
+server:
+ port: 3379
+
+spring:
+ application:
+ name: testSleuthApp
+ sleuth:
+ log:
+ json:
+ enabled: true
+
+logging:
+ pattern:
+ level: '%clr([trace=%X{X-Trace-Id:-},span=%X{X-Span-Id:-}]){yellow} %5p'
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/test/java/sample/SampleSleuthApplicationTests.java b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/test/java/sample/SampleSleuthApplicationTests.java
new file mode 100644
index 000000000..0b9c935b1
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-stream/src/test/java/sample/SampleSleuthApplicationTests.java
@@ -0,0 +1,18 @@
+package sample;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = SampleSleuthApplication.class)
+@WebAppConfiguration
+public class SampleSleuthApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/docker-compose.yml b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/docker-compose.yml
new file mode 100644
index 000000000..96412aba0
--- /dev/null
+++ b/spring-cloud-sleuth-samples/spring-cloud-sleuth-sample-zipkin/docker-compose.yml
@@ -0,0 +1,39 @@
+mysql:
+ image: openzipkin/zipkin-mysql:1.26.0
+ ports:
+ - 3306:3306
+collector:
+ image: openzipkin/zipkin-collector:1.26.0
+ environment:
+ - TRANSPORT_TYPE=scribe
+ - STORAGE_TYPE=mysql
+ expose:
+ - 9410
+ ports:
+ - 9410:9410
+ - 9900:9900
+ links:
+ - mysql:storage
+query:
+ image: openzipkin/zipkin-query:1.26.0
+ environment:
+ # Remove TRANSPORT_TYPE to disable tracing
+ - TRANSPORT_TYPE=http
+ - STORAGE_TYPE=mysql
+ expose:
+ - 9411
+ ports:
+ - 9411:9411
+ - 9901:9901
+ links:
+ - mysql:storage
+web:
+ image: openzipkin/zipkin-web:1.26.0
+ environment:
+ # Remove TRANSPORT_TYPE to disable tracing
+ - TRANSPORT_TYPE=http
+ ports:
+ - 8080:8080
+ - 9990:9990
+ links:
+ - query