From 83012a5aa629c57560a85462ce8610d55f053901 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Fri, 4 Nov 2016 12:23:14 +0100 Subject: [PATCH] Text map introduction (#439) without this change we had to treat every mean of injecting and extracting span information separately with this change all means of communications are abstracted. That way you configure things once only. part of #237 --- README.adoc | 4 +- .../cloud/sleuth/HttpSpanExtractor.java | 11 ++ .../cloud/sleuth/HttpSpanInjector.java | 11 ++ .../cloud/sleuth/SpanTextMap.java | 31 ++++ .../AbstractTraceChannelInterceptor.java | 19 +-- .../HeaderBasedMessagingExtractor.java | 61 +++++++ .../HeaderBasedMessagingInjector.java | 102 +++++++++++ .../IntegrationTraceChannelInterceptor.java | 9 +- .../messaging/MessagingSpanExtractor.java | 119 ------------- .../messaging/MessagingSpanInjector.java | 161 ------------------ .../MessagingSpanTextMapExtractor.java | 14 ++ .../MessagingSpanTextMapInjector.java | 14 ++ .../messaging/MessagingTextMap.java | 74 ++++++++ .../messaging/TraceChannelInterceptor.java | 53 ++---- .../messaging/TraceMessageHeaders.java | 3 - .../TraceSpanMessagingAutoConfiguration.java | 14 +- ...aceSpringIntegrationAutoConfiguration.java | 8 +- .../TraceWebSocketAutoConfiguration.java | 10 +- .../web/HttpServletRequestExtractor.java | 108 ------------ .../web/HttpServletRequestTextMap.java | 62 +++++++ .../sleuth/instrument/web/TraceFilter.java | 22 +-- .../web/TraceWebAutoConfiguration.java | 23 +-- .../web/ZipkinHttpSpanExtractor.java | 92 ++++++++++ .../web/ZipkinHttpSpanInjector.java | 53 ++++++ .../AbstractTraceHttpRequestInterceptor.java | 8 +- .../web/client/HttpRequestInjector.java | 58 ------- .../web/client/HttpRequestTextMap.java | 67 ++++++++ ...eAsyncClientHttpRequestFactoryWrapper.java | 9 +- .../client/TraceRestTemplateInterceptor.java | 4 +- .../TraceWebAsyncClientAutoConfiguration.java | 5 +- .../TraceWebClientAutoConfiguration.java | 10 +- .../client/feign/FeignRequestInjector.java | 79 --------- .../web/client/feign/FeignRequestTextMap.java | 86 ++++++++++ .../web/client/feign/TraceFeignClient.java | 18 +- ...acheHttpClientRibbonRequestCustomizer.java | 25 ++- .../OkHttpClientRibbonRequestCustomizer.java | 26 ++- .../zuul/RequestContextInjector.java | 66 ------- .../zuul/RequestContextTextMap.java | 49 ++++++ .../RestClientRibbonRequestCustomizer.java | 19 ++- .../SpanInjectingRibbonRequestCustomizer.java | 29 ++-- .../instrument/zuul/TracePreZuulFilter.java | 15 +- .../zuul/TraceZuulAutoConfiguration.java | 15 +- .../cloud/sleuth/util/TextMapUtil.java | 28 +++ .../async/issues/issue410/Issue410Tests.java | 7 + .../MessagingSpanExtractorTests.java | 43 ++--- .../messaging/MessagingSpanInjectorTests.java | 22 ++- .../TraceChannelInterceptorTests.java | 102 ++++------- ...extPropagationChannelInterceptorTests.java | 6 +- .../web/HttpServletRequestExtractorTests.java | 20 ++- ...raceCustomFilterResponseInjectorTests.java | 48 ++++-- .../web/TraceFilterCustomExtractorTests.java | 83 +++++---- .../TraceFilterMockChainIntegrationTests.java | 4 +- .../instrument/web/TraceFilterTests.java | 3 +- ...stTemplateInterceptorIntegrationTests.java | 3 +- .../TraceRestTemplateInterceptorTests.java | 3 +- .../web/client/feign/FeignRetriesTests.java | 4 + .../client/feign/TraceFeignClientTests.java | 4 + .../common/AbstractMvcIntegrationTest.java | 6 +- ...ttpClientRibbonRequestCustomizerTests.java | 4 +- ...ttpClientRibbonRequestCustomizerTests.java | 4 +- ...estClientRibbonRequestCustomizerTests.java | 4 +- .../zuul/TracePreZuulFilterTests.java | 7 +- .../cloud/sleuth/util/TextMapUtilTests.java | 31 ++++ 63 files changed, 1156 insertions(+), 946 deletions(-) create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanExtractor.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanInjector.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractor.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapExtractor.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapInjector.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingTextMap.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractor.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestTextMap.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestInjector.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestTextMap.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestInjector.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTextMap.java delete mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextInjector.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextTextMap.java create mode 100644 spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/TextMapUtil.java create mode 100644 spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/TextMapUtilTests.java diff --git a/README.adoc b/README.adoc index d20d1c2a4..964988f2e 100644 --- a/README.adoc +++ b/README.adoc @@ -636,11 +636,11 @@ a modified file in the correct place. Just commit it and push the change. If you don't have an IDE preference we would recommend that you use http://www.springsource.com/developer/sts[Spring Tools Suite] or http://eclipse.org[Eclipse] when working with the code. We use the -http://eclipse.org/m2e/[m2eclipe] eclipse plugin for maven support. Other IDEs and tools +http://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools should also work without issue as long as they use Maven 3.3.3 or better. ==== Importing into eclipse with m2eclipse -We recommend the http://eclipse.org/m2e/[m2eclipe] eclipse plugin when working with +We recommend the http://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with eclipse. If you don't already have m2eclipse installed it is available from the "eclipse marketplace". diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanExtractor.java new file mode 100644 index 000000000..de2b443ad --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanExtractor.java @@ -0,0 +1,11 @@ +package org.springframework.cloud.sleuth; + +/** + * Contract for extracting tracing headers from a {@link SpanTextMap} + * via HTTP headers + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public interface HttpSpanExtractor extends SpanExtractor { +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanInjector.java new file mode 100644 index 000000000..5b7e5eb6c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/HttpSpanInjector.java @@ -0,0 +1,11 @@ +package org.springframework.cloud.sleuth; + +/** + * Contract for injecting tracing headers from a {@link SpanTextMap} + * via HTTP headers + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public interface HttpSpanInjector extends SpanInjector { +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java new file mode 100644 index 000000000..54e77e2d2 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanTextMap.java @@ -0,0 +1,31 @@ +package org.springframework.cloud.sleuth; + +import java.util.Iterator; +import java.util.Map; + +/** + * Adopted from: https://github.com/opentracing/opentracing-java/blob/master/opentracing-api/src/main/java/io/opentracing/propagation/TextMap.java + * + * TextMap is a built-in carrier for {@link SpanInjector} and {@link SpanExtractor}. TextMap implementations allows Tracers to + * read and write key:value String pairs from arbitrary underlying sources of data. + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public interface SpanTextMap extends Iterable> { + /** + * Gets an iterator over arbitrary key:value pairs from the TextMapReader. + * + * @return entries in the TextMap backing store; note that for some Formats, the iterator may include entries that + * were never injected by a Tracer implementation (e.g., unrelated HTTP headers) + */ + Iterator> iterator(); + + /** + * Puts a key:value pair into the TextMapWriter's backing store. + * + * @param key a String, possibly with constraints dictated by the particular Format this TextMap is paired with + * @param value a String, possibly with constraints dictated by the particular Format this TextMap is paired with + */ + void put(String key, String value); +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/AbstractTraceChannelInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/AbstractTraceChannelInterceptor.java index df22ff6e8..95bed7071 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/AbstractTraceChannelInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/AbstractTraceChannelInterceptor.java @@ -5,17 +5,14 @@ import java.lang.invoke.MethodHandles; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.integration.channel.AbstractMessageChannel; import org.springframework.integration.context.IntegrationObjectSupport; -import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.support.ChannelInterceptorAdapter; import org.springframework.messaging.support.ExecutorChannelInterceptor; -import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.ClassUtils; /** @@ -40,12 +37,12 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter private final Tracer tracer; private final TraceKeys traceKeys; - private final SpanExtractor> spanExtractor; - private final SpanInjector> spanInjector; + private final MessagingSpanTextMapExtractor spanExtractor; + private final MessagingSpanTextMapInjector spanInjector; protected AbstractTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, - SpanExtractor> spanExtractor, - SpanInjector> spanInjector) { + MessagingSpanTextMapExtractor spanExtractor, + MessagingSpanTextMapInjector spanInjector) { this.tracer = tracer; this.traceKeys = traceKeys; this.spanExtractor = spanExtractor; @@ -60,7 +57,7 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter return this.traceKeys; } - protected SpanInjector> getSpanInjector() { + protected MessagingSpanTextMapInjector getSpanInjector() { return this.spanInjector; } @@ -68,9 +65,9 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter * Returns a span given the message and a channel. Returns {@code null} if ids are * missing. */ - protected Span buildSpan(Message message) { + protected Span buildSpan(SpanTextMap carrier) { try { - return this.spanExtractor.joinTrace(message); + return this.spanExtractor.joinTrace(carrier); } catch (Exception e) { log.error("Exception occurred while trying to extract span from carrier", e); return null; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java new file mode 100644 index 000000000..7d1349af1 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingExtractor.java @@ -0,0 +1,61 @@ +package org.springframework.cloud.sleuth.instrument.messaging; + +import java.util.Map; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.cloud.sleuth.util.TextMapUtil; + +/** + * Default implementation for messaging + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtractor { + + @Override + public Span joinTrace(SpanTextMap textMap) { + Map carrier = TextMapUtil.asMap(textMap); + if (!hasHeader(carrier, TraceMessageHeaders.SPAN_ID_NAME) + || !hasHeader(carrier, TraceMessageHeaders.TRACE_ID_NAME)) { + return null; + // TODO: Consider throwing IllegalArgumentException; + } + return extractSpanFromHeaders(carrier, Span.builder()); + } + + private Span extractSpanFromHeaders(Map carrier, Span.SpanBuilder builder) { + Span.SpanBuilder spanBuilder = builder; + long traceId = Span + .hexToId(carrier.get(TraceMessageHeaders.TRACE_ID_NAME)); + long spanId = Span.hexToId(carrier.get(TraceMessageHeaders.SPAN_ID_NAME)); + spanBuilder = spanBuilder.traceId(traceId).spanId(spanId); + spanBuilder.exportable( + Span.SPAN_SAMPLED.equals(carrier.get(TraceMessageHeaders.SAMPLED_NAME))); + String processId = carrier.get(TraceMessageHeaders.PROCESS_ID_NAME); + String spanName = carrier.get(TraceMessageHeaders.SPAN_NAME_NAME); + if (spanName != null) { + spanBuilder.name(spanName); + } + if (processId != null) { + spanBuilder.processId(processId); + } + setParentIdIfApplicable(carrier, spanBuilder, TraceMessageHeaders.PARENT_ID_NAME); + spanBuilder.remote(true); + return spanBuilder.build(); + } + + boolean hasHeader(Map message, String name) { + return message.containsKey(name); + } + + private void setParentIdIfApplicable(Map carrier, Span.SpanBuilder spanBuilder, + String spanParentIdHeader) { + String parentId = carrier.get(spanParentIdHeader); + if (parentId != null) { + spanBuilder.parent(Span.hexToId(parentId)); + } + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java new file mode 100644 index 000000000..f5e840d8a --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/HeaderBasedMessagingInjector.java @@ -0,0 +1,102 @@ +package org.springframework.cloud.sleuth.instrument.messaging; + +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.cloud.sleuth.TraceKeys; +import org.springframework.cloud.sleuth.util.TextMapUtil; +import org.springframework.util.StringUtils; + +/** + * Default implementation for messaging + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public class HeaderBasedMessagingInjector implements MessagingSpanTextMapInjector { + + private final TraceKeys traceKeys; + + public HeaderBasedMessagingInjector(TraceKeys traceKeys) { + this.traceKeys = traceKeys; + } + + @Override + public void inject(Span span, SpanTextMap carrier) { + Map map = TextMapUtil.asMap(carrier); + if (span == null) { + if (!isSampled(map, TraceMessageHeaders.SAMPLED_NAME)) { + carrier.put(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); + return; + } + return; + } + addHeaders(span, carrier); + } + + private boolean isSampled(Map initialMessage, String sampledHeaderName) { + return Span.SPAN_SAMPLED.equals(initialMessage.get(sampledHeaderName)); + } + + private void addHeaders(Span span, SpanTextMap textMap) { + addHeader(textMap, TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); + addHeader(textMap, TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); + if (span.isExportable()) { + addAnnotations(this.traceKeys, textMap, span); + Long parentId = getFirst(span.getParents()); + if (parentId != null) { + addHeader(textMap, TraceMessageHeaders.PARENT_ID_NAME, Span.idToHex(parentId)); + } + addHeader(textMap, TraceMessageHeaders.SPAN_NAME_NAME, span.getName()); + addHeader(textMap, TraceMessageHeaders.PROCESS_ID_NAME, span.getProcessId()); + addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_SAMPLED); + } + else { + addHeader(textMap, TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); + } + } + + private void addAnnotations(TraceKeys traceKeys, SpanTextMap spanTextMap, Span span) { + Map map = TextMapUtil.asMap(spanTextMap); + for (String name : traceKeys.getMessage().getHeaders()) { + if (map.containsKey(name)) { + String key = traceKeys.getMessage().getPrefix() + name.toLowerCase(); + Object value = map.get(name); + if (value == null) { + value = "null"; + } + // TODO: better way to serialize? + tagIfEntryMissing(span, key, value.toString()); + } + } + addPayloadAnnotations(traceKeys, map, span); + } + + private void addPayloadAnnotations(TraceKeys traceKeys, Map map, Span span) { + if (map.containsKey(traceKeys.getMessage().getPayload().getType())) { + tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(), + map.get(traceKeys.getMessage().getPayload().getType())); + tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(), + map.get(traceKeys.getMessage().getPayload().getSize())); + } + } + + private void tagIfEntryMissing(Span span, String key, String value) { + if (!span.tags().containsKey(key)) { + span.tag(key, value); + } + } + + private void addHeader(SpanTextMap textMap, String name, String value) { + if (StringUtils.hasText(value)) { + textMap.put(name, value); + } + } + + private Long getFirst(List parents) { + return parents.isEmpty() ? null : parents.get(0); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/IntegrationTraceChannelInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/IntegrationTraceChannelInterceptor.java index 824870955..5cc14ca11 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/IntegrationTraceChannelInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/IntegrationTraceChannelInterceptor.java @@ -16,15 +16,11 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.integration.channel.ChannelInterceptorAware; import org.springframework.integration.channel.interceptor.VetoCapableInterceptor; -import org.springframework.messaging.Message; import org.springframework.messaging.support.ChannelInterceptor; -import org.springframework.messaging.support.MessageBuilder; /** * @author Dave Syer @@ -32,9 +28,10 @@ import org.springframework.messaging.support.MessageBuilder; */ class IntegrationTraceChannelInterceptor extends TraceChannelInterceptor implements VetoCapableInterceptor { + public IntegrationTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, - SpanExtractor> spanExtractor, - SpanInjector> spanInjector) { + MessagingSpanTextMapExtractor spanExtractor, + MessagingSpanTextMapInjector spanInjector) { super(tracer, traceKeys, spanExtractor, spanInjector); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractor.java deleted file mode 100644 index b7cdc809f..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractor.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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.messaging; - -import java.lang.invoke.MethodHandles; -import java.util.Random; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Span.SpanBuilder; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.messaging.Message; - -/** - * Creates a {@link SpanBuilder} from {@link Message} - * - * @author Marcin Grzejszczak - * @since 1.0.0 - */ -class MessagingSpanExtractor implements SpanExtractor> { - - private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()); - - private final Random random; - - public MessagingSpanExtractor(Random random) { - this.random = random; - } - - @Override - public Span joinTrace(Message carrier) { - if ((!hasHeader(carrier, Span.TRACE_ID_NAME) - || !hasHeader(carrier, Span.SPAN_ID_NAME)) - && (!hasHeader(carrier, TraceMessageHeaders.SPAN_ID_NAME) - || !hasHeader(carrier, TraceMessageHeaders.TRACE_ID_NAME))) { - return null; - // TODO: Consider throwing IllegalArgumentException; - } - if (hasHeader(carrier, Span.TRACE_ID_NAME) - || hasHeader(carrier, Span.SPAN_ID_NAME)) { - log.warn("Deprecated trace headers detected. Please upgrade Sleuth to 1.1 " - + "or start sending headers present in the TraceMessageHeaders class"); - return extractSpanFromOldHeaders(carrier, Span.builder()); - } - return extractSpanFromNewHeaders(carrier, Span.builder()); - } - - // Backwards compatibility - private Span extractSpanFromOldHeaders(Message carrier, SpanBuilder spanBuilder) { - return extractSpanFromHeaders(carrier, spanBuilder, Span.TRACE_ID_NAME, Span.SPAN_ID_NAME, - Span.SAMPLED_NAME, Span.PROCESS_ID_NAME, Span.SPAN_NAME_NAME, Span.PARENT_ID_NAME); - } - - private Span extractSpanFromNewHeaders(Message carrier, SpanBuilder spanBuilder) { - return extractSpanFromHeaders(carrier, spanBuilder, TraceMessageHeaders.TRACE_ID_NAME, - TraceMessageHeaders.SPAN_ID_NAME, TraceMessageHeaders.SAMPLED_NAME, - TraceMessageHeaders.PROCESS_ID_NAME, TraceMessageHeaders.SPAN_NAME_NAME, - TraceMessageHeaders.PARENT_ID_NAME); - } - - private Span extractSpanFromHeaders(Message carrier, SpanBuilder spanBuilder, - String traceIdHeader, String spanIdHeader, String spanSampledHeader, - String spanProcessIdHeader, String spanNameHeader, String spanParentIdHeader) { - long traceId = Span - .hexToId(getHeader(carrier, traceIdHeader)); - long spanId = hasHeader(carrier, spanIdHeader) - ? Span.hexToId(getHeader(carrier, spanIdHeader)) - : this.random.nextLong(); - spanBuilder = spanBuilder.traceId(traceId).spanId(spanId); - spanBuilder.exportable( - Span.SPAN_SAMPLED.equals(getHeader(carrier, spanSampledHeader))); - String processId = getHeader(carrier, spanProcessIdHeader); - String spanName = getHeader(carrier, spanNameHeader); - if (spanName != null) { - spanBuilder.name(spanName); - } - if (processId != null) { - spanBuilder.processId(processId); - } - setParentIdIfApplicable(carrier, spanBuilder, spanParentIdHeader); - spanBuilder.remote(true); - return spanBuilder.build(); - } - - String getHeader(Message message, String name) { - return getHeader(message, name, String.class); - } - - T getHeader(Message message, String name, Class type) { - return message.getHeaders().get(name, type); - } - - boolean hasHeader(Message message, String name) { - return message.getHeaders().containsKey(name); - } - - private void setParentIdIfApplicable(Message carrier, SpanBuilder spanBuilder, - String spanParentIdHeader) { - String parentId = getHeader(carrier, spanParentIdHeader); - if (parentId != null) { - spanBuilder.parent(Span.hexToId(parentId)); - } - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java deleted file mode 100644 index 11098b447..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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.messaging; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanInjector; -import org.springframework.cloud.sleuth.TraceKeys; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; -import org.springframework.messaging.support.MessageHeaderAccessor; -import org.springframework.messaging.support.NativeMessageHeaderAccessor; -import org.springframework.util.StringUtils; - -/** - * Creates a {@link Span.SpanBuilder} from {@link Message} - * - * @author Marcin Grzejszczak - */ -class MessagingSpanInjector implements SpanInjector> { - - private final TraceKeys traceKeys; - - public MessagingSpanInjector(TraceKeys traceKeys) { - this.traceKeys = traceKeys; - } - - @Override - public void inject(Span span, MessageBuilder carrier) { - Message initialMessage = carrier.build(); - MessageHeaderAccessor accessor = MessageHeaderAccessor - .getMutableAccessor(initialMessage); - if (span == null) { - if (!isSampled(initialMessage, Span.SAMPLED_NAME) || - !isSampled(initialMessage, TraceMessageHeaders.SAMPLED_NAME)) { - // Backwards compatibility - accessor.setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); - accessor.setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); - carrier.setHeaders(accessor); - return; - } - return; - } - Map headers = new HashMap<>(); - addOldHeaders(span, initialMessage, accessor, headers); - addNewHeaders(span, initialMessage, accessor, headers); - accessor.copyHeaders(headers); - if (accessor instanceof NativeMessageHeaderAccessor) { - NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor; - for (String name : headers.keySet()) { - nativeAccessor.setNativeHeader(name, headers.get(name)); - } - } - carrier.setHeaders(accessor); - } - - private boolean isSampled(Message initialMessage, String sampledHeaderName) { - return Span.SPAN_SAMPLED - .equals(initialMessage.getHeaders().get(sampledHeaderName)); - } - - // Backwards compatibility - private void addOldHeaders(Span span, Message initialMessage, - MessageHeaderAccessor accessor, Map headers) { - addHeaders(span, initialMessage, accessor, headers, Span.TRACE_ID_NAME, - Span.SPAN_ID_NAME, Span.PARENT_ID_NAME, Span.SPAN_NAME_NAME, Span.PROCESS_ID_NAME, - Span.SAMPLED_NAME, TraceMessageHeaders.OLD_SPAN_HEADER); - } - - private void addNewHeaders(Span span, Message initialMessage, - MessageHeaderAccessor accessor, Map headers) { - addHeaders(span, initialMessage, accessor, headers, TraceMessageHeaders.TRACE_ID_NAME, - TraceMessageHeaders.SPAN_ID_NAME, TraceMessageHeaders.PARENT_ID_NAME, TraceMessageHeaders.SPAN_NAME_NAME, - TraceMessageHeaders.PROCESS_ID_NAME, TraceMessageHeaders.SAMPLED_NAME, TraceMessageHeaders.SPAN_HEADER); - } - - private void addHeaders(Span span, Message initialMessage, - MessageHeaderAccessor accessor, Map headers, String traceIdHeader, - String spanIdHeader, String parentIdHeader, String spanNameHeader, String processIdHeader, - String spanSampledHeader, String spanHeader) { - addHeader(headers, traceIdHeader, Span.idToHex(span.getTraceId())); - addHeader(headers, spanIdHeader, Span.idToHex(span.getSpanId())); - if (span.isExportable()) { - addAnnotations(this.traceKeys, initialMessage, span); - Long parentId = getFirst(span.getParents()); - if (parentId != null) { - addHeader(headers, parentIdHeader, Span.idToHex(parentId)); - } - addHeader(headers, spanNameHeader, span.getName()); - addHeader(headers, processIdHeader, span.getProcessId()); - addHeader(headers, spanSampledHeader, Span.SPAN_SAMPLED); - } - else { - addHeader(headers, spanSampledHeader, Span.SPAN_NOT_SAMPLED); - } - accessor.setHeader(spanHeader, span); - } - - private void addAnnotations(TraceKeys traceKeys, Message message, Span span) { - for (String name : traceKeys.getMessage().getHeaders()) { - if (message.getHeaders().containsKey(name)) { - String key = traceKeys.getMessage().getPrefix() + name.toLowerCase(); - Object value = message.getHeaders().get(name); - if (value == null) { - value = "null"; - } - // TODO: better way to serialize? - tagIfEntryMissing(span, key, value.toString()); - } - } - addPayloadAnnotations(traceKeys, message.getPayload(), span); - } - - private void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) { - if (payload != null) { - tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(), - payload.getClass().getCanonicalName()); - if (payload instanceof String) { - tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(), - String.valueOf(((String) payload).length())); - } - else if (payload instanceof byte[]) { - tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getSize(), - String.valueOf(((byte[]) payload).length)); - } - } - } - - private void tagIfEntryMissing(Span span, String key, String value) { - if (!span.tags().containsKey(key)) { - span.tag(key, value); - } - } - - private void addHeader(Map headers, String name, String value) { - if (StringUtils.hasText(value)) { - headers.put(name, value); - } - } - - private Long getFirst(List parents) { - return parents.isEmpty() ? null : parents.get(0); - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapExtractor.java new file mode 100644 index 000000000..4ac3a2490 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapExtractor.java @@ -0,0 +1,14 @@ +package org.springframework.cloud.sleuth.instrument.messaging; + +import org.springframework.cloud.sleuth.SpanExtractor; +import org.springframework.cloud.sleuth.SpanTextMap; + +/** + * Contract for extracting tracing headers from a {@link SpanTextMap} + * via message headers + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public interface MessagingSpanTextMapExtractor extends SpanExtractor { +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapInjector.java new file mode 100644 index 000000000..ad32edaa5 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanTextMapInjector.java @@ -0,0 +1,14 @@ +package org.springframework.cloud.sleuth.instrument.messaging; + +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.SpanTextMap; + +/** + * Contract for injecting tracing headers from a {@link SpanTextMap} + * via message headers + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public interface MessagingSpanTextMapInjector extends SpanInjector { +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingTextMap.java new file mode 100644 index 000000000..5b75d46a6 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingTextMap.java @@ -0,0 +1,74 @@ +/* + * 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.messaging; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; +import org.springframework.messaging.support.MessageHeaderAccessor; +import org.springframework.messaging.support.NativeMessageHeaderAccessor; +import org.springframework.util.StringUtils; + +/** + * A {@link SpanTextMap} abstraction over {@link MessageBuilder} + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +class MessagingTextMap implements SpanTextMap { + + private final MessageBuilder delegate; + + public MessagingTextMap(MessageBuilder delegate) { + this.delegate = delegate; + } + + @Override + public Iterator> iterator() { + Map map = new HashMap<>(); + for (Map.Entry entry : this.delegate.build().getHeaders() + .entrySet()) { + map.put(entry.getKey(), String.valueOf(entry.getValue())); + } + return map.entrySet().iterator(); + } + + @Override + @SuppressWarnings("unchecked") + public void put(String key, String value) { + if (!StringUtils.hasText(value)) { + return; + } + Message initialMessage = this.delegate.build(); + MessageHeaderAccessor accessor = MessageHeaderAccessor + .getMutableAccessor(initialMessage); + Map headers = new HashMap<>(); + headers.put(key, value); + accessor.copyHeaders(headers); + if (accessor instanceof NativeMessageHeaderAccessor) { + NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor; + for (String name : headers.keySet()) { + nativeAccessor.setNativeHeader(name, headers.get(name)); + } + } + this.delegate.copyHeaders(accessor.toMessageHeaders()); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptor.java index 171ab01ad..e61ef7b5b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptor.java @@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.messaging; import org.springframework.cloud.sleuth.Log; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.sampler.NeverSampler; @@ -41,21 +39,21 @@ import org.springframework.messaging.support.MessageHeaderAccessor; public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, - SpanExtractor> spanExtractor, - SpanInjector> spanInjector) { + MessagingSpanTextMapExtractor spanExtractor, + MessagingSpanTextMapInjector spanInjector) { super(tracer, traceKeys, spanExtractor, spanInjector); } @Override public void afterSendCompletion(Message message, MessageChannel channel, boolean sent, Exception ex) { - Span spanFromHeader = getSpanFromHeader(message); - if (containsServerReceived(spanFromHeader)) { - spanFromHeader.logEvent(Span.SERVER_SEND); - } else if (spanFromHeader != null) { - spanFromHeader.logEvent(Span.CLIENT_RECV); + Span currentSpan = getTracer().getCurrentSpan(); + if (containsServerReceived(currentSpan)) { + currentSpan.logEvent(Span.SERVER_SEND); + } else if (currentSpan != null) { + currentSpan.logEvent(Span.CLIENT_RECV); } addErrorTag(ex); - getTracer().close(spanFromHeader); + getTracer().close(currentSpan); } private boolean containsServerReceived(Span span) { @@ -72,22 +70,18 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { @Override public Message preSend(Message message, MessageChannel channel) { + MessageBuilder messageBuilder = MessageBuilder.fromMessage(message); Span parentSpan = getTracer().isTracing() ? getTracer().getCurrentSpan() - : buildSpan(message); + : buildSpan(new MessagingTextMap(messageBuilder)); String name = getMessageChannelName(channel); Span span = startSpan(parentSpan, name, message); - MessageBuilder messageBuilder = MessageBuilder.fromMessage(message); - // Backwards compatibility - if (message.getHeaders().containsKey(TraceMessageHeaders.OLD_MESSAGE_SENT_FROM_CLIENT) || - message.getHeaders().containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) { + if (message.getHeaders().containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) { span.logEvent(Span.SERVER_RECV); } else { span.logEvent(Span.CLIENT_SEND); - // Backwards compatibility - messageBuilder.setHeader(TraceMessageHeaders.OLD_MESSAGE_SENT_FROM_CLIENT, true); messageBuilder.setHeader(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT, true); } - getSpanInjector().inject(span, messageBuilder); + getSpanInjector().inject(span, new MessagingTextMap(messageBuilder)); MessageHeaderAccessor headers = MessageHeaderAccessor.getMutableAccessor(message); headers.copyHeaders(messageBuilder.build().getHeaders()); return new GenericMessage(message.getPayload(), headers.getMessageHeaders()); @@ -97,9 +91,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { if (span != null) { return getTracer().createSpan(name, span); } - // Backwards compatibility - if (Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(Span.SAMPLED_NAME)) || - Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) { + if (Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) { return getTracer().createSpan(name, NeverSampler.INSTANCE); } return getTracer().createSpan(name); @@ -108,7 +100,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { @Override public Message beforeHandle(Message message, MessageChannel channel, MessageHandler handler) { - Span spanFromHeader = getSpanFromHeader(message); + Span spanFromHeader = getTracer().getCurrentSpan(); if (spanFromHeader!= null) { spanFromHeader.logEvent(Span.SERVER_RECV); } @@ -119,7 +111,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { @Override public void afterMessageHandled(Message message, MessageChannel channel, MessageHandler handler, Exception ex) { - Span spanFromHeader = getSpanFromHeader(message); + Span spanFromHeader = getTracer().getCurrentSpan(); if (spanFromHeader!= null) { spanFromHeader.logEvent(Span.SERVER_SEND); addErrorTag(ex); @@ -133,19 +125,4 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { } } - private Span getSpanFromHeader(Message message) { - if (message == null) { - return null; - } - Object object = message.getHeaders().get(TraceMessageHeaders.OLD_SPAN_HEADER); - if (object instanceof Span) { - return (Span) object; - } - object = message.getHeaders().get(TraceMessageHeaders.SPAN_HEADER); - if (object instanceof Span) { - return (Span) object; - } - return null; - } - } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java index 1bd173148..4ed97a6b1 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceMessageHeaders.java @@ -33,9 +33,6 @@ public class TraceMessageHeaders { public static final String TRACE_ID_NAME = "spanTraceId"; public static final String SPAN_NAME_NAME = "spanName"; - static final String OLD_SPAN_HEADER = "X-Current-Span"; - static final String OLD_MESSAGE_SENT_FROM_CLIENT = "X-Message-Sent"; - static final String SPAN_HEADER = "currentSpan"; static final String MESSAGE_SENT_FROM_CLIENT = "messageSent"; private TraceMessageHeaders() {} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpanMessagingAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpanMessagingAutoConfiguration.java index 30a0470eb..bc1bdcd49 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpanMessagingAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpanMessagingAutoConfiguration.java @@ -20,13 +20,11 @@ import java.util.Random; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; /** * AutoConfiguration containing Span extractor and injector for messaging. Will be reused @@ -41,12 +39,14 @@ import org.springframework.messaging.support.MessageBuilder; public class TraceSpanMessagingAutoConfiguration { @Bean - public SpanExtractor> messagingSpanExtractor(Random random) { - return new MessagingSpanExtractor(random); + @ConditionalOnMissingBean + public MessagingSpanTextMapExtractor messagingSpanExtractor() { + return new HeaderBasedMessagingExtractor(); } @Bean - public SpanInjector> messagingSpanInjector(TraceKeys traceKeys) { - return new MessagingSpanInjector(traceKeys); + @ConditionalOnMissingBean + public MessagingSpanTextMapInjector messagingSpanInjector(TraceKeys traceKeys) { + return new HeaderBasedMessagingInjector(traceKeys); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java index d9eb1b008..31111ea55 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java @@ -23,16 +23,12 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.config.GlobalChannelInterceptor; -import org.springframework.messaging.Message; -import org.springframework.messaging.support.MessageBuilder; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -56,8 +52,8 @@ public class TraceSpringIntegrationAutoConfiguration { @Bean @GlobalChannelInterceptor(patterns = "${spring.sleuth.integration.patterns:*}") public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer, - TraceKeys traceKeys, Random random, SpanExtractor> spanExtractor, - SpanInjector> spanInjector) { + TraceKeys traceKeys, Random random, MessagingSpanTextMapExtractor spanExtractor, + MessagingSpanTextMapInjector spanInjector) { return new IntegrationTraceChannelInterceptor(tracer, traceKeys, spanExtractor, spanInjector); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/websocket/TraceWebSocketAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/websocket/TraceWebSocketAutoConfiguration.java index 40bcc504b..86c2ed4e2 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/websocket/TraceWebSocketAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/websocket/TraceWebSocketAutoConfiguration.java @@ -5,16 +5,14 @@ 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.ConditionalOnProperty; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; +import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanTextMapExtractor; +import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanTextMapInjector; import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptor; import org.springframework.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration; import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.Message; import org.springframework.messaging.simp.config.ChannelRegistration; -import org.springframework.messaging.support.MessageBuilder; import org.springframework.stereotype.Component; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration; @@ -43,9 +41,9 @@ public class TraceWebSocketAutoConfiguration @Autowired TraceKeys traceKeys; @Autowired - SpanExtractor> spanExtractor; + MessagingSpanTextMapExtractor spanExtractor; @Autowired - SpanInjector> spanInjector; + MessagingSpanTextMapInjector spanInjector; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractor.java deleted file mode 100644 index 2a682bfe6..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractor.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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.web; - -import java.lang.invoke.MethodHandles; -import java.util.regex.Pattern; -import javax.servlet.http.HttpServletRequest; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Span.SpanBuilder; -import org.springframework.cloud.sleuth.SpanExtractor; -import org.springframework.util.StringUtils; -import org.springframework.web.util.UrlPathHelper; - -/** - * Creates a {@link SpanBuilder} from {@link HttpServletRequest} - * - * @author Marcin Grzejszczak - * - * @since 1.0.0 - */ -class HttpServletRequestExtractor implements SpanExtractor { - - private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()); - private static final String HTTP_COMPONENT = "http"; - - private final Pattern skipPattern; - - private UrlPathHelper urlPathHelper = new UrlPathHelper(); - - public HttpServletRequestExtractor(Pattern skipPattern) { - this.skipPattern = skipPattern; - } - - @Override - public Span joinTrace(HttpServletRequest carrier) { - if (carrier.getHeader(Span.TRACE_ID_NAME) == null) { - // can't build a Span without trace id - return null; - } - try { - String uri = this.urlPathHelper.getPathWithinApplication(carrier); - boolean skip = this.skipPattern.matcher(uri).matches() - || Span.SPAN_NOT_SAMPLED.equals(carrier.getHeader(Span.SAMPLED_NAME)); - long traceId = Span - .hexToId(carrier.getHeader(Span.TRACE_ID_NAME)); - long spanId = spanId(carrier, traceId); - return buildParentSpan(carrier, uri, skip, traceId, spanId); - } catch (Exception e) { - log.error("Exception occurred while trying to extract span from carrier", e); - return null; - } - } - - private long spanId(HttpServletRequest carrier, long traceId) { - String spanId = carrier.getHeader(Span.SPAN_ID_NAME); - if (spanId == null) { - if (log.isDebugEnabled()) { - log.debug("Request is missing a span id but it has a trace id. We'll assume that this is " - + "a root span with span id equal to trace id"); - } - return traceId; - } else { - return Span.hexToId(spanId); - } - } - - private Span buildParentSpan(HttpServletRequest carrier, String uri, boolean skip, - long traceId, long spanId) { - SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId); - String processId = carrier.getHeader(Span.PROCESS_ID_NAME); - String parentName = carrier.getHeader(Span.SPAN_NAME_NAME); - if (StringUtils.hasText(parentName)) { - span.name(parentName); - } - else { - span.name(HTTP_COMPONENT + ":/parent" + uri); - } - if (StringUtils.hasText(processId)) { - span.processId(processId); - } - if (carrier.getHeader(Span.PARENT_ID_NAME) != null) { - span.parent(Span - .hexToId(carrier.getHeader(Span.PARENT_ID_NAME))); - } - span.remote(true); - if (skip) { - span.exportable(false); - } - return span.build(); - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestTextMap.java new file mode 100644 index 000000000..2981271c8 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestTextMap.java @@ -0,0 +1,62 @@ +/* + * 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.web; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.web.util.UrlPathHelper; + +/** + * A {@link SpanTextMap} abstraction over {@link HttpServletRequest} + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +class HttpServletRequestTextMap implements SpanTextMap { + + private final HttpServletRequest delegate; + private final Map additionalHeaders = new HashMap<>(); + + HttpServletRequestTextMap(HttpServletRequest delegate) { + this.delegate = delegate; + UrlPathHelper urlPathHelper = new UrlPathHelper(); + this.additionalHeaders.put(ZipkinHttpSpanExtractor.URI_HEADER, + urlPathHelper.getPathWithinApplication(delegate)); + } + + @Override + public Iterator> iterator() { + Map map = new HashMap<>(); + Enumeration headerNames = this.delegate.getHeaderNames(); + while (headerNames != null && headerNames.hasMoreElements()) { + String name = headerNames.nextElement(); + map.put(name, this.delegate.getHeader(name)); + } + map.putAll(this.additionalHeaders); + return map.entrySet().iterator(); + } + + @Override + public void put(String key, String value) { + this.additionalHeaders.put(key, value); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java index 96ed118a7..c31f92d84 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java @@ -15,23 +15,23 @@ */ package org.springframework.cloud.sleuth.instrument.web; -import java.io.IOException; -import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Enumeration; -import java.util.regex.Pattern; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.HttpSpanExtractor; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanExtractor; import org.springframework.cloud.sleuth.SpanReporter; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; @@ -88,20 +88,20 @@ public class TraceFilter extends GenericFilterBean { private final TraceKeys traceKeys; private final Pattern skipPattern; private final SpanReporter spanReporter; - private final SpanExtractor spanExtractor; + private final HttpSpanExtractor spanExtractor; private final HttpTraceKeysInjector httpTraceKeysInjector; private UrlPathHelper urlPathHelper = new UrlPathHelper(); public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter, - SpanExtractor spanExtractor, + HttpSpanExtractor spanExtractor, HttpTraceKeysInjector httpTraceKeysInjector) { this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), spanReporter, spanExtractor, httpTraceKeysInjector); } public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern, - SpanReporter spanReporter, SpanExtractor spanExtractor, + SpanReporter spanReporter, HttpSpanExtractor spanExtractor, HttpTraceKeysInjector httpTraceKeysInjector) { this.tracer = tracer; this.traceKeys = traceKeys; @@ -288,7 +288,7 @@ public class TraceFilter extends GenericFilterBean { } return spanFromRequest; } - Span parent = this.spanExtractor.joinTrace(request); + Span parent = this.spanExtractor.joinTrace(new HttpServletRequestTextMap(request)); if (parent != null) { if (log.isDebugEnabled()) { log.debug("Found a parent span " + parent + " in the request"); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java index 43117dae3..7e6b23d32 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java @@ -15,7 +15,6 @@ */ package org.springframework.cloud.sleuth.instrument.web; -import javax.servlet.http.HttpServletRequest; import java.util.regex.Pattern; import org.springframework.beans.factory.BeanFactory; @@ -30,7 +29,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.embedded.FilterRegistrationBean; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.sleuth.SpanExtractor; +import org.springframework.cloud.sleuth.HttpSpanExtractor; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.SpanReporter; import org.springframework.cloud.sleuth.TraceKeys; @@ -90,10 +90,7 @@ public class TraceWebAutoConfiguration { } @Bean - public FilterRegistrationBean traceWebFilter(Tracer tracer, TraceKeys traceKeys, - SkipPatternProvider skipPatternProvider, SpanReporter spanReporter, - SpanExtractor spanExtractor, - HttpTraceKeysInjector httpTraceKeysInjector, TraceFilter traceFilter) { + public FilterRegistrationBean traceWebFilter(TraceFilter traceFilter) { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(traceFilter); filterRegistrationBean.setDispatcherTypes(ASYNC, ERROR, FORWARD, INCLUDE, REQUEST); filterRegistrationBean.setOrder(TraceFilter.ORDER); @@ -103,16 +100,22 @@ public class TraceWebAutoConfiguration { @Bean public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys, SkipPatternProvider skipPatternProvider, SpanReporter spanReporter, - SpanExtractor spanExtractor, + HttpSpanExtractor spanExtractor, HttpTraceKeysInjector httpTraceKeysInjector) { return new TraceFilter(tracer, traceKeys, skipPatternProvider.skipPattern(), spanReporter, spanExtractor, httpTraceKeysInjector); } @Bean - public SpanExtractor httpServletRequestSpanExtractor( - SkipPatternProvider skipPatternProvider) { - return new HttpServletRequestExtractor(skipPatternProvider.skipPattern()); + @ConditionalOnMissingBean + public HttpSpanExtractor httpSpanExtractor(@Value("${spring.sleuth.web.skipPattern:}") String skipPattern) { + return new ZipkinHttpSpanExtractor(Pattern.compile(skipPattern)); + } + + @Bean + @ConditionalOnMissingBean + public HttpSpanInjector httpSpanInjector() { + return new ZipkinHttpSpanInjector(); } @Configuration diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java new file mode 100644 index 000000000..caaca81c1 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanExtractor.java @@ -0,0 +1,92 @@ +package org.springframework.cloud.sleuth.instrument.web; + +import java.lang.invoke.MethodHandles; +import java.util.Map; +import java.util.regex.Pattern; + +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.HttpSpanExtractor; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.cloud.sleuth.util.TextMapUtil; +import org.springframework.util.StringUtils; + +/** + * Default implementation, compatible with Zipkin propagation. + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public class ZipkinHttpSpanExtractor implements HttpSpanExtractor { + + private static final org.apache.commons.logging.Log log = LogFactory.getLog( + MethodHandles.lookup().lookupClass()); + static final String URI_HEADER = "X-Span-Uri"; + private static final String HTTP_COMPONENT = "http"; + + private final Pattern skipPattern; + + public ZipkinHttpSpanExtractor(Pattern skipPattern) { + this.skipPattern = skipPattern; + } + + @Override + public Span joinTrace(SpanTextMap textMap) { + Map carrier = TextMapUtil.asMap(textMap); + if (carrier.get(Span.TRACE_ID_NAME) == null) { + // can't build a Span without trace id + return null; + } + try { + String uri = carrier.get(URI_HEADER); + boolean skip = this.skipPattern.matcher(uri).matches() + || Span.SPAN_NOT_SAMPLED.equals(carrier.get(Span.SAMPLED_NAME)); + long traceId = Span + .hexToId(carrier.get(Span.TRACE_ID_NAME)); + long spanId = spanId(carrier, traceId); + return buildParentSpan(carrier, uri, skip, traceId, spanId); + } catch (Exception e) { + log.error("Exception occurred while trying to extract span from carrier", e); + return null; + } + } + + private long spanId(Map carrier, long traceId) { + String spanId = carrier.get(Span.SPAN_ID_NAME); + if (spanId == null) { + if (log.isDebugEnabled()) { + log.debug("Request is missing a span id but it has a trace id. We'll assume that this is " + + "a root span with span id equal to trace id"); + } + return traceId; + } else { + return Span.hexToId(spanId); + } + } + + private Span buildParentSpan(Map carrier, String uri, boolean skip, + long traceId, long spanId) { + Span.SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId); + String processId = carrier.get(Span.PROCESS_ID_NAME); + String parentName = carrier.get(Span.SPAN_NAME_NAME); + if (StringUtils.hasText(parentName)) { + span.name(parentName); + } + else { + span.name(HTTP_COMPONENT + ":/parent" + uri); + } + if (StringUtils.hasText(processId)) { + span.processId(processId); + } + if (carrier.containsKey(Span.PARENT_ID_NAME)) { + span.parent(Span + .hexToId(carrier.get(Span.PARENT_ID_NAME))); + } + span.remote(true); + if (skip) { + span.exportable(false); + } + return span.build(); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java new file mode 100644 index 000000000..8b19df931 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ZipkinHttpSpanInjector.java @@ -0,0 +1,53 @@ +package org.springframework.cloud.sleuth.instrument.web; + +import java.util.Map; + +import org.springframework.cloud.sleuth.HttpSpanInjector; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.util.StringUtils; + +/** + * Default implementation of {@link HttpSpanInjector}, compatible with Zipkin propagation. + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public class ZipkinHttpSpanInjector implements HttpSpanInjector { + + @Override + public void inject(Span span, SpanTextMap carrier) { + setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId()); + setIdHeader(carrier, Span.SPAN_ID_NAME, span.getSpanId()); + setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); + setHeader(carrier, Span.SPAN_NAME_NAME, span.getName()); + setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span)); + setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId()); + } + + private Long getParentId(Span span) { + return !span.getParents().isEmpty() ? span.getParents().get(0) : null; + } + + private void setHeader(SpanTextMap carrier, String name, String value) { + if (StringUtils.hasText(value) && !entryPresent(carrier, name)) { + carrier.put(name, value); + } + } + + private void setIdHeader(SpanTextMap carrier, String name, Long value) { + if (value != null) { + setHeader(carrier, name, Span.idToHex(value)); + } + } + + private boolean entryPresent(SpanTextMap carrier, String name) { + for (Map.Entry entry : carrier) { + if (entry.getKey().equals(name)) { + return true; + } + } + return false; + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java index 413cf88f7..03bc73d5a 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/AbstractTraceHttpRequestInterceptor.java @@ -21,8 +21,8 @@ import java.net.URI; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; import org.springframework.http.HttpRequest; @@ -38,11 +38,11 @@ abstract class AbstractTraceHttpRequestInterceptor { protected static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()); protected final Tracer tracer; - protected final SpanInjector spanInjector; + protected final HttpSpanInjector spanInjector; protected final HttpTraceKeysInjector keysInjector; protected AbstractTraceHttpRequestInterceptor(Tracer tracer, - SpanInjector spanInjector, HttpTraceKeysInjector keysInjector) { + HttpSpanInjector spanInjector, HttpTraceKeysInjector keysInjector) { this.tracer = tracer; this.spanInjector = spanInjector; this.keysInjector = keysInjector; @@ -56,7 +56,7 @@ abstract class AbstractTraceHttpRequestInterceptor { URI uri = request.getURI(); String spanName = uriScheme(uri) + ":" + uri.getPath(); Span newSpan = this.tracer.createSpan(spanName); - this.spanInjector.inject(newSpan, request); + this.spanInjector.inject(newSpan, new HttpRequestTextMap(request)); addRequestTags(request); newSpan.logEvent(Span.CLIENT_SEND); if (log.isDebugEnabled()) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestInjector.java deleted file mode 100644 index 8f12f3150..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestInjector.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.web.client; - -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanInjector; -import org.springframework.http.HttpRequest; -import org.springframework.util.StringUtils; - -/** - * Span injector that injects tracing info to {@link HttpRequest} - * - * @author Marcin Grzejszczak - * - * @since 1.0.0 - */ -class HttpRequestInjector implements SpanInjector { - - @Override - public void inject(Span span, HttpRequest carrier) { - setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId()); - setIdHeader(carrier, Span.SPAN_ID_NAME, span.getSpanId()); - setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); - setHeader(carrier, Span.SPAN_NAME_NAME, span.getName()); - setIdHeader(carrier, Span.PARENT_ID_NAME, getParentId(span)); - setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId()); - } - - private Long getParentId(Span span) { - return !span.getParents().isEmpty() ? span.getParents().get(0) : null; - } - - private void setHeader(HttpRequest request, String name, String value) { - if (StringUtils.hasText(value) && !request.getHeaders().containsKey(name)) { - request.getHeaders().add(name, value); - } - } - - private void setIdHeader(HttpRequest request, String name, Long value) { - if (value != null) { - setHeader(request, name, Span.idToHex(value)); - } - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestTextMap.java new file mode 100644 index 000000000..417681490 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestTextMap.java @@ -0,0 +1,67 @@ +/* + * 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.web.client; + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.http.HttpRequest; +import org.springframework.util.StringUtils; + +/** + * A {@link SpanTextMap} abstraction over {@link HttpRequest} + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +class HttpRequestTextMap implements SpanTextMap { + + private final HttpRequest delegate; + + HttpRequestTextMap(HttpRequest delegate) { + this.delegate = delegate; + } + + @Override + public Iterator> iterator() { + final Iterator>> iterator = this.delegate.getHeaders() + .entrySet().iterator(); + return new Iterator>() { + @Override public boolean hasNext() { + return iterator.hasNext(); + } + + @Override public Map.Entry next() { + Map.Entry> next = iterator.next(); + List value = next.getValue(); + return new AbstractMap.SimpleEntry<>(next.getKey(), value.isEmpty() ? "" : value.get(0)); + } + }; + } + + @Override + public void put(String key, String value) { + if (!StringUtils.hasText(value)) { + return; + } + this.delegate.getHeaders().put(key, Collections.singletonList(value)); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java index 49e26f6c4..152316d3b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java @@ -19,12 +19,11 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.io.IOException; import java.net.URI; -import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; import org.springframework.core.task.AsyncListenableTaskExecutor; import org.springframework.http.HttpMethod; -import org.springframework.http.HttpRequest; import org.springframework.http.client.AsyncClientHttpRequest; import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequest; @@ -56,7 +55,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp * @see org.springframework.web.client.AsyncRestTemplate#AsyncRestTemplate(AsyncClientHttpRequestFactory) */ public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, - SpanInjector spanInjector, + HttpSpanInjector spanInjector, AsyncClientHttpRequestFactory asyncDelegate, HttpTraceKeysInjector httpTraceKeysInjector) { super(tracer, spanInjector, httpTraceKeysInjector); @@ -70,7 +69,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp * has a wrapped task executor via the {@link TraceAsyncListenableTaskExecutor} */ public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, - SpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { + HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { super(tracer, spanInjector, httpTraceKeysInjector); SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = defaultClientHttpRequestFactory(); this.asyncDelegate = simpleClientHttpRequestFactory; @@ -78,7 +77,7 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp } public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, - SpanInjector spanInjector, + HttpSpanInjector spanInjector, AsyncClientHttpRequestFactory asyncDelegate, ClientHttpRequestFactory syncDelegate, HttpTraceKeysInjector httpTraceKeysInjector) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java index ce71beb84..1ef20e719 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java @@ -18,8 +18,8 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.io.IOException; +import org.springframework.cloud.sleuth.HttpSpanInjector; 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.cloud.sleuth.util.ExceptionUtils; @@ -41,7 +41,7 @@ import org.springframework.http.client.ClientHttpResponse; public class TraceRestTemplateInterceptor extends AbstractTraceHttpRequestInterceptor implements ClientHttpRequestInterceptor { - public TraceRestTemplateInterceptor(Tracer tracer, SpanInjector spanInjector, + public TraceRestTemplateInterceptor(Tracer tracer, HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { super(tracer, spanInjector, httpTraceKeysInjector); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java index d5fb8f2e6..f4d1e70ef 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebAsyncClientAutoConfiguration.java @@ -22,14 +22,13 @@ 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.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncListenableTaskExecutor; -import org.springframework.http.HttpRequest; import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.http.client.SimpleClientHttpRequestFactory; @@ -54,7 +53,7 @@ public class TraceWebAsyncClientAutoConfiguration { @Autowired Tracer tracer; @Autowired private HttpTraceKeysInjector httpTraceKeysInjector; - @Autowired private SpanInjector spanInjector; + @Autowired private HttpSpanInjector spanInjector; @Autowired(required = false) private ClientHttpRequestFactory clientHttpRequestFactory; @Autowired(required = false) private AsyncClientHttpRequestFactory asyncClientHttpRequestFactory; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java index 6585b0b13..8f7094c33 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java @@ -26,13 +26,12 @@ 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.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.web.client.RestTemplate; @@ -54,16 +53,11 @@ public class TraceWebClientAutoConfiguration { @Bean @ConditionalOnMissingBean public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer, - SpanInjector spanInjector, + HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { return new TraceRestTemplateInterceptor(tracer, spanInjector, httpTraceKeysInjector); } - @Bean - public SpanInjector httpRequestSpanInjector() { - return new HttpRequestInjector(); - } - @Configuration protected static class TraceInterceptorConfiguration { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestInjector.java deleted file mode 100644 index f1323a35e..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestInjector.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.web.client.feign; - -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicReference; - -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanInjector; -import org.springframework.util.StringUtils; - -import feign.Request; - -/** - * Span injector that injects tracing info to {@link Request} via {@link AtomicReference} - * since {@link Request} is immutable. - * - * @author Marcin Grzejszczak - * - * @since 1.0.0 - */ -class FeignRequestInjector implements SpanInjector> { - - @Override - public void inject(Span span, AtomicReference carrier) { - String method = carrier.get().method(); - String url = carrier.get().url(); - Map> headers = new HashMap<>(carrier.get().headers()); - byte[] body = carrier.get().body(); - Charset charset = carrier.get().charset(); - if (span == null) { - setHeader(headers, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); - carrier.set(Request.create(method, url, headers, body, charset)); - return; - } - setHeader(headers, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); - setHeader(headers, Span.SPAN_NAME_NAME, span.getName()); - setHeader(headers, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - setHeader(headers, Span.SAMPLED_NAME, span.isExportable() ? - Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); - Long parentId = getParentId(span); - if (parentId != null) { - setHeader(headers, Span.PARENT_ID_NAME, Span.idToHex(parentId)); - } - setHeader(headers, Span.PROCESS_ID_NAME, span.getProcessId()); - carrier.set(Request.create(method, url, headers, body, charset)); - } - - private Long getParentId(Span span) { - return !span.getParents().isEmpty() ? span.getParents().get(0) : null; - } - - protected void setHeader(Map> headers, String name, String value) { - if (StringUtils.hasText(value) && !headers.containsKey(name)) { - List list = new ArrayList<>(); - list.add(value); - headers.put(name, list); - } - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTextMap.java new file mode 100644 index 000000000..287603d04 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTextMap.java @@ -0,0 +1,86 @@ +/* + * 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.web.client.feign; + +import java.nio.charset.Charset; +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.util.StringUtils; + +import feign.Request; + +/** + * A {@link SpanTextMap} abstraction over {@link AtomicReference} + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +class FeignRequestTextMap implements SpanTextMap { + + private final AtomicReference delegate; + + FeignRequestTextMap(AtomicReference delegate) { + this.delegate = delegate; + } + + @Override + public Iterator> iterator() { + final Iterator>> iterator = this.delegate.get().headers().entrySet().iterator(); + return new Iterator>() { + @Override public boolean hasNext() { + return iterator.hasNext(); + } + + @Override public Map.Entry next() { + Map.Entry> next = iterator.next(); + Collection value = next.getValue(); + return new AbstractMap.SimpleEntry<>(next.getKey(), value.isEmpty() ? "" : value.iterator().next()); + } + }; + } + + @Override + public void put(String key, String value) { + if (!StringUtils.hasText(value)) { + return; + } + String method = this.delegate.get().method(); + String url = this.delegate.get().url(); + Map> headers = new HashMap<>(this.delegate.get().headers()); + byte[] body = this.delegate.get().body(); + Charset charset = this.delegate.get().charset(); + addHeader(key, value, headers); + this.delegate.set(Request.create(method, url, headers, body, charset)); + } + + private void addHeader(String key, String value, + Map> headers) { + if (!headers.containsKey(key)) { + List list = new ArrayList<>(); + list.add(value); + headers.put(key, list); + } + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClient.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClient.java index 33acce88b..c3226fae2 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClient.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClient.java @@ -24,14 +24,15 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.BeanFactory; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; +import org.springframework.cloud.sleuth.util.ExceptionUtils; import feign.Client; import feign.Request; import feign.Response; -import org.springframework.cloud.sleuth.util.ExceptionUtils; /** * A Feign Client that closes a Span if there is no response body. In other cases Span @@ -49,7 +50,7 @@ class TraceFeignClient implements Client { private HttpTraceKeysInjector keysInjector; private final BeanFactory beanFactory; private Tracer tracer; - private final FeignRequestInjector spanInjector = new FeignRequestInjector(); + private HttpSpanInjector spanInjector; TraceFeignClient(BeanFactory beanFactory) { this.beanFactory = beanFactory; @@ -70,7 +71,7 @@ class TraceFeignClient implements Client { } try { AtomicReference feignRequest = new AtomicReference<>(request); - this.spanInjector.inject(span, feignRequest); + spanInjector().inject(span, new FeignRequestTextMap(feignRequest)); span.logEvent(Span.CLIENT_SEND); addRequestTags(request); Request modifiedRequest = feignRequest.get(); @@ -102,17 +103,24 @@ class TraceFeignClient implements Client { */ private void addRequestTags(Request request) { URI uri = URI.create(request.url()); - getKeysInjector().addRequestTags(uri.toString(), uri.getHost(), uri.getPath(), + keysInjector().addRequestTags(uri.toString(), uri.getHost(), uri.getPath(), request.method(), request.headers()); } - private HttpTraceKeysInjector getKeysInjector() { + private HttpTraceKeysInjector keysInjector() { if (this.keysInjector == null) { this.keysInjector = this.beanFactory.getBean(HttpTraceKeysInjector.class); } return this.keysInjector; } + private HttpSpanInjector spanInjector() { + if (this.spanInjector == null) { + this.spanInjector = this.beanFactory.getBean(HttpSpanInjector.class); + } + return this.spanInjector; + } + private void closeSpan(Span span) { if (span != null) { if (log.isDebugEnabled()) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizer.java index 0715052d0..7ab591946 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizer.java @@ -16,7 +16,13 @@ package org.springframework.cloud.sleuth.instrument.zuul; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.apache.http.Header; import org.apache.http.client.methods.RequestBuilder; +import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.Tracer; /** @@ -37,9 +43,20 @@ class ApacheHttpClientRibbonRequestCustomizer extends SpanInjectingRibbonRequest } @Override - void setHeader(RequestBuilder builder, String name, String value) { - if (value != null) { - builder.setHeader(name, value); - } + protected SpanTextMap toSpanTextMap(final RequestBuilder context) { + return new SpanTextMap() { + @Override public Iterator> iterator() { + Map map = new HashMap<>(); + for (Header header : context.build().getAllHeaders()) { + map.put(header.getName(), header.getValue()); + } + return map.entrySet().iterator(); + } + + @Override public void put(String key, String value) { + context.addHeader(key, value); + } + }; } + } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizer.java index 01d11adb8..c6948533d 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizer.java @@ -16,6 +16,12 @@ package org.springframework.cloud.sleuth.instrument.zuul; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.Tracer; import okhttp3.Request; @@ -38,9 +44,21 @@ class OkHttpClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCust } @Override - void setHeader(Request.Builder builder, String name, String value) { - if (value != null) { - builder.addHeader(name, value); - } + protected SpanTextMap toSpanTextMap(final Request.Builder context) { + return new SpanTextMap() { + @Override public Iterator> iterator() { + Map map = new HashMap<>(); + for (Map.Entry> entry : context.build().headers().toMultimap().entrySet()) { + if (!entry.getValue().isEmpty()) { + map.put(entry.getKey(), entry.getValue().get(0)); + } + } + return map.entrySet().iterator(); + } + + @Override public void put(String key, String value) { + context.addHeader(key, value); + } + }; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextInjector.java deleted file mode 100644 index ca730870e..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextInjector.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 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; - -/** - * Span injector that injects tracing info to {@link RequestContext} - * - * @author Marcin Grzejszczak - * @since 1.0.0 - */ -class RequestContextInjector implements SpanInjector { - - @Override - public void inject(Span span, RequestContext carrier) { - Map requestHeaders = carrier.getZuulRequestHeaders(); - if (span == null) { - setHeader(requestHeaders, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); - return; - } - setHeader(requestHeaders, Span.SPAN_ID_NAME, span.getSpanId()); - setHeader(requestHeaders, Span.TRACE_ID_NAME, span.getTraceId()); - setHeader(requestHeaders, Span.SPAN_NAME_NAME, span.getName()); - setHeader(requestHeaders, Span.SAMPLED_NAME, span.isExportable() ? - Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); - setHeader(requestHeaders, Span.PARENT_ID_NAME, getParentId(span)); - setHeader(requestHeaders, Span.PROCESS_ID_NAME, span.getProcessId()); - } - - private Long getParentId(Span span) { - return !span.getParents().isEmpty() ? span.getParents().get(0) : null; - } - - public void setHeader(Map request, String name, String value) { - if (StringUtils.hasText(value) && !request.containsKey(name)) { - request.put(name, value); - } - } - - public void setHeader(Map request, String name, Long value) { - if (value != null) { - setHeader(request, name, Span.idToHex(value)); - } - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextTextMap.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextTextMap.java new file mode 100644 index 000000000..43110d251 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextTextMap.java @@ -0,0 +1,49 @@ +/* + * 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 java.util.Iterator; +import java.util.Map; + +import com.netflix.zuul.context.RequestContext; + +import org.springframework.cloud.sleuth.SpanTextMap; + +/** + * A {@link SpanTextMap} abstraction over {@link RequestContext} + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +class RequestContextTextMap implements SpanTextMap { + + private final RequestContext carrier; + + RequestContextTextMap(RequestContext carrier) { + this.carrier = carrier; + } + + @Override + public Iterator> iterator() { + return this.carrier.getZuulRequestHeaders().entrySet().iterator(); + } + + @Override + public void put(String key, String value) { + this.carrier.getZuulRequestHeaders().put(key, value); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizer.java index d3fca982e..3b882e255 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizer.java @@ -16,8 +16,12 @@ package org.springframework.cloud.sleuth.instrument.zuul; +import java.util.Iterator; +import java.util.Map; + import com.netflix.client.http.HttpRequest; +import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.Tracer; /** @@ -38,9 +42,16 @@ class RestClientRibbonRequestCustomizer extends SpanInjectingRibbonRequestCustom } @Override - void setHeader(HttpRequest.Builder builder, String name, String value) { - if (value != null) { - builder.header(name, value); - } + protected SpanTextMap toSpanTextMap(final HttpRequest.Builder context) { + context.build().getHttpHeaders(); + return new SpanTextMap() { + @Override public Iterator> iterator() { + return context.build().getHttpHeaders().getAllHeaders().iterator(); + } + + @Override public void put(String key, String value) { + context.header(key, value); + } + }; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/SpanInjectingRibbonRequestCustomizer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/SpanInjectingRibbonRequestCustomizer.java index db7fbf1ce..90575c1c2 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/SpanInjectingRibbonRequestCustomizer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/SpanInjectingRibbonRequestCustomizer.java @@ -23,6 +23,7 @@ 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 org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.Tracer; /** @@ -30,14 +31,14 @@ import org.springframework.cloud.sleuth.Tracer; * 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)} + * with the context class name and {@link SpanInjectingRibbonRequestCustomizer#toSpanTextMap(Object)} * to tell Sleuth how to set a header using the particular library. * * @author Marcin Grzejszczak * @since 1.1.0 */ abstract class SpanInjectingRibbonRequestCustomizer implements RibbonRequestCustomizer, - SpanInjector { + SpanInjector { private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()); @@ -50,30 +51,30 @@ abstract class SpanInjectingRibbonRequestCustomizer implements RibbonRequestC @Override public void customize(T context) { Span span = getCurrentSpan(); - inject(span, context); + inject(span, toSpanTextMap(context)); span.logEvent(Span.CLIENT_SEND); if (log.isDebugEnabled()) { log.debug("Span in the RibbonRequestCustomizer is" + span); } } + + protected abstract SpanTextMap toSpanTextMap(T context); @Override - public void inject(Span span, T carrier) { + public void inject(Span span, SpanTextMap carrier) { if (span == null) { - setHeader(carrier, Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); + carrier.put(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED); return; } - setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ? + carrier.put(Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED); - setHeader(carrier, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); - setHeader(carrier, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - setHeader(carrier, Span.SPAN_NAME_NAME, span.getName()); + carrier.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); + carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); + carrier.put(Span.SPAN_NAME_NAME, span.getName()); if (getParentId(span) != null) { - setHeader(carrier, Span.PARENT_ID_NAME, - Span.idToHex(getParentId(span))); + carrier.put(Span.PARENT_ID_NAME, Span.idToHex(getParentId(span))); } - setHeader(carrier, Span.PROCESS_ID_NAME, - span.getProcessId()); + carrier.put(Span.PROCESS_ID_NAME, span.getProcessId()); } private Long getParentId(Span span) { @@ -84,6 +85,4 @@ abstract class SpanInjectingRibbonRequestCustomizer implements RibbonRequestC private Span getCurrentSpan() { return this.tracer.getCurrentSpan(); } - - abstract void setHeader(T builder, String name, String value); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java index 6d610fed7..9c9f142cb 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java @@ -16,21 +16,22 @@ 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.HttpSpanInjector; 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.cloud.sleuth.instrument.web.TraceRequestAttributes; -import java.lang.invoke.MethodHandles; -import java.net.URI; - /** * 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. @@ -45,10 +46,10 @@ public class TracePreZuulFilter extends ZuulFilter { private static final String ZUUL_COMPONENT = "zuul"; private final Tracer tracer; - private final SpanInjector spanInjector; + private final HttpSpanInjector spanInjector; private final HttpTraceKeysInjector httpTraceKeysInjector; - public TracePreZuulFilter(Tracer tracer, SpanInjector spanInjector, + public TracePreZuulFilter(Tracer tracer, HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { this.tracer = tracer; this.spanInjector = spanInjector; @@ -76,7 +77,7 @@ public class TracePreZuulFilter extends ZuulFilter { markRequestAsHandled(ctx); Span newSpan = this.tracer.createSpan(span.getName(), span); newSpan.tag(Span.SPAN_LOCAL_COMPONENT_TAG_NAME, ZUUL_COMPONENT); - this.spanInjector.inject(newSpan, ctx); + this.spanInjector.inject(newSpan, new RequestContextTextMap(ctx)); this.httpTraceKeysInjector.addRequestTags(newSpan, URI.create(ctx.getRequest().getRequestURI()), ctx.getRequest().getMethod()); if (log.isDebugEnabled()) { log.debug("New Zuul Span is " + newSpan + ""); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java index fbe34bebf..5b53c7322 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java @@ -15,6 +15,9 @@ */ package org.springframework.cloud.sleuth.instrument.zuul; +import com.netflix.client.http.HttpRequest; +import com.netflix.zuul.ZuulFilter; + import org.apache.http.client.methods.RequestBuilder; import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -24,7 +27,7 @@ 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.support.RibbonRequestCustomizer; -import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; @@ -33,9 +36,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import okhttp3.Request; -import com.netflix.client.http.HttpRequest; -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} @@ -55,7 +55,7 @@ public class TraceZuulAutoConfiguration { @Bean @ConditionalOnMissingBean public TracePreZuulFilter tracePreZuulFilter(Tracer tracer, - SpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { + HttpSpanInjector spanInjector, HttpTraceKeysInjector httpTraceKeysInjector) { return new TracePreZuulFilter(tracer, spanInjector, httpTraceKeysInjector); } @@ -65,11 +65,6 @@ public class TraceZuulAutoConfiguration { return new TracePostZuulFilter(tracer, traceKeys); } - @Bean - public SpanInjector requestContextSpanInjector() { - return new RequestContextInjector(); - } - @Bean public TraceRibbonCommandFactoryBeanPostProcessor traceRibbonCommandFactoryBeanPostProcessor(BeanFactory beanFactory) { return new TraceRibbonCommandFactoryBeanPostProcessor(beanFactory); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/TextMapUtil.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/TextMapUtil.java new file mode 100644 index 000000000..5ecaa239c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/util/TextMapUtil.java @@ -0,0 +1,28 @@ +package org.springframework.cloud.sleuth.util; + +import java.util.Comparator; +import java.util.Map; +import java.util.TreeMap; + +/** + * Utility class related to {@link org.springframework.cloud.sleuth.SpanTextMap} + * + * @author Marcin Grzejszczak + * @since 1.2.0 + */ +public final class TextMapUtil { + + private TextMapUtil() {} + + public static Map asMap(Iterable> iterable) { + Map map = new TreeMap<>(new Comparator() { + @Override public int compare(String o1, String o2) { + return o1.toLowerCase().compareTo(o2.toLowerCase()); + } + }); + for (Map.Entry entry : iterable) { + map.put(entry.getKey(), entry.getValue()); + } + return map; + } +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java index dd3c17a08..1e67d3061 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/issues/issue410/Issue410Tests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.sleuth.instrument.async.issues.issue410; +import java.lang.invoke.MethodHandles; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -63,6 +64,8 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; @TestPropertySource(properties = {"ribbon.eureka.enabled=false", "feign.hystrix.enabled=false", "server.port=0"}) public class Issue410Tests { + private static final Log log = LogFactory.getLog(MethodHandles.lookup().lookupClass()); + @Autowired Environment environment; @Autowired Tracer tracer; @Autowired AsyncTask asyncTask; @@ -71,6 +74,7 @@ public class Issue410Tests { @Test public void should_pass_tracing_info_for_tasks_running_without_a_pool() { Span span = this.tracer.createSpan("foo"); + log.info("Starting test"); try { String response = this.restTemplate.getForObject("http://localhost:" + port() + "/without_pool", String.class); @@ -87,6 +91,7 @@ public class Issue410Tests { @Test public void should_pass_tracing_info_for_tasks_running_with_a_pool() { Span span = this.tracer.createSpan("foo"); + log.info("Starting test"); try { String response = this.restTemplate.getForObject("http://localhost:" + port() + "/with_pool", String.class); @@ -106,6 +111,7 @@ public class Issue410Tests { @Test public void should_pass_tracing_info_for_completable_futures_with_executor() { Span span = this.tracer.createSpan("foo"); + log.info("Starting test"); try { String response = this.restTemplate.getForObject("http://localhost:" + port() + "/completable", String.class); @@ -125,6 +131,7 @@ public class Issue410Tests { @Test public void should_pass_tracing_info_for_completable_futures_with_task_scheduler() { Span span = this.tracer.createSpan("foo"); + log.info("Starting test"); try { String response = this.restTemplate.getForObject("http://localhost:" + port() + "/taskScheduler", String.class); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractorTests.java index acd296951..1122e96b8 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractorTests.java @@ -21,8 +21,6 @@ import java.util.Map; import java.util.Random; import org.junit.Test; -import org.springframework.cloud.sleuth.Span; -import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import org.springframework.util.StringUtils; @@ -32,24 +30,23 @@ import static org.assertj.core.api.BDDAssertions.then; import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; public class MessagingSpanExtractorTests { - MessagingSpanExtractor extractor = new MessagingSpanExtractor(new Random()); + HeaderBasedMessagingExtractor extractor = new HeaderBasedMessagingExtractor(); @Test public void should_return_null_if_trace_or_span_is_missing() { - Message message = MessageBuilder.createMessage("", headers()); - then(this.extractor.joinTrace(message)).isNull(); + then(this.extractor.joinTrace( + new MessagingTextMap(MessageBuilder.withPayload("")))).isNull(); - message = MessageBuilder.createMessage("", headers("trace")); - then(this.extractor.joinTrace(message)).isNull(); + then(this.extractor.joinTrace( + new MessagingTextMap(MessageBuilder.withPayload("").copyHeaders(headers("trace"))))).isNull(); } @Test public void should_set_random_traceid_if_header_value_is_invalid() { - Message message = MessageBuilder.createMessage("", - headers("invalid", randomId())); - try { - this.extractor.joinTrace(message); + this.extractor.joinTrace( + new MessagingTextMap(MessageBuilder.withPayload("") + .copyHeaders(headers("invalid", randomId())))); fail("should throw an exception"); } catch (IllegalArgumentException e) { then(e).hasMessageContaining("Malformed id"); @@ -58,11 +55,10 @@ public class MessagingSpanExtractorTests { @Test public void should_set_random_spanid_if_header_value_is_invalid() { - Message message = MessageBuilder.createMessage("", - headers(randomId(), "invalid")); - try { - this.extractor.joinTrace(message); + this.extractor.joinTrace( + new MessagingTextMap(MessageBuilder.withPayload("") + .copyHeaders(headers(randomId(), "invalid")))); fail("should throw an exception"); } catch (IllegalArgumentException e) { then(e).hasMessageContaining("Malformed id"); @@ -71,21 +67,16 @@ public class MessagingSpanExtractorTests { @Test public void should_not_throw_exception_if_parent_id_is_invalid() { - Message message = MessageBuilder.createMessage("", - headers(randomId(), randomId(), "invalid")); - try { - this.extractor.joinTrace(message); + this.extractor.joinTrace( + new MessagingTextMap(MessageBuilder.withPayload("") + .copyHeaders(headers(randomId(), randomId(), "invalid")))); fail("should throw an exception"); } catch (IllegalArgumentException e) { then(e).hasMessageContaining("Malformed id"); } } - private MessageHeaders headers() { - return headers(null, null, null); - } - private MessageHeaders headers(String traceId) { return headers(traceId, null, null); } @@ -97,13 +88,13 @@ public class MessagingSpanExtractorTests { private MessageHeaders headers(String traceId, String spanId, String parentId) { Map map = new HashMap<>(); if (StringUtils.hasText(traceId)) { - map.put(Span.TRACE_ID_NAME, traceId); + map.put(TraceMessageHeaders.TRACE_ID_NAME, traceId); } if (StringUtils.hasText(spanId)) { - map.put(Span.SPAN_ID_NAME, spanId); + map.put(TraceMessageHeaders.SPAN_ID_NAME, spanId); } if (StringUtils.hasText(parentId)) { - map.put(Span.PARENT_ID_NAME, parentId); + map.put(TraceMessageHeaders.PARENT_ID_NAME, parentId); } return new MessageHeaders(map); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java index 1be79222a..8fd2a8bec 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java @@ -16,9 +16,6 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import static org.assertj.core.api.Assertions.assertThat; -import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.assertThat; - import org.junit.Test; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.TraceKeys; @@ -29,15 +26,16 @@ import org.springframework.messaging.support.MessageBuilder; import org.springframework.messaging.support.MessageHeaderAccessor; import org.springframework.messaging.support.NativeMessageHeaderAccessor; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.assertThat; + /** * @author Dave Syer * */ public class MessagingSpanInjectorTests { - private TraceKeys traceKeys = new TraceKeys(); - private MessagingSpanInjector messagingSpanInjector = new MessagingSpanInjector( - this.traceKeys); + private HeaderBasedMessagingInjector spanInjector = new HeaderBasedMessagingInjector(new TraceKeys()); @Test public void spanHeadersAdded() { @@ -45,9 +43,9 @@ public class MessagingSpanInjectorTests { Message message = new GenericMessage<>("Hello World"); MessageBuilder messageBuilder = MessageBuilder.fromMessage(message); - this.messagingSpanInjector.inject(span, messageBuilder); + this.spanInjector.inject(span, new MessagingTextMap(messageBuilder)); - assertThat(messageBuilder.build().getHeaders()).containsKey(Span.SPAN_ID_NAME); + assertThat(messageBuilder.build().getHeaders()).containsKey(TraceMessageHeaders.SPAN_ID_NAME); } @Test @@ -55,9 +53,9 @@ public class MessagingSpanInjectorTests { Span span = spanWithStringPayloadType(); MessageBuilder messageBuilder = messageWithIntegerPayloadType(); - this.messagingSpanInjector.inject(span, messageBuilder); + this.spanInjector.inject(span, new MessagingTextMap(messageBuilder)); - assertThat(messageBuilder.build().getHeaders()).containsKeys(Span.SPAN_ID_NAME, + assertThat(messageBuilder.build().getHeaders()).containsKeys(TraceMessageHeaders.SPAN_ID_NAME, "message/payload-type"); assertThat(span).hasATag("message/payload-type", "java.lang.String"); } @@ -83,14 +81,14 @@ public class MessagingSpanInjectorTests { MessageBuilder messageBuilder = MessageBuilder .fromMessage(messageToBuild); - this.messagingSpanInjector.inject(span, messageBuilder); + this.spanInjector.inject(span, new MessagingTextMap(messageBuilder)); Message message = messageBuilder.build(); assertThat(message.getHeaders()) .containsKey(NativeMessageHeaderAccessor.NATIVE_HEADERS); MessageHeaderAccessor natives = NativeMessageHeaderAccessor .getMutableAccessor(message); - assertThat(natives.getMessageHeaders()).containsKey(Span.SPAN_ID_NAME); + assertThat(natives.getMessageHeaders()).containsKey(TraceMessageHeaders.SPAN_ID_NAME); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java index f01752ce3..35cfdc2b6 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptorTests.java @@ -16,10 +16,6 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import static org.assertj.core.api.BDDAssertions.then; -import static org.junit.Assert.assertNotNull; -import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; - import java.util.HashMap; import java.util.Map; @@ -41,6 +37,7 @@ import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterce import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.TestSpanContextHolder; import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator; +import org.springframework.cloud.sleuth.util.ExceptionUtils; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.integration.channel.DirectChannel; @@ -54,6 +51,10 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import static org.assertj.core.api.BDDAssertions.then; +import static org.junit.Assert.assertNotNull; +import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; + /** * @author Dave Syer */ @@ -102,6 +103,7 @@ public class TraceChannelInterceptorTests implements MessageHandler { @After public void close() { + then(ExceptionUtils.getLastException()).isNull(); TestSpanContextHolder.removeCurrentSpan(); this.tracedChannel.unsubscribe(this); this.ignoredChannel.unsubscribe(this); @@ -111,10 +113,10 @@ public class TraceChannelInterceptorTests implements MessageHandler { @Test public void nonExportableSpanCreation() { this.tracedChannel.send(MessageBuilder.withPayload("hi") - .setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build()); + .setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build()); assertNotNull("message was null", this.message); - String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); + String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class); then(spanId).isNotNull(); then(TestSpanContextHolder.getCurrentSpan()).isNull(); then(this.span.isExportable()).isFalse(); @@ -123,7 +125,7 @@ public class TraceChannelInterceptorTests implements MessageHandler { @Test public void messageHeadersStillMutable() { this.tracedChannel.send(MessageBuilder.withPayload("hi") - .setHeader(Span.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build()); + .setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build()); assertNotNull("message was null", this.message); MessageHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(this.message, MessageHeaderAccessor.class); assertNotNull("Message header accessor should be still available", accessor); @@ -131,64 +133,18 @@ public class TraceChannelInterceptorTests implements MessageHandler { @Test public void parentSpanIncluded() { - this.tracedChannel.send(MessageBuilder.withPayload("hi") - .setHeader(Span.TRACE_ID_NAME, Span.idToHex(10L)) - .setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build()); - then(this.message).isNotNull(); - - String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); - then(spanId).isNotNull(); - long traceId = Span - .hexToId(this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class)); - then(traceId).isEqualTo(10L); - then(spanId).isNotEqualTo(20L); - then(this.accumulator.getSpans()).hasSize(1); - } - - // #332 - @Test - public void shouldSendNewAndOldHeadersWhenNewHeadersWerePassed() { this.tracedChannel.send(MessageBuilder.withPayload("hi") .setHeader(TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(10L)) .setHeader(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)).build()); then(this.message).isNotNull(); - String newSpanId = thenNewSpanIdEqualsOldSpanId(); - thenNewTraceIdEqualsOldTraceId(); - then(newSpanId).isNotEqualTo(20L); - then(this.accumulator.getSpans()).hasSize(1); - } - - private String thenNewSpanIdEqualsOldSpanId() { - String newSpanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, - String.class); - then(newSpanId).isNotNull(); - String oldSpanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); - then(oldSpanId).isEqualTo(newSpanId); - return newSpanId; - } - - // #332 - @Test - public void shouldSendNewAndOldHeadersWhenOldHeadersWerePassed() { - this.tracedChannel.send(MessageBuilder.withPayload("hi") - .setHeader(Span.TRACE_ID_NAME, Span.idToHex(10L)) - .setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build()); - then(this.message).isNotNull(); - - String newSpanId = thenNewSpanIdEqualsOldSpanId(); - thenNewTraceIdEqualsOldTraceId(); - then(newSpanId).isNotEqualTo(20L); - then(this.accumulator.getSpans()).hasSize(1); - } - - private void thenNewTraceIdEqualsOldTraceId() { - long traceId = Span.hexToId(this.message.getHeaders() - .get(TraceMessageHeaders.TRACE_ID_NAME, String.class)); + String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class); + then(spanId).isNotNull(); + long traceId = Span + .hexToId(this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class)); then(traceId).isEqualTo(10L); - long oldTraceId = Span - .hexToId(this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class)); - then(oldTraceId).isEqualTo(traceId); + then(spanId).isNotEqualTo(20L); + then(this.accumulator.getSpans()).hasSize(1); } @Test @@ -196,10 +152,10 @@ public class TraceChannelInterceptorTests implements MessageHandler { this.tracedChannel.send(MessageBuilder.withPayload("hi").build()); then(this.message).isNotNull(); - String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); + String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class); then(spanId).isNotNull(); - String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class); + String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class); then(traceId).isNotNull(); then(TestSpanContextHolder.getCurrentSpan()).isNull(); } @@ -208,16 +164,18 @@ public class TraceChannelInterceptorTests implements MessageHandler { public void shouldLogClientReceivedClientSentEventWhenTheMessageIsSentAndReceived() { this.tracedChannel.send(MessageBuilder.withPayload("hi").build()); - then(this.span.logs()).extracting("event").contains(Span.CLIENT_SEND, + then(this.accumulator.getSpans()).hasSize(1); + then(this.accumulator.getSpans().get(0).logs()).extracting("event").contains(Span.CLIENT_SEND, Span.CLIENT_RECV); } @Test public void shouldLogServerReceivedServerSentEventWhenTheMessageIsPropagatedToTheNextListener() { this.tracedChannel.send(MessageBuilder.withPayload("hi") - .setHeader("X-Message-Sent", true).build()); + .setHeader(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT, true).build()); - then(this.span.logs()).extracting("event").contains(Span.SERVER_RECV, + then(this.accumulator.getSpans()).hasSize(1); + then(this.accumulator.getSpans().get(0).logs()).extracting("event").contains(Span.SERVER_RECV, Span.SERVER_SEND); } @@ -228,10 +186,10 @@ public class TraceChannelInterceptorTests implements MessageHandler { this.tracer.close(span); then(this.message).isNotNull(); - String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); + String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class); then(spanId).isNotNull(); - String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class); + String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class); then(traceId).isNotNull(); then(TestSpanContextHolder.getCurrentSpan()).isNull(); } @@ -245,10 +203,10 @@ public class TraceChannelInterceptorTests implements MessageHandler { this.tracer.close(span); then(this.message).isNotNull(); - String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); + String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class); then(spanId).isNotNull(); - String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class); + String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class); then(traceId).isNotNull(); then(TestSpanContextHolder.getCurrentSpan()).isNull(); } @@ -280,10 +238,10 @@ public class TraceChannelInterceptorTests implements MessageHandler { this.ignoredChannel.send(MessageBuilder.withPayload("hi").build()); then(this.message).isNotNull(); - String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class); + String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class); then(spanId).isNull(); - String traceId = this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class); + String traceId = this.message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class); then(traceId).isNull(); then(this.accumulator.getSpans()).isEmpty(); @@ -295,8 +253,8 @@ public class TraceChannelInterceptorTests implements MessageHandler { String hex128Bits = "463ac35c9f6413ad48485a3953bb6124"; String lower64Bits = "48485a3953bb6124"; this.tracedChannel.send(MessageBuilder.withPayload("hi") - .setHeader(Span.TRACE_ID_NAME, hex128Bits) - .setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build()); + .setHeader(TraceMessageHeaders.TRACE_ID_NAME, hex128Bits) + .setHeader(TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(20L)).build()); then(this.message).isNotNull(); long traceId = Span.hexToId(this.message.getHeaders() diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java index 5461d7283..357e4a115 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/TraceContextPropagationChannelInterceptorTests.java @@ -77,15 +77,15 @@ public class TraceContextPropagationChannelInterceptorTests { assertNotNull("message was null", message); Long spanId = Span - .hexToId(message.getHeaders().get(Span.SPAN_ID_NAME, String.class)); + .hexToId(message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class)); assertNotEquals("spanId was equal to parent's id", expectedSpanId, spanId); long traceId = Span - .hexToId(message.getHeaders().get(Span.TRACE_ID_NAME, String.class)); + .hexToId(message.getHeaders().get(TraceMessageHeaders.TRACE_ID_NAME, String.class)); assertNotNull("traceId was null", traceId); Long parentId = Span - .hexToId(message.getHeaders().get(Span.PARENT_ID_NAME, String.class)); + .hexToId(message.getHeaders().get(TraceMessageHeaders.PARENT_ID_NAME, String.class)); assertEquals("parentId was not equal to parent's id", expectedSpanId, parentId); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java index 6cc85427f..66e118e1f 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractorTests.java @@ -16,9 +16,11 @@ package org.springframework.cloud.sleuth.instrument.web; -import java.util.Random; -import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; +import java.util.Arrays; +import java.util.Random; +import java.util.Vector; +import java.util.regex.Pattern; import org.junit.Before; import org.junit.Test; @@ -34,7 +36,7 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; public class HttpServletRequestExtractorTests { @Mock HttpServletRequest request; - HttpServletRequestExtractor extractor = new HttpServletRequestExtractor( + ZipkinHttpSpanExtractor extractor = new ZipkinHttpSpanExtractor( Pattern.compile("")); @Before @@ -45,7 +47,7 @@ public class HttpServletRequestExtractorTests { @Test public void should_return_null_if_there_is_no_trace_id() { - then(extractor.joinTrace(request)).isNull(); + then(extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull(); } @Test @@ -53,7 +55,7 @@ public class HttpServletRequestExtractorTests { BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME)) .willReturn("invalid"); - then(this.extractor.joinTrace(this.request)).isNull(); + then(this.extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull(); } @Test @@ -63,7 +65,7 @@ public class HttpServletRequestExtractorTests { BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME)) .willReturn("invalid"); - then(this.extractor.joinTrace(this.request)).isNull(); + then(this.extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull(); } @Test @@ -75,7 +77,7 @@ public class HttpServletRequestExtractorTests { BDDMockito.given(this.request.getHeader(Span.PARENT_ID_NAME)) .willReturn("invalid"); - then(this.extractor.joinTrace(this.request)).isNull(); + then(this.extractor.joinTrace(new HttpServletRequestTextMap(this.request))).isNull(); } @Test @@ -83,12 +85,14 @@ public class HttpServletRequestExtractorTests { String hex128Bits = "463ac35c9f6413ad48485a3953bb6124"; String lower64Bits = "48485a3953bb6124"; + BDDMockito.given(this.request.getHeaderNames()) + .willReturn(new Vector<>(Arrays.asList(Span.TRACE_ID_NAME, Span.SPAN_ID_NAME)).elements()); BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME)) .willReturn(hex128Bits); BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME)) .willReturn(lower64Bits); - Span span = this.extractor.joinTrace(this.request); + Span span = this.extractor.joinTrace(new HttpServletRequestTextMap(this.request)); then(span.getTraceId()).isEqualTo(Span.hexToId(lower64Bits)); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java index 72fa73a7c..36bd3f8c9 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceCustomFilterResponseInjectorTests.java @@ -22,8 +22,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.SpanTextMap; import org.springframework.cloud.sleuth.Tracer; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; @@ -46,7 +47,10 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.net.URI; +import java.util.Collection; +import java.util.Enumeration; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; import static org.assertj.core.api.BDDAssertions.then; @@ -83,8 +87,7 @@ public class TraceCustomFilterResponseInjectorTests { int port; // tag::configuration[] - @Bean - SpanInjector customHttpServletResponseSpanInjector() { + @Bean HttpSpanInjector customHttpServletResponseSpanInjector() { return new CustomHttpServletResponseSpanInjector(); } @@ -113,22 +116,22 @@ public class TraceCustomFilterResponseInjectorTests { } // tag::injector[] - static class CustomHttpServletResponseSpanInjector - implements SpanInjector { + static class CustomHttpServletResponseSpanInjector extends ZipkinHttpSpanInjector { @Override - public void inject(Span span, HttpServletResponse carrier) { - carrier.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); - carrier.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); + public void inject(Span span, SpanTextMap carrier) { + super.inject(span, carrier); + carrier.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); + carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); } } static class HttpResponseInjectingTraceFilter extends GenericFilterBean { private final Tracer tracer; - private final SpanInjector spanInjector; + private final HttpSpanInjector spanInjector; - public HttpResponseInjectingTraceFilter(Tracer tracer, SpanInjector spanInjector) { + public HttpResponseInjectingTraceFilter(Tracer tracer, HttpSpanInjector spanInjector) { this.tracer = tracer; this.spanInjector = spanInjector; } @@ -137,9 +140,32 @@ public class TraceCustomFilterResponseInjectorTests { public void doFilter(ServletRequest request, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletResponse response = (HttpServletResponse) servletResponse; Span currentSpan = this.tracer.getCurrentSpan(); - this.spanInjector.inject(currentSpan, response); + this.spanInjector.inject(currentSpan, new HttpServletResponseTextMap(response)); filterChain.doFilter(request, response); } + + class HttpServletResponseTextMap implements SpanTextMap { + + private final HttpServletResponse delegate; + + HttpServletResponseTextMap(HttpServletResponse delegate) { + this.delegate = delegate; + } + + @Override + public Iterator> iterator() { + Map map = new HashMap<>(); + for (String header : this.delegate.getHeaderNames()) { + map.put(header, this.delegate.getHeader(header)); + } + return map.entrySet().iterator(); + } + + @Override + public void put(String key, String value) { + this.delegate.addHeader(key, value); + } + } } // end::injector[] diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java index 3fb83db62..96f2aadf7 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterCustomExtractorTests.java @@ -16,6 +16,11 @@ package org.springframework.cloud.sleuth.instrument.web; +import java.net.URI; +import java.util.HashMap; +import java.util.Map; + +import org.assertj.core.api.BDDAssertions; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -23,16 +28,19 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.sleuth.HttpSpanExtractor; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanExtractor; import org.springframework.cloud.sleuth.SpanReporter; +import org.springframework.cloud.sleuth.SpanTextMap; +import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.util.ArrayListSpanAccumulator; +import org.springframework.cloud.sleuth.util.TextMapUtil; import org.springframework.context.ApplicationListener; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.http.HttpHeaders; import org.springframework.http.RequestEntity; import org.springframework.http.ResponseEntity; @@ -43,15 +51,8 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; -import javax.servlet.http.HttpServletRequest; -import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; - import static com.jayway.awaitility.Awaitility.await; import static java.util.concurrent.TimeUnit.SECONDS; -import static org.assertj.core.api.BDDAssertions.then; import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; @RunWith(SpringJUnit4ClassRunner.class) @@ -59,11 +60,11 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then; webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DirtiesContext public class TraceFilterCustomExtractorTests { - @Autowired Random random; @Autowired RestTemplate restTemplate; @Autowired Config config; @Autowired CustomRestController customRestController; @Autowired ArrayListSpanAccumulator accumulator; + @Autowired Tracer tracer; @Before public void setup() { @@ -73,23 +74,23 @@ public class TraceFilterCustomExtractorTests { @Test @SuppressWarnings("unchecked") public void should_create_a_valid_span_from_custom_headers() { - long spanId = this.random.nextLong(); - long traceId = this.random.nextLong(); - RequestEntity requestEntity = RequestEntity - .get(URI.create("http://localhost:" + this.config.port + "/headers")) - .header("correlationId", Span.idToHex(traceId)) - .header("mySpanId", Span.idToHex(spanId)).build(); - - @SuppressWarnings("rawtypes") - ResponseEntity responseEntity = this.restTemplate.exchange(requestEntity, - Map.class); + final Span newSpan = this.tracer.createSpan("new_span"); + ResponseEntity responseEntity = null; + try { + RequestEntity requestEntity = RequestEntity + .get(URI.create("http://localhost:" + this.config.port + "/headers")) + .build(); + responseEntity = this.restTemplate.exchange(requestEntity, Map.class); + } finally { + this.tracer.close(newSpan); + } await().atMost(5, SECONDS).until(() -> then(this.accumulator.getSpans().stream().filter( - span -> span.getSpanId() == spanId).findFirst().get()) - .hasTraceIdEqualTo(traceId)); - then(responseEntity.getBody()) - .containsEntry("correlationid", Span.idToHex(traceId)) - .containsEntry("myspanid", Span.idToHex(spanId)) + span -> span.getSpanId() == newSpan.getSpanId()).findFirst().get()) + .hasTraceIdEqualTo(newSpan.getTraceId())); + BDDAssertions.then(responseEntity.getBody()) + .containsEntry("correlationid", Span.idToHex(newSpan.getTraceId())) + .containsKey("myspanid") .as("input request headers"); } @@ -101,9 +102,13 @@ public class TraceFilterCustomExtractorTests { // tag::configuration[] @Bean - @Primary - SpanExtractor customHttpServletRequestSpanExtractor() { - return new CustomHttpServletRequestSpanExtractor(); + HttpSpanInjector customHttpSpanInjector() { + return new CustomHttpSpanInjector(); + } + + @Bean + HttpSpanExtractor customHttpSpanExtractor() { + return new CustomHttpSpanExtractor(); } // end::configuration[] @@ -134,24 +139,34 @@ public class TraceFilterCustomExtractorTests { } // tag::extractor[] - static class CustomHttpServletRequestSpanExtractor - implements SpanExtractor { + static class CustomHttpSpanExtractor implements HttpSpanExtractor { - @Override - public Span joinTrace(HttpServletRequest carrier) { - long traceId = Span.hexToId(carrier.getHeader("correlationId")); - long spanId = Span.hexToId(carrier.getHeader("mySpanId")); + @Override public Span joinTrace(SpanTextMap carrier) { + Map map = TextMapUtil.asMap(carrier); + long traceId = Span.hexToId(map.get("correlationid")); + long spanId = Span.hexToId(map.get("myspanid")); // extract all necessary headers Span.SpanBuilder builder = Span.builder().traceId(traceId).spanId(spanId); // build rest of the Span return builder.build(); } } + + static class CustomHttpSpanInjector implements HttpSpanInjector { + + @Override + public void inject(Span span, SpanTextMap carrier) { + carrier.put("correlationId", Span.idToHex(span.getTraceId())); + carrier.put("mySpanId", Span.idToHex(span.getSpanId())); + } + } // end::extractor[] @RestController static class CustomRestController { + @Autowired Tracer tracer; + @RequestMapping("/headers") public Map headers(@RequestHeader HttpHeaders headers) { Map map = new HashMap<>(); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java index 5f29beea6..ce76d86da 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterMockChainIntegrationTests.java @@ -73,7 +73,7 @@ public class TraceFilterMockChainIntegrationTests { @Test public void startsNewTrace() throws Exception { TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(), - new HttpServletRequestExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)), + new ZipkinHttpSpanExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)), keysInjector); filter.doFilter(this.request, this.response, this.filterChain); assertNull(TestSpanContextHolder.getCurrentSpan()); @@ -85,7 +85,7 @@ public class TraceFilterMockChainIntegrationTests { this.request = builder().header(Span.SPAN_ID_NAME, generator.nextLong()) .header(Span.TRACE_ID_NAME, generator.nextLong()).buildRequest(new MockServletContext()); TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(), - new HttpServletRequestExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)), + new ZipkinHttpSpanExtractor(Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)), keysInjector); filter.doFilter(this.request, this.response, this.filterChain); assertNull(TestSpanContextHolder.getCurrentSpan()); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java index db7a6a213..b6f541a09 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java @@ -27,6 +27,7 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.springframework.cloud.sleuth.DefaultSpanNamer; +import org.springframework.cloud.sleuth.HttpSpanExtractor; import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.SpanExtractor; @@ -66,7 +67,7 @@ public class TraceFilterTests { @Mock SpanLogger spanLogger; ArrayListSpanAccumulator spanReporter = new ArrayListSpanAccumulator(); - SpanExtractor spanExtractor = new HttpServletRequestExtractor(Pattern + HttpSpanExtractor spanExtractor = new ZipkinHttpSpanExtractor(Pattern .compile(TraceFilter.DEFAULT_SKIP_PATTERN)); private Tracer tracer; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java index 73a363ecc..3853a0450 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorIntegrationTests.java @@ -32,6 +32,7 @@ import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.assertions.ListOfSpans; import org.springframework.cloud.sleuth.assertions.SleuthAssertions; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; +import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector; import org.springframework.cloud.sleuth.log.NoOpSpanLogger; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; @@ -67,7 +68,7 @@ public class TraceRestTemplateInterceptorIntegrationTests { this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(), new NoOpSpanLogger(), this.spanAccumulator); this.template.setInterceptors(Arrays.asList( - new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(), + new TraceRestTemplateInterceptor(this.tracer, new ZipkinHttpSpanInjector(), new HttpTraceKeysInjector(this.tracer, new TraceKeys())))); TestSpanContextHolder.removeCurrentSpan(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java index dc9e58b3b..857394153 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java @@ -30,6 +30,7 @@ import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; +import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector; import org.springframework.cloud.sleuth.log.NoOpSpanLogger; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; @@ -70,7 +71,7 @@ public class TraceRestTemplateInterceptorTests { this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(), new NoOpSpanLogger(), this.spanAccumulator); this.template.setInterceptors(Arrays.asList( - new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector(), + new TraceRestTemplateInterceptor(this.tracer, new ZipkinHttpSpanInjector(), new HttpTraceKeysInjector(this.tracer, new TraceKeys())))); TestSpanContextHolder.removeCurrentSpan(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java index daaf6fba4..a99a71f48 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRetriesTests.java @@ -32,10 +32,12 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.DefaultSpanNamer; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Span; 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.instrument.web.ZipkinHttpSpanInjector; import org.springframework.cloud.sleuth.log.NoOpSpanLogger; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; @@ -76,6 +78,8 @@ public class FeignRetriesTests { TestSpanContextHolder.removeCurrentSpan(); BDDMockito.given(this.beanFactory.getBean(HttpTraceKeysInjector.class)) .willReturn(new HttpTraceKeysInjector(this.tracer, new TraceKeys())); + BDDMockito.given(this.beanFactory.getBean(HttpSpanInjector.class)) + .willReturn(new ZipkinHttpSpanInjector()); BDDMockito.given(this.beanFactory.getBean(Tracer.class)).willReturn(this.tracer); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientTests.java index ce8f7ea21..cf77c30ad 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientTests.java @@ -31,11 +31,13 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.springframework.beans.factory.BeanFactory; import org.springframework.cloud.sleuth.DefaultSpanNamer; +import org.springframework.cloud.sleuth.HttpSpanInjector; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.assertions.SleuthAssertions; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; +import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector; import org.springframework.cloud.sleuth.log.NoOpSpanLogger; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.trace.DefaultTracer; @@ -69,6 +71,8 @@ public class TraceFeignClientTests { ExceptionUtils.setFail(true); BDDMockito.given(this.beanFactory.getBean(HttpTraceKeysInjector.class)) .willReturn(new HttpTraceKeysInjector(this.tracer, new TraceKeys())); + BDDMockito.given(this.beanFactory.getBean(HttpSpanInjector.class)) + .willReturn(new ZipkinHttpSpanInjector()); BDDMockito.given(this.beanFactory.getBean(Tracer.class)).willReturn(this.tracer); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java index 1ad11b4d3..3f20599f7 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcIntegrationTest.java @@ -1,10 +1,8 @@ package org.springframework.cloud.sleuth.instrument.web.common; -import javax.servlet.http.HttpServletRequest; - import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.sleuth.SpanExtractor; +import org.springframework.cloud.sleuth.HttpSpanExtractor; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; @@ -34,7 +32,7 @@ public abstract class AbstractMvcIntegrationTest { protected MockMvc mockMvc; @Autowired protected Tracer tracer; @Autowired protected TraceKeys traceKeys; - @Autowired protected SpanExtractor spanExtractor; + @Autowired protected HttpSpanExtractor spanExtractor; @Autowired protected HttpTraceKeysInjector httpTraceKeysInjector; @Before diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java index 34e6a613e..28c97ead3 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java @@ -50,7 +50,7 @@ public class ApacheHttpClientRibbonRequestCustomizerTests { 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); + this.customizer.inject(null, this.customizer.toSpanTextMap(requestBuilder)); HttpUriRequest request = requestBuilder.build(); Header header = request.getFirstHeader(Span.SAMPLED_NAME); @@ -62,7 +62,7 @@ public class ApacheHttpClientRibbonRequestCustomizerTests { 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); + this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder)); HttpUriRequest request = requestBuilder.build(); thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1"); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java index f3e1c0d8c..08e6cdb55 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java @@ -49,7 +49,7 @@ public class OkHttpClientRibbonRequestCustomizerTests { 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); + this.customizer.inject(null, this.customizer.toSpanTextMap(requestBuilder)); Request request = requestBuilder.build(); then(request.header(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED); @@ -59,7 +59,7 @@ public class OkHttpClientRibbonRequestCustomizerTests { 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); + this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder)); Request request = requestBuilder.build(); thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1"); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java index 4fc895e21..12b37bbc5 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java @@ -49,7 +49,7 @@ public class RestClientRibbonRequestCustomizerTests { 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); + this.customizer.inject(null, this.customizer.toSpanTextMap(requestBuilder)); HttpRequest request = requestBuilder.build(); then(request.getHttpHeaders().getFirstValue(Span.SAMPLED_NAME)).isEqualTo(Span.SPAN_NOT_SAMPLED); @@ -59,7 +59,7 @@ public class RestClientRibbonRequestCustomizerTests { 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); + this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder)); HttpRequest request = requestBuilder.build(); thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1"); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java index fd4bf49af..05ca52aeb 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilterTests.java @@ -35,6 +35,7 @@ import org.springframework.cloud.sleuth.NoOpSpanReporter; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.instrument.web.HttpTraceKeysInjector; +import org.springframework.cloud.sleuth.instrument.web.ZipkinHttpSpanInjector; import org.springframework.cloud.sleuth.log.NoOpSpanLogger; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.sampler.NeverSampler; @@ -55,7 +56,7 @@ public class TracePreZuulFilterTests { private DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter()); - private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer, new RequestContextInjector(), + private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer, new ZipkinHttpSpanInjector(), new HttpTraceKeysInjector(this.tracer, new TraceKeys())); @After @@ -106,7 +107,7 @@ public class TracePreZuulFilterTests { Span startedSpan = this.tracer.createSpan("http:start"); final AtomicReference span = new AtomicReference<>(); - new TracePreZuulFilter(this.tracer, new RequestContextInjector(), + new TracePreZuulFilter(this.tracer, new ZipkinHttpSpanInjector(), new HttpTraceKeysInjector(this.tracer, new TraceKeys())) { @Override public Object run() { @@ -127,7 +128,7 @@ public class TracePreZuulFilterTests { Span startedSpan = this.tracer.createSpan("http:start"); final AtomicReference span = new AtomicReference<>(); - new TracePreZuulFilter(this.tracer, new RequestContextInjector(), + new TracePreZuulFilter(this.tracer, new ZipkinHttpSpanInjector(), new HttpTraceKeysInjector(this.tracer, new TraceKeys())) { @Override public Object run() { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/TextMapUtilTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/TextMapUtilTests.java new file mode 100644 index 000000000..4f8669439 --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/util/TextMapUtilTests.java @@ -0,0 +1,31 @@ +package org.springframework.cloud.sleuth.util; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import static org.assertj.core.api.BDDAssertions.then; + +/** + * @author Marcin Grzejszczak + */ +public class TextMapUtilTests { + + @Test + public void should_convert_an_iterable_to_a_caseinsensitive_map() throws Exception { + List> iterable = new ArrayList<>(); + iterable.add(new AbstractMap.SimpleEntry<>("foo", "bar")); + + Map map = TextMapUtil.asMap(iterable); + + then(map) + .containsKey("foo") + .containsKey("FOO") + .containsKey("FoO") + .contains(new AbstractMap.SimpleEntry<>("foo", "bar")); + } + +} \ No newline at end of file