Added RibbonCommand instrumentation (#318)
with this change RibbonRequestCustomizers are created for - Netflix HttpClient - OkHttp client - Apache HttpClient those customizers know how to inject span into the given context. Also a wrapper over a RibbonCommandFactory bean gets created that injects http trace keys to the given span fixes #313
This commit is contained in:
committed by
GitHub
parent
61e70deff5
commit
48d46b07bb
@@ -75,6 +75,16 @@
|
||||
<artifactId>rxjava</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>aspectjrt</artifactId>
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Customization of a Ribbon request for Apache HttpClient
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class ApacheHttpClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCustomizer<RequestBuilder> {
|
||||
|
||||
ApacheHttpClientRibbonRequestCustomizer(Tracer tracer) {
|
||||
super(tracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Class aClass) {
|
||||
return aClass == RequestBuilder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(RequestBuilder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.setHeader(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* Customization of a Ribbon request for OkHttp
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class OkHttpClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCustomizer<Request.Builder> {
|
||||
|
||||
OkHttpClientRibbonRequestCustomizer(Tracer tracer) {
|
||||
super(tracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Class aClass) {
|
||||
return aClass == Request.Builder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(Request.Builder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.addHeader(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,17 +18,16 @@ package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link RequestContext}
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class RequestContextInjector implements SpanInjector<RequestContext> {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Customization of a Ribbon request for Netflix HttpClient
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class RestClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCustomizer<HttpRequest.Builder> {
|
||||
|
||||
RestClientRibbonRequestCustomizer(Tracer tracer) {
|
||||
super(tracer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accepts(Class aClass) {
|
||||
return aClass == HttpRequest.Builder.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
void setHeader(HttpRequest.Builder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,23 +16,49 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.client.http.HttpRequest.Builder;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* Span injector that injects tracing info to {@link Builder}
|
||||
* 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.
|
||||
* In order to add a new implementation of the {@link RibbonRequestCustomizer} it's
|
||||
* necessary only to provide the {@link RibbonRequestCustomizer#accepts(Class)} method
|
||||
* with the context class name and {@link SpanInjectingRibbonRequestCustomizer#setHeader(Object, String, String)}
|
||||
* to tell Sleuth how to set a header using the particular library.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.0.0
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class RequestBuilderContextInjector implements SpanInjector<Builder> {
|
||||
abstract class SpanInjectingRibbonRequestCustomizer<T> implements RibbonRequestCustomizer<T>,
|
||||
SpanInjector<T> {
|
||||
|
||||
private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private final Tracer tracer;
|
||||
|
||||
SpanInjectingRibbonRequestCustomizer(Tracer tracer) {
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Span span, Builder carrier) {
|
||||
public void customize(T context) {
|
||||
Span span = getCurrentSpan();
|
||||
inject(span, context);
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span in the RibbonRequestCustomizer is" + span);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inject(Span span, T carrier) {
|
||||
if (span == null) {
|
||||
setHeader(carrier, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED);
|
||||
return;
|
||||
@@ -55,9 +81,9 @@ class RequestBuilderContextInjector implements SpanInjector<Builder> {
|
||||
? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
public void setHeader(HttpRequest.Builder builder, String name, String value) {
|
||||
if (value != null) {
|
||||
builder.header(name, value);
|
||||
}
|
||||
private Span getCurrentSpan() {
|
||||
return this.tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
abstract void setHeader(T builder, String name, String value);
|
||||
}
|
||||
@@ -19,6 +19,11 @@ package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.net.URI;
|
||||
|
||||
import com.netflix.zuul.ExecutionStatus;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.ZuulFilterResult;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
@@ -26,11 +31,6 @@ import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
|
||||
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.
|
||||
@@ -108,5 +108,4 @@ public class TracePreZuulFilter extends ZuulFilter {
|
||||
public int filterOrder() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URISyntaxException;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* Propagates traces downstream via http headers that contain trace metadata.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommandFactory {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TraceRestClientRibbonCommandFactory.class);
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpRequest.Builder> spanInjector;
|
||||
private final HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
public TraceRestClientRibbonCommandFactory(SpringClientFactory clientFactory,
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
super(clientFactory);
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
this.httpTraceKeysInjector = httpTraceKeysInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("deprecation")
|
||||
public RestClientRibbonCommand create(RibbonCommandContext context) {
|
||||
RestClient restClient = getClientFactory().getClient(context.getServiceId(),
|
||||
RestClient.class);
|
||||
try {
|
||||
return new TraceRestClientRibbonCommand(context.getServiceId(), restClient,
|
||||
getVerb(context.getVerb()), context.getUri(), context.getRetryable(),
|
||||
context.getHeaders(), context.getParams(), context.getRequestEntity(),
|
||||
this.tracer, this.spanInjector, this.httpTraceKeysInjector);
|
||||
}
|
||||
catch (URISyntaxException e) {
|
||||
log.error("Exception occurred while trying to create the TraceRestClientRibbonCommand", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
class TraceRestClientRibbonCommand extends RestClientRibbonCommand {
|
||||
|
||||
private final Tracer tracer;
|
||||
private final SpanInjector<HttpRequest.Builder> spanInjector;
|
||||
private final HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public TraceRestClientRibbonCommand(String commandKey, RestClient restClient,
|
||||
HttpRequest.Verb verb, String uri, Boolean retryable,
|
||||
MultiValueMap<String, String> headers,
|
||||
MultiValueMap<String, String> params, InputStream requestEntity,
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector,
|
||||
HttpTraceKeysInjector httpTraceKeysInjector)
|
||||
throws URISyntaxException {
|
||||
super(commandKey, restClient, verb, uri, retryable, headers, params,
|
||||
requestEntity);
|
||||
this.tracer = tracer;
|
||||
this.spanInjector = spanInjector;
|
||||
this.httpTraceKeysInjector = httpTraceKeysInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void customizeRequest(HttpRequest.Builder requestBuilder) {
|
||||
Span span = getCurrentSpan();
|
||||
this.spanInjector.inject(span, requestBuilder);
|
||||
this.httpTraceKeysInjector.addRequestTags(span, getUri(), getVerb().verb());
|
||||
span.logEvent(Span.CLIENT_SEND);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Span in RibbonCommandFactory is" + span);
|
||||
}
|
||||
}
|
||||
|
||||
private Span getCurrentSpan() {
|
||||
return this.tracer.getCurrentSpan();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
|
||||
/**
|
||||
* Propagates traces downstream via http headers that contain trace metadata.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 1.1.0
|
||||
*/
|
||||
class TraceRibbonCommandFactory implements RibbonCommandFactory {
|
||||
|
||||
private final RibbonCommandFactory delegate;
|
||||
private final Tracer tracer;
|
||||
private final HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
public TraceRibbonCommandFactory(RibbonCommandFactory delegate,
|
||||
Tracer tracer, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
this.delegate = delegate;
|
||||
this.tracer = tracer;
|
||||
this.httpTraceKeysInjector = httpTraceKeysInjector;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RibbonCommand create(RibbonCommandContext context) {
|
||||
RibbonCommand ribbonCommand = this.delegate.create(context);
|
||||
Span span = this.tracer.getCurrentSpan();
|
||||
this.httpTraceKeysInjector.addRequestTags(span, context.uri(), context.getMethod());
|
||||
return ribbonCommand;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
|
||||
/**
|
||||
* Post processor that wraps a {@link org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory}
|
||||
* in its trace representation.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
* @since 1.1.0
|
||||
*/
|
||||
final class TraceRibbonCommandFactoryBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final BeanFactory beanFactory;
|
||||
private Tracer tracer;
|
||||
private HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
|
||||
TraceRibbonCommandFactoryBeanPostProcessor(BeanFactory beanFactory) {
|
||||
this.beanFactory = beanFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
if (bean instanceof RibbonCommandFactory) {
|
||||
return new TraceRibbonCommandFactory((RibbonCommandFactory) bean, getTracer(), getHttpTraceKeysInjector());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName)
|
||||
throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
Tracer getTracer() {
|
||||
if (this.tracer == null) {
|
||||
this.tracer = this.beanFactory.getBean(Tracer.class);
|
||||
}
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
HttpTraceKeysInjector getHttpTraceKeysInjector() {
|
||||
if (this.httpTraceKeysInjector == null) {
|
||||
this.httpTraceKeysInjector = this.beanFactory.getBean(HttpTraceKeysInjector.class);
|
||||
}
|
||||
return this.httpTraceKeysInjector;
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,19 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
import org.apache.http.client.methods.RequestBuilder;
|
||||
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.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.ribbon.support.RibbonRequestCustomizer;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
@@ -30,9 +36,7 @@ import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import okhttp3.Request;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration}
|
||||
@@ -56,13 +60,6 @@ public class TraceZuulAutoConfiguration {
|
||||
return new TracePreZuulFilter(tracer, spanInjector, httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory,
|
||||
Tracer tracer, SpanInjector<HttpRequest.Builder> spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) {
|
||||
return new TraceRestClientRibbonCommandFactory(factory, tracer, spanInjector,
|
||||
httpTraceKeysInjector);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TracePostZuulFilter tracePostZuulFilter(Tracer tracer, TraceKeys traceKeys) {
|
||||
@@ -75,8 +72,26 @@ public class TraceZuulAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SpanInjector<HttpRequest.Builder> requestBuilderContextSpanInjector() {
|
||||
return new RequestBuilderContextInjector();
|
||||
public TraceRibbonCommandFactoryBeanPostProcessor traceRibbonCommandFactoryBeanPostProcessor(BeanFactory beanFactory) {
|
||||
return new TraceRibbonCommandFactoryBeanPostProcessor(beanFactory);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "com.netflix.client.http.HttpRequest.Builder")
|
||||
public RibbonRequestCustomizer<HttpRequest.Builder> restClientRibbonRequestCustomizer(Tracer tracer) {
|
||||
return new RestClientRibbonRequestCustomizer(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "org.apache.http.client.methods.RequestBuilder")
|
||||
public RibbonRequestCustomizer<RequestBuilder> apacheHttpRibbonRequestCustomizer(Tracer tracer) {
|
||||
return new ApacheHttpClientRibbonRequestCustomizer(tracer);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "okhttp3.Request.Builder")
|
||||
public RibbonRequestCustomizer<Request.Builder> okHttpRibbonRequestCustomizer(Tracer tracer) {
|
||||
return new OkHttpClientRibbonRequestCustomizer(tracer);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
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.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class ApacheHttpClientRibbonRequestCustomizerTests {
|
||||
|
||||
@Mock Tracer tracer;
|
||||
@InjectMocks ApacheHttpClientRibbonRequestCustomizer customizer;
|
||||
Span span = Span.builder().name("name").spanId(1L).traceId(2L).parent(3L)
|
||||
.processId("processId").build();
|
||||
|
||||
@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 {
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET");
|
||||
|
||||
this.customizer.inject(null, requestBuilder);
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
Header header = request.getFirstHeader(Span.SAMPLED_NAME);
|
||||
then(header.getName()).isEqualTo(Span.SAMPLED_NAME);
|
||||
then(header.getValue()).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
RequestBuilder requestBuilder = RequestBuilder.create("GET");
|
||||
|
||||
this.customizer.inject(this.span, requestBuilder);
|
||||
|
||||
HttpUriRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "2");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "3");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.PROCESS_ID_NAME, "processId");
|
||||
}
|
||||
|
||||
private void thenThereIsAHeaderWithNameAndValue(HttpUriRequest request, String name, String value) {
|
||||
Header header = request.getFirstHeader(name);
|
||||
then(header.getName()).isEqualTo(name);
|
||||
then(header.getValue()).isEqualTo(value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import okhttp3.Request;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class OkHttpClientRibbonRequestCustomizerTests {
|
||||
|
||||
@Mock Tracer tracer;
|
||||
@InjectMocks OkHttpClientRibbonRequestCustomizer customizer;
|
||||
Span span = Span.builder().name("name").spanId(1L).traceId(2L).parent(3L)
|
||||
.processId("processId").build();
|
||||
|
||||
@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 {
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(null, requestBuilder);
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
then(request.header(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
Request.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(this.span, requestBuilder);
|
||||
|
||||
Request request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "2");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "3");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.PROCESS_ID_NAME, "processId");
|
||||
}
|
||||
|
||||
private void thenThereIsAHeaderWithNameAndValue(Request request, String name, String value) {
|
||||
then(request.header(name)).isEqualTo(value);
|
||||
}
|
||||
|
||||
private Request.Builder requestBuilder() {
|
||||
return new Request.Builder().get().url("http://localhost:8080/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class RestClientRibbonRequestCustomizerTests {
|
||||
|
||||
@Mock Tracer tracer;
|
||||
@InjectMocks RestClientRibbonRequestCustomizer customizer;
|
||||
Span span = Span.builder().name("name").spanId(1L).traceId(2L).parent(3L)
|
||||
.processId("processId").build();
|
||||
|
||||
@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 {
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(null, requestBuilder);
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
then(request.getHttpHeaders().getFirstValue(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_set_tracing_headers_on_the_context_when_there_is_a_span() throws Exception {
|
||||
HttpRequest.Builder requestBuilder = requestBuilder();
|
||||
|
||||
this.customizer.inject(this.span, requestBuilder);
|
||||
|
||||
HttpRequest request = requestBuilder.build();
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "2");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "3");
|
||||
thenThereIsAHeaderWithNameAndValue(request, Span.PROCESS_ID_NAME, "processId");
|
||||
}
|
||||
|
||||
private void thenThereIsAHeaderWithNameAndValue(HttpRequest request, String name, String value) {
|
||||
then(request.getHttpHeaders().getFirstValue(name)).isEqualTo(value);
|
||||
}
|
||||
|
||||
private HttpRequest.Builder requestBuilder() {
|
||||
return new HttpRequest.Builder().verb(HttpRequest.Verb.GET).uri("http://localhost:8080/");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceRibbonCommandFactoryBeanPostProcessorTests {
|
||||
|
||||
@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("");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_wrap_ribbon_command_factory_in_a_trace_representation() {
|
||||
then(this.postProcessor.postProcessBeforeInitialization(ribbonCommandFactory, "name")).isInstanceOf(TraceRibbonCommandFactory.class);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.client.http.HttpRequest;
|
||||
import com.netflix.niws.client.http.RestClient;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
@@ -27,43 +26,44 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommand;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandFactory;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanInjector;
|
||||
import org.springframework.cloud.sleuth.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector;
|
||||
import org.springframework.cloud.sleuth.trace.TestSpanContextHolder;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceRestClientRibbonCommandFactoryTest {
|
||||
public class TraceRibbonCommandFactoryTest {
|
||||
|
||||
@Mock Tracer tracer;
|
||||
@Mock SpringClientFactory springClientFactory;
|
||||
SpanInjector<HttpRequest.Builder> spanInjector = new RequestBuilderContextInjector();
|
||||
@Mock HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory;
|
||||
HttpTraceKeysInjector httpTraceKeysInjector;
|
||||
@Mock RibbonCommandFactory ribbonCommandFactory;
|
||||
TraceRibbonCommandFactory traceRibbonCommandFactory;
|
||||
Span span = Span.builder().name("name").spanId(1L).traceId(2L).parent(3L)
|
||||
.processId("processId").build();
|
||||
|
||||
@Before
|
||||
@SuppressWarnings({ "deprecation", "unchecked" })
|
||||
public void setup() {
|
||||
this.traceRestClientRibbonCommandFactory = new TraceRestClientRibbonCommandFactory(
|
||||
this.springClientFactory, this.tracer, this.spanInjector,
|
||||
this.httpTraceKeysInjector = new HttpTraceKeysInjector(this.tracer, new TraceKeys());
|
||||
this.traceRibbonCommandFactory = new TraceRibbonCommandFactory(
|
||||
this.ribbonCommandFactory, this.tracer,
|
||||
httpTraceKeysInjector);
|
||||
given(this.springClientFactory.getClient(anyString(), any(Class.class)))
|
||||
.willReturn(new RestClient());
|
||||
Span span = Span.builder().name("name").spanId(1L).traceId(2L).parent(3L)
|
||||
.processId("processId").build();
|
||||
given(this.tracer.getCurrentSpan()).willReturn(span);
|
||||
given(this.tracer.isTracing()).willReturn(true);
|
||||
}
|
||||
@@ -75,34 +75,11 @@ public class TraceRestClientRibbonCommandFactoryTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_wrap_ribbon_command_in_a_sleuth_representation() throws Exception {
|
||||
RestClientRibbonCommand restClientRibbonCommand = this.traceRestClientRibbonCommandFactory
|
||||
.create(ribbonCommandContext());
|
||||
public void should_attach_trace_headers_to_the_span() throws Exception {
|
||||
this.traceRibbonCommandFactory.create(ribbonCommandContext());
|
||||
|
||||
then(restClientRibbonCommand).isInstanceOf(
|
||||
TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_attach_trace_headers_to_the_sent_request() throws Exception {
|
||||
RestClientRibbonCommand restClientRibbonCommand = this.traceRestClientRibbonCommandFactory
|
||||
.create(ribbonCommandContext());
|
||||
TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand traceRestClientRibbonCommand = (TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand) restClientRibbonCommand;
|
||||
HttpRequest.Builder builder = new HttpRequest.Builder();
|
||||
|
||||
traceRestClientRibbonCommand.customizeRequest(builder);
|
||||
|
||||
HttpRequest httpRequest = builder.build();
|
||||
then(httpRequest.getHttpHeaders().getFirstValue(Span.SPAN_ID_NAME))
|
||||
.isEqualTo("1");
|
||||
then(httpRequest.getHttpHeaders().getFirstValue(Span.TRACE_ID_NAME))
|
||||
.isEqualTo("2");
|
||||
then(httpRequest.getHttpHeaders().getFirstValue(Span.SPAN_NAME_NAME))
|
||||
.isEqualTo("name");
|
||||
then(httpRequest.getHttpHeaders().getFirstValue(Span.PARENT_ID_NAME))
|
||||
.isEqualTo("3");
|
||||
then(httpRequest.getHttpHeaders().getFirstValue(Span.PROCESS_ID_NAME))
|
||||
.isEqualTo("processId");
|
||||
then(this.span).hasATag("http.method", "GET");
|
||||
then(this.span).hasATag("http.url", "http://localhost:1234/foo");
|
||||
}
|
||||
|
||||
private RibbonCommandContext ribbonCommandContext() {
|
||||
Reference in New Issue
Block a user