Updated zuul
This commit is contained in:
@@ -1158,7 +1158,7 @@ Decorating Spring Integration Executor Channel with `TraceableExecutorService` w
|
||||
|
||||
=== Zuul
|
||||
|
||||
We're registering Zuul filters to propagate the tracing information (the request header is enriched with tracing data).
|
||||
We're instrumenting the Zuul Ribbon integration by enriching the Ribbon requests with tracing information.
|
||||
To disable Zuul support set the `spring.sleuth.zuul.enabled` property to `false`.
|
||||
|
||||
== Running examples
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpClientHandler;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.TraceContext;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* The pre and post filters use the same handler logic
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 2.0.0
|
||||
*/
|
||||
abstract class AbstractTraceZuulFilter extends ZuulFilter {
|
||||
|
||||
static final String ZUUL_CURRENT_SPAN =
|
||||
AbstractTraceZuulFilter.class.getName() + ".CURRENT_SPAN";
|
||||
|
||||
static final Propagation.Setter<RequestContext, String> SETTER = new Propagation.Setter<RequestContext, String>() {
|
||||
@Override public void put(RequestContext carrier, String key, String value) {
|
||||
carrier.getZuulRequestHeaders().put(key, value);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "RequestContext::getZuulRequestHeaders::put";
|
||||
}
|
||||
};
|
||||
|
||||
final Tracer tracer;
|
||||
HttpClientHandler<RequestContext, HttpServletResponse> handler;
|
||||
TraceContext.Injector<RequestContext> injector;
|
||||
|
||||
AbstractTraceZuulFilter(HttpTracing httpTracing) {
|
||||
this.tracer = httpTracing.tracing().tracer();
|
||||
this.handler = HttpClientHandler
|
||||
.create(httpTracing, new AbstractTraceZuulFilter.HttpAdapter());
|
||||
this.injector = httpTracing.tracing().propagation().injector(SETTER);
|
||||
}
|
||||
|
||||
static final class HttpAdapter
|
||||
extends brave.http.HttpClientAdapter<RequestContext, HttpServletResponse> {
|
||||
|
||||
@Override public String method(RequestContext request) {
|
||||
return request.getRequest().getMethod();
|
||||
}
|
||||
|
||||
@Override public String url(RequestContext request) {
|
||||
return request.getRequest().getRequestURI();
|
||||
}
|
||||
|
||||
@Override public String requestHeader(RequestContext request, String name) {
|
||||
Object result = request.getZuulRequestHeaders().get(name);
|
||||
return result != null ? result.toString() : null;
|
||||
}
|
||||
|
||||
@Override public Integer statusCode(HttpServletResponse response) {
|
||||
return response.getStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.http.HttpClientAdapter;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
|
||||
/**
|
||||
* Customization of a Ribbon request for Apache HttpClient
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class ApacheHttpClientRibbonRequestCustomizer extends
|
||||
SpanInjectingRibbonRequestCustomizer<RequestBuilder> {
|
||||
|
||||
static final Propagation.Setter<RequestBuilder, String> SETTER = new Propagation.Setter<RequestBuilder, String>() {
|
||||
@Override public void put(RequestBuilder carrier, String key, String value) {
|
||||
if (carrier.getFirstHeader(key) != null) {
|
||||
return;
|
||||
}
|
||||
carrier.addHeader(key, value);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "RequestBuilder::addHeader";
|
||||
}
|
||||
};
|
||||
|
||||
ApacheHttpClientRibbonRequestCustomizer(HttpTracing tracer) {
|
||||
super(tracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Class aClass) {
|
||||
return aClass == RequestBuilder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpClientAdapter<RequestBuilder, RequestBuilder> handlerClientAdapter() {
|
||||
return new HttpClientAdapter<RequestBuilder, RequestBuilder>() {
|
||||
@Override public String method(RequestBuilder request) {
|
||||
return request.getMethod();
|
||||
}
|
||||
|
||||
@Override public String url(RequestBuilder request) {
|
||||
return request.getUri().toString();
|
||||
}
|
||||
|
||||
@Override public String requestHeader(RequestBuilder request, String name) {
|
||||
Header header = request.getFirstHeader(name);
|
||||
if (header == null) {
|
||||
return null;
|
||||
}
|
||||
return header.getValue();
|
||||
}
|
||||
|
||||
@Override public Integer statusCode(RequestBuilder response) {
|
||||
throw new UnsupportedOperationException("response not supported");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override protected Propagation.Setter<RequestBuilder, String> setter() {
|
||||
return SETTER;
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
final class HttpAdapter
|
||||
extends brave.http.HttpClientAdapter<RequestContext, HttpServletResponse> {
|
||||
|
||||
@Override public String method(RequestContext request) {
|
||||
return request.getRequest().getMethod();
|
||||
}
|
||||
|
||||
@Override public String url(RequestContext request) {
|
||||
return request.getRequest().getRequestURI();
|
||||
}
|
||||
|
||||
@Override public String requestHeader(RequestContext request, String name) {
|
||||
Object result = request.getZuulRequestHeaders().get(name);
|
||||
return result != null ? result.toString() : null;
|
||||
}
|
||||
|
||||
@Override public Integer statusCode(HttpServletResponse response) {
|
||||
return response.getStatus();
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.http.HttpClientAdapter;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Customization of a Ribbon request for OkHttp
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class OkHttpClientRibbonRequestCustomizer extends
|
||||
SpanInjectingRibbonRequestCustomizer<Request.Builder> {
|
||||
|
||||
static final Propagation.Setter<Request.Builder, String> SETTER =
|
||||
new Propagation.Setter<Request.Builder, String>() {
|
||||
@Override public void put(Request.Builder carrier, String key, String value) {
|
||||
if (carrier.build().header(key) != null) {
|
||||
return;
|
||||
}
|
||||
carrier.addHeader(key, value);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "RequestBuilder::addHeader";
|
||||
}
|
||||
};
|
||||
|
||||
OkHttpClientRibbonRequestCustomizer(HttpTracing tracer) {
|
||||
super(tracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Class aClass) {
|
||||
return aClass == Request.Builder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpClientAdapter<Request.Builder, Request.Builder> handlerClientAdapter() {
|
||||
return new HttpClientAdapter<Request.Builder, Request.Builder>() {
|
||||
@Override public String method(Request.Builder request) {
|
||||
return request.build().method();
|
||||
}
|
||||
|
||||
@Override public String url(Request.Builder request) {
|
||||
return request.build().url().uri().toString();
|
||||
}
|
||||
|
||||
@Override public String requestHeader(Request.Builder request, String name) {
|
||||
return request.build().header(name);
|
||||
}
|
||||
|
||||
@Override public Integer statusCode(Request.Builder response) {
|
||||
throw new UnsupportedOperationException("response not supported");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override protected Propagation.Setter<Request.Builder, String> setter() {
|
||||
return SETTER;
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.http.HttpClientAdapter;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
|
||||
/**
|
||||
* Customization of a Ribbon request for Netflix HttpClient
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class RestClientRibbonRequestCustomizer extends
|
||||
SpanInjectingRibbonRequestCustomizer<HttpRequest.Builder> {
|
||||
|
||||
static final Propagation.Setter<HttpRequest.Builder, String> SETTER =
|
||||
new Propagation.Setter<HttpRequest.Builder, String>() {
|
||||
@Override public void put(HttpRequest.Builder carrier, String key, String value) {
|
||||
if (carrier.build().getHttpHeaders().containsHeader(key)) {
|
||||
return;
|
||||
}
|
||||
carrier.header(key, value);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return "RequestBuilder::addHeader";
|
||||
}
|
||||
};
|
||||
|
||||
RestClientRibbonRequestCustomizer(HttpTracing tracer) {
|
||||
super(tracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Class aClass) {
|
||||
return aClass == HttpRequest.Builder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HttpClientAdapter<HttpRequest.Builder, HttpRequest.Builder> handlerClientAdapter() {
|
||||
return new HttpClientAdapter<HttpRequest.Builder, HttpRequest.Builder>() {
|
||||
@Override public String method(HttpRequest.Builder request) {
|
||||
return request.build().getVerb().verb();
|
||||
}
|
||||
|
||||
@Override public String url(HttpRequest.Builder request) {
|
||||
return request.build().getUri().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String requestHeader(HttpRequest.Builder request, String name) {
|
||||
return request.build().getHttpHeaders().getFirstValue(name);
|
||||
}
|
||||
|
||||
@Override public Integer statusCode(HttpRequest.Builder response) {
|
||||
throw new UnsupportedOperationException("response not supported");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override protected Propagation.Setter<HttpRequest.Builder, String> setter() {
|
||||
return SETTER;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpClientHandler;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
|
||||
/**
|
||||
* Abstraction over customization of Ribbon Requests. All clients will inject the span
|
||||
* into their respective context. The only difference is how those contexts set the headers.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
abstract class SpanInjectingRibbonRequestCustomizer<T> implements RibbonRequestCustomizer<T> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(SpanInjectingRibbonRequestCustomizer.class);
|
||||
|
||||
private final Tracer tracer;
|
||||
HttpClientHandler<T, T> handler;
|
||||
TraceContext.Injector<T> injector;
|
||||
|
||||
SpanInjectingRibbonRequestCustomizer(HttpTracing httpTracing) {
|
||||
this.tracer = httpTracing.tracing().tracer();
|
||||
this.handler = HttpClientHandler
|
||||
.create(httpTracing, handlerClientAdapter());
|
||||
this.injector = httpTracing.tracing().propagation().injector(setter());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void customize(T context) {
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
this.handler.handleSend(this.injector, context);
|
||||
return;
|
||||
}
|
||||
Span childSpan = this.handler.handleSend(this.injector, context, span);
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(childSpan)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span in the RibbonRequestCustomizer is" + span);
|
||||
}
|
||||
} finally {
|
||||
childSpan.finish();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract brave.http.HttpClientAdapter<T, T> handlerClientAdapter();
|
||||
|
||||
protected abstract Propagation.Setter<T, String> setter();
|
||||
|
||||
Span getCurrentSpan() {
|
||||
return this.tracer.currentSpan();
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**8
|
||||
* A post request {@link ZuulFilter} that publishes an event upon start of the filtering
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TracePostZuulFilter extends AbstractTraceZuulFilter {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TracePostZuulFilter.class);
|
||||
|
||||
public static ZuulFilter create(Tracing tracing) {
|
||||
return new TracePostZuulFilter(HttpTracing.create(tracing));
|
||||
}
|
||||
|
||||
public static ZuulFilter create(HttpTracing httpTracing) {
|
||||
return new TracePostZuulFilter(httpTracing);
|
||||
}
|
||||
|
||||
TracePostZuulFilter(HttpTracing httpTracing) {
|
||||
super(httpTracing);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return getCurrentSpan() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() {
|
||||
Span span = getCurrentSpan();
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Closing current client span " + span);
|
||||
}
|
||||
HttpServletResponse response = RequestContext.getCurrentContext()
|
||||
.getResponse();
|
||||
this.handler.handleReceive(response, null, span);
|
||||
} finally {
|
||||
if (span != null) {
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Span getCurrentSpan() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
if (ctx == null || ctx.getRequest() == null) {
|
||||
return null;
|
||||
}
|
||||
return (Span) ctx.getRequest().getAttribute(ZUUL_CURRENT_SPAN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterType() {
|
||||
return "post";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceFilter;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceRequestAttributes;
|
||||
|
||||
import com.netflix.zuul.ExecutionStatus;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.ZuulFilterResult;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TracePreZuulFilter extends AbstractTraceZuulFilter {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TracePreZuulFilter.class);
|
||||
private static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName() + ".TRACE";
|
||||
private static final String TRACE_CLOSE_SPAN_REQUEST_ATTR =
|
||||
TraceFilter.class.getName() + ".CLOSE_SPAN";
|
||||
|
||||
public static ZuulFilter create(Tracing tracing, ErrorParser errorParser) {
|
||||
return new TracePreZuulFilter(HttpTracing.create(tracing), errorParser);
|
||||
}
|
||||
|
||||
public static ZuulFilter create(HttpTracing httpTracing, ErrorParser errorParser) {
|
||||
return new TracePreZuulFilter(httpTracing, errorParser);
|
||||
}
|
||||
|
||||
private final ErrorParser errorParser;
|
||||
|
||||
TracePreZuulFilter(HttpTracing httpTracing, ErrorParser errorParser) {
|
||||
super(httpTracing);
|
||||
this.errorParser = errorParser;
|
||||
}
|
||||
|
||||
@Override public ZuulFilterResult runFilter() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
Span span = this.handler.handleSend(this.injector, ctx);
|
||||
ZuulFilterResult result = null;
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
markRequestAsHandled(ctx, span);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("New Zuul Span is " + span + "");
|
||||
}
|
||||
result = super.runFilter();
|
||||
return result;
|
||||
}
|
||||
finally {
|
||||
if (result != null && ExecutionStatus.SUCCESS != result.getStatus()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(
|
||||
"The result of Zuul filter execution was not successful thus "
|
||||
+ "will close the current span " + span);
|
||||
}
|
||||
this.errorParser.parseErrorTags(span, result.getException());
|
||||
span.finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TraceFilter will not create the "fallback" span
|
||||
private void markRequestAsHandled(RequestContext ctx, Span span) {
|
||||
ctx.getRequest()
|
||||
.setAttribute(TraceRequestAttributes.HANDLED_SPAN_REQUEST_ATTR, "true");
|
||||
ctx.getRequest().setAttribute(TraceRequestAttributes.ERROR_HANDLED_SPAN_REQUEST_ATTR,
|
||||
"true");
|
||||
ctx.getRequest().setAttribute(TRACE_REQUEST_ATTR, span);
|
||||
ctx.getRequest().setAttribute(TRACE_CLOSE_SPAN_REQUEST_ATTR, true);
|
||||
ctx.getRequest().setAttribute(ZUUL_CURRENT_SPAN, span);
|
||||
}
|
||||
|
||||
@Override public String filterType() {
|
||||
return "pre";
|
||||
}
|
||||
|
||||
@Override public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public Object run() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -16,11 +16,23 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpClientHandler;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.TraceContext;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.http.client.ClientHttpResponse;
|
||||
import rx.Observable;
|
||||
|
||||
/**
|
||||
* Propagates traces downstream via http headers that contain trace metadata.
|
||||
@@ -31,25 +43,77 @@ import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory
|
||||
*/
|
||||
class TraceRibbonCommandFactory implements RibbonCommandFactory {
|
||||
|
||||
private final RibbonCommandFactory delegate;
|
||||
private final HttpTracing tracing;
|
||||
static final Propagation.Setter<RibbonCommandContext, String> SETTER = new Propagation.Setter<RibbonCommandContext, String>() {
|
||||
@Override public void put(RibbonCommandContext carrier, String key, String value) {
|
||||
carrier.getHeaders().put(key, Collections.singletonList(value));
|
||||
}
|
||||
|
||||
public TraceRibbonCommandFactory(RibbonCommandFactory delegate,
|
||||
HttpTracing tracing) {
|
||||
@Override public String toString() {
|
||||
return "RibbonCommandContext::headers::put";
|
||||
}
|
||||
};
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceRibbonCommandFactory.class);
|
||||
|
||||
final HttpTracing tracing;
|
||||
final Tracer tracer;
|
||||
final RibbonCommandFactory delegate;
|
||||
HttpClientHandler<RibbonCommandContext, ClientHttpResponse> handler;
|
||||
TraceContext.Injector<RibbonCommandContext> injector;
|
||||
|
||||
TraceRibbonCommandFactory(RibbonCommandFactory delegate, HttpTracing httpTracing) {
|
||||
this.tracing = httpTracing;
|
||||
this.delegate = delegate;
|
||||
this.tracing = tracing;
|
||||
this.tracer = httpTracing.tracing().tracer();
|
||||
this.handler = HttpClientHandler
|
||||
.create(httpTracing, new TraceRibbonCommandFactory.HttpAdapter());
|
||||
this.injector = httpTracing.tracing().propagation().injector(SETTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonCommand create(RibbonCommandContext context) {
|
||||
RibbonCommand ribbonCommand = this.delegate.create(context);
|
||||
Span span = this.tracing.tracing().tracer().currentSpan();
|
||||
this.tracing.clientParser().request(new TraceRibbonCommandFactory.HttpAdapter(), context, span);
|
||||
return ribbonCommand;
|
||||
public RibbonCommand create(final RibbonCommandContext context) {
|
||||
final RibbonCommand ribbonCommand = this.delegate.create(context);
|
||||
Span span = this.tracer.currentSpan();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Will set contents of the span " + this.tracer.currentSpan() + " in the ribbon command");
|
||||
}
|
||||
return new RibbonCommand() {
|
||||
@Override public ClientHttpResponse execute() {
|
||||
Span span = TraceRibbonCommandFactory.this.handler.handleSend(TraceRibbonCommandFactory.this.injector, context);
|
||||
ClientHttpResponse response = null;
|
||||
Throwable error = null;
|
||||
try (Tracer.SpanInScope ws = TraceRibbonCommandFactory.this.tracer.withSpanInScope(span)) {
|
||||
return response = ribbonCommand.execute();
|
||||
} catch (RuntimeException | Error e) {
|
||||
error = e;
|
||||
throw e;
|
||||
} finally {
|
||||
TraceRibbonCommandFactory.this.handler.handleReceive(response, error, span);
|
||||
}
|
||||
}
|
||||
|
||||
// currently only .execute() is used in Zuul
|
||||
@Override public Future<ClientHttpResponse> queue() {
|
||||
parseRequest(context, span);
|
||||
return ribbonCommand.queue();
|
||||
}
|
||||
|
||||
// currently only .execute() is used in Zuul
|
||||
@Override public Observable<ClientHttpResponse> observe() {
|
||||
parseRequest(context, span);
|
||||
return ribbonCommand.observe();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private void parseRequest(RibbonCommandContext context, Span span) {
|
||||
TraceRibbonCommandFactory.this.tracing.clientParser()
|
||||
.request(new TraceRibbonCommandFactory.HttpAdapter(), context, span);
|
||||
}
|
||||
|
||||
static final class HttpAdapter
|
||||
extends brave.http.HttpClientAdapter<RibbonCommandContext, RibbonCommand> {
|
||||
extends brave.http.HttpClientAdapter<RibbonCommandContext, ClientHttpResponse> {
|
||||
|
||||
@Override public String method(RibbonCommandContext request) {
|
||||
return request.getMethod();
|
||||
@@ -64,8 +128,12 @@ class TraceRibbonCommandFactory implements RibbonCommandFactory {
|
||||
return result != null ? result.toString() : null;
|
||||
}
|
||||
|
||||
@Override public Integer statusCode(RibbonCommand response) {
|
||||
throw new UnsupportedOperationException("RibbonCommand doesn't support status code");
|
||||
@Override public Integer statusCode(ClientHttpResponse response) {
|
||||
try {
|
||||
return response.getRawStatusCode();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.1.0
|
||||
* @since 2.0.0
|
||||
*/
|
||||
final class TraceRibbonCommandFactoryBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
@@ -42,15 +42,16 @@ final class TraceRibbonCommandFactoryBeanPostProcessor implements BeanPostProces
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof RibbonCommandFactory) {
|
||||
return new TraceRibbonCommandFactory((RibbonCommandFactory) bean, tracing());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof RibbonCommandFactory
|
||||
&& !(bean instanceof TraceRibbonCommandFactory)) {
|
||||
return new TraceRibbonCommandFactory((RibbonCommandFactory) bean, tracing());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,24 +16,18 @@
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.http.HttpTracing;
|
||||
import okhttp3.Request;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceWebServletAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
* enables span information propagation when using Zuul.
|
||||
@@ -49,40 +43,12 @@ import com.netflix.zuul.ZuulFilter;
|
||||
@AutoConfigureAfter(TraceWebServletAutoConfiguration.class)
|
||||
public class TraceZuulAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZuulFilter tracePreZuulFilter(HttpTracing tracer,
|
||||
ErrorParser errorParser) {
|
||||
return TracePreZuulFilter.create(tracer, errorParser);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public ZuulFilter tracePostZuulFilter(HttpTracing tracer) {
|
||||
return TracePostZuulFilter.create(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceRibbonCommandFactoryBeanPostProcessor traceRibbonCommandFactoryBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new TraceRibbonCommandFactoryBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "com.netflix.client.http.HttpRequest.Builder")
|
||||
public RibbonRequestCustomizer<HttpRequest.Builder> restClientRibbonRequestCustomizer(HttpTracing tracer) {
|
||||
return new RestClientRibbonRequestCustomizer(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.apache.http.client.methods.RequestBuilder")
|
||||
public RibbonRequestCustomizer<RequestBuilder> apacheHttpRibbonRequestCustomizer(HttpTracing tracer) {
|
||||
return new ApacheHttpClientRibbonRequestCustomizer(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "okhttp3.Request.Builder")
|
||||
public RibbonRequestCustomizer<Request.Builder> okHttpRibbonRequestCustomizer(HttpTracing tracer) {
|
||||
return new OkHttpClientRibbonRequestCustomizer(tracer);
|
||||
@ConditionalOnClass(RibbonCommand.class)
|
||||
static class RibbonConfig {
|
||||
@Bean
|
||||
public TraceRibbonCommandFactoryBeanPostProcessor traceRibbonCommandFactoryBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new TraceRibbonCommandFactoryBeanPostProcessor(beanFactory);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -23,8 +23,8 @@ import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceHandlerInterceptor;
|
||||
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
|
||||
import org.springframework.cloud.sleuth.instrument.web.TraceHandlerInterceptor;
|
||||
|
||||
/**
|
||||
* Bean post processor that wraps {@link ZuulHandlerMapping} in its
|
||||
@@ -46,6 +46,12 @@ class TraceZuulHandlerMappingBeanPostProcessor implements BeanPostProcessor {
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof ZuulHandlerMapping) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Attaching trace interceptor to bean [" + beanName + "] of type [" + bean.getClass().getSimpleName() + "]");
|
||||
@@ -56,10 +62,4 @@ class TraceZuulHandlerMappingBeanPostProcessor implements BeanPostProcessor {
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.sleuth.util.SpanUtil;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class ApacheHttpClientRibbonRequestCustomizerTests {
|
||||
|
||||
private static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
private static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
private static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(this.traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
brave.Span span = this.tracing.tracer().nextSpan().name("name").start();
|
||||
ApacheHttpClientRibbonRequestCustomizer customizer =
|
||||
new ApacheHttpClientRibbonRequestCustomizer(this.httpTracing) {
|
||||
@Override brave.Span getCurrentSpan() {
|
||||
return span;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void should_accept_customizer_when_apache_http_client_is_passed() throws Exception {
|
||||
then(this.customizer.accepts(String.class)).isFalse();
|
||||
then(this.customizer.accepts(RequestBuilder.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_not_sampled_on_the_context_when_there_is_no_span() throws Exception {
|
||||
this.span = null;
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.sampler(Sampler.NEVER_SAMPLE)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET").setUri("http://foo");
|
||||
|
||||
new ApacheHttpClientRibbonRequestCustomizer(httpTracing) {
|
||||
@Override brave.Span getCurrentSpan() {
|
||||
return span;
|
||||
}
|
||||
}.customize(requestBuilder);
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
Header header = request.getFirstHeader(SAMPLED_NAME);
|
||||
then(header.getName()).isEqualTo(SAMPLED_NAME);
|
||||
then(header.getValue()).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET").setUri("http://foo");
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, SPAN_ID_NAME, SpanUtil.idToHex(this.span.context().spanId()));
|
||||
thenThereIsAHeaderWithNameAndValue(request, TRACE_ID_NAME, this.span.context().traceIdString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_set_duplicate_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET").setUri("http://foo");
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, SPAN_ID_NAME, SpanUtil.idToHex(this.span.context().spanId()));
|
||||
thenThereIsAHeaderWithNameAndValue(request, TRACE_ID_NAME, this.span.context().traceIdString());
|
||||
}
|
||||
|
||||
public void thenThereIsAHeaderWithNameAndValue(HttpUriRequest request, String name, String value) {
|
||||
then(request.getHeaders(name)).hasSize(1);
|
||||
Header header = request.getFirstHeader(name);
|
||||
then(header.getName()).isEqualTo(name);
|
||||
then(header.getValue()).isEqualTo(value);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import okhttp3.Request;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.sleuth.util.SpanUtil;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class OkHttpClientRibbonRequestCustomizerTests {
|
||||
|
||||
private static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
private static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
private static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(this.traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
brave.Span span = this.tracing.tracer().nextSpan().name("name").start();
|
||||
OkHttpClientRibbonRequestCustomizer customizer =
|
||||
new OkHttpClientRibbonRequestCustomizer(this.httpTracing) {
|
||||
@Override brave.Span getCurrentSpan() {
|
||||
return span;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void should_accept_customizer_when_apache_http_client_is_passed() throws Exception {
|
||||
then(this.customizer.accepts(String.class)).isFalse();
|
||||
then(this.customizer.accepts(Request.Builder.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_not_sampled_on_the_context_when_there_is_no_span() throws Exception {
|
||||
this.span = null;
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.sampler(Sampler.NEVER_SAMPLE)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
new OkHttpClientRibbonRequestCustomizer(httpTracing) {
|
||||
@Override brave.Span getCurrentSpan() {
|
||||
return span;
|
||||
}
|
||||
}.customize(requestBuilder);
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
then(request.header(SAMPLED_NAME)).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
|
||||
thenThereIsAHeaderWithNameAndValue(request, SPAN_ID_NAME, SpanUtil.idToHex(this.span.context().spanId()));
|
||||
thenThereIsAHeaderWithNameAndValue(request, TRACE_ID_NAME, this.span.context().traceIdString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_set_duplicate_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, SPAN_ID_NAME, SpanUtil.idToHex(this.span.context().spanId()));
|
||||
thenThereIsAHeaderWithNameAndValue(request, TRACE_ID_NAME, this.span.context().traceIdString());
|
||||
}
|
||||
|
||||
private void thenThereIsAHeaderWithNameAndValue(Request request, String name, String value) {
|
||||
then(request.headers(name)).hasSize(1);
|
||||
then(request.header(name)).isEqualTo(value);
|
||||
}
|
||||
|
||||
private Request.Builder requestBuilder() {
|
||||
return new Request.Builder().get().url("http://localhost:8080/");
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.Test;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.sleuth.util.SpanUtil;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
public class RestClientRibbonRequestCustomizerTests {
|
||||
|
||||
private static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
private static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
private static final String SPAN_ID_NAME = "X-B3-SpanId";
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(this.traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
brave.Span span = this.tracing.tracer().nextSpan().name("name").start();
|
||||
RestClientRibbonRequestCustomizer customizer =
|
||||
new RestClientRibbonRequestCustomizer(this.httpTracing) {
|
||||
@Override brave.Span getCurrentSpan() {
|
||||
return span;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void should_accept_customizer_when_apache_http_client_is_passed() throws Exception {
|
||||
then(this.customizer.accepts(String.class)).isFalse();
|
||||
then(this.customizer.accepts(HttpRequest.Builder.class)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_not_sampled_on_the_context_when_there_is_no_span() throws Exception {
|
||||
this.span = null;
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.sampler(Sampler.NEVER_SAMPLE)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
new RestClientRibbonRequestCustomizer(httpTracing) {
|
||||
@Override brave.Span getCurrentSpan() {
|
||||
return span;
|
||||
}
|
||||
}.customize(requestBuilder);
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
then(request.getHttpHeaders().getFirstValue(SAMPLED_NAME)).isEqualTo("0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, SPAN_ID_NAME, SpanUtil.idToHex(this.span.context().spanId()));
|
||||
thenThereIsAHeaderWithNameAndValue(request, TRACE_ID_NAME, this.span.context().traceIdString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_set_duplicate_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.customize(requestBuilder);
|
||||
this.customizer.customize(requestBuilder);
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, SPAN_ID_NAME, SpanUtil.idToHex(this.span.context().spanId()));
|
||||
thenThereIsAHeaderWithNameAndValue(request, TRACE_ID_NAME, this.span.context().traceIdString());
|
||||
}
|
||||
|
||||
private void thenThereIsAHeaderWithNameAndValue(HttpRequest request, String name, String value) {
|
||||
then(request.getHttpHeaders().getAllHeaders()
|
||||
.stream().filter(stringStringEntry -> stringStringEntry.getKey().equals(name)).collect(
|
||||
Collectors.toList())).hasSize(1);
|
||||
then(request.getHttpHeaders().getFirstValue(name)).isEqualTo(value);
|
||||
}
|
||||
|
||||
private HttpRequest.Builder requestBuilder() {
|
||||
return new HttpRequest.Builder().verb(HttpRequest.Verb.GET).uri("http://localhost:8080/");
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.List;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.netflix.zuul.metrics.EmptyTracerFactory;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.monitoring.TracerFactory;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TracePostZuulFilterTests {
|
||||
|
||||
@Mock HttpServletRequest httpServletRequest;
|
||||
@Mock HttpServletResponse httpServletResponse;
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(this.traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
private TracePostZuulFilter filter = new TracePostZuulFilter(this.httpTracing);
|
||||
RequestContext requestContext = new RequestContext();
|
||||
|
||||
@After
|
||||
public void clean() {
|
||||
RequestContext.getCurrentContext().unset();
|
||||
this.httpTracing.tracing().close();
|
||||
RequestContext.testSetCurrentContext(null);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
BDDMockito.given(this.httpServletResponse.getStatus()).willReturn(200);
|
||||
this.requestContext.setRequest(this.httpServletRequest);
|
||||
this.requestContext.setResponse(this.httpServletResponse);
|
||||
RequestContext.testSetCurrentContext(this.requestContext);
|
||||
TracerFactory.initialize(new EmptyTracerFactory());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterPublishesEventAndClosesSpan() throws Exception {
|
||||
Span span = this.tracing.tracer().nextSpan().name("http:start").start();
|
||||
BDDMockito.given(this.httpServletRequest
|
||||
.getAttribute(TracePostZuulFilter.ZUUL_CURRENT_SPAN)).willReturn(span);
|
||||
BDDMockito.given(this.httpServletResponse.getStatus()).willReturn(456);
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) {
|
||||
this.filter.runFilter();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(1);
|
||||
// initial span
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("http.status_code", "456");
|
||||
then(spans.get(0).name()).isEqualTo("http:start");
|
||||
then(this.tracing.tracer().currentSpan()).isNull();
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.sampler.Sampler;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.monitoring.MonitoringHelper;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.ErrorParser;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TracePreZuulFilterTests {
|
||||
static final String TRACE_ID_NAME = "X-B3-TraceId";
|
||||
static final String SAMPLED_NAME = "X-B3-Sampled";
|
||||
|
||||
@Mock HttpServletRequest httpServletRequest;
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
Tracer tracer = this.tracing.tracer();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(this.traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
ErrorParser errorParser = new ExceptionMessageErrorParser();
|
||||
|
||||
private TracePreZuulFilter filter = new TracePreZuulFilter(this.httpTracing, this.errorParser);
|
||||
|
||||
@After
|
||||
public void clean() {
|
||||
RequestContext.getCurrentContext().unset();
|
||||
this.tracing.close();
|
||||
RequestContext.testSetCurrentContext(null);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MonitoringHelper.initMocks();
|
||||
RequestContext requestContext = new RequestContext();
|
||||
BDDMockito.given(this.httpServletRequest.getRequestURI()).willReturn("http://foo.bar");
|
||||
BDDMockito.given(this.httpServletRequest.getMethod()).willReturn("GET");
|
||||
requestContext.setRequest(this.httpServletRequest);
|
||||
RequestContext.testSetCurrentContext(requestContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAddsHeaders() throws Exception {
|
||||
Span span = this.tracer.nextSpan().name("http:start").start();
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
this.filter.runFilter();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
then(ctx.getZuulRequestHeaders().get(TRACE_ID_NAME))
|
||||
.isNotNull();
|
||||
then(ctx.getZuulRequestHeaders().get(SAMPLED_NAME))
|
||||
.isEqualTo("1");
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSampledIfNotExportable() throws Exception {
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.sampler(Sampler.NEVER_SAMPLE)
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
HttpTracing httpTracing = HttpTracing.create(tracing);
|
||||
this.filter = new TracePreZuulFilter(httpTracing, this.errorParser);
|
||||
|
||||
Span span = tracing.tracer().nextSpan().name("http:start").start();
|
||||
|
||||
try (Tracer.SpanInScope ws = tracing.tracer().withSpanInScope(span)) {
|
||||
this.filter.runFilter();
|
||||
} finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
then(ctx.getZuulRequestHeaders().get(TRACE_ID_NAME))
|
||||
.isNotNull();
|
||||
then(ctx.getZuulRequestHeaders().get(SAMPLED_NAME))
|
||||
.isEqualTo("0");
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCloseSpanWhenExceptionIsThrown() throws Exception {
|
||||
Span startedSpan = this.tracer.nextSpan().name("http:start").start();
|
||||
final AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
try (Tracer.SpanInScope ws = tracing.tracer().withSpanInScope(startedSpan)) {
|
||||
new TracePreZuulFilter(this.httpTracing, this.errorParser) {
|
||||
@Override
|
||||
public Object run() {
|
||||
super.run();
|
||||
span.set(
|
||||
TracePreZuulFilterTests.this.tracer.currentSpan());
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
}.runFilter();
|
||||
} finally {
|
||||
startedSpan.finish();
|
||||
}
|
||||
|
||||
then(startedSpan).isNotEqualTo(span.get());
|
||||
List<zipkin2.Span> spans = this.reporter.getSpans();
|
||||
then(spans).hasSize(2);
|
||||
// initial span
|
||||
then(spans.get(0).tags())
|
||||
.containsEntry("http.method", "GET")
|
||||
.containsEntry("error", "foo");
|
||||
// span from zuul
|
||||
then(spans.get(1).name()).isEqualTo("http:start");
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotCloseSpanWhenNoExceptionIsThrown() throws Exception {
|
||||
Span startedSpan = this.tracer.nextSpan().name("http:start").start();
|
||||
final AtomicReference<Span> span = new AtomicReference<>();
|
||||
|
||||
try (Tracer.SpanInScope ws = tracing.tracer().withSpanInScope(startedSpan)) {
|
||||
new TracePreZuulFilter(this.httpTracing, this.errorParser) {
|
||||
@Override
|
||||
public Object run() {
|
||||
span.set(
|
||||
TracePreZuulFilterTests.this.tracer.currentSpan());
|
||||
return super.run();
|
||||
}
|
||||
}.runFilter();
|
||||
} finally {
|
||||
startedSpan.finish();
|
||||
}
|
||||
|
||||
then(startedSpan).isNotEqualTo(span.get());
|
||||
then(this.tracer.currentSpan()).isNull();
|
||||
then(this.reporter.getSpans()).isNotEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,13 +16,22 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@@ -32,18 +41,34 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceRibbonCommandFactoryBeanPostProcessorTests {
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
.spanReporter(this.reporter)
|
||||
.build();
|
||||
TraceKeys traceKeys = new TraceKeys();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(SleuthHttpParserAccessor.getClient(this.traceKeys))
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
|
||||
@Mock RibbonCommandFactory ribbonCommandFactory;
|
||||
@Mock BeanFactory beanFactory;
|
||||
@InjectMocks TraceRibbonCommandFactoryBeanPostProcessor postProcessor;
|
||||
|
||||
@Test
|
||||
public void should_return_a_bean_as_it_is_if_its_not_a_ribbon_command_Factory() {
|
||||
then(this.postProcessor.postProcessBeforeInitialization("", "name")).isEqualTo("");
|
||||
then(this.postProcessor.postProcessAfterInitialization("", "name")).isEqualTo("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_wrap_ribbon_command_factory_in_a_trace_representation() {
|
||||
then(this.postProcessor.postProcessBeforeInitialization(ribbonCommandFactory, "name")).isInstanceOf(
|
||||
then(this.postProcessor.postProcessAfterInitialization(ribbonCommandFactory, "name")).isInstanceOf(
|
||||
TraceRibbonCommandFactory.class);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
BDDMockito.given(this.beanFactory.getBean(HttpTracing.class)).willReturn(this.httpTracing);
|
||||
}
|
||||
}
|
||||
@@ -23,23 +23,24 @@ import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.BDDMockito;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.sleuth.ExceptionMessageErrorParser;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.SleuthHttpParserAccessor;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonCommandContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
@@ -48,7 +49,6 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceRibbonCommandFactoryTest {
|
||||
|
||||
|
||||
ArrayListSpanReporter reporter = new ArrayListSpanReporter();
|
||||
Tracing tracing = Tracing.newBuilder()
|
||||
.currentTraceContext(CurrentTraceContext.Default.create())
|
||||
@@ -60,6 +60,7 @@ public class TraceRibbonCommandFactoryTest {
|
||||
.serverParser(SleuthHttpParserAccessor.getServer(this.traceKeys, new ExceptionMessageErrorParser()))
|
||||
.build();
|
||||
@Mock RibbonCommandFactory ribbonCommandFactory;
|
||||
@Mock RibbonCommand ribbonCommand;
|
||||
TraceRibbonCommandFactory traceRibbonCommandFactory;
|
||||
Span span = this.tracing.tracer().nextSpan().name("name");
|
||||
|
||||
@@ -68,6 +69,9 @@ public class TraceRibbonCommandFactoryTest {
|
||||
public void setup() {
|
||||
this.traceRibbonCommandFactory = new TraceRibbonCommandFactory(
|
||||
this.ribbonCommandFactory, this.httpTracing);
|
||||
BDDMockito.given(this.ribbonCommandFactory
|
||||
.create(BDDMockito.any(RibbonCommandContext.class)))
|
||||
.willReturn(this.ribbonCommand);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -79,16 +83,21 @@ public class TraceRibbonCommandFactoryTest {
|
||||
@Test
|
||||
public void should_attach_trace_headers_to_the_span() throws Exception {
|
||||
try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(this.span)) {
|
||||
this.traceRibbonCommandFactory.create(ribbonCommandContext());
|
||||
RibbonCommand ribbonCommand = this.traceRibbonCommandFactory
|
||||
.create(ribbonCommandContext());
|
||||
ribbonCommand.execute();
|
||||
} finally {
|
||||
this.span.finish();
|
||||
}
|
||||
|
||||
then(this.reporter.getSpans()).hasSize(1);
|
||||
then(this.reporter.getSpans()).hasSize(2);
|
||||
// RPC
|
||||
zipkin2.Span span = this.reporter.getSpans().get(0);
|
||||
then(span.tags())
|
||||
.containsEntry("http.method", "GET")
|
||||
.containsEntry("http.url", "http://localhost:1234/foo");
|
||||
zipkin2.Span main = this.reporter.getSpans().get(1);
|
||||
then(main.name()).isEqualTo("name");
|
||||
}
|
||||
|
||||
private RibbonCommandContext ribbonCommandContext() {
|
||||
|
||||
Reference in New Issue
Block a user