Updated HTTP client side span with HTTP tags
After this change when sending an HTTP request, the client side span will have all the necessary HTTP related tags. Fixes #290
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Injects HTTP related keys to the current span.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public class HttpTraceKeysInjector {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
public HttpTraceKeysInjector(Tracer tracer, TraceKeys traceKeys) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request to the current Span
|
||||
*/
|
||||
public void addRequestTags(String url, String host, String path, String method) {
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getUrl(), url);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getHost(), host);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getPath(), path);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getMethod(), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request to the given Span
|
||||
*/
|
||||
public void addRequestTags(Span span, String url, String host, String path, String method) {
|
||||
tagSpan(span, this.traceKeys.getHttp().getUrl(), url);
|
||||
tagSpan(span, this.traceKeys.getHttp().getHost(), host);
|
||||
tagSpan(span, this.traceKeys.getHttp().getPath(), path);
|
||||
tagSpan(span, this.traceKeys.getHttp().getMethod(), method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds tags from the HTTP request together with headers to the current Span
|
||||
*/
|
||||
public void addRequestTags(String url, String host, String path, String method,
|
||||
Map<String, ? extends Collection<String>> headers) {
|
||||
addRequestTags(url, host, path, method);
|
||||
addRequestTagsFromHeaders(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a tag to the given, exportable Span
|
||||
*/
|
||||
public void tagSpan(Span span, String key, String value) {
|
||||
if (span != null && span.isExportable()) {
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRequestTagsFromHeaders(Map<String, ? extends Collection<String>> headers) {
|
||||
for (String name : this.traceKeys.getHttp().getHeaders()) {
|
||||
for (Map.Entry<String, ? extends Collection<String>> entry : headers.entrySet()) {
|
||||
addTagForEntry(name, entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addTagForEntry(String name, Collection<String> list) {
|
||||
String key = this.traceKeys.getHttp().getPrefix() + name.toLowerCase();
|
||||
String value = list.size() == 1 ? list.iterator().next()
|
||||
: StringUtils.collectionToDelimitedString(list, ",", "'", "'");
|
||||
this.tracer.addTag(key, value);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,17 +15,16 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
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.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
@@ -79,24 +78,29 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
private final SpanReporter spanReporter;
|
||||
private final SpanExtractor<HttpServletRequest> spanExtractor;
|
||||
private final SpanInjector<HttpServletResponse> spanInjector;
|
||||
private final HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
private UrlPathHelper urlPathHelper = new UrlPathHelper();
|
||||
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter,
|
||||
SpanExtractor<HttpServletRequest> spanExtractor, SpanInjector<HttpServletResponse> spanInjector) {
|
||||
SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
SpanInjector<HttpServletResponse> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), spanReporter,
|
||||
spanExtractor, spanInjector);
|
||||
spanExtractor, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern,
|
||||
SpanReporter spanReporter, SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
SpanInjector<HttpServletResponse> spanInjector) {
|
||||
SpanInjector<HttpServletResponse> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.skipPattern = skipPattern;
|
||||
this.spanReporter = spanReporter;
|
||||
this.spanExtractor = spanExtractor;
|
||||
this.spanInjector = spanInjector;
|
||||
this.httpTraceKeysInjector = httpTraceKeysInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,10 +119,9 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
spanFromRequest = createSpan(request, skip, spanFromRequest, name);
|
||||
Throwable exception = null;
|
||||
try {
|
||||
addRequestTags(request);
|
||||
this.spanInjector.inject(spanFromRequest, response);
|
||||
// Add headers before filter chain in case one of the filters flushes the
|
||||
// response...
|
||||
this.spanInjector.inject(spanFromRequest, response);
|
||||
filterChain.doFilter(request, response);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
@@ -149,6 +152,12 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
}
|
||||
}
|
||||
|
||||
private void addRequestTagsForParentSpan(HttpServletRequest request, Span spanFromRequest) {
|
||||
if (spanFromRequest.getName().contains("parent")) {
|
||||
addRequestTags(spanFromRequest, request);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a span and appends it as the current request's attribute
|
||||
*/
|
||||
@@ -157,9 +166,9 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
if (spanFromRequest != null) {
|
||||
return spanFromRequest;
|
||||
}
|
||||
Span parent = this.spanExtractor
|
||||
.joinTrace(request);
|
||||
Span parent = this.spanExtractor.joinTrace(request);
|
||||
if (parent != null) {
|
||||
addRequestTagsForParentSpan(request, parent);
|
||||
spanFromRequest = this.tracer.createSpan(name, parent);
|
||||
if (parent.isRemote()) {
|
||||
parent.logEvent(Span.SERVER_RECV);
|
||||
@@ -180,12 +189,10 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
}
|
||||
|
||||
/** Override to add annotations not defined in {@link TraceKeys}. */
|
||||
protected void addRequestTags(HttpServletRequest request) {
|
||||
protected void addRequestTags(Span span, HttpServletRequest request) {
|
||||
String uri = this.urlPathHelper.getPathWithinApplication(request);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getUrl(), getFullUrl(request));
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getHost(), request.getServerName());
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getPath(), uri);
|
||||
this.tracer.addTag(this.traceKeys.getHttp().getMethod(), request.getMethod());
|
||||
this.httpTraceKeysInjector.addRequestTags(span, getFullUrl(request),
|
||||
request.getServerName(), uri, request.getMethod());
|
||||
for (String name : this.traceKeys.getHttp().getHeaders()) {
|
||||
Enumeration<String> values = request.getHeaders(name);
|
||||
if (values.hasMoreElements()) {
|
||||
@@ -193,7 +200,7 @@ public class TraceFilter extends OncePerRequestFilter {
|
||||
ArrayList<String> list = Collections.list(values);
|
||||
String value = list.size() == 1 ? list.get(0)
|
||||
: StringUtils.collectionToDelimitedString(list, ",", "'", "'");
|
||||
this.tracer.addTag(key, value);
|
||||
this.httpTraceKeysInjector.tagSpan(span, key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,9 +76,10 @@ public class TraceWebAutoConfiguration {
|
||||
public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys,
|
||||
SkipPatternProvider skipPatternProvider, SpanReporter spanReporter,
|
||||
SpanExtractor<HttpServletRequest> spanExtractor,
|
||||
SpanInjector<HttpServletResponse> spanInjector) {
|
||||
SpanInjector<HttpServletResponse> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
return new TraceFilter(tracer, traceKeys, skipPatternProvider.skipPattern(),
|
||||
spanReporter, spanExtractor, spanInjector);
|
||||
spanReporter, spanExtractor, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -21,25 +21,26 @@ import java.net.URI;
|
||||
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.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* Abstraction over classes that interact with Http requests. Allows you
|
||||
* to enrich the request headers with trace related information.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
abstract class AbstractTraceHttpRequestInterceptor {
|
||||
|
||||
protected final Tracer tracer;
|
||||
protected final SpanInjector<HttpRequest> spanInjector;
|
||||
protected final HttpTraceKeysInjector keysInjector;
|
||||
|
||||
protected AbstractTraceHttpRequestInterceptor(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector) {
|
||||
SpanInjector<HttpRequest> spanInjector, HttpTraceKeysInjector keysInjector) {
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
this.keysInjector = keysInjector;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,6 +52,7 @@ abstract class AbstractTraceHttpRequestInterceptor {
|
||||
String spanName = uriScheme(uri) + ":" + uri.getPath();
|
||||
Span newSpan = this.tracer.createSpan(spanName);
|
||||
this.spanInjector.inject(newSpan, request);
|
||||
addRequestTags(request);
|
||||
newSpan.logEvent(Span.CLIENT_SEND);
|
||||
}
|
||||
|
||||
@@ -58,6 +60,17 @@ abstract class AbstractTraceHttpRequestInterceptor {
|
||||
return uri.getScheme() == null ? "http" : uri.getScheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds HTTP tags to the client side span
|
||||
*/
|
||||
protected void addRequestTags(HttpRequest request) {
|
||||
this.keysInjector.addRequestTags(request.getURI().toString(),
|
||||
request.getURI().getHost(),
|
||||
request.getURI().getPath(),
|
||||
request.getMethod().name(),
|
||||
request.getHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current span and log the client received event
|
||||
*/
|
||||
|
||||
@@ -21,6 +21,7 @@ import java.net.URI;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpRequest;
|
||||
@@ -54,9 +55,11 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
*
|
||||
* @see org.springframework.web.client.AsyncRestTemplate#AsyncRestTemplate(AsyncClientHttpRequestFactory)
|
||||
*/
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector<HttpRequest> spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate) {
|
||||
super(tracer, spanInjector);
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
this.asyncDelegate = asyncDelegate;
|
||||
this.syncDelegate = asyncDelegate instanceof ClientHttpRequestFactory ?
|
||||
(ClientHttpRequestFactory) asyncDelegate : defaultClientHttpRequestFactory();
|
||||
@@ -66,16 +69,20 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
* Default implementation that creates a {@link SimpleClientHttpRequestFactory} that
|
||||
* has a wrapped task executor via the {@link TraceAsyncListenableTaskExecutor}
|
||||
*/
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector<HttpRequest> spanInjector) {
|
||||
super(tracer, spanInjector);
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = defaultClientHttpRequestFactory();
|
||||
this.asyncDelegate = simpleClientHttpRequestFactory;
|
||||
this.syncDelegate = simpleClientHttpRequestFactory;
|
||||
}
|
||||
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector<HttpRequest> spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate, ClientHttpRequestFactory syncDelegate) {
|
||||
super(tracer, spanInjector);
|
||||
public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector,
|
||||
AsyncClientHttpRequestFactory asyncDelegate,
|
||||
ClientHttpRequestFactory syncDelegate,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
this.asyncDelegate = asyncDelegate;
|
||||
this.syncDelegate = syncDelegate;
|
||||
}
|
||||
@@ -97,6 +104,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
throws IOException {
|
||||
AsyncClientHttpRequest request = this.asyncDelegate
|
||||
.createAsyncRequest(uri, httpMethod);
|
||||
addRequestTags(request);
|
||||
publishStartEvent(request);
|
||||
return request;
|
||||
}
|
||||
@@ -105,6 +113,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp
|
||||
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod)
|
||||
throws IOException {
|
||||
ClientHttpRequest request = this.syncDelegate.createRequest(uri, httpMethod);
|
||||
addRequestTags(request);
|
||||
publishStartEvent(request);
|
||||
return request;
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import java.io.IOException;
|
||||
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.http.HttpRequest;
|
||||
import org.springframework.http.client.ClientHttpRequestExecution;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
@@ -38,8 +39,9 @@ import org.springframework.http.client.ClientHttpResponse;
|
||||
public class TraceRestTemplateInterceptor extends AbstractTraceHttpRequestInterceptor
|
||||
implements ClientHttpRequestInterceptor {
|
||||
|
||||
public TraceRestTemplateInterceptor(Tracer tracer, SpanInjector<HttpRequest> spanInjector) {
|
||||
super(tracer, spanInjector);
|
||||
public TraceRestTemplateInterceptor(Tracer tracer, SpanInjector<HttpRequest> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(tracer, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,7 @@ 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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
@@ -53,6 +54,7 @@ import org.springframework.web.client.AsyncRestTemplate;
|
||||
public class TraceWebAsyncClientAutoConfiguration {
|
||||
|
||||
@Autowired Tracer tracer;
|
||||
@Autowired HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
@Autowired SpanInjector<HttpRequest> spanInjector;
|
||||
@Autowired(required = false) ClientHttpRequestFactory clientHttpRequestFactory;
|
||||
@Autowired(required = false) AsyncClientHttpRequestFactory asyncClientHttpRequestFactory;
|
||||
@@ -71,7 +73,7 @@ public class TraceWebAsyncClientAutoConfiguration {
|
||||
(AsyncClientHttpRequestFactory) clientFactory : defaultClientHttpRequestFactory(this.tracer);
|
||||
}
|
||||
return new TraceAsyncClientHttpRequestFactoryWrapper(this.tracer, this.spanInjector,
|
||||
asyncClientFactory, clientFactory);
|
||||
asyncClientFactory, clientFactory, this.httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
private SimpleClientHttpRequestFactory defaultClientHttpRequestFactory(Tracer tracer) {
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
@@ -29,8 +28,10 @@ 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.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpRequest;
|
||||
@@ -55,8 +56,9 @@ public class TraceWebClientAutoConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer,
|
||||
SpanInjector<HttpRequest> spanInjector) {
|
||||
return new TraceRestTemplateInterceptor(tracer, spanInjector);
|
||||
SpanInjector<HttpRequest> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
return new TraceRestTemplateInterceptor(tracer, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -64,6 +66,12 @@ 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 {
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
|
||||
import feign.Feign;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
@@ -34,9 +35,9 @@ final class SleuthFeignBuilder {
|
||||
|
||||
private SleuthFeignBuilder() {}
|
||||
|
||||
static Feign.Builder builder(Tracer tracer) {
|
||||
static Feign.Builder builder(Tracer tracer, HttpTraceKeysInjector keysInjector) {
|
||||
return HystrixFeign.builder()
|
||||
.client(new TraceFeignClient(tracer))
|
||||
.client(new TraceFeignClient(tracer, keysInjector))
|
||||
.retryer(new TraceFeignRetryer(tracer))
|
||||
.decoder(new TraceFeignDecoder(tracer))
|
||||
.errorDecoder(new TraceFeignErrorDecoder(tracer));
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
@@ -37,21 +39,25 @@ import feign.RetryableException;
|
||||
final class TraceFeignClient extends FeignEventPublisher implements Client {
|
||||
|
||||
private final Client delegate;
|
||||
private final HttpTraceKeysInjector keysInjector;
|
||||
|
||||
TraceFeignClient(Tracer tracer) {
|
||||
TraceFeignClient(Tracer tracer, HttpTraceKeysInjector keysInjector) {
|
||||
super(tracer);
|
||||
this.delegate = new Client.Default(null, null);
|
||||
this.keysInjector = keysInjector;
|
||||
}
|
||||
|
||||
TraceFeignClient(Tracer tracer, Client delegate) {
|
||||
TraceFeignClient(Tracer tracer, Client delegate, HttpTraceKeysInjector keysInjector) {
|
||||
super(tracer);
|
||||
this.delegate = delegate;
|
||||
this.keysInjector = keysInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(Request request, Request.Options options) throws IOException {
|
||||
Response response = null;
|
||||
Response response;
|
||||
try {
|
||||
addRequestTags(request);
|
||||
response = this.delegate.execute(request, options);
|
||||
}
|
||||
catch (RetryableException | IOException e) {
|
||||
@@ -69,4 +75,13 @@ final class TraceFeignClient extends FeignEventPublisher implements Client {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds HTTP tags to the client side span
|
||||
*/
|
||||
private void addRequestTags(Request request) {
|
||||
URI uri = URI.create(request.url());
|
||||
this.keysInjector.addRequestTags(uri.toString(), uri.getHost(), uri.getPath(),
|
||||
request.method(), request.headers());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
import feign.FeignException;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import feign.codec.Decoder;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -39,18 +38,21 @@ import org.springframework.cloud.netflix.feign.FeignContext;
|
||||
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
|
||||
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import feign.Client;
|
||||
import feign.Feign;
|
||||
import feign.FeignException;
|
||||
import feign.RequestInterceptor;
|
||||
import feign.RequestTemplate;
|
||||
import feign.Response;
|
||||
import feign.codec.Decoder;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
|
||||
@@ -74,8 +76,8 @@ public class TraceFeignClientAutoConfiguration {
|
||||
@Scope("prototype")
|
||||
@ConditionalOnClass(HystrixCommand.class)
|
||||
@ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true)
|
||||
Feign.Builder feignHystrixBuilder(Tracer tracer, TraceKeys traceKeys) {
|
||||
return SleuthFeignBuilder.builder(tracer);
|
||||
Feign.Builder feignHystrixBuilder(Tracer tracer, HttpTraceKeysInjector keysInjector) {
|
||||
return SleuthFeignBuilder.builder(tracer, keysInjector);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -86,7 +88,6 @@ public class TraceFeignClientAutoConfiguration {
|
||||
FeignBeanPostProcessor feignBeanPostProcessor(TraceFeignObjectWrapper traceFeignObjectWrapper) {
|
||||
return new FeignBeanPostProcessor(traceFeignObjectWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -123,7 +124,7 @@ public class TraceFeignClientAutoConfiguration {
|
||||
* an existing one if a retry takes place.
|
||||
*/
|
||||
@Bean
|
||||
public RequestInterceptor traceIdRequestInterceptor(Tracer tracer) {
|
||||
RequestInterceptor traceIdRequestInterceptor(Tracer tracer) {
|
||||
return new TraceFeignRequestInterceptor(tracer, feignRequestTemplateInjector());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
|
||||
import feign.Client;
|
||||
import feign.Retryer;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Class that wraps Feign related classes into their Trace representative
|
||||
@@ -17,6 +19,7 @@ final class TraceFeignObjectWrapper {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private Tracer tracer;
|
||||
private HttpTraceKeysInjector keysInjector;
|
||||
|
||||
TraceFeignObjectWrapper(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
@@ -28,7 +31,7 @@ final class TraceFeignObjectWrapper {
|
||||
} else if (bean instanceof Retryer && !(bean instanceof TraceFeignRetryer)) {
|
||||
return new TraceFeignRetryer(getTracer(), (Retryer) bean);
|
||||
} else if (bean instanceof Client && !(bean instanceof TraceFeignClient)) {
|
||||
return new TraceFeignClient(getTracer(), (Client) bean);
|
||||
return new TraceFeignClient(getTracer(), (Client) bean, getHttpTraceKeysInjector());
|
||||
} else if (bean instanceof ErrorDecoder && !(bean instanceof TraceFeignErrorDecoder)) {
|
||||
return new TraceFeignErrorDecoder(getTracer(), (ErrorDecoder) bean);
|
||||
}
|
||||
@@ -36,9 +39,16 @@ final class TraceFeignObjectWrapper {
|
||||
}
|
||||
|
||||
private Tracer getTracer() {
|
||||
if (this.tracer==null) {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
private HttpTraceKeysInjector getHttpTraceKeysInjector() {
|
||||
if (this.keysInjector == null) {
|
||||
this.keysInjector = this.beanFactory.getBean(HttpTraceKeysInjector.class);
|
||||
}
|
||||
return this.keysInjector;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,12 +88,7 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
|
||||
public SpanAssert hasATag(String tagKey, String tagValue) {
|
||||
isNotNull();
|
||||
if (!this.actual.tags().containsKey(tagKey)) {
|
||||
String message = String.format("Expected span to have the tag with key <%s>. "
|
||||
+ "Found tags are <%s>", tagKey, this.actual.tags());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
assertThatTagIsPresent(tagKey);
|
||||
String foundTagValue = this.actual.tags().get(tagKey);
|
||||
if (!foundTagValue.equals(tagValue)) {
|
||||
String message = String.format("Expected span to have the tag with key <%s> and value <%s>. "
|
||||
@@ -104,6 +99,28 @@ public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
|
||||
return this;
|
||||
}
|
||||
|
||||
public SpanAssert matchesATag(String tagKey, String tagRegex) {
|
||||
isNotNull();
|
||||
assertThatTagIsPresent(tagKey);
|
||||
String foundTagValue = this.actual.tags().get(tagKey);
|
||||
if (!foundTagValue.matches(tagRegex)) {
|
||||
String message = String.format("Expected span to have the tag with key <%s> and match a regex <%s>. "
|
||||
+ "Found value for that tag is <%s>", tagKey, tagRegex, foundTagValue);
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private void assertThatTagIsPresent(String tagKey) {
|
||||
if (!this.actual.tags().containsKey(tagKey)) {
|
||||
String message = String.format("Expected span to have the tag with key <%s>. "
|
||||
+ "Found tags are <%s>", tagKey, this.actual.tags());
|
||||
log.error(message);
|
||||
failWithMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
public SpanAssert hasLoggedAnEvent(String event) {
|
||||
isNotNull();
|
||||
if (!this.actual.logs().stream().map(org.springframework.cloud.sleuth.Log::getEvent)
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
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.instrument.messaging.TraceChannelInterceptorTests.App;
|
||||
@@ -195,5 +196,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
return new MessagingTemplate(channel());
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.IntegrationTest;
|
||||
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.instrument.messaging.TraceContextPropagationChannelInterceptorTests.App;
|
||||
@@ -98,5 +99,9 @@ public class TraceContextPropagationChannelInterceptorTests {
|
||||
return new QueueChannel();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler testSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.messaging.websocket;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
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.test.SpringApplicationConfiguration;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptor;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -32,6 +33,8 @@ import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessa
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -67,5 +70,9 @@ public class TraceWebSocketAutoConfigurationTest {
|
||||
public void registerStompEndpoints(StompEndpointRegistry registry) {
|
||||
registry.addEndpoint("/hello").withSockJS();
|
||||
}
|
||||
|
||||
@Bean Sampler testSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,5 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
@@ -16,8 +9,10 @@ import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
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.instrument.DefaultTestAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -37,6 +32,13 @@ import org.springframework.web.client.RestTemplate;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
import org.springframework.web.context.request.async.WebAsyncTask;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringApplicationConfiguration(classes = {
|
||||
RestTemplateTraceAspectIntegrationTests.Config.class })
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -119,6 +121,11 @@ public class RestTemplateTraceAspectIntegrationTests {
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
|
||||
@RestController
|
||||
|
||||
@@ -59,7 +59,8 @@ public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegra
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys,
|
||||
new NoOpSpanReporter(), this.spanExtractor, this.spanInjector));
|
||||
new NoOpSpanReporter(), this.spanExtractor, this.spanInjector,
|
||||
this.httpTraceKeysInjector));
|
||||
}
|
||||
|
||||
private MvcResult whenSentPingWithTraceIdAndNotSampling(Long traceId)
|
||||
|
||||
@@ -16,18 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -35,9 +31,11 @@ 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.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -53,19 +51,18 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(TraceFilterCustomExtractorTests.Config.class)
|
||||
@WebIntegrationTest(randomPort = true)
|
||||
@DirtiesContext
|
||||
public class TraceFilterCustomExtractorTests {
|
||||
@Autowired
|
||||
Random random;
|
||||
@Autowired
|
||||
RestTemplate restTemplate;
|
||||
@Autowired
|
||||
Config config;
|
||||
@Autowired
|
||||
CustomRestController customRestController;
|
||||
@Autowired Random random;
|
||||
@Autowired RestTemplate restTemplate;
|
||||
@Autowired Config config;
|
||||
@Autowired CustomRestController customRestController;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -126,6 +123,11 @@ public class TraceFilterCustomExtractorTests {
|
||||
CustomRestController customRestController() {
|
||||
return new CustomRestController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
|
||||
// tag::extractor[]
|
||||
|
||||
@@ -16,10 +16,12 @@ import org.slf4j.MDC;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties;
|
||||
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.instrument.DefaultTestAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -165,5 +167,10 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
return managementServerProperties;
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,7 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
new Random(), new DefaultSpanNamer(),
|
||||
new NoOpSpanLogger(), new NoOpSpanReporter());
|
||||
private TraceKeys traceKeys = new TraceKeys();
|
||||
private HttpTraceKeysInjector keysInjector = new HttpTraceKeysInjector(this.tracer, this.traceKeys);
|
||||
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
@@ -73,7 +74,7 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
public void startsNewTrace() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(),
|
||||
new HttpServletRequestExtractor(new Random(), Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
new HttpServletResponseInjector());
|
||||
new HttpServletResponseInjector(), keysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
@@ -85,7 +86,7 @@ public class TraceFilterMockChainIntegrationTests {
|
||||
.header(Span.TRACE_ID_NAME, generator.nextLong()).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(),
|
||||
new HttpServletRequestExtractor(new Random(), Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)),
|
||||
new HttpServletResponseInjector());
|
||||
new HttpServletResponseInjector(), keysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
@@ -16,11 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -38,6 +38,7 @@ 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.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockFilterChain;
|
||||
@@ -46,8 +47,8 @@ import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockServletContext;
|
||||
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.entry;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.assertThat;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.entry;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -60,13 +61,14 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder
|
||||
public class TraceFilterTests {
|
||||
|
||||
@Mock SpanLogger spanLogger;
|
||||
@Mock SpanReporter spanReporter;
|
||||
ArrayListSpanAccumulator spanReporter = new ArrayListSpanAccumulator();
|
||||
SpanExtractor<HttpServletRequest> spanExtractor = new HttpServletRequestExtractor(new Random(), Pattern
|
||||
.compile(TraceFilter.DEFAULT_SKIP_PATTERN));
|
||||
SpanInjector<HttpServletResponse> spanInjector = new HttpServletResponseInjector();
|
||||
|
||||
private Tracer tracer;
|
||||
private TraceKeys traceKeys = new TraceKeys();
|
||||
private HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
private Span span;
|
||||
|
||||
@@ -90,6 +92,7 @@ public class TraceFilterTests {
|
||||
this.response = new MockHttpServletResponse();
|
||||
this.response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
this.filterChain = new MockFilterChain();
|
||||
this.httpTraceKeysInjector = new HttpTraceKeysInjector(this.tracer, this.traceKeys);
|
||||
}
|
||||
|
||||
public MockHttpServletRequestBuilder builder() {
|
||||
@@ -101,7 +104,7 @@ public class TraceFilterTests {
|
||||
public void notTraced() throws Exception {
|
||||
this.sampler = NeverSampler.INSTANCE;
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
|
||||
this.request = get("/favicon.ico").accept(MediaType.ALL)
|
||||
.buildRequest(new MockServletContext());
|
||||
@@ -115,9 +118,11 @@ public class TraceFilterTests {
|
||||
@Test
|
||||
public void startsNewTrace() throws Exception {
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
verifyHttpTags();
|
||||
|
||||
verifyCurrentSpanStatusCode(HttpStatus.OK);
|
||||
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
@@ -129,15 +134,27 @@ public class TraceFilterTests {
|
||||
.header(Span.PARENT_ID_NAME, Span.idToHex(3L))
|
||||
.buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
// this creates a child span which is why we'd expect the parents to include 1L)
|
||||
assertThat(this.span.getParents()).containsOnly(1L);
|
||||
assertThat(parentSpan())
|
||||
.hasATag("http.url", "http://localhost/?foo=bar")
|
||||
.hasATag("http.host", "localhost")
|
||||
.hasATag("http.path", "/")
|
||||
.hasATag("http.method", "GET");
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
private Span parentSpan() {
|
||||
Optional<Span> parent = this.spanReporter.getSpans().stream()
|
||||
.filter(span -> span.getName().contains("parent")).findFirst();
|
||||
assertThat(parent.isPresent()).isTrue();
|
||||
return parent.get();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void continuesSpanInRequestAttr() throws Exception {
|
||||
Span span = this.tracer.createSpan("http:foo");
|
||||
@@ -146,11 +163,9 @@ public class TraceFilterTests {
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
verifyHttpTags();
|
||||
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
@@ -160,10 +175,10 @@ public class TraceFilterTests {
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
verifyHttpTags();
|
||||
verifyParentSpanHttpTags();
|
||||
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
@@ -175,11 +190,11 @@ public class TraceFilterTests {
|
||||
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
assertThat(this.span.tags()).contains(entry("http.x-foo", "bar"));
|
||||
assertThat(parentSpan().tags()).contains(entry("http.x-foo", "bar"));
|
||||
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
@@ -189,7 +204,7 @@ public class TraceFilterTests {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, 10L)
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanIsStoppedVeryfingReporter(),
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
}
|
||||
@@ -205,20 +220,22 @@ public class TraceFilterTests {
|
||||
|
||||
this.traceKeys.getHttp().getHeaders().add("x-foo");
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
this.request.addHeader("X-Foo", "bar");
|
||||
this.request.addHeader("X-Foo", "spam");
|
||||
filter.doFilter(this.request, this.response, this.filterChain);
|
||||
|
||||
assertThat(this.span.tags()).contains(entry("http.x-foo", "'bar','spam'"));
|
||||
assertThat(parentSpan().tags()).contains(entry("http.x-foo", "'bar','spam'"));
|
||||
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void catchesException() throws Exception {
|
||||
this.request = builder().header(Span.SPAN_ID_NAME, 10L)
|
||||
.header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext());
|
||||
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter,
|
||||
this.spanExtractor, this.spanInjector);
|
||||
this.spanExtractor, this.spanInjector, this.httpTraceKeysInjector);
|
||||
this.filterChain = new MockFilterChain() {
|
||||
@Override
|
||||
public void doFilter(javax.servlet.ServletRequest request,
|
||||
@@ -233,24 +250,28 @@ public class TraceFilterTests {
|
||||
catch (RuntimeException e) {
|
||||
assertEquals("Planned", e.getMessage());
|
||||
}
|
||||
verifyHttpTags(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
verifyParentSpanHttpTags(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
|
||||
assertNull(TestSpanContextHolder.getCurrentSpan());
|
||||
}
|
||||
|
||||
public void verifyHttpTags() {
|
||||
verifyHttpTags(HttpStatus.OK);
|
||||
public void verifyParentSpanHttpTags() {
|
||||
verifyParentSpanHttpTags(HttpStatus.OK);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the expansion of {@link import
|
||||
* org.springframework.cloud.sleuth.instrument.TraceKeys}.
|
||||
*/
|
||||
public void verifyHttpTags(HttpStatus status) {
|
||||
assertThat(this.span.tags()).contains(entry("http.host", "localhost"),
|
||||
public void verifyParentSpanHttpTags(HttpStatus status) {
|
||||
assertThat(parentSpan().tags()).contains(entry("http.host", "localhost"),
|
||||
entry("http.url", "http://localhost/?foo=bar"), entry("http.path", "/"),
|
||||
entry("http.method", "GET"));
|
||||
verifyCurrentSpanStatusCode(status);
|
||||
|
||||
}
|
||||
|
||||
private void verifyCurrentSpanStatusCode(HttpStatus status) {
|
||||
// Status is only interesting in non-success case. Omitting it saves at least
|
||||
// 20bytes per span.
|
||||
if (status.is2xxSuccessful()) {
|
||||
|
||||
@@ -29,7 +29,9 @@ 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.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;
|
||||
@@ -59,7 +61,8 @@ public class TraceRestTemplateInterceptorIntegrationTests {
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(),
|
||||
new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter());
|
||||
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector())));
|
||||
new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys()))));
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
@@ -68,7 +71,7 @@ public class TraceRestTemplateInterceptorIntegrationTests {
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
// issue #198
|
||||
// Issue #198
|
||||
@Test
|
||||
public void spanRemovedFromThreadUponException() throws IOException {
|
||||
this.mockWebServer.enqueue(new MockResponse().setSocketPolicy(SocketPolicy.DISCONNECT_AT_START));
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
@@ -26,12 +27,14 @@ import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
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.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.http.HttpHeaders;
|
||||
import org.springframework.http.client.ClientHttpRequestInterceptor;
|
||||
import org.springframework.test.web.client.MockMvcClientHttpRequestFactory;
|
||||
@@ -60,12 +63,15 @@ public class TraceRestTemplateInterceptorTests {
|
||||
|
||||
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 TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(),
|
||||
new HttpTraceKeysInjector(this.tracer, new TraceKeys()))));
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
}
|
||||
|
||||
@@ -95,6 +101,21 @@ public class TraceRestTemplateInterceptorTests {
|
||||
then(Span.hexToId(headers.get(Span.PARENT_ID_NAME))).isEqualTo(2L);
|
||||
}
|
||||
|
||||
// Issue #290
|
||||
@Test
|
||||
public void requestHeadersAddedWhenTracing() {
|
||||
this.tracer.continueSpan(Span.builder().traceId(1L).spanId(2L).parent(3L).build());
|
||||
|
||||
this.template.getForEntity("/foo?a=b", Map.class);
|
||||
|
||||
List<Span> spans = spanAccumulator.getSpans();
|
||||
then(spans).isNotEmpty();
|
||||
then(spans.get(0))
|
||||
.hasATag("http.url", "/foo?a=b")
|
||||
.hasATag("http.path", "/foo")
|
||||
.hasATag("http.method", "GET");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSampledHeaderAddedWhenNotExportable() {
|
||||
this.tracer.continueSpan(Span.builder().traceId(1L).spanId(2L).exportable(false).build());
|
||||
@@ -145,6 +166,10 @@ public class TraceRestTemplateInterceptorTests {
|
||||
return map;
|
||||
}
|
||||
|
||||
@RequestMapping("/foo")
|
||||
public void foo() {
|
||||
}
|
||||
|
||||
@RequestMapping("/exception")
|
||||
public Map<String, String> exception() {
|
||||
throw new RuntimeException("foo");
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -37,9 +33,11 @@ import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.SleuthAssertions;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -51,6 +49,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = {
|
||||
WebClientDiscoveryExceptionTests.TestConfiguration.class })
|
||||
@@ -59,13 +61,9 @@ import org.springframework.web.client.RestTemplate;
|
||||
@DirtiesContext
|
||||
public class WebClientDiscoveryExceptionTests {
|
||||
|
||||
@Autowired
|
||||
TestFeignInterfaceWithException testFeignInterfaceWithException;
|
||||
@Autowired
|
||||
@LoadBalanced
|
||||
RestTemplate template;
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
@Autowired TestFeignInterfaceWithException testFeignInterfaceWithException;
|
||||
@Autowired @LoadBalanced RestTemplate template;
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
@Before
|
||||
public void open() {
|
||||
@@ -128,6 +126,11 @@ public class WebClientDiscoveryExceptionTests {
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
|
||||
@@ -16,15 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import static junitparams.JUnitParamsRunner.$;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
@@ -40,9 +39,11 @@ import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.assertions.SleuthAssertions;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -54,13 +55,14 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import junitparams.JUnitParamsRunner;
|
||||
import junitparams.Parameters;
|
||||
|
||||
import static junitparams.JUnitParamsRunner.$;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(JUnitParamsRunner.class)
|
||||
@SpringApplicationConfiguration(classes = {
|
||||
WebClientExceptionTests.TestConfiguration.class })
|
||||
@@ -73,13 +75,9 @@ public class WebClientExceptionTests {
|
||||
@Rule
|
||||
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
|
||||
@Autowired
|
||||
TestFeignInterfaceWithException testFeignInterfaceWithException;
|
||||
@Autowired
|
||||
@LoadBalanced
|
||||
RestTemplate template;
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
@Autowired TestFeignInterfaceWithException testFeignInterfaceWithException;
|
||||
@Autowired @LoadBalanced RestTemplate template;
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
@Before
|
||||
public void open() {
|
||||
@@ -139,6 +137,11 @@ public class WebClientExceptionTests {
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler alwaysSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -16,17 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import static junitparams.JUnitParamsRunner.$;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Random;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
@@ -41,9 +42,11 @@ import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
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.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -58,52 +61,52 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
import junitparams.JUnitParamsRunner;
|
||||
import junitparams.Parameters;
|
||||
|
||||
import static junitparams.JUnitParamsRunner.$;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
@RunWith(JUnitParamsRunner.class)
|
||||
@SpringApplicationConfiguration(classes = { WebClientTests.TestConfiguration.class })
|
||||
@WebIntegrationTest(value = { "spring.application.name=fooservice" }, randomPort = true)
|
||||
public class WebClientTests {
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule SCR = new SpringClassRule();
|
||||
@Rule
|
||||
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
@ClassRule public static final SpringClassRule SCR = new SpringClassRule();
|
||||
@Rule public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
|
||||
@Autowired
|
||||
TestFeignInterface testFeignInterface;
|
||||
@Autowired
|
||||
@LoadBalanced
|
||||
RestTemplate template;
|
||||
@Autowired
|
||||
Listener listener;
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
@Autowired TestFeignInterface testFeignInterface;
|
||||
@Autowired @LoadBalanced RestTemplate template;
|
||||
@Autowired Listener listener;
|
||||
@Autowired Tracer tracer;
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
TestSpanContextHolder.removeCurrentSpan();
|
||||
this.listener.getEvents().clear();
|
||||
this.listener.getSpans().clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
@Parameters
|
||||
@SuppressWarnings("unchecked")
|
||||
public void shouldCreateANewSpanWhenNoPreviousTracingWasPresent(
|
||||
public void shouldCreateANewSpanWithClientSideTagsWhenNoPreviousTracingWasPresent(
|
||||
ResponseEntityProvider provider) {
|
||||
ResponseEntity<String> response = provider.get(this);
|
||||
|
||||
then(getHeader(response, Span.TRACE_ID_NAME)).isNotNull();
|
||||
then(getHeader(response, Span.SPAN_ID_NAME)).isNotNull();
|
||||
then(this.listener.getEvents()).isNotEmpty();
|
||||
then(this.listener.getSpans()).isNotEmpty();
|
||||
Optional<Span> noTraceSpan = this.listener.getSpans().stream().filter(span ->
|
||||
"http:/notrace".equals(span.getName()) && !span.tags().isEmpty()).findFirst();
|
||||
then(noTraceSpan.isPresent()).isTrue();
|
||||
// TODO: matches cause there is an issue with Feign not providing the full URL at the interceptor level
|
||||
then(noTraceSpan.get()).matchesATag("http.url", ".*/notrace")
|
||||
.hasATag("http.path", "/notrace")
|
||||
.hasATag("http.method", "GET");
|
||||
}
|
||||
|
||||
Object[] parametersForShouldCreateANewSpanWhenNoPreviousTracingWasPresent() {
|
||||
Object[] parametersForShouldCreateANewSpanWithClientSideTagsWhenNoPreviousTracingWasPresent() {
|
||||
return $(
|
||||
(ResponseEntityProvider) (tests) -> tests.testFeignInterface.getNoTrace(),
|
||||
(ResponseEntityProvider) (tests) -> tests.template
|
||||
@@ -123,7 +126,7 @@ public class WebClientTests {
|
||||
|
||||
then(response.getBody().get(Span.TRACE_ID_NAME)).isNotNull();
|
||||
then(response.getBody().get(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
then(this.listener.getEvents()).isNotEmpty();
|
||||
then(this.listener.getSpans()).isNotEmpty();
|
||||
}
|
||||
|
||||
Object[] parametersForShouldPropagateNotSamplingHeader() {
|
||||
@@ -152,7 +155,7 @@ public class WebClientTests {
|
||||
}
|
||||
|
||||
private Span spanWithClientEvents() {
|
||||
return this.listener.getEvents().stream()
|
||||
return this.listener.getSpans().stream()
|
||||
.filter(span -> span.logs().stream()
|
||||
.filter(log -> log.getEvent().contains(Span.CLIENT_RECV)
|
||||
|| log.getEvent().contains(Span.CLIENT_SEND))
|
||||
@@ -241,13 +244,18 @@ public class WebClientTests {
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean
|
||||
Sampler testSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
public static class Listener implements SpanReporter {
|
||||
private List<Span> events = new ArrayList<>();
|
||||
|
||||
public List<Span> getEvents() {
|
||||
public List<Span> getSpans() {
|
||||
return this.events;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,15 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
|
||||
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 feign.codec.Decoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
@@ -37,9 +40,11 @@ import org.springframework.cloud.netflix.feign.EnableFeignClients;
|
||||
import org.springframework.cloud.netflix.feign.FeignClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
||||
import org.springframework.cloud.netflix.ribbon.RibbonClients;
|
||||
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.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -53,9 +58,8 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import feign.codec.Decoder;
|
||||
import feign.codec.ErrorDecoder;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@@ -169,6 +173,10 @@ public class FeignClientServerErrorTests {
|
||||
return new RestTemplate();
|
||||
}
|
||||
|
||||
@Bean Sampler testSampler() {
|
||||
return new AlwaysSampler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@FeignClient(value = "fooservice")
|
||||
|
||||
@@ -9,6 +9,7 @@ import org.springframework.cloud.sleuth.SpanExtractor;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.util.ExceptionUtils;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.test.context.web.WebAppConfiguration;
|
||||
@@ -36,6 +37,7 @@ public abstract class AbstractMvcIntegrationTest {
|
||||
@Autowired protected TraceKeys traceKeys;
|
||||
@Autowired protected SpanExtractor<HttpServletRequest> spanExtractor;
|
||||
@Autowired protected SpanInjector<HttpServletResponse> spanInjector;
|
||||
@Autowired protected HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
|
||||
Reference in New Issue
Block a user