Updated zuul

This commit is contained in:
Marcin Grzejszczak
2018-01-30 17:55:30 +01:00
parent 4682920675
commit d27d91e557
20 changed files with 145 additions and 1384 deletions

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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/");
}
}

View File

@@ -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/");
}
}

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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() {