Merge branch '2.2.x'
This commit is contained in:
@@ -838,12 +838,38 @@ Sleuth will search for beans of those types and automatically apply customizatio
|
||||
|
||||
=== HTTP
|
||||
|
||||
If a customization of client / server parsing of the HTTP related spans is
|
||||
required, just register a bean of type `brave.http.HttpClientParser` or
|
||||
`brave.http.HttpServerParser`. If client /server sampling is required, just
|
||||
register a bean of type `brave.sampler.SamplerFunction<HttpRequest>` and name
|
||||
the bean `sleuthHttpClientSampler` for client sampler and
|
||||
`sleuthHttpServerSampler` for server sampler.
|
||||
==== Data Policy
|
||||
|
||||
The default span data policy for HTTP requests is described in Brave:
|
||||
https://github.com/openzipkin/brave/tree/master/instrumentation/http#span-data-policy
|
||||
|
||||
To add different data to the span, you need to register a bean of type
|
||||
`brave.http.HttpRequestParser` or `brave.http.HttpResponseParser` based on when
|
||||
the data is collected.
|
||||
|
||||
The bean names correspond to the request or response side, and whether it is
|
||||
a client or server. For example, `sleuthHttpClientRequestParser` changes what
|
||||
is collected before a client request is sent to the server.
|
||||
|
||||
For your convenience `@HttpClientRequestParser`, `@HttpClientResponseParser`
|
||||
and corresponding server annotations can be used to inject the proper beans
|
||||
or to reference the bean names via their static String `NAME` fields.
|
||||
|
||||
Here's an example adding the HTTP url in addition to defaults:
|
||||
[source,java]
|
||||
----
|
||||
@Configuration
|
||||
class Config {
|
||||
include::{project-root}/tests/spring-cloud-sleuth-instrumentation-mvc-tests/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterWebIntegrationTests.java[tags=custom_parser,indent=2]
|
||||
}
|
||||
----
|
||||
|
||||
==== Sampling
|
||||
|
||||
If client /server sampling is required, just register a bean of type
|
||||
`brave.sampler.SamplerFunction<HttpRequest>` and name the bean
|
||||
`sleuthHttpClientSampler` for client sampler and `sleuthHttpServerSampler`
|
||||
for server sampler.
|
||||
|
||||
For your convenience the `@HttpClientSampler` and `@HttpServerSampler`
|
||||
annotations can be used to inject the proper beans or to reference the bean
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import brave.http.HttpRequestParser;
|
||||
import brave.http.HttpTracing;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Annotate a client {@link HttpRequestParser} that should be injected to
|
||||
* {@link HttpTracing.Builder#clientRequestParser(HttpRequestParser)}.
|
||||
*
|
||||
* @see Qualifier
|
||||
* @since 2.2.2
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Qualifier(HttpClientRequestParser.NAME)
|
||||
public @interface HttpClientRequestParser {
|
||||
|
||||
/**
|
||||
* Default name for Sleuth HTTP client request parser.
|
||||
*/
|
||||
String NAME = "sleuthHttpClientRequestParser";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import brave.http.HttpResponseParser;
|
||||
import brave.http.HttpTracing;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Annotate a client {@link HttpResponseParser} that should be injected to
|
||||
* {@link HttpTracing.Builder#clientResponseParser(HttpResponseParser)}.
|
||||
*
|
||||
* @see Qualifier
|
||||
* @since 2.2.2
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Qualifier(HttpClientResponseParser.NAME)
|
||||
public @interface HttpClientResponseParser {
|
||||
|
||||
/**
|
||||
* Default name for Sleuth HTTP client response parser.
|
||||
*/
|
||||
String NAME = "sleuthHttpClientResponseParser";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import brave.http.HttpRequestParser;
|
||||
import brave.http.HttpTracing;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Annotate a server {@link HttpRequestParser} that should be injected to
|
||||
* {@link HttpTracing.Builder#serverRequestParser(HttpRequestParser)}.
|
||||
*
|
||||
* @see Qualifier
|
||||
* @since 2.2.2
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Qualifier(HttpServerRequestParser.NAME)
|
||||
public @interface HttpServerRequestParser {
|
||||
|
||||
/**
|
||||
* Default name for Sleuth HTTP server request parser.
|
||||
*/
|
||||
String NAME = "sleuthHttpServerRequestParser";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import brave.http.HttpResponseParser;
|
||||
import brave.http.HttpTracing;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
/**
|
||||
* Annotate a server {@link HttpResponseParser} that should be injected to
|
||||
* {@link HttpTracing.Builder#serverResponseParser(HttpResponseParser)}.
|
||||
*
|
||||
* @see Qualifier
|
||||
* @since 2.2.2
|
||||
*/
|
||||
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE,
|
||||
ElementType.ANNOTATION_TYPE })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
@Qualifier(HttpServerResponseParser.NAME)
|
||||
public @interface HttpServerResponseParser {
|
||||
|
||||
/**
|
||||
* Default name for Sleuth HTTP server response parser.
|
||||
*/
|
||||
String NAME = "sleuthHttpServerResponseParser";
|
||||
|
||||
}
|
||||
@@ -19,11 +19,10 @@ package org.springframework.cloud.sleuth.instrument.web;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import brave.ErrorParser;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpClientParser;
|
||||
import brave.http.HttpRequest;
|
||||
import brave.http.HttpServerParser;
|
||||
import brave.http.HttpRequestParser;
|
||||
import brave.http.HttpResponseParser;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.http.HttpTracingCustomizer;
|
||||
import brave.sampler.SamplerFunction;
|
||||
@@ -61,17 +60,47 @@ public class TraceHttpAutoConfiguration {
|
||||
@ConditionalOnMissingBean
|
||||
// NOTE: stable bean name as might be used outside sleuth
|
||||
HttpTracing httpTracing(Tracing tracing, SkipPatternProvider provider,
|
||||
HttpClientParser clientParser, HttpServerParser serverParser,
|
||||
@Nullable @HttpClientRequestParser HttpRequestParser httpClientRequestParser,
|
||||
@Nullable @HttpClientResponseParser HttpResponseParser httpClientResponseParser,
|
||||
@Nullable brave.http.HttpClientParser clientParser,
|
||||
@Nullable @HttpServerRequestParser HttpRequestParser httpServerRequestParser,
|
||||
@Nullable @HttpServerResponseParser HttpResponseParser httpServerResponseParser,
|
||||
@Nullable brave.http.HttpServerParser serverParser,
|
||||
@HttpClientSampler SamplerFunction<HttpRequest> httpClientSampler,
|
||||
@Nullable @HttpServerSampler SamplerFunction<HttpRequest> httpServerSampler) {
|
||||
SamplerFunction<HttpRequest> combinedSampler = combineUserProvidedSamplerWithSkipPatternSampler(
|
||||
httpServerSampler, provider);
|
||||
HttpTracing.Builder builder = HttpTracing.newBuilder(tracing)
|
||||
.clientParser(clientParser).serverParser(serverParser)
|
||||
.clientSampler(httpClientSampler).serverSampler(combinedSampler);
|
||||
|
||||
if (httpClientRequestParser != null || httpClientResponseParser != null) {
|
||||
if (httpClientRequestParser != null) {
|
||||
builder.clientRequestParser(httpClientRequestParser);
|
||||
}
|
||||
if (httpClientResponseParser != null) {
|
||||
builder.clientResponseParser(httpClientResponseParser);
|
||||
}
|
||||
}
|
||||
else if (clientParser != null) { // consider deprecated last
|
||||
builder.clientParser(clientParser);
|
||||
}
|
||||
|
||||
if (httpServerRequestParser != null || httpServerResponseParser != null) {
|
||||
if (httpServerRequestParser != null) {
|
||||
builder.serverRequestParser(httpServerRequestParser);
|
||||
}
|
||||
if (httpServerResponseParser != null) {
|
||||
builder.serverResponseParser(httpServerResponseParser);
|
||||
}
|
||||
}
|
||||
else if (serverParser != null) { // consider deprecated last
|
||||
builder.serverParser(serverParser);
|
||||
}
|
||||
|
||||
for (HttpTracingCustomizer customizer : this.httpTracingCustomizers) {
|
||||
customizer.customize(builder);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@@ -86,23 +115,6 @@ public class TraceHttpAutoConfiguration {
|
||||
return new CompositeHttpSampler(skipPatternSampler, serverSampler);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HttpClientParser httpClientParser(ErrorParser errorParser) {
|
||||
return new HttpClientParser() {
|
||||
@Override
|
||||
protected ErrorParser errorParser() {
|
||||
return errorParser;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
HttpServerParser defaultHttpServerParser() {
|
||||
return new HttpServerParser();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(name = HttpClientSampler.NAME)
|
||||
SamplerFunction<HttpRequest> sleuthHttpClientSampler(
|
||||
|
||||
@@ -152,7 +152,7 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
|
||||
// Start a new client span with the appropriate parent
|
||||
TraceContext parent = req.currentContext().getOrDefault(TraceContext.class,
|
||||
null);
|
||||
WrappedHttpClientRequest request = new WrappedHttpClientRequest(req);
|
||||
HttpClientRequestWrapper request = new HttpClientRequestWrapper(req);
|
||||
|
||||
clientSpan = handler().handleSendWithParent(request, parent);
|
||||
parseConnectionAddress(connection, clientSpan);
|
||||
@@ -240,18 +240,18 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
|
||||
if (clientSpan == null) {
|
||||
return; // Unexpected. In the handle method, without a span to finish!
|
||||
}
|
||||
WrappedHttpClientResponse response = resp != null
|
||||
? new WrappedHttpClientResponse(resp) : null;
|
||||
HttpClientResponseWrapper response = resp != null
|
||||
? new HttpClientResponseWrapper(resp) : null;
|
||||
handler().handleReceive(response, error, clientSpan);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class WrappedHttpClientRequest extends brave.http.HttpClientRequest {
|
||||
static final class HttpClientRequestWrapper extends brave.http.HttpClientRequest {
|
||||
|
||||
final HttpClientRequest delegate;
|
||||
|
||||
WrappedHttpClientRequest(HttpClientRequest delegate) {
|
||||
HttpClientRequestWrapper(HttpClientRequest delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@@ -287,11 +287,11 @@ class HttpClientBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
}
|
||||
|
||||
static final class WrappedHttpClientResponse extends brave.http.HttpClientResponse {
|
||||
static final class HttpClientResponseWrapper extends brave.http.HttpClientResponse {
|
||||
|
||||
final HttpClientResponse delegate;
|
||||
|
||||
WrappedHttpClientResponse(HttpClientResponse delegate) {
|
||||
HttpClientResponseWrapper(HttpClientResponse delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.util.function.Function;
|
||||
|
||||
import brave.Span;
|
||||
import brave.http.HttpClientHandler;
|
||||
import brave.http.HttpClientRequest;
|
||||
import brave.http.HttpClientResponse;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.CurrentTraceContext.Scope;
|
||||
@@ -122,7 +124,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
final Function<? super Publisher<DataBuffer>, ? extends Publisher<DataBuffer>> scopePassingTransformer;
|
||||
|
||||
// Lazy initialized fields
|
||||
HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler;
|
||||
HttpClientHandler<HttpClientRequest, HttpClientResponse> handler;
|
||||
|
||||
CurrentTraceContext currentTraceContext;
|
||||
|
||||
@@ -148,7 +150,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
return this.currentTraceContext;
|
||||
}
|
||||
|
||||
HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler() {
|
||||
HttpClientHandler<HttpClientRequest, HttpClientResponse> handler() {
|
||||
if (this.handler == null) {
|
||||
this.handler = HttpClientHandler.create(this.httpTracing.get());
|
||||
}
|
||||
@@ -161,7 +163,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
|
||||
final ClientRequest request;
|
||||
|
||||
final HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler;
|
||||
final HttpClientHandler<HttpClientRequest, HttpClientResponse> handler;
|
||||
|
||||
final CurrentTraceContext currentTraceContext;
|
||||
|
||||
@@ -185,7 +187,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
|
||||
Context context = subscriber.currentContext();
|
||||
|
||||
HttpClientRequest wrapper = new HttpClientRequest(request);
|
||||
ClientRequestWrapper wrapper = new ClientRequestWrapper(request);
|
||||
Span span = handler.handleSendWithParent(wrapper, parent);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("HttpClientHandler::handleSend: " + span);
|
||||
@@ -209,7 +211,7 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
|
||||
final Span clientSpan;
|
||||
|
||||
final HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler;
|
||||
final HttpClientHandler<HttpClientRequest, HttpClientResponse> handler;
|
||||
|
||||
final Function<? super Publisher<DataBuffer>, ? extends Publisher<DataBuffer>> scopePassingTransformer;
|
||||
|
||||
@@ -308,20 +310,20 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
}
|
||||
|
||||
void handleReceive(@Nullable ClientResponse res, @Nullable Throwable error) {
|
||||
HttpClientResponse response = res != null ? new HttpClientResponse(res)
|
||||
ClientResponseWrapper response = res != null ? new ClientResponseWrapper(res)
|
||||
: null;
|
||||
this.handler.handleReceive(response, error, clientSpan);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class HttpClientRequest extends brave.http.HttpClientRequest {
|
||||
private static final class ClientRequestWrapper extends HttpClientRequest {
|
||||
|
||||
final ClientRequest delegate;
|
||||
|
||||
final ClientRequest.Builder builder;
|
||||
|
||||
HttpClientRequest(ClientRequest delegate) {
|
||||
ClientRequestWrapper(ClientRequest delegate) {
|
||||
this.delegate = delegate;
|
||||
this.builder = ClientRequest.from(delegate);
|
||||
}
|
||||
@@ -362,11 +364,11 @@ final class TraceExchangeFilterFunction implements ExchangeFilterFunction {
|
||||
|
||||
}
|
||||
|
||||
static final class HttpClientResponse extends brave.http.HttpClientResponse {
|
||||
static final class ClientResponseWrapper extends HttpClientResponse {
|
||||
|
||||
final ClientResponse delegate;
|
||||
|
||||
HttpClientResponse(ClientResponse delegate) {
|
||||
ClientResponseWrapper(ClientResponse delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 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.
|
||||
@@ -76,7 +76,7 @@ final class TraceFeignObjectWrapper {
|
||||
|
||||
private Object instrumentedFeignLoadBalancerClient(Object bean) {
|
||||
if (AopUtils.getTargetClass(bean).equals(FeignBlockingLoadBalancerClient.class)) {
|
||||
FeignBlockingLoadBalancerClient client = ((FeignBlockingLoadBalancerClient) bean);
|
||||
FeignBlockingLoadBalancerClient client = ProxyUtils.getTargetObject(bean);
|
||||
return new TraceFeignBlockingLoadBalancerClient(
|
||||
(Client) new TraceFeignObjectWrapper(this.beanFactory)
|
||||
.wrap(client.getDelegate()),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 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.
|
||||
@@ -25,10 +25,12 @@ import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.http.HttpClientHandler;
|
||||
import brave.http.HttpClientRequest;
|
||||
import brave.http.HttpClientResponse;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.Propagation;
|
||||
import brave.propagation.CurrentTraceContext;
|
||||
import brave.propagation.CurrentTraceContext.Scope;
|
||||
import feign.Client;
|
||||
import feign.Request;
|
||||
import feign.Response;
|
||||
@@ -36,6 +38,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.cloud.util.ProxyUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Feign client wrapper.
|
||||
@@ -47,37 +50,14 @@ final class TracingFeignClient implements Client {
|
||||
|
||||
private static final Log log = LogFactory.getLog(TracingFeignClient.class);
|
||||
|
||||
static final Propagation.Setter<Map<String, Collection<String>>, String> SETTER = new Propagation.Setter<Map<String, Collection<String>>, String>() {
|
||||
@Override
|
||||
public void put(Map<String, Collection<String>> carrier, String key,
|
||||
String value) {
|
||||
if (!carrier.containsKey(key)) {
|
||||
carrier.put(key, Collections.singletonList(value));
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Added key [" + key + "] and header value [" + value + "]");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("Key [" + key + "] already there in the headers");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Map::set";
|
||||
}
|
||||
};
|
||||
|
||||
final Tracer tracer;
|
||||
final CurrentTraceContext currentTraceContext;
|
||||
|
||||
final Client delegate;
|
||||
|
||||
final HttpClientHandler<brave.http.HttpClientRequest, brave.http.HttpClientResponse> handler;
|
||||
final HttpClientHandler<HttpClientRequest, HttpClientResponse> handler;
|
||||
|
||||
TracingFeignClient(HttpTracing httpTracing, Client delegate) {
|
||||
this.tracer = httpTracing.tracing().tracer();
|
||||
this.currentTraceContext = httpTracing.tracing().currentTraceContext();
|
||||
this.handler = HttpClientHandler.create(httpTracing);
|
||||
Client delegateTarget = ProxyUtils.getTargetObject(delegate);
|
||||
this.delegate = delegateTarget instanceof TracingFeignClient
|
||||
@@ -90,29 +70,27 @@ final class TracingFeignClient implements Client {
|
||||
|
||||
@Override
|
||||
public Response execute(Request req, Request.Options options) throws IOException {
|
||||
HttpClientRequest request = new HttpClientRequest(req);
|
||||
RequestWrapper request = new RequestWrapper(req);
|
||||
Span span = this.handler.handleSend(request);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Handled send of " + span);
|
||||
}
|
||||
HttpClientResponse response = null;
|
||||
Response res = null;
|
||||
Throwable error = null;
|
||||
try (Tracer.SpanInScope ws = this.tracer.withSpanInScope(span)) {
|
||||
Response res = this.delegate.execute(request.build(), options);
|
||||
if (res != null) {
|
||||
response = new HttpClientResponse(res);
|
||||
}
|
||||
else { // possibly null on bad implementation or mocks
|
||||
response = new HttpClientResponse(
|
||||
Response.builder().request(req).build());
|
||||
try (Scope ws = this.currentTraceContext.newScope(span.context())) {
|
||||
res = this.delegate.execute(request.build(), options);
|
||||
if (res == null) { // possibly null on bad implementation or mocks
|
||||
res = Response.builder().request(req).build();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
catch (IOException | RuntimeException | Error e) {
|
||||
catch (Throwable e) {
|
||||
error = e;
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
ResponseWrapper response = res != null
|
||||
? new ResponseWrapper(request, res, error) : null;
|
||||
this.handler.handleReceive(response, error, span);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -121,20 +99,22 @@ final class TracingFeignClient implements Client {
|
||||
}
|
||||
}
|
||||
|
||||
void handleSendAndReceive(Span span, Request request, Response response,
|
||||
Throwable error) {
|
||||
this.handler.handleSend(new HttpClientRequest(request), span);
|
||||
this.handler.handleReceive(
|
||||
response != null ? new HttpClientResponse(response) : null, error, span);
|
||||
void handleSendAndReceive(Span span, Request req, @Nullable Response res,
|
||||
@Nullable Throwable error) {
|
||||
RequestWrapper request = new RequestWrapper(req);
|
||||
this.handler.handleSend(request, span);
|
||||
ResponseWrapper response = res != null ? new ResponseWrapper(request, res, error)
|
||||
: null;
|
||||
this.handler.handleReceive(response, error, span);
|
||||
}
|
||||
|
||||
static final class HttpClientRequest extends brave.http.HttpClientRequest {
|
||||
static final class RequestWrapper extends HttpClientRequest {
|
||||
|
||||
final Request delegate;
|
||||
|
||||
Map<String, Collection<String>> headers;
|
||||
|
||||
HttpClientRequest(Request delegate) {
|
||||
RequestWrapper(Request delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@@ -202,22 +182,41 @@ final class TracingFeignClient implements Client {
|
||||
|
||||
}
|
||||
|
||||
static final class HttpClientResponse extends brave.http.HttpClientResponse {
|
||||
static final class ResponseWrapper extends HttpClientResponse {
|
||||
|
||||
final Response delegate;
|
||||
final RequestWrapper request;
|
||||
|
||||
HttpClientResponse(Response delegate) {
|
||||
this.delegate = delegate;
|
||||
final Response response;
|
||||
|
||||
@Nullable
|
||||
final Throwable error;
|
||||
|
||||
ResponseWrapper(RequestWrapper request, Response response,
|
||||
@Nullable Throwable error) {
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object unwrap() {
|
||||
return delegate;
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RequestWrapper request() {
|
||||
return request;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public Throwable error() {
|
||||
return error;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int statusCode() {
|
||||
return delegate.status();
|
||||
return response.status();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import brave.http.HttpRequest;
|
||||
import brave.http.HttpRequestParser;
|
||||
import brave.http.HttpResponseParser;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.sampler.SamplerFunction;
|
||||
import org.junit.Test;
|
||||
@@ -85,6 +87,86 @@ public class TraceHttpAutoConfigurationTests {
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHttpClientParser() {
|
||||
contextRunner().run((context) -> {
|
||||
HttpRequestParser clientRequestParser = context.getBean(HttpTracing.class)
|
||||
.clientRequestParser();
|
||||
HttpResponseParser clientResponseParser = context.getBean(HttpTracing.class)
|
||||
.clientResponseParser();
|
||||
|
||||
then(clientRequestParser).isInstanceOf(HttpRequestParser.Default.class);
|
||||
then(clientResponseParser).isInstanceOf(HttpResponseParser.Default.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresUserProvidedHttpClientParser() {
|
||||
contextRunner().withUserConfiguration(HttpClientParserConfig.class)
|
||||
.run((context) -> {
|
||||
HttpRequestParser clientRequestParser = context
|
||||
.getBean(HttpTracing.class).clientRequestParser();
|
||||
HttpResponseParser clientResponseParser = context
|
||||
.getBean(HttpTracing.class).clientResponseParser();
|
||||
|
||||
then(clientRequestParser)
|
||||
.isSameAs(HttpClientParserConfig.REQUEST_PARSER);
|
||||
then(clientResponseParser)
|
||||
.isSameAs(HttpClientParserConfig.RESPONSE_PARSER);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultHttpServerParser() {
|
||||
contextRunner().run((context) -> {
|
||||
HttpRequestParser serverRequestParser = context.getBean(HttpTracing.class)
|
||||
.serverRequestParser();
|
||||
HttpResponseParser serverResponseParser = context.getBean(HttpTracing.class)
|
||||
.serverResponseParser();
|
||||
|
||||
then(serverRequestParser).isInstanceOf(HttpRequestParser.Default.class);
|
||||
then(serverResponseParser).isInstanceOf(HttpResponseParser.Default.class);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuresUserProvidedHttpServerParser() {
|
||||
contextRunner().withUserConfiguration(HttpServerParserConfig.class)
|
||||
.run((context) -> {
|
||||
HttpRequestParser serverRequestParser = context
|
||||
.getBean(HttpTracing.class).serverRequestParser();
|
||||
HttpResponseParser serverResponseParser = context
|
||||
.getBean(HttpTracing.class).serverResponseParser();
|
||||
|
||||
then(serverRequestParser)
|
||||
.isSameAs(HttpServerParserConfig.REQUEST_PARSER);
|
||||
then(serverResponseParser)
|
||||
.isSameAs(HttpServerParserConfig.RESPONSE_PARSER);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows bean aliases work to configure the same instance for both client and server
|
||||
*/
|
||||
@Test
|
||||
public void configuresUserProvidedHttpClientAndServerParser() {
|
||||
contextRunner().withUserConfiguration(HttpParserConfig.class).run((context) -> {
|
||||
HttpRequestParser serverRequestParser = context.getBean(HttpTracing.class)
|
||||
.serverRequestParser();
|
||||
HttpResponseParser serverResponseParser = context.getBean(HttpTracing.class)
|
||||
.serverResponseParser();
|
||||
HttpRequestParser clientRequestParser = context.getBean(HttpTracing.class)
|
||||
.clientRequestParser();
|
||||
HttpResponseParser clientResponseParser = context.getBean(HttpTracing.class)
|
||||
.clientResponseParser();
|
||||
|
||||
then(clientRequestParser).isSameAs(HttpParserConfig.REQUEST_PARSER);
|
||||
then(clientResponseParser).isSameAs(HttpParserConfig.RESPONSE_PARSER);
|
||||
then(serverRequestParser).isSameAs(HttpParserConfig.REQUEST_PARSER);
|
||||
then(serverResponseParser).isSameAs(HttpParserConfig.RESPONSE_PARSER);
|
||||
});
|
||||
}
|
||||
|
||||
private ApplicationContextRunner contextRunner(String... propertyValues) {
|
||||
return new ApplicationContextRunner().withPropertyValues(propertyValues)
|
||||
.withConfiguration(AutoConfigurations.of(TraceAutoConfiguration.class,
|
||||
@@ -117,3 +199,63 @@ class HttpServerSamplerConfig {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class HttpClientParserConfig {
|
||||
|
||||
static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> {
|
||||
};
|
||||
static final HttpResponseParser RESPONSE_PARSER = (r, c, s) -> {
|
||||
};
|
||||
|
||||
@Bean(HttpClientRequestParser.NAME)
|
||||
HttpRequestParser sleuthHttpClientRequestParser() {
|
||||
return REQUEST_PARSER;
|
||||
}
|
||||
|
||||
@Bean(HttpClientResponseParser.NAME)
|
||||
HttpResponseParser sleuthHttpClientResponseParser() {
|
||||
return RESPONSE_PARSER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class HttpServerParserConfig {
|
||||
|
||||
static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> {
|
||||
};
|
||||
static final HttpResponseParser RESPONSE_PARSER = (r, c, s) -> {
|
||||
};
|
||||
|
||||
@Bean(HttpServerRequestParser.NAME)
|
||||
HttpRequestParser sleuthHttpServerRequestParser() {
|
||||
return REQUEST_PARSER;
|
||||
}
|
||||
|
||||
@Bean(HttpServerResponseParser.NAME)
|
||||
HttpResponseParser sleuthHttpServerResponseParser() {
|
||||
return RESPONSE_PARSER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
class HttpParserConfig {
|
||||
|
||||
static final HttpRequestParser REQUEST_PARSER = (r, c, s) -> {
|
||||
};
|
||||
static final HttpResponseParser RESPONSE_PARSER = (r, c, s) -> {
|
||||
};
|
||||
|
||||
@Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME })
|
||||
HttpRequestParser sleuthHttpServerRequestParser() {
|
||||
return REQUEST_PARSER;
|
||||
}
|
||||
|
||||
@Bean(name = { HttpClientResponseParser.NAME, HttpServerResponseParser.NAME })
|
||||
HttpResponseParser sleuthHttpServerResponseParser() {
|
||||
return RESPONSE_PARSER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.assertj.core.api.BDDAssertions;
|
||||
import org.junit.Test;
|
||||
import org.mockito.BDDMockito;
|
||||
|
||||
import org.springframework.cloud.sleuth.instrument.web.client.TraceExchangeFilterFunction.HttpClientResponse;
|
||||
import org.springframework.cloud.sleuth.instrument.web.client.TraceExchangeFilterFunction.ClientResponseWrapper;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
|
||||
public class TraceExchangeFilterFunctionHttpClientResponseTests {
|
||||
@@ -29,7 +29,7 @@ public class TraceExchangeFilterFunctionHttpClientResponseTests {
|
||||
public void should_return_0_when_invalid_status_code_is_returned() {
|
||||
ClientResponse clientResponse = BDDMockito.mock(ClientResponse.class);
|
||||
BDDMockito.given(clientResponse.rawStatusCode()).willReturn(-1);
|
||||
HttpClientResponse response = new HttpClientResponse(clientResponse);
|
||||
ClientResponseWrapper response = new ClientResponseWrapper(clientResponse);
|
||||
|
||||
Integer statusCode = response.statusCode();
|
||||
|
||||
@@ -40,7 +40,7 @@ public class TraceExchangeFilterFunctionHttpClientResponseTests {
|
||||
public void should_return_status_code_when_valid_status_code_is_returned() {
|
||||
ClientResponse clientResponse = BDDMockito.mock(ClientResponse.class);
|
||||
BDDMockito.given(clientResponse.rawStatusCode()).willReturn(200);
|
||||
HttpClientResponse response = new HttpClientResponse(clientResponse);
|
||||
ClientResponseWrapper response = new ClientResponseWrapper(clientResponse);
|
||||
|
||||
Integer statusCode = response.statusCode();
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.HashMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpClientParser;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.StrictScopeDecorator;
|
||||
import brave.propagation.ThreadLocalCurrentTraceContext;
|
||||
@@ -69,8 +68,7 @@ public class FeignRetriesTests {
|
||||
.addScopeDecorator(StrictScopeDecorator.create()).build())
|
||||
.spanReporter(this.reporter).build();
|
||||
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(new HttpClientParser()).build();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build();
|
||||
|
||||
@Before
|
||||
@After
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign;
|
||||
import java.io.IOException;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpClientParser;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.StrictScopeDecorator;
|
||||
import brave.propagation.ThreadLocalCurrentTraceContext;
|
||||
@@ -57,8 +56,7 @@ public class TraceFeignAspectTests {
|
||||
.addScopeDecorator(StrictScopeDecorator.create()).build())
|
||||
.build();
|
||||
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(new HttpClientParser()).build();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build();
|
||||
|
||||
TraceFeignAspect traceFeignAspect;
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.List;
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpClientParser;
|
||||
import brave.http.HttpTracing;
|
||||
import brave.propagation.StrictScopeDecorator;
|
||||
import brave.propagation.ThreadLocalCurrentTraceContext;
|
||||
@@ -59,8 +58,7 @@ public class TracingFeignClientTests {
|
||||
|
||||
Tracer tracer = this.tracing.tracer();
|
||||
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing)
|
||||
.clientParser(new HttpClientParser()).build();
|
||||
HttpTracing httpTracing = HttpTracing.newBuilder(this.tracing).build();
|
||||
|
||||
@Mock
|
||||
Client client;
|
||||
|
||||
@@ -19,13 +19,13 @@ package sample;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
|
||||
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableWebSocketMessageBroker
|
||||
public class SampleWebsocketApplication extends AbstractWebSocketMessageBrokerConfigurer {
|
||||
public class SampleWebsocketApplication implements WebSocketMessageBrokerConfigurer {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleWebsocketApplication.class, args);
|
||||
|
||||
@@ -60,6 +60,18 @@ https://www.w3.org/2001/XMLSchema-instance ">
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-sleuth</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-stream</artifactId>
|
||||
<type>test-jar</type>
|
||||
<scope>test</scope>
|
||||
<classifier>test-binder</classifier>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
|
||||
@@ -65,6 +65,7 @@ public class TraceContextPropagationChannelInterceptorTests {
|
||||
public void testSpanPropagation() {
|
||||
Span span = this.tracing.tracer().nextSpan().name("http:testSendMessage").start();
|
||||
String expectedSpanId = SpanUtil.idToHex(span.context().spanId());
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) {
|
||||
this.channel.send(MessageBuilder.withPayload("hi").build());
|
||||
}
|
||||
@@ -72,6 +73,10 @@ public class TraceContextPropagationChannelInterceptorTests {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
assertThatNewSpanIdWasSetOnMessage(expectedSpanId);
|
||||
}
|
||||
|
||||
private void assertThatNewSpanIdWasSetOnMessage(String expectedSpanId) {
|
||||
Message<?> message = this.channel.receive(0);
|
||||
assertThat(message).as("message was null").isNotNull();
|
||||
|
||||
@@ -88,7 +93,6 @@ public class TraceContextPropagationChannelInterceptorTests {
|
||||
String.class);
|
||||
assertThat(parentId).as("parentId was not equal to parent's id")
|
||||
.isEqualTo(this.reporter.getSpans().get(0).id());
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2013-2019 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
|
||||
*
|
||||
* https://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.messaging;
|
||||
|
||||
import brave.Span;
|
||||
import brave.Tracer;
|
||||
import brave.Tracing;
|
||||
import brave.sampler.Sampler;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.sleuth.instrument.util.SpanUtil;
|
||||
import org.springframework.cloud.sleuth.util.ArrayListSpanReporter;
|
||||
import org.springframework.cloud.stream.binder.test.OutputDestination;
|
||||
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration;
|
||||
import org.springframework.cloud.stream.function.StreamBridge;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@SpringBootTest(classes = TraceStreamChannelInterceptorTests.App.class,
|
||||
properties = "spring.cloud.stream.source=testSupplier")
|
||||
@DirtiesContext
|
||||
public class TraceStreamChannelInterceptorTests {
|
||||
|
||||
@Autowired
|
||||
private OutputDestination channel;
|
||||
|
||||
@Autowired
|
||||
private Tracing tracing;
|
||||
|
||||
@Autowired
|
||||
private StreamBridge streamBridge;
|
||||
|
||||
@Autowired
|
||||
private ArrayListSpanReporter reporter;
|
||||
|
||||
@AfterEach
|
||||
public void close() {
|
||||
this.reporter.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpanPropagationViaBridge() {
|
||||
Span span = this.tracing.tracer().nextSpan().name("http:testSendMessage").start();
|
||||
String expectedSpanId = SpanUtil.idToHex(span.context().spanId());
|
||||
|
||||
try (Tracer.SpanInScope ws = this.tracing.tracer().withSpanInScope(span)) {
|
||||
this.streamBridge.send("testSupplier-out-0", "hi");
|
||||
}
|
||||
finally {
|
||||
span.finish();
|
||||
}
|
||||
|
||||
assertThatNewSpanIdWasSetOnMessage(expectedSpanId);
|
||||
}
|
||||
|
||||
private void assertThatNewSpanIdWasSetOnMessage(String expectedSpanId) {
|
||||
Message<?> message = this.channel.receive(0);
|
||||
assertThat(message).as("message was null").isNotNull();
|
||||
|
||||
String spanId = message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME,
|
||||
String.class);
|
||||
assertThat(spanId).as("spanId was equal to parent's id")
|
||||
.isNotEqualTo(expectedSpanId);
|
||||
|
||||
String traceId = message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME,
|
||||
String.class);
|
||||
assertThat(traceId).as("traceId was null").isNotNull();
|
||||
|
||||
String parentId = message.getHeaders().get(TraceMessageHeaders.PARENT_ID_NAME,
|
||||
String.class);
|
||||
// [0] - producer
|
||||
// [1] - http:testsendmessage
|
||||
assertThat(parentId).as("parentId was not equal to parent's id")
|
||||
.isEqualTo(this.reporter.getSpans().get(1).id());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableAutoConfiguration
|
||||
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
|
||||
static class App {
|
||||
|
||||
@Bean
|
||||
Sampler testSampler() {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ArrayListSpanReporter reporter() {
|
||||
return new ArrayListSpanReporter();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.sleuth.instrument.messaging.issues.issue_943;
|
||||
import brave.sampler.Sampler;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
@@ -30,8 +29,7 @@ import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableAutoConfiguration(exclude = { DataSourceAutoConfiguration.class,
|
||||
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
|
||||
HibernateJpaAutoConfiguration.class })
|
||||
@ImportResource("classpath:beans/applicationContext.xml")
|
||||
@EnableIntegration
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
import brave.Tracing;
|
||||
import brave.http.HttpRequest;
|
||||
import brave.http.HttpRequestParser;
|
||||
import brave.sampler.Sampler;
|
||||
import brave.sampler.SamplerFunction;
|
||||
import org.assertj.core.api.BDDAssertions;
|
||||
@@ -66,9 +67,6 @@ public class TraceFilterWebIntegrationTests {
|
||||
@Rule
|
||||
public OutputCaptureRule capture = new OutputCaptureRule();
|
||||
|
||||
@Autowired
|
||||
Tracing tracer;
|
||||
|
||||
@Autowired
|
||||
ArrayListSpanReporter accumulator;
|
||||
|
||||
@@ -85,6 +83,16 @@ public class TraceFilterWebIntegrationTests {
|
||||
this.accumulator.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_tag_url() {
|
||||
new RestTemplate().getForObject("http://localhost:" + port() + "/good",
|
||||
String.class);
|
||||
|
||||
then(Tracing.current().tracer().currentSpan()).isNull();
|
||||
then(this.accumulator.getSpans()).hasSize(1);
|
||||
then(this.accumulator.getSpans().get(0).tags()).containsKey("http.url");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void should_not_create_a_span_for_error_controller() {
|
||||
try {
|
||||
@@ -146,7 +154,12 @@ public class TraceFilterWebIntegrationTests {
|
||||
public static class Config {
|
||||
|
||||
@Bean
|
||||
ExceptionThrowingController controller() {
|
||||
GoodController goodController() {
|
||||
return new GoodController();
|
||||
}
|
||||
|
||||
@Bean
|
||||
ExceptionThrowingController badController() {
|
||||
return new ExceptionThrowingController();
|
||||
}
|
||||
|
||||
@@ -160,6 +173,19 @@ public class TraceFilterWebIntegrationTests {
|
||||
return Sampler.ALWAYS_SAMPLE;
|
||||
}
|
||||
|
||||
// tag::custom_parser[]
|
||||
@Bean(name = { HttpClientRequestParser.NAME, HttpServerRequestParser.NAME })
|
||||
HttpRequestParser sleuthHttpServerRequestParser() {
|
||||
return (req, context, span) -> {
|
||||
HttpRequestParser.DEFAULT.parse(req, context, span);
|
||||
String url = req.url();
|
||||
if (url != null) {
|
||||
span.tag("http.url", url);
|
||||
}
|
||||
};
|
||||
}
|
||||
// end::custom_parser[]
|
||||
|
||||
// tag::custom_server_sampler[]
|
||||
@Bean(name = HttpServerSampler.NAME)
|
||||
SamplerFunction<HttpRequest> myHttpSampler(SkipPatternProvider provider) {
|
||||
@@ -188,6 +214,16 @@ public class TraceFilterWebIntegrationTests {
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
public static class GoodController {
|
||||
|
||||
@RequestMapping("/good")
|
||||
public String beGood() {
|
||||
return "good";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@RestController
|
||||
public static class ExceptionThrowingController {
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.reactive.ClientHttpConnector;
|
||||
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
@@ -118,9 +119,13 @@ public class WebClientBraveTests
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClient.Builder webClientBuilder(HttpClient httpClient) {
|
||||
return WebClient.builder()
|
||||
.clientConnector(new ReactorClientHttpConnector(httpClient));
|
||||
ClientHttpConnector clientHttpConnector(HttpClient httpClient) {
|
||||
return new ReactorClientHttpConnector(httpClient);
|
||||
}
|
||||
|
||||
@Bean
|
||||
WebClient.Builder webClientBuilder(ClientHttpConnector clientHttpConnector) {
|
||||
return WebClient.builder().clientConnector(clientHttpConnector);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user