Add ribbon/zuul features and docs plus a sample
This commit is contained in:
29
README.adoc
29
README.adoc
@@ -13,9 +13,6 @@ Spans are started and stopped, and they keep track of their timing information.
|
||||
*Trace:* A set of spans forming a tree-like structure. For example, if you are running a distributed big-data store, a trace might be formed by a put request.
|
||||
|
||||
|
||||
|
||||
== This is a Developer Preview
|
||||
|
||||
== Features
|
||||
|
||||
* Adds trace and span ids to the Slf4J MDC, so you can extract all the logs from a given trace or span in a log aggregator. Example configuration:
|
||||
@@ -29,22 +26,40 @@ logging:
|
||||
+
|
||||
(notice the `%X` entries from the MDC).
|
||||
|
||||
* Optionally log span data in JSON format for harvesting in a log aggregator (set `spring.sleuth.log.json.enabled=true`).
|
||||
|
||||
* Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations, key-value annotations. Loosely based on HTrace, but Zipkin (Dapper) compatible.
|
||||
|
||||
* Instruments common ingress and egress points from Spring applications (servlet filter, rest template, scheduled actions).
|
||||
* Instruments common ingress and egress points from Spring applications (servlet filter, rest template, scheduled actions, message channels, zuul filters, feign client).
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces (using Brave). By default it sends them via Thrift to a Zipkin collector service on localhost (port 9410). Configure the location of the service using `spring.zipkin.[host,port]`.
|
||||
|
||||
== Running the sample
|
||||
== 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 sample application
|
||||
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 `sleuth.sample.logging.collector.enabled=true`.
|
||||
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:
|
||||
|
||||
* `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-messaging`: a Spring Integration application with two HTTP endpoints ("/" and "/xform")
|
||||
|
||||
* `spring-cloud-sleuth-sample-ribbon`: two endpoints ("/" and "/call") that make calls to the "zipkin" sample via Ribbon. Also has `@EnableZUulProxy" so if the other samples are running they are proxied at "/messaging", "/zipkin", "/vanilla" (see "/routes" for a list).
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
== Building
|
||||
|
||||
|
||||
@@ -2,8 +2,6 @@ image::https://api.travis-ci.org/spring-cloud/spring-cloud-sleuth.svg?branch=mas
|
||||
|
||||
include::intro.adoc[]
|
||||
|
||||
== This is a Developer Preview
|
||||
|
||||
== Features
|
||||
|
||||
* Adds trace and span ids to the Slf4J MDC, so you can extract all the logs from a given trace or span in a log aggregator. Example configuration:
|
||||
@@ -17,22 +15,40 @@ logging:
|
||||
+
|
||||
(notice the `%X` entries from the MDC).
|
||||
|
||||
* Optionally log span data in JSON format for harvesting in a log aggregator (set `spring.sleuth.log.json.enabled=true`).
|
||||
|
||||
* Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations, key-value annotations. Loosely based on HTrace, but Zipkin (Dapper) compatible.
|
||||
|
||||
* Instruments common ingress and egress points from Spring applications (servlet filter, rest template, scheduled actions).
|
||||
* Instruments common ingress and egress points from Spring applications (servlet filter, rest template, scheduled actions, message channels, zuul filters, feign client).
|
||||
|
||||
* If `spring-cloud-sleuth-zipkin` then the app will generate and collect Zipkin-compatible traces (using Brave). By default it sends them via Thrift to a Zipkin collector service on localhost (port 9410). Configure the location of the service using `spring.zipkin.[host,port]`.
|
||||
|
||||
== Running the sample
|
||||
== 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 sample application
|
||||
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 `sleuth.sample.logging.collector.enabled=true`.
|
||||
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:
|
||||
|
||||
* `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-messaging`: a Spring Integration application with two HTTP endpoints ("/" and "/xform")
|
||||
|
||||
* `spring-cloud-sleuth-sample-ribbon`: two endpoints ("/" and "/call") that make calls to the "zipkin" sample via Ribbon. Also has `@EnableZUulProxy" so if the other samples are running they are proxied at "/messaging", "/zipkin", "/vanilla" (see "/routes" for a list).
|
||||
|
||||
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.
|
||||
|
||||
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).
|
||||
|
||||
== Building
|
||||
|
||||
|
||||
BIN
docs/src/main/asciidoc/images/zipkin-trace-screenshot.png
Normal file
BIN
docs/src/main/asciidoc/images/zipkin-trace-screenshot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 233 KiB |
@@ -48,10 +48,14 @@
|
||||
<artifactId>spring-cloud-starter-feign</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zuul</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.integration</groupId>
|
||||
<artifactId>spring-integration-core</artifactId>
|
||||
<version>${spring-integration.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
||||
@@ -56,6 +56,7 @@ public interface Trace {
|
||||
String SPAN_NAME_NAME = "X-Span-Name";
|
||||
String PARENT_ID_NAME = "X-Parent-Id";
|
||||
String PROCESS_ID_NAME = "X-Process-Id";
|
||||
String NOT_SAMPLED_NAME = "X-Not-Sampled";
|
||||
|
||||
/**
|
||||
* Creates a trace scope wrapping a new span.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
|
||||
@@ -37,7 +38,11 @@ import org.springframework.messaging.Message;
|
||||
public class SpanMessageHeaders {
|
||||
|
||||
public static Message<?> addSpanHeaders(Message<?> message, Span span) {
|
||||
if (span==null) {
|
||||
if (span == null) {
|
||||
if (!message.getHeaders().containsKey(NOT_SAMPLED_NAME)) {
|
||||
return MessageBuilder.fromMessage(message)
|
||||
.setHeader(NOT_SAMPLED_NAME, "").build();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
Map<String, String> headers = new HashMap<String, String>();
|
||||
@@ -50,14 +55,13 @@ public class SpanMessageHeaders {
|
||||
}
|
||||
|
||||
private static void addHeader(Map<String, String> headers, String name, String value) {
|
||||
if (value!=null) {
|
||||
if (value != null) {
|
||||
headers.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFirst(List<String> parents) {
|
||||
return parents==null || parents.isEmpty() ? null : parents.get(0);
|
||||
return parents == null || parents.isEmpty() ? null : parents.get(0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
|
||||
@@ -47,8 +48,10 @@ public class TraceChannelInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
if (TraceContextHolder.isTracing()) {
|
||||
return SpanMessageHeaders.addSpanHeaders(message, TraceContextHolder.getCurrentSpan());
|
||||
if (TraceContextHolder.isTracing()
|
||||
|| message.getHeaders().containsKey(NOT_SAMPLED_NAME)) {
|
||||
return SpanMessageHeaders.addSpanHeaders(message,
|
||||
TraceContextHolder.getCurrentSpan());
|
||||
}
|
||||
String spanId = getHeader(message, SPAN_ID_NAME);
|
||||
String traceId = getHeader(message, TRACE_ID_NAME);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
|
||||
@@ -82,13 +83,17 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
throws ServletException, IOException {
|
||||
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(request);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches();
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| getHeader(request, response, NOT_SAMPLED_NAME) != null;
|
||||
|
||||
TraceScope traceScope = (TraceScope) request.getAttribute(TRACE_REQUEST_ATTR);
|
||||
if (traceScope != null) {
|
||||
this.trace.continueSpan(traceScope.getSpan());
|
||||
}
|
||||
else if (!skip) {
|
||||
else if (skip) {
|
||||
addToResponseIfNotPresent(response, NOT_SAMPLED_NAME, "");
|
||||
}
|
||||
else {
|
||||
String spanId = getHeader(request, response, SPAN_ID_NAME);
|
||||
String traceId = getHeader(request, response, TRACE_ID_NAME);
|
||||
String name = "http" + uri;
|
||||
@@ -133,7 +138,7 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
}
|
||||
finally {
|
||||
if (isAsyncStarted(request) || request.isAsyncStarted()) {
|
||||
//TODO: how to deal with response annotations and async?
|
||||
// TODO: how to deal with response annotations and async?
|
||||
return;
|
||||
}
|
||||
if (traceScope != null) {
|
||||
@@ -145,11 +150,10 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
|
||||
protected void addRequestAnnotations(HttpServletRequest request) {
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(request);
|
||||
this.trace.addKVAnnotation("/http/request/uri",
|
||||
request.getRequestURL().toString());
|
||||
this.trace.addKVAnnotation("/http/request/uri", request.getRequestURL()
|
||||
.toString());
|
||||
this.trace.addKVAnnotation("/http/request/endpoint", uri);
|
||||
this.trace.addKVAnnotation("/http/request/method",
|
||||
request.getMethod());
|
||||
this.trace.addKVAnnotation("/http/request/method", request.getMethod());
|
||||
|
||||
Enumeration<String> headerNames = request.getHeaderNames();
|
||||
while (headerNames.hasMoreElements()) {
|
||||
@@ -157,7 +161,7 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
Enumeration<String> values = request.getHeaders(name);
|
||||
while (values.hasMoreElements()) {
|
||||
String value = values.nextElement();
|
||||
String key = "/http/request/headers/"+name.toLowerCase();
|
||||
String key = "/http/request/headers/" + name.toLowerCase();
|
||||
this.trace.addKVAnnotation(key, value);
|
||||
|
||||
}
|
||||
@@ -170,7 +174,7 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
|
||||
for (String name : response.getHeaderNames()) {
|
||||
for (String value : response.getHeaders(name)) {
|
||||
String key = "/http/response/headers/"+name.toLowerCase();
|
||||
String key = "/http/response/headers/" + name.toLowerCase();
|
||||
this.trace.addKVAnnotation(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
|
||||
@@ -61,6 +62,7 @@ ApplicationEventPublisherAware {
|
||||
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
if (getCurrentSpan() == null) {
|
||||
setHeader(request, NOT_SAMPLED_NAME, "");
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
setHeader(request, SPAN_ID_NAME, getCurrentSpan().getSpanId());
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 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 org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_NAME_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.TraceContextHolder.getCurrentSpan;
|
||||
import static org.springframework.cloud.sleuth.TraceContextHolder.isTracing;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.event.ClientSentEvent;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class TracePreZuulFilter extends ZuulFilter implements
|
||||
ApplicationEventPublisherAware {
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Override
|
||||
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
|
||||
this.publisher = publisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
Map<String, String> response = ctx.getZuulRequestHeaders();
|
||||
// N.B. this will only work with the simple host filter (not ribbon) unless you set hystrix.execution.isolation.strategy=SEMAPHORE
|
||||
if (getCurrentSpan() == null) {
|
||||
setHeader(response, NOT_SAMPLED_NAME, "");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
setHeader(response, SPAN_ID_NAME, getCurrentSpan().getSpanId());
|
||||
setHeader(response, TRACE_ID_NAME, getCurrentSpan().getTraceId());
|
||||
setHeader(response, SPAN_NAME_NAME, getCurrentSpan().getName());
|
||||
setHeader(response, PARENT_ID_NAME, getParentId(getCurrentSpan()));
|
||||
setHeader(response, PROCESS_ID_NAME, getCurrentSpan().getProcessId());
|
||||
publish(new ClientSentEvent(this, getCurrentSpan()));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
ReflectionUtils.rethrowRuntimeException(ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getParentId(Span span) {
|
||||
return span.getParents() != null && !span.getParents().isEmpty() ? span
|
||||
.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
public void setHeader(Map<String, String> request, String name, String value) {
|
||||
if (value != null && !request.containsKey(name) && isTracing()) {
|
||||
request.put(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterType() {
|
||||
return "pre";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void publish(ApplicationEvent event) {
|
||||
if (this.publisher != null) {
|
||||
this.publisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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 org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
|
||||
/**
|
||||
* Registers beans that add tracing to requests
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "spring.sleuth.zuul.enabled", matchIfMissing = true)
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(ZuulFilter.class)
|
||||
@ConditionalOnBean(Trace.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
public class TraceZuulAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TracePreZuulFilter traceZuulFilter() {
|
||||
return new TracePreZuulFilter();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,4 +8,5 @@ org.springframework.cloud.sleuth.instrument.async.AsyncDefaultAutoConfiguration,
|
||||
org.springframework.cloud.sleuth.instrument.scheduling.TraceSchedulingAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.web.client.TraceWebClientAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.web.client.TraceFeignClientAutoConfiguration
|
||||
org.springframework.cloud.sleuth.instrument.web.client.TraceFeignClientAutoConfiguration,\
|
||||
org.springframework.cloud.sleuth.instrument.zuul.TraceZuulAutoConfiguration
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
|
||||
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
|
||||
|
||||
@@ -48,7 +50,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes=App.class)
|
||||
@SpringApplicationConfiguration(classes = App.class)
|
||||
@IntegrationTest
|
||||
@DirtiesContext
|
||||
public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
@@ -78,6 +80,16 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
this.channel.unsubscribe(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoSpanCreation() {
|
||||
this.channel.send(MessageBuilder.withPayload("hi").setHeader(NOT_SAMPLED_NAME, "")
|
||||
.build());
|
||||
assertNotNull("message was null", this.message);
|
||||
|
||||
String spanId = this.message.getHeaders().get(SPAN_ID_NAME, String.class);
|
||||
assertNull("spanId was not null", spanId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpanCreation() {
|
||||
this.channel.send(MessageBuilder.withPayload("hi").build());
|
||||
@@ -92,7 +104,8 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
|
||||
@Test
|
||||
public void testHeaderCreation() {
|
||||
TraceScope traceScope = this.trace.startSpan("testSendMessage", new AlwaysSampler(), null);
|
||||
TraceScope traceScope = this.trace.startSpan("testSendMessage",
|
||||
new AlwaysSampler(), null);
|
||||
this.channel.send(MessageBuilder.withPayload("hi").build());
|
||||
traceScope.close();
|
||||
assertNotNull("message was null", this.message);
|
||||
|
||||
@@ -88,7 +88,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
public Map<String, String> home(@RequestHeader HttpHeaders headers) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
addHeaders(map, headers, Trace.SPAN_ID_NAME, Trace.TRACE_ID_NAME,
|
||||
Trace.PARENT_ID_NAME, Trace.SPAN_NAME_NAME, Trace.PROCESS_ID_NAME);
|
||||
Trace.PARENT_ID_NAME);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<modules>
|
||||
<module>spring-cloud-sleuth-sample</module>
|
||||
<module>spring-cloud-sleuth-sample-messaging</module>
|
||||
<module>spring-cloud-sleuth-sample-ribbon</module>
|
||||
<module>spring-cloud-sleuth-sample-zipkin</module>
|
||||
</modules>
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public class SampleMessagingApplication {
|
||||
|
||||
// Use this for debugging (or if there is no Zipkin collector running on port 9410)
|
||||
@Bean
|
||||
@ConditionalOnProperty("span.logging.enabled")
|
||||
@ConditionalOnProperty(value="sample.zipkin.enabled", havingValue="false")
|
||||
public SpanCollector spanCollector() {
|
||||
return new LoggingSpanCollectorImpl();
|
||||
}
|
||||
|
||||
@@ -13,6 +13,6 @@ logging:
|
||||
pattern:
|
||||
console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([trace=%X{X-Trace-Id:-},span=%X{X-Span-Id:-}]){yellow} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex'
|
||||
|
||||
span:
|
||||
logging:
|
||||
# enabled: true
|
||||
sample:
|
||||
zipkin:
|
||||
# enabled: false
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-sleuth-sample-ribbon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>spring-cloud-sleuth-sample-ribbon</name>
|
||||
<description>Spring Cloud Sleuth Sample</description>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<!--skip deploy -->
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-zuul</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@RestController
|
||||
public class SampleController {
|
||||
|
||||
@Autowired
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@SneakyThrows
|
||||
@RequestMapping("/")
|
||||
public String hi() {
|
||||
final Random random = new Random();
|
||||
Thread.sleep(random.nextInt(1000));
|
||||
String s = this.restTemplate.getForObject("http://zipkin/hi2", String.class);
|
||||
return "hi/" + s;
|
||||
}
|
||||
|
||||
@SneakyThrows
|
||||
@RequestMapping("/call")
|
||||
public String traced() {
|
||||
String s = this.restTemplate.getForObject("http://zipkin/call", String.class);
|
||||
return "call/" + s;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
|
||||
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;
|
||||
|
||||
import com.github.kristofa.brave.LoggingSpanCollectorImpl;
|
||||
import com.github.kristofa.brave.SpanCollector;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
@EnableAsync
|
||||
@EnableZuulProxy
|
||||
public class SampleRibbonApplication {
|
||||
|
||||
@Bean
|
||||
public Sampler<?> defaultSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleRibbonApplication.class, args);
|
||||
}
|
||||
|
||||
// Use this for debugging (or if there is no Zipkin collector running on port 9410)
|
||||
@Bean
|
||||
@ConditionalOnProperty(value="sample.zipkin.enabled", havingValue="false")
|
||||
public SpanCollector spanCollector() {
|
||||
return new LoggingSpanCollectorImpl();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
server:
|
||||
port: 3382
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: testSleuthRibbon
|
||||
sleuth:
|
||||
log:
|
||||
json:
|
||||
enabled: true
|
||||
|
||||
zipkin:
|
||||
ribbon:
|
||||
listOfServers: localhost:3380
|
||||
messaging:
|
||||
ribbon:
|
||||
listOfServers: localhost:3381
|
||||
vanilla:
|
||||
ribbon:
|
||||
listOfServers: localhost:3379
|
||||
|
||||
hystrix:
|
||||
execution:
|
||||
isolation:
|
||||
strategy: SEMAPHORE
|
||||
|
||||
zuul:
|
||||
routes:
|
||||
zipkin:
|
||||
url: http://localhost:3380
|
||||
messaging:
|
||||
url: http://localhost:3381
|
||||
vanilla:
|
||||
url: http://localhost:3379
|
||||
|
||||
logging:
|
||||
pattern:
|
||||
console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([trace=%X{X-Trace-Id:-},span=%X{X-Span-Id:-}]){yellow} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex'
|
||||
|
||||
endpoints:
|
||||
health:
|
||||
sensitive: false
|
||||
restart:
|
||||
enabled: true
|
||||
shutdown:
|
||||
enabled: true
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.sample;
|
||||
package sample;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.sample;
|
||||
package sample;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.Callable;
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.sample;
|
||||
package sample;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
@@ -49,7 +49,7 @@ public class SampleZipkinApplication {
|
||||
|
||||
// Use this for debugging (or if there is no Zipkin collector running on port 9410)
|
||||
@Bean
|
||||
@ConditionalOnProperty("span.logging.enabled")
|
||||
@ConditionalOnProperty(value="sample.zipkin.enabled", havingValue="false")
|
||||
public SpanCollector spanCollector() {
|
||||
return new LoggingSpanCollectorImpl();
|
||||
}
|
||||
@@ -9,9 +9,9 @@ logging:
|
||||
pattern:
|
||||
console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([trace=%X{X-Trace-Id:-},span=%X{X-Span-Id:-}]){yellow} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex'
|
||||
|
||||
span:
|
||||
logging:
|
||||
# enabled: true
|
||||
sample:
|
||||
zipkin:
|
||||
# enabled: false
|
||||
|
||||
endpoints:
|
||||
health:
|
||||
|
||||
Reference in New Issue
Block a user