Merge remote-tracking branch 'Upstream/1.0.x' into disable-retry
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
<name>Benchmarks</name>
|
||||
<description>Benchmarks (JMH)</description>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<artifactId>benchmarks</artifactId>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-sleuth-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -14,7 +14,7 @@
|
||||
<properties>
|
||||
<docs.main>spring-cloud-sleuth</docs.main>
|
||||
<!-- Comma separated list of whitelisted branches -->
|
||||
<docs.whitelisted.branches>1.0.x</docs.whitelisted.branches>
|
||||
<docs.whitelisted.branches>1.0.x,1.1.x</docs.whitelisted.branches>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
</properties>
|
||||
<profiles>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
:github-raw: http://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: http://github.com/{github-repo}/tree/{github-tag}
|
||||
:toc: left
|
||||
:toclevels: 8
|
||||
:nofooter:
|
||||
|
||||
Spring Cloud Sleuth
|
||||
@@ -37,7 +38,9 @@ fixed fraction of spans.
|
||||
|
||||
NOTE: the `PercentageBasedSampler` is the default if you are using
|
||||
`spring-cloud-sleuth-zipkin` or `spring-cloud-sleuth-stream`. You can
|
||||
configure the exports using `spring.sleuth.sampler.percentage`.
|
||||
configure the exports using `spring.sleuth.sampler.percentage`. The passed
|
||||
value needs to be a double from `0.0` to `1.0` so it's not a percentage.
|
||||
For backwards compatibility reasons we're not changing the property name.
|
||||
|
||||
A sampler can be installed just by creating a bean definition, e.g:
|
||||
|
||||
@@ -227,8 +230,7 @@ to your bean definition.
|
||||
|
||||
=== HTTP
|
||||
|
||||
For HTTP these are the beans responsible for creation of a Span from a `HttpServletRequest`
|
||||
and filling in the `HttpServletResponse` with tracing information.
|
||||
For HTTP these are the beans responsible for creation of a Span from a `HttpServletRequest`.
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@@ -236,11 +238,6 @@ For HTTP these are the beans responsible for creation of a Span from a `HttpServ
|
||||
public SpanExtractor<HttpServletRequest> httpServletRequestSpanExtractor() {
|
||||
...
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<HttpServletResponse> httpServletResponseSpanInjector() {
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
You can override them by providing your own implementation and by adding a `@Primary` annotation
|
||||
@@ -261,20 +258,27 @@ This is a an example of a `SpanExtractor`
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=extractor,indent=0]
|
||||
----
|
||||
|
||||
The following `SpanInjector` could be created
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=injector,indent=0]
|
||||
----
|
||||
|
||||
And you could register them like this:
|
||||
And you could register it like this:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
Spring Cloud Sleuth does not add trace/span related headers to the Http Response for security reasons. If you need the headers then a custom `SpanInjector`
|
||||
that injects the headers into the Http Response and a Servlet filter which makes use of this can be added the following way:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java[tags=injector,indent=0]
|
||||
----
|
||||
|
||||
And you could register them like this:
|
||||
[source,java]
|
||||
----
|
||||
include::../../../..//spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java[tags=configuration,indent=0]
|
||||
----
|
||||
|
||||
=== Custom SA tag in Zipkin
|
||||
|
||||
Sometimes you want to create a manual Span that will wrap a call to an external service which is not instrumented.
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -5,7 +5,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Sleuth</name>
|
||||
<description>Spring Cloud Sleuth</description>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -90,6 +90,7 @@ public class Span {
|
||||
public static final String SPAN_NOT_SAMPLED = "0";
|
||||
|
||||
public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc";
|
||||
public static final String SPAN_ERROR_TAG_NAME = "error";
|
||||
|
||||
/**
|
||||
* <b>cr</b> - Client Receive. Signifies the end of the span. The client has
|
||||
|
||||
@@ -43,8 +43,7 @@ class TraceExecutorBeanPostProcessor implements BeanPostProcessor {
|
||||
if (bean instanceof ThreadPoolTaskExecutor && !(bean instanceof TaskScheduler) &&
|
||||
!(bean instanceof LazyTraceThreadPoolTaskExecutor)) {
|
||||
return new LazyTraceThreadPoolTaskExecutor(this.beanFactory, (ThreadPoolTaskExecutor) bean);
|
||||
}
|
||||
if (bean instanceof Executor && !(bean instanceof TaskScheduler) && !(bean instanceof LazyTraceExecutor)) {
|
||||
} else if (bean instanceof Executor && !(bean instanceof TaskScheduler) && !(bean instanceof LazyTraceExecutor)) {
|
||||
return new LazyTraceExecutor(this.beanFactory, (Executor) bean);
|
||||
}
|
||||
return bean;
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package org.springframework.cloud.sleuth.instrument.messaging;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
@@ -22,6 +26,8 @@ import org.springframework.util.ClassUtils;
|
||||
abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
implements ExecutorChannelInterceptor {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
/**
|
||||
* If a span comes from messaging components then it will have this value as a prefix
|
||||
* to its name.
|
||||
@@ -63,7 +69,12 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
* missing.
|
||||
*/
|
||||
protected Span buildSpan(Message<?> message) {
|
||||
return this.spanExtractor.joinTrace(message);
|
||||
try {
|
||||
return this.spanExtractor.joinTrace(message);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while trying to extract span from carrier", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String getChannelName(MessageChannel channel) {
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
@@ -53,6 +54,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
} else if (spanFromHeader != null) {
|
||||
spanFromHeader.logEvent(Span.CLIENT_RECV);
|
||||
}
|
||||
addErrorTag(ex);
|
||||
getTracer().close(spanFromHeader);
|
||||
}
|
||||
|
||||
@@ -120,10 +122,17 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
Span spanFromHeader = getSpanFromHeader(message);
|
||||
if (spanFromHeader!= null) {
|
||||
spanFromHeader.logEvent(Span.SERVER_SEND);
|
||||
addErrorTag(ex);
|
||||
}
|
||||
getTracer().detach(spanFromHeader);
|
||||
}
|
||||
|
||||
private void addErrorTag(Exception ex) {
|
||||
if (ex != null) {
|
||||
getTracer().addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(ex));
|
||||
}
|
||||
}
|
||||
|
||||
private Span getSpanFromHeader(Message<?> message) {
|
||||
if (message == null) {
|
||||
return null;
|
||||
|
||||
@@ -54,13 +54,18 @@ class HttpServletRequestExtractor implements SpanExtractor<HttpServletRequest> {
|
||||
// can't build a Span without trace id
|
||||
return null;
|
||||
}
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(carrier);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| Span.SPAN_NOT_SAMPLED.equals(carrier.getHeader(Span.SAMPLED_NAME));
|
||||
long traceId = Span
|
||||
.hexToId(carrier.getHeader(Span.TRACE_ID_NAME));
|
||||
long spanId = spanId(carrier, traceId);
|
||||
return buildParentSpan(carrier, uri, skip, traceId, spanId);
|
||||
try {
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(carrier);
|
||||
boolean skip = this.skipPattern.matcher(uri).matches()
|
||||
|| Span.SPAN_NOT_SAMPLED.equals(carrier.getHeader(Span.SAMPLED_NAME));
|
||||
long traceId = Span
|
||||
.hexToId(carrier.getHeader(Span.TRACE_ID_NAME));
|
||||
long spanId = spanId(carrier, traceId);
|
||||
return buildParentSpan(carrier, uri, skip, traceId, spanId);
|
||||
} catch (Exception e) {
|
||||
log.error("Exception occurred while trying to extract span from carrier", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private long spanId(HttpServletRequest carrier, long traceId) {
|
||||
|
||||
@@ -15,18 +15,18 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -36,6 +36,7 @@ import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.http.HttpStatus;
|
||||
@@ -134,19 +135,13 @@ public class TraceFilter extends GenericFilterBean {
|
||||
return;
|
||||
}
|
||||
String name = HTTP_COMPONENT + ":" + uri;
|
||||
try {
|
||||
spanFromRequest = createSpan(request, skip, spanFromRequest, name);
|
||||
} catch (IllegalArgumentException e) {
|
||||
filterChain.doFilter(request, response);
|
||||
response.sendError(HttpStatus.BAD_REQUEST.value(),
|
||||
"Exception tracing request [" + e.getMessage() + "]");
|
||||
return;
|
||||
}
|
||||
Throwable exception = null;
|
||||
try {
|
||||
spanFromRequest = createSpan(request, skip, spanFromRequest, name);
|
||||
filterChain.doFilter(request, response);
|
||||
} catch (Throwable e) {
|
||||
exception = e;
|
||||
this.tracer.addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(e));
|
||||
throw e;
|
||||
} finally {
|
||||
if (isAsyncStarted(request) || request.isAsyncStarted()) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.web.ErrorController;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.cloud.sleuth.util.SpanNameUtil;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
@@ -150,6 +151,9 @@ public class TraceHandlerInterceptor extends HandlerInterceptorAdapter {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing span " + span);
|
||||
}
|
||||
if (ex != null) {
|
||||
getTracer().addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(ex));
|
||||
}
|
||||
getTracer().close(span);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -83,6 +83,12 @@ public class TraceWebAutoConfiguration {
|
||||
return new TraceSpringDataBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpTraceKeysInjector httpTraceKeysInjector(Tracer tracer, TraceKeys traceKeys) {
|
||||
return new HttpTraceKeysInjector(tracer, traceKeys);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean traceWebFilter(Tracer tracer, TraceKeys traceKeys,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter,
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* Helper annotation to enable Sleuth web client
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.0.11
|
||||
*/
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ ElementType.TYPE, ElementType.METHOD})
|
||||
@Documented
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.client.enabled", matchIfMissing = true)
|
||||
@interface SleuthWebClientEnabled {
|
||||
}
|
||||
@@ -18,9 +18,11 @@ package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
@@ -59,6 +61,7 @@ public class TraceRestTemplateInterceptor extends AbstractTraceHttpRequestInterc
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Exception occurred while trying to execute the request. Will close the span [" + currentSpan() + "]", e);
|
||||
}
|
||||
this.tracer.addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(e));
|
||||
this.tracer.close(currentSpan());
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -22,11 +22,10 @@ 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.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
@@ -46,21 +45,18 @@ import org.springframework.web.client.AsyncRestTemplate;
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@SleuthWebClientEnabled
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.async.client.enabled", matchIfMissing = true)
|
||||
@ConditionalOnClass(AsyncRestTemplate.class)
|
||||
@ConditionalOnBean(SpanAccessor.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@ConditionalOnBean(HttpTraceKeysInjector.class)
|
||||
@AutoConfigureAfter(TraceWebAutoConfiguration.class)
|
||||
public class TraceWebAsyncClientAutoConfiguration {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired
|
||||
private HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
@Autowired
|
||||
private SpanInjector<HttpRequest> spanInjector;
|
||||
@Autowired(required = false)
|
||||
private ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
@Autowired(required = false)
|
||||
private AsyncClientHttpRequestFactory asyncClientHttpRequestFactory;
|
||||
@Autowired private HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
@Autowired private SpanInjector<HttpRequest> spanInjector;
|
||||
@Autowired(required = false) private ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
@Autowired(required = false) private AsyncClientHttpRequestFactory asyncClientHttpRequestFactory;
|
||||
|
||||
private TraceAsyncClientHttpRequestFactoryWrapper traceAsyncClientHttpRequestFactory() {
|
||||
ClientHttpRequestFactory clientFactory = this.clientHttpRequestFactory;
|
||||
|
||||
@@ -26,12 +26,10 @@ 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.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpRequest;
|
||||
@@ -47,10 +45,10 @@ import org.springframework.web.client.RestTemplate;
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "spring.sleuth.web.client.enabled", matchIfMissing = true)
|
||||
@SleuthWebClientEnabled
|
||||
@ConditionalOnClass(RestTemplate.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@ConditionalOnBean(HttpTraceKeysInjector.class)
|
||||
@AutoConfigureAfter(TraceWebAutoConfiguration.class)
|
||||
public class TraceWebClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -66,12 +64,6 @@ public class TraceWebClientAutoConfiguration {
|
||||
return new HttpRequestInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public HttpTraceKeysInjector httpTraceKeysInjector(Tracer tracer, TraceKeys traceKeys) {
|
||||
return new HttpTraceKeysInjector(tracer, traceKeys);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class TraceInterceptorConfiguration {
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
|
||||
/**
|
||||
* A Feign Client that closes a Span if there is no response body. In other cases Span
|
||||
@@ -134,11 +135,7 @@ class TraceFeignClient implements Client {
|
||||
private void logError(Exception e) {
|
||||
Span span = getTracer().getCurrentSpan();
|
||||
if (span != null) {
|
||||
String message = e.getMessage() != null ? e.getMessage() : e.toString();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Appending exception [" + message + "] to span " + span);
|
||||
}
|
||||
getTracer().addTag("error", message);
|
||||
getTracer().addTag(Span.SPAN_ERROR_TAG_NAME, ExceptionUtils.getExceptionMessage(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.netflix.feign.FeignAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
@@ -45,7 +46,7 @@ import feign.Feign;
|
||||
@ConditionalOnClass(Client.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||
@AutoConfigureAfter(SleuthHystrixAutoConfiguration.class)
|
||||
@AutoConfigureAfter({SleuthHystrixAutoConfiguration.class, TraceWebAutoConfiguration.class})
|
||||
public class TraceFeignClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -16,14 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
|
||||
import com.netflix.zuul.ExecutionStatus;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.ZuulFilterResult;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
@@ -32,6 +28,9 @@ import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceRequestAttributes;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* A pre request {@link ZuulFilter} that sets tracing related headers on the request
|
||||
* from the current span. We're doing so to ensure tracing propagates to the next hop.
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
@@ -26,15 +30,11 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
* enables span information propagation when using Zuul.
|
||||
@@ -47,7 +47,7 @@ import com.netflix.zuul.context.RequestContext;
|
||||
@ConditionalOnWebApplication
|
||||
@ConditionalOnClass(ZuulFilter.class)
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@AutoConfigureAfter(TraceWebAutoConfiguration.class)
|
||||
public class TraceZuulAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -55,4 +55,8 @@ public final class ExceptionUtils {
|
||||
ExceptionUtils.fail = fail;
|
||||
ExceptionUtils.lastException = null;
|
||||
}
|
||||
|
||||
public static String getExceptionMessage(Throwable e) {
|
||||
return e.getMessage() != null ? e.getMessage() : e.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.async.issues.issue410;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
@@ -23,7 +25,9 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
@@ -31,6 +35,7 @@ import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.async.LazyTraceExecutor;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -47,7 +52,7 @@ import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
@@ -66,27 +71,71 @@ public class Issue410Tests {
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_tasks_running_without_a_pool() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/without_pool", String.class);
|
||||
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/without_pool", String.class);
|
||||
|
||||
then(response).isEqualTo(Span.idToHex(span.getTraceId()));
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.asyncTask.getSpan().get()).isNotNull();
|
||||
then(this.asyncTask.getSpan().get().getTraceId()).isEqualTo(span.getTraceId());
|
||||
});
|
||||
then(response).isEqualTo(Span.idToHex(span.getTraceId()));
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.asyncTask.getSpan().get()).isNotNull();
|
||||
then(this.asyncTask.getSpan().get().getTraceId()).isEqualTo(span.getTraceId());
|
||||
});
|
||||
} finally {
|
||||
this.tracer.close(span);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_tasks_running_with_a_pool() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/with_pool", String.class);
|
||||
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/with_pool", String.class);
|
||||
then(response).isEqualTo(Span.idToHex(span.getTraceId()));
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.asyncTask.getSpan().get()).isNotNull();
|
||||
then(this.asyncTask.getSpan().get().getTraceId()).isEqualTo(span.getTraceId());
|
||||
});
|
||||
} finally {
|
||||
this.tracer.close(span);
|
||||
}
|
||||
}
|
||||
|
||||
then(response).isEqualTo(Span.idToHex(span.getTraceId()));
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.asyncTask.getSpan().get()).isNotNull();
|
||||
then(this.asyncTask.getSpan().get().getTraceId()).isEqualTo(span.getTraceId());
|
||||
});
|
||||
/**
|
||||
* Related to issue #423
|
||||
*/
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_completable_futures_with_executor() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/completable", String.class);
|
||||
|
||||
then(response).isEqualTo(Span.idToHex(span.getTraceId()));
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.asyncTask.getSpan().get()).isNotNull();
|
||||
then(this.asyncTask.getSpan().get().getTraceId()).isEqualTo(span.getTraceId());
|
||||
});
|
||||
} finally {
|
||||
this.tracer.close(span);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Related to issue #423
|
||||
*/
|
||||
@Test
|
||||
public void should_pass_tracing_info_for_completable_futures_with_task_scheduler() {
|
||||
Span span = this.tracer.createSpan("foo");
|
||||
try {
|
||||
String response = this.restTemplate.getForObject("http://localhost:" + port() + "/taskScheduler", String.class);
|
||||
|
||||
then(response).isEqualTo(Span.idToHex(span.getTraceId()));
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.asyncTask.getSpan().get()).isNotNull();
|
||||
then(this.asyncTask.getSpan().get().getTraceId()).isEqualTo(span.getTraceId());
|
||||
});
|
||||
} finally {
|
||||
this.tracer.close(span);
|
||||
}
|
||||
}
|
||||
|
||||
private int port() {
|
||||
@@ -123,6 +172,9 @@ class AsyncTask {
|
||||
private AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired @Qualifier("poolTaskExecutor") Executor executor;
|
||||
@Autowired @Qualifier("taskScheduler") Executor taskScheduler;
|
||||
@Autowired BeanFactory beanFactory;
|
||||
|
||||
@Async("poolTaskExecutor")
|
||||
public void runWithPool() {
|
||||
@@ -136,6 +188,58 @@ class AsyncTask {
|
||||
this.span.set(this.tracer.getCurrentSpan());
|
||||
}
|
||||
|
||||
public Span completableFutures() throws ExecutionException, InterruptedException {
|
||||
log.info("This task is running with completable future");
|
||||
CompletableFuture<Span> span1 = CompletableFuture
|
||||
.supplyAsync(() -> {
|
||||
AsyncTask.log.info("First completable future");
|
||||
return AsyncTask.this.tracer.getCurrentSpan();
|
||||
}, AsyncTask.this.executor);
|
||||
CompletableFuture<Span> span2 = CompletableFuture
|
||||
.supplyAsync(() -> {
|
||||
AsyncTask.log.info("Second completable future");
|
||||
return AsyncTask.this.tracer.getCurrentSpan();
|
||||
}, AsyncTask.this.executor);
|
||||
CompletableFuture<Span> response = CompletableFuture.allOf(span1, span2)
|
||||
.thenApply(ignoredVoid -> {
|
||||
AsyncTask.log.info("Third completable future");
|
||||
Span joinedSpan1 = span1.join();
|
||||
Span joinedSpan2 = span2.join();
|
||||
then(joinedSpan2).isNotNull();
|
||||
then(joinedSpan1).hasTraceIdEqualTo(joinedSpan2.getTraceId());
|
||||
AsyncTask.log.info("TraceIds are correct");
|
||||
return joinedSpan2;
|
||||
});
|
||||
this.span.set(response.get());
|
||||
return this.span.get();
|
||||
}
|
||||
|
||||
public Span taskScheduler() throws ExecutionException, InterruptedException {
|
||||
log.info("This task is running with completable future");
|
||||
CompletableFuture<Span> span1 = CompletableFuture
|
||||
.supplyAsync(() -> {
|
||||
AsyncTask.log.info("First completable future");
|
||||
return AsyncTask.this.tracer.getCurrentSpan();
|
||||
}, new LazyTraceExecutor(AsyncTask.this.beanFactory, AsyncTask.this.taskScheduler));
|
||||
CompletableFuture<Span> span2 = CompletableFuture
|
||||
.supplyAsync(() -> {
|
||||
AsyncTask.log.info("Second completable future");
|
||||
return AsyncTask.this.tracer.getCurrentSpan();
|
||||
}, new LazyTraceExecutor(AsyncTask.this.beanFactory, AsyncTask.this.taskScheduler));
|
||||
CompletableFuture<Span> response = CompletableFuture.allOf(span1, span2)
|
||||
.thenApply(ignoredVoid -> {
|
||||
AsyncTask.log.info("Third completable future");
|
||||
Span joinedSpan1 = span1.join();
|
||||
Span joinedSpan2 = span2.join();
|
||||
then(joinedSpan2).isNotNull();
|
||||
then(joinedSpan1).hasTraceIdEqualTo(joinedSpan2.getTraceId());
|
||||
AsyncTask.log.info("TraceIds are correct");
|
||||
return joinedSpan2;
|
||||
});
|
||||
this.span.set(response.get());
|
||||
return this.span.get();
|
||||
}
|
||||
|
||||
public AtomicReference<Span> getSpan() {
|
||||
return span;
|
||||
}
|
||||
@@ -165,4 +269,16 @@ class Application {
|
||||
return Span.idToHex(this.tracer.getCurrentSpan().getTraceId());
|
||||
}
|
||||
|
||||
@RequestMapping("/completable")
|
||||
public String completable() throws ExecutionException, InterruptedException {
|
||||
log.info("Executing completable");
|
||||
return Span.idToHex(this.asyncTask.completableFutures().getTraceId());
|
||||
}
|
||||
|
||||
@RequestMapping("/taskScheduler")
|
||||
public String taskScheduler() throws ExecutionException, InterruptedException {
|
||||
log.info("Executing completable via task scheduler");
|
||||
return Span.idToHex(this.asyncTask.taskScheduler().getTraceId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.assertions.SleuthAssertions;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptorTests.App;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
@@ -87,7 +88,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
this.message = message;
|
||||
this.span = TestSpanContextHolder.getCurrentSpan();
|
||||
if (message.getHeaders().containsKey("THROW_EXCEPTION")) {
|
||||
throw new RuntimeException();
|
||||
throw new RuntimeException("A terrible exception has occurred");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,6 +269,9 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
then(this.message).isNotNull();
|
||||
this.tracer.close(span);
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
then(new ListOfSpans(this.accumulator.getSpans()))
|
||||
.hasASpanWithTagEqualTo(Span.SPAN_ERROR_TAG_NAME,
|
||||
"A terrible exception has occurred");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -281,7 +285,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class);
|
||||
then(traceId).isNull();
|
||||
|
||||
then(accumulator.getSpans()).isEmpty();
|
||||
then(this.accumulator.getSpans()).isEmpty();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
}
|
||||
|
||||
@@ -299,6 +303,18 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
then(traceId).isEqualTo(Span.hexToId(lower64Bits));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotBreakWhenInvalidHeadersAreSent() {
|
||||
this.tracedChannel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(TraceMessageHeaders.PARENT_ID_NAME, "-")
|
||||
.setHeader(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(10L))
|
||||
.setHeader(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)).build());
|
||||
|
||||
then(this.message).isNotNull();
|
||||
then(this.accumulator.getSpans()).isNotEmpty();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class App {
|
||||
|
||||
@@ -16,9 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -28,7 +28,6 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
@@ -54,12 +53,7 @@ public class HttpServletRequestExtractorTests {
|
||||
BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME))
|
||||
.willReturn("invalid");
|
||||
|
||||
try {
|
||||
this.extractor.joinTrace(this.request);
|
||||
fail("should throw an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
then(e).hasMessageContaining("Malformed id");
|
||||
}
|
||||
then(this.extractor.joinTrace(this.request)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -69,12 +63,7 @@ public class HttpServletRequestExtractorTests {
|
||||
BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME))
|
||||
.willReturn("invalid");
|
||||
|
||||
try {
|
||||
this.extractor.joinTrace(this.request);
|
||||
fail("should throw an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
then(e).hasMessageContaining("Malformed id");
|
||||
}
|
||||
then(this.extractor.joinTrace(this.request)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -86,12 +75,7 @@ public class HttpServletRequestExtractorTests {
|
||||
BDDMockito.given(this.request.getHeader(Span.PARENT_ID_NAME))
|
||||
.willReturn("invalid");
|
||||
|
||||
try {
|
||||
this.extractor.joinTrace(this.request);
|
||||
fail("should throw an exception");
|
||||
} catch (IllegalArgumentException e) {
|
||||
then(e).hasMessageContaining("Malformed id");
|
||||
}
|
||||
then(this.extractor.joinTrace(this.request)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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.web;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.RequestEntity;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.filter.GenericFilterBean;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(TraceCustomFilterResponseInjectorTests.Config.class)
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
@DirtiesContext
|
||||
public class TraceCustomFilterResponseInjectorTests {
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
@Autowired CustomRestController customRestController;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void should_inject_trace_and_span_ids_in_response_headers() {
|
||||
RequestEntity<?> requestEntity = RequestEntity
|
||||
.get(URI.create("http://localhost:" + this.config.port + "/headers"))
|
||||
.build();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity, Map.class);
|
||||
|
||||
then(responseEntity.getHeaders())
|
||||
.containsKeys(Span.TRACE_ID_NAME, Span.SPAN_ID_NAME)
|
||||
.as("Trace headers must be present in response headers");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
static class Config
|
||||
implements ApplicationListener<EmbeddedServletContainerInitializedEvent> {
|
||||
int port;
|
||||
|
||||
// tag::configuration[]
|
||||
@Bean
|
||||
SpanInjector<HttpServletResponse> customHttpServletResponseSpanInjector() {
|
||||
return new CustomHttpServletResponseSpanInjector();
|
||||
}
|
||||
|
||||
@Bean
|
||||
HttpResponseInjectingTraceFilter responseInjectingTraceFilter(Tracer tracer) {
|
||||
return new HttpResponseInjectingTraceFilter(tracer, customHttpServletResponseSpanInjector());
|
||||
}
|
||||
// end::configuration[]
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
|
||||
this.port = event.getEmbeddedServletContainer().getPort();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
CustomRestController customRestController() {
|
||||
return new CustomRestController();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// tag::injector[]
|
||||
static class CustomHttpServletResponseSpanInjector
|
||||
implements SpanInjector<HttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpServletResponse carrier) {
|
||||
carrier.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
|
||||
carrier.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
|
||||
}
|
||||
}
|
||||
|
||||
static class HttpResponseInjectingTraceFilter extends GenericFilterBean {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpServletResponse> spanInjector;
|
||||
|
||||
public HttpResponseInjectingTraceFilter(Tracer tracer, SpanInjector<HttpServletResponse> spanInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
|
||||
HttpServletResponse response = (HttpServletResponse) servletResponse;
|
||||
Span currentSpan = this.tracer.getCurrentSpan();
|
||||
this.spanInjector.inject(currentSpan, response);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
// end::injector[]
|
||||
|
||||
@RestController
|
||||
static class CustomRestController {
|
||||
|
||||
@RequestMapping("/headers")
|
||||
public Map<String, String> headers(@RequestHeader HttpHeaders headers) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (String key : headers.keySet()) {
|
||||
map.put(key, headers.getFirst(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -34,7 +33,6 @@ import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
@@ -83,13 +81,13 @@ public class TraceFilterCustomExtractorTests {
|
||||
.header("mySpanId", Span.idToHex(spanId)).build();
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
ResponseEntity<Map> responseHeaders = this.restTemplate.exchange(requestEntity,
|
||||
ResponseEntity<Map> responseEntity = this.restTemplate.exchange(requestEntity,
|
||||
Map.class);
|
||||
|
||||
await().until(() -> then(this.accumulator.getSpans().stream().filter(
|
||||
span -> span.getSpanId() == spanId).findFirst().get())
|
||||
.hasTraceIdEqualTo(traceId));
|
||||
then(responseHeaders.getBody())
|
||||
then(responseEntity.getBody())
|
||||
.containsEntry("correlationid", Span.idToHex(traceId))
|
||||
.containsEntry("myspanid", Span.idToHex(spanId))
|
||||
.as("input request headers");
|
||||
@@ -107,12 +105,6 @@ public class TraceFilterCustomExtractorTests {
|
||||
SpanExtractor<HttpServletRequest> customHttpServletRequestSpanExtractor() {
|
||||
return new CustomHttpServletRequestSpanExtractor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
SpanInjector<HttpServletResponse> customHttpServletResponseSpanInjector() {
|
||||
return new CustomHttpServletResponseSpanInjector();
|
||||
}
|
||||
// end::configuration[]
|
||||
|
||||
@Override
|
||||
@@ -157,19 +149,6 @@ public class TraceFilterCustomExtractorTests {
|
||||
}
|
||||
// end::extractor[]
|
||||
|
||||
// tag::injector[]
|
||||
static class CustomHttpServletResponseSpanInjector
|
||||
implements SpanInjector<HttpServletResponse> {
|
||||
|
||||
@Override
|
||||
public void inject(Span span, HttpServletResponse carrier) {
|
||||
carrier.addHeader("correlationId", Span.idToHex(span.getTraceId()));
|
||||
carrier.addHeader("mySpanId", Span.idToHex(span.getSpanId()));
|
||||
// inject the rest of Span values to the header
|
||||
}
|
||||
}
|
||||
// end::injector[]
|
||||
|
||||
@RestController
|
||||
static class CustomRestController {
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
@@ -32,12 +33,14 @@ import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanReporter;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.log.SpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.sampler.NeverSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
@@ -285,7 +288,7 @@ public class TraceFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catchesException() throws Exception {
|
||||
public void shouldAnnotateSpanWithErrorWhenExceptionIsThrown() throws Exception {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, PARENT_ID)
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
@@ -296,7 +299,7 @@ public class TraceFilterTests {
|
||||
javax.servlet.ServletResponse response)
|
||||
throws java.io.IOException, javax.servlet.ServletException {
|
||||
throw new RuntimeException("Planned");
|
||||
};
|
||||
}
|
||||
};
|
||||
try {
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
@@ -307,6 +310,8 @@ public class TraceFilterTests {
|
||||
verifyParentSpanHttpTags(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
then(new ListOfSpans(this.spanReporter.getSpans()))
|
||||
.hasASpanWithTagEqualTo(Span.SPAN_ERROR_TAG_NAME, "Planned");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -323,7 +328,7 @@ public class TraceFilterTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returns400IfSpanIsMalformed() throws Exception {
|
||||
public void returns400IfSpanIsMalformedAndCreatesANewSpan() throws Exception {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, "asd")
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
@@ -331,8 +336,26 @@ public class TraceFilterTests {
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(new ArrayList<>(this.spanReporter.getSpans())).isNotEmpty();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
then(this.response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value());
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
then(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returns200IfSpanParentIsMalformedAndCreatesANewSpan() throws Exception {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, PARENT_ID)
|
||||
.header(Span.PARENT_ID_NAME, "-")
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.httpTraceKeysInjector);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
then(new ArrayList<>(this.spanReporter.getSpans())).isNotEmpty();
|
||||
then(TestSpanContextHolder.getCurrentSpan()).isNull();
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
then(this.response.getStatus()).isEqualTo(HttpStatus.OK.value());
|
||||
}
|
||||
|
||||
public void verifyParentSpanHttpTags() {
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.boot.test.WebIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
@@ -74,6 +75,9 @@ public class TraceFilterWebIntegrationTests {
|
||||
.doesNotHaveASpanWithName("error")
|
||||
.hasASpanWithTagEqualTo("http.status_code", "500");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
then(new ListOfSpans(this.accumulator.getSpans()))
|
||||
.hasASpanWithTagEqualTo(Span.SPAN_ERROR_TAG_NAME,
|
||||
"Request processing failed; nested exception is java.lang.RuntimeException: Throwing exception");
|
||||
}
|
||||
|
||||
private int port() {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
import org.springframework.boot.test.SpringApplicationConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@IntegrationTest({ "spring.sleuth.web.enabled=true", "spring.sleuth.web.client.enabled=false"})
|
||||
@SpringApplicationConfiguration(classes = { TraceWebDisabledTests.Config.class })
|
||||
public class TraceWebDisabledTests {
|
||||
|
||||
@Test
|
||||
public void should_load_context() {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
public static class Config {}
|
||||
}
|
||||
@@ -27,15 +27,16 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.DefaultSpanNamer;
|
||||
import org.springframework.cloud.sleuth.NoOpSpanReporter;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.assertions.SleuthAssertions;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.log.NoOpSpanLogger;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
@@ -59,10 +60,12 @@ public class TraceRestTemplateInterceptorIntegrationTests {
|
||||
|
||||
private DefaultTracer tracer;
|
||||
|
||||
private ArrayListSpanAccumulator spanAccumulator = new ArrayListSpanAccumulator();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter());
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), this.spanAccumulator);
|
||||
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys()))));
|
||||
@@ -91,6 +94,9 @@ public class TraceRestTemplateInterceptorIntegrationTests {
|
||||
|
||||
SleuthAssertions.then(this.tracer.getCurrentSpan()).isEqualTo(span);
|
||||
this.tracer.close(span);
|
||||
SleuthAssertions.then(new ListOfSpans(this.spanAccumulator.getSpans()))
|
||||
.hasASpanWithTagEqualTo(Span.SPAN_ERROR_TAG_NAME,
|
||||
"Read timed out");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
}
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ public class TraceFeignClientTests {
|
||||
then(this.tracer.getCurrentSpan()).isEqualTo(span);
|
||||
then(this.spanAccumulator.getSpans().get(0))
|
||||
.hasNotLoggedAnEvent(Span.CLIENT_RECV)
|
||||
.hasATag("error", "exception has occurred");
|
||||
.hasATag(Span.SPAN_ERROR_TAG_NAME, "exception has occurred");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,11 +20,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -44,6 +39,7 @@ 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.assertions.ListOfSpans;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -58,10 +54,16 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.jayway.awaitility.Awaitility;
|
||||
import com.netflix.hystrix.exception.HystrixRuntimeException;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
/**
|
||||
* Related to https://github.com/spring-cloud/spring-cloud-sleuth/issues/257
|
||||
@@ -76,10 +78,12 @@ public class FeignClientServerErrorTests {
|
||||
|
||||
@Autowired TestFeignInterface feignInterface;
|
||||
@Autowired TestFeignWithCustomConfInterface customConfFeignInterface;
|
||||
@Autowired Listener listener;
|
||||
@Rule public OutputCapture capture = new OutputCapture();
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
this.listener.clear();
|
||||
ExceptionUtils.setFail(true);
|
||||
}
|
||||
|
||||
@@ -90,11 +94,17 @@ public class FeignClientServerErrorTests {
|
||||
} catch (HystrixRuntimeException e) {
|
||||
}
|
||||
|
||||
// ugly :/ waiting for rx thread to complete
|
||||
Thread.sleep(100);
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
then(new ListOfSpans(this.listener.getEvents()))
|
||||
.hasASpanWithTagEqualTo(Span.SPAN_ERROR_TAG_NAME,
|
||||
"Request processing failed; nested exception is java.lang.RuntimeException: Internal Error");
|
||||
then(new ListOfSpans(this.listener.getEvents()))
|
||||
.hasASpanWithTagEqualTo(Span.SPAN_ERROR_TAG_NAME,
|
||||
"Internal Error");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -104,11 +114,11 @@ public class FeignClientServerErrorTests {
|
||||
} catch (HystrixRuntimeException e) {
|
||||
}
|
||||
|
||||
// ugly :/ waiting for rx thread to complete
|
||||
Thread.sleep(100);
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -118,11 +128,10 @@ public class FeignClientServerErrorTests {
|
||||
} catch (HystrixRuntimeException e) {
|
||||
}
|
||||
|
||||
// ugly :/ waiting for rx thread to complete
|
||||
Thread.sleep(100);
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.capture.toString()).doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -132,11 +141,10 @@ public class FeignClientServerErrorTests {
|
||||
} catch (HystrixRuntimeException e) {
|
||||
}
|
||||
|
||||
// ugly :/ waiting for rx thread to complete
|
||||
Thread.sleep(100);
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.capture.toString()).doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -146,11 +154,10 @@ public class FeignClientServerErrorTests {
|
||||
} catch (HystrixRuntimeException e) {
|
||||
}
|
||||
|
||||
// ugly :/ waiting for rx thread to complete
|
||||
Thread.sleep(100);
|
||||
then(this.capture.toString())
|
||||
.doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
Awaitility.await().until(() -> {
|
||||
then(this.capture.toString()).doesNotContain("Tried to close span but it is not the current span");
|
||||
then(ExceptionUtils.getLastException()).isNull();
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -226,7 +233,11 @@ public class FeignClientServerErrorTests {
|
||||
private List<Span> events = new ArrayList<>();
|
||||
|
||||
public List<Span> getEvents() {
|
||||
return this.events;
|
||||
return new ArrayList<>(this.events);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.events.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -246,8 +257,7 @@ public class FeignClientServerErrorTests {
|
||||
@RequestHeader(Span.TRACE_ID_NAME) String traceId,
|
||||
@RequestHeader(Span.SPAN_ID_NAME) String spanId,
|
||||
@RequestHeader(Span.PARENT_ID_NAME) String parentId) {
|
||||
return new ResponseEntity<>("internal error",
|
||||
HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
throw new RuntimeException("Internal Error");
|
||||
}
|
||||
|
||||
@RequestMapping("/notfound")
|
||||
|
||||
@@ -50,4 +50,22 @@ public class ExceptionUtilsTest {
|
||||
then(e).isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_print_error_message_when_there_is_one() throws Exception {
|
||||
Throwable e = new RuntimeException("Foo");
|
||||
|
||||
String message = ExceptionUtils.getExceptionMessage(e);
|
||||
|
||||
then(message).isEqualTo("Foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_print_to_string_when_there_is_no_error() throws Exception {
|
||||
Throwable e = new RuntimeException();
|
||||
|
||||
String message = ExceptionUtils.getExceptionMessage(e);
|
||||
|
||||
then(message).isEqualTo("java.lang.RuntimeException");
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
<configuration>
|
||||
<include resource="org/springframework/boot/logging/logback/base.xml"/>
|
||||
<logger name="org.springframework.cloud.sleuth" level="TRACE"/>
|
||||
<logger name="org.springframework.boot.autoconfigure.logging" level="DEBUG"/>
|
||||
<logger name="org.springframework.cloud.sleuth.log" level="DEBUG"/>
|
||||
<logger name="org.springframework.cloud.sleuth.trace" level="DEBUG"/>
|
||||
<logger name="org.springframework.cloud.sleuth.instrument.rxjava" level="DEBUG"/>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-sleuth-dependencies</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-sleuth-dependencies</name>
|
||||
<description>Spring Cloud Sleuth Dependencies</description>
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth-samples</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<dependencies>
|
||||
|
||||
@@ -83,7 +83,14 @@ public class StreamSpanReporter implements SpanReporter {
|
||||
@Override
|
||||
public void report(Span span) {
|
||||
if (span.isExportable()) {
|
||||
this.queue.add(span);
|
||||
try {
|
||||
this.queue.add(span);
|
||||
} catch (Exception e) {
|
||||
this.spanMetricReporter.incrementDroppedSpans(1);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("The span " + span + " will not be sent to Zipkin due to [" + e + "]");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("The span " + span + " will not be sent to Zipkin due to sampling");
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2013-2016 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.stream;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
|
||||
|
||||
import static org.mockito.BDDMockito.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class StreamSpanReporterTests {
|
||||
|
||||
@Mock HostLocator endpointLocator;
|
||||
@Mock SpanMetricReporter spanMetricReporter;
|
||||
@InjectMocks StreamSpanReporter reporter;
|
||||
|
||||
@Test
|
||||
public void should_not_throw_an_exception_when_queue_size_is_exceeded() throws Exception {
|
||||
ArrayBlockingQueue<Span> queue = new ArrayBlockingQueue<>(1);
|
||||
queue.add(Span.builder().name("foo").build());
|
||||
this.reporter.setQueue(queue);
|
||||
|
||||
this.reporter.report(Span.builder().name("bar").exportable(true).build());
|
||||
|
||||
then(spanMetricReporter).should().incrementDroppedSpans(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-sleuth</artifactId>
|
||||
<version>1.0.10.BUILD-SNAPSHOT</version>
|
||||
<version>1.0.11.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-starter-zipkin</artifactId>
|
||||
|
||||
Reference in New Issue
Block a user