diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java index a16c49892..56c255af4 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java @@ -65,6 +65,7 @@ public class Span { public static final String SPAN_EXPORT_NAME = "X-Span-Export"; public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc"; + /** * cr - Client Receive. Signifies the end of the span. The client has successfully received the * response from the server side. If one subtracts the cs timestamp from this timestamp one diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanExtractor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanExtractor.java new file mode 100644 index 000000000..8ccc007f5 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanExtractor.java @@ -0,0 +1,39 @@ +/* + * 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; + +/** + * Adopted from OpenTracing + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +public interface SpanExtractor { + /** Returns a SpanBuilder provided + * a “carrier” object from which to extract identifying information needed by the new Span instance. + * + * If the carrier object has no such span stored within it, a new Span is created. + * + * Unless there’s an error, it returns a Span. + * The Span generated from the builder can be used in the host process like any other. + * + * (Note that some OpenTracing implementations consider the Spans on either side of an RPC to have the same identity, + * and others consider the caller to be the parent and the receiver to be the child). + */ + Span joinTrace(T carrier); +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanInjector.java new file mode 100644 index 000000000..e8c9015ca --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/SpanInjector.java @@ -0,0 +1,37 @@ +/* + * 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; + +/** + * Adopted from OpenTracing + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +public interface SpanInjector { + /** Takes two arguments: + * a Span instance, and + * a “carrier” object in which to inject that Span for cross-process propagation. + * + * A “carrier” object is some sort of http or rpc envelope, for example HeaderGroup (from Apache HttpComponents). + * + * Attempting to inject to a carrier that has been registered/configured to this Tracer will result in a + * IllegalStateException. + */ + void inject(Span span, T carrier); +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceHeaders.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceHeaders.java new file mode 100644 index 000000000..b14799b92 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceHeaders.java @@ -0,0 +1,128 @@ +/* + * 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; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Names of default headers that need to be sent between processes + * for tracing to be operational. + * + * Default Zipkin Headers are + * + * + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +@ConfigurationProperties("spring.sleuth.headers") +public class TraceHeaders { + + private Zipkin zipkin; + private Sleuth sleuth; + + public Zipkin getZipkin() { + return this.zipkin; + } + + public void setZipkin(Zipkin zipkin) { + this.zipkin = zipkin; + } + + public Sleuth getSleuth() { + return this.sleuth; + } + + public void setSleuth(Sleuth sleuth) { + this.sleuth = sleuth; + } + + private static class Zipkin { + private String traceId = "X-B3-TraceId"; + private String spanId = "X-B3-SpanId"; + private String parentSpanId = "X-B3-ParentSpanId"; + private String sampled = "X-B3-Sampled"; + + public String getTraceId() { + return this.traceId; + } + + public void setTraceId(String traceId) { + this.traceId = traceId; + } + + public String getSpanId() { + return this.spanId; + } + + public void setSpanId(String spanId) { + this.spanId = spanId; + } + + public String getParentSpanId() { + return this.parentSpanId; + } + + public void setParentSpanId(String parentSpanId) { + this.parentSpanId = parentSpanId; + } + + public String getSampled() { + return this.sampled; + } + + public void setSampled(String sampled) { + this.sampled = sampled; + } + } + + private static class Sleuth { + private String processId = "X-Process-Id"; + private String spanName = "X-Span-Name"; + private String exportable = "X-Span-Export"; + + public String getProcessId() { + return this.processId; + } + + public void setProcessId(String processId) { + this.processId = processId; + } + + public String getSpanName() { + return this.spanName; + } + + public void setSpanName(String spanName) { + this.spanName = spanName; + } + + public String getExportable() { + return this.exportable; + } + + public void setExportable(String exportable) { + this.exportable = exportable; + } + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java index 3f46fa2f3..8b1d08d2e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/autoconfig/TraceAutoConfiguration.java @@ -62,8 +62,8 @@ public class TraceAutoConfiguration { @Bean @ConditionalOnMissingBean(Tracer.class) public DefaultTracer traceManager(Sampler sampler, Random random, - SpanNamer spanNamer, SpanLogger spanLogger, - SpanReporter spanReporter) { + SpanNamer spanNamer, SpanLogger spanLogger, + SpanReporter spanReporter) { return new DefaultTracer(sampler, random, spanNamer, spanLogger, spanReporter); } 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 e8f464502..4e8ae7a2b 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 @@ -1,16 +1,17 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import java.util.Random; - import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Tracer; +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.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; /** @@ -31,16 +32,17 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter protected static final String MESSAGE_COMPONENT = "message"; private final Tracer tracer; - - private final Random random; - private final TraceKeys traceKeys; + private final SpanExtractor spanExtractor; + private final SpanInjector spanInjector; protected AbstractTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, - Random random) { + SpanExtractor spanExtractor, + SpanInjector spanInjector) { this.tracer = tracer; this.traceKeys = traceKeys; - this.random = random; + this.spanExtractor = spanExtractor; + this.spanInjector = spanInjector; } protected Tracer getTracer() { @@ -51,49 +53,16 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter return this.traceKeys; } + protected SpanInjector getSpanInjector() { + return this.spanInjector; + } + /** - * Returns a span given the message and a channel. Returns null when there was no - * trace id passed initially. + * Returns a span given the message and a channel. Returns {@code null} if ids + * are missing. */ protected Span buildSpan(Message message) { - if (!hasHeader(message, Span.TRACE_ID_NAME) - || !hasHeader(message, Span.SPAN_ID_NAME)) { - return null; // cannot build a span without ids - } - long spanId = hasHeader(message, Span.SPAN_ID_NAME) - ? Span.hexToId(getHeader(message, Span.SPAN_ID_NAME)) - : this.random.nextLong(); - long traceId = Span.hexToId(getHeader(message, Span.TRACE_ID_NAME)); - Span.SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId); - if (hasHeader(message, Span.NOT_SAMPLED_NAME)) { - span.exportable(false); - } - String parentId = getHeader(message, Span.PARENT_ID_NAME); - String processId = getHeader(message, Span.PROCESS_ID_NAME); - String spanName = getHeader(message, Span.SPAN_NAME_NAME); - if (spanName != null) { - span.name(spanName); - } - if (processId != null) { - span.processId(processId); - } - if (parentId != null) { - span.parent(Span.hexToId(parentId)); - } - span.remote(true); - return span.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); + return this.spanExtractor.joinTrace(message); } String getChannelName(MessageChannel channel) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageBuilderHolder.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageBuilderHolder.java new file mode 100644 index 000000000..560817563 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessageBuilderHolder.java @@ -0,0 +1,42 @@ +/* + * 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 org.springframework.messaging.Message; +import org.springframework.messaging.support.MessageBuilder; + +/** + * Utility class to contain both {@link MessageBuilder} and the {@link Message}. + * + * {@link MessageBuilder} is mutable + * {@link Message} is immutable + * + * @author Marcin Grzejszczak + */ +public class MessageBuilderHolder { + final MessageBuilder messageBuilder; + final Message message; + + public MessageBuilderHolder(MessageBuilder messageBuilder, Message message) { + this.messageBuilder = messageBuilder; + this.message = message; + } + public MessageBuilderHolder(MessageBuilder messageBuilder) { + this.messageBuilder = messageBuilder; + this.message = messageBuilder.build(); + } +} 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 new file mode 100644 index 000000000..a18d905d0 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanExtractor.java @@ -0,0 +1,83 @@ +/* + * 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.Random; + +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 + */ +public class MessagingSpanExtractor implements SpanExtractor { + + 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)) { + return null; + //TODO: Consider throwing IllegalArgumentException; + } + long spanId = hasHeader(carrier, Span.SPAN_ID_NAME) + ? Span.hexToId(getHeader(carrier, Span.SPAN_ID_NAME)) + : this.random.nextLong(); + long traceId = Span.hexToId(getHeader(carrier, Span.TRACE_ID_NAME)); + SpanBuilder spanBuilder = Span.builder().traceId(traceId).spanId(spanId); + if (hasHeader(carrier, Span.NOT_SAMPLED_NAME)) { + spanBuilder.exportable(false); + } + String parentId = getHeader(carrier, Span.PARENT_ID_NAME); + String processId = getHeader(carrier, Span.PROCESS_ID_NAME); + String spanName = getHeader(carrier, Span.SPAN_NAME_NAME); + if (spanName != null) { + spanBuilder.name(spanName); + } + if (processId != null) { + spanBuilder.processId(processId); + } + if (parentId != null) { + spanBuilder.parent(Span.hexToId(parentId)); + } + 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); + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SpanMessageHeaders.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java similarity index 67% rename from spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SpanMessageHeaders.java rename to spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java index 847f1fc81..f37996296 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/SpanMessageHeaders.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjector.java @@ -1,5 +1,5 @@ /* - * Copyright 2015 the original author or authors. + * 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. @@ -21,6 +21,7 @@ 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; @@ -29,53 +30,40 @@ import org.springframework.messaging.support.NativeMessageHeaderAccessor; import org.springframework.util.StringUtils; /** - * Utility for manipulating message headers related to span data. + * Creates a {@link Span.SpanBuilder} from {@link Message} * - * @author Dave Syer + * @author Marcin Grzejszczak * + * @since 1.0.0 */ -public class SpanMessageHeaders { +public class MessagingSpanInjector implements SpanInjector { public static final String SPAN_HEADER = "X-Current-Span"; - public static Span getSpanFromHeader(Message message) { - if (message == null) { - return null; - } - Object object = message.getHeaders().get(SPAN_HEADER); - if (object instanceof Span) { - return (Span) object; - } - return null; + private final TraceKeys traceKeys; + + public MessagingSpanInjector(TraceKeys traceKeys) { + this.traceKeys = traceKeys; } - /** - * Adds default headers for a message. Check {@link Span} constants for - * more information what the default headers are. If a span already has - * a tag set it will not get overridden. - * - * @param traceKeys - the global configuration for trace keys - * @param message - message to which headers will be added - * @param span - span from which headers will be taken - * @return the input message with updated headers - */ - public static Message addSpanHeaders(TraceKeys traceKeys, Message message, - Span span) { + @Override + public void inject(Span span, MessageBuilder carrier) { + Message initialMessage = carrier.build(); MessageHeaderAccessor accessor = MessageHeaderAccessor - .getMutableAccessor(message); + .getMutableAccessor(initialMessage); if (span == null) { - if (!message.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) { + if (!initialMessage.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) { accessor.setHeader(Span.NOT_SAMPLED_NAME, "true"); - return MessageBuilder.createMessage(message.getPayload(), - accessor.getMessageHeaders()); + carrier.setHeaders(accessor); + return; } - return message; + return; } Map headers = new HashMap<>(); addHeader(headers, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); addHeader(headers, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); if (span.isExportable()) { - addAnnotations(traceKeys, message, span); + addAnnotations(this.traceKeys, initialMessage, span); Long parentId = getFirst(span.getParents()); if (parentId != null) { addHeader(headers, Span.PARENT_ID_NAME, Span.idToHex(parentId)); @@ -94,11 +82,10 @@ public class SpanMessageHeaders { nativeAccessor.setNativeHeader(name, headers.get(name)); } } - return MessageBuilder.createMessage(message.getPayload(), - accessor.getMessageHeaders()); + carrier.setHeaders(accessor); } - public static void addAnnotations(TraceKeys traceKeys, Message message, + private void addAnnotations(TraceKeys traceKeys, Message message, Span span) { for (String name : traceKeys.getMessage().getHeaders()) { if (message.getHeaders().containsKey(name)) { @@ -113,7 +100,7 @@ public class SpanMessageHeaders { addPayloadAnnotations(traceKeys, message.getPayload(), span); } - static void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) { + private void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) { if (payload != null) { tagIfEntryMissing(span, traceKeys.getMessage().getPayload().getType(), payload.getClass().getCanonicalName()); @@ -128,21 +115,20 @@ public class SpanMessageHeaders { } } - private static void tagIfEntryMissing(Span span, String key, String value) { + private void tagIfEntryMissing(Span span, String key, String value) { if (!span.tags().containsKey(key)) { span.tag(key, value); } } - private static void addHeader(Map headers, String name, + private void addHeader(Map headers, String name, String value) { if (StringUtils.hasText(value)) { headers.put(name, value); } } - private static Long getFirst(List parents) { + 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/TraceChannelInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceChannelInterceptor.java index 7e72b756c..8a70f7ac2 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 @@ -16,15 +16,16 @@ package org.springframework.cloud.sleuth.instrument.messaging; -import java.util.Random; - import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Tracer; +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; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.support.MessageBuilder; /** * A channel interceptor that automatically starts / continues / closes and detaches spans. @@ -34,13 +35,17 @@ import org.springframework.messaging.MessageHandler; */ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { - public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, Random random) { - super(tracer, traceKeys, random); + private static final String SPAN_HEADER = "X-Current-Span"; + + public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, + SpanExtractor spanExtractor, + SpanInjector spanInjector) { + super(tracer, traceKeys, spanExtractor, spanInjector); } @Override public void postSend(Message message, MessageChannel channel, boolean sent) { - getTracer().close(SpanMessageHeaders.getSpanFromHeader(message)); + getTracer().close(getSpanFromHeader(message)); } @Override @@ -49,7 +54,9 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { : buildSpan(message); String name = getMessageChannelName(channel); Span span = startSpan(parentSpan, name, message); - return SpanMessageHeaders.addSpanHeaders(getTraceKeys(), message, span); + MessageBuilder messageBuilder = MessageBuilder.fromMessage(message); + getSpanInjector().inject(span, messageBuilder); + return messageBuilder.build(); } private Span startSpan(Span span, String name, Message message) { @@ -65,14 +72,25 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor { @Override public Message beforeHandle(Message message, MessageChannel channel, MessageHandler handler) { - getTracer().continueSpan(SpanMessageHeaders.getSpanFromHeader(message)); + getTracer().continueSpan(getSpanFromHeader(message)); return message; } @Override public void afterMessageHandled(Message message, MessageChannel channel, MessageHandler handler, Exception ex) { - getTracer().detach(SpanMessageHeaders.getSpanFromHeader(message)); + getTracer().detach(getSpanFromHeader(message)); + } + + private Span getSpanFromHeader(Message message) { + if (message == null) { + return null; + } + Object object = message.getHeaders().get(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/TraceSpringIntegrationAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/messaging/TraceSpringIntegrationAutoConfiguration.java index 532c5e301..2c4a50b0e 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 @@ -18,17 +18,22 @@ package org.springframework.cloud.sleuth.instrument.messaging; import java.util.Random; +import org.springframework.beans.factory.annotation.Qualifier; 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.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.SpanExtractor; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; import org.springframework.cloud.sleuth.TraceKeys; 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 Auto-configuration} @@ -50,8 +55,26 @@ public class TraceSpringIntegrationAutoConfiguration { @Bean @GlobalChannelInterceptor public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer, - TraceKeys traceKeys, Random random) { - return new TraceChannelInterceptor(tracer, traceKeys, random); + TraceKeys traceKeys, Random random, + @Qualifier("messagingSpanExtractor") SpanExtractor spanExtractor, + @Qualifier("messagingSpanInjector") SpanInjector spanInjector) { + return new TraceChannelInterceptor(tracer, traceKeys, spanExtractor, spanInjector); + } + + // TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work + @Bean + @Qualifier("messagingSpanExtractor") + @ConditionalOnProperty(value = "spring.sleuth.integration.injector.enabled", matchIfMissing = true) + public SpanExtractor messagingSpanExtractor(Random random) { + return new MessagingSpanExtractor(random); + } + + // TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work + @Bean + @Qualifier("messagingSpanInjector") + @ConditionalOnProperty(value = "spring.sleuth.integration.injector.enabled", matchIfMissing = true) + public SpanInjector messagingSpanInjector(TraceKeys traceKeys) { + return new MessagingSpanInjector(traceKeys); } } 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 decc7e6ab..1a70c7388 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 @@ -3,12 +3,24 @@ package org.springframework.cloud.sleuth.instrument.messaging.websocket; import java.util.Random; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.sleuth.Tracer; +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.MessagingSpanExtractor; +import org.springframework.cloud.sleuth.instrument.messaging.MessagingSpanInjector; import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterceptor; +import org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration; +import org.springframework.context.annotation.Bean; +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; @@ -24,19 +36,17 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry; * @see AbstractWebSocketMessageBrokerConfigurer */ @Component +@Configuration +@AutoConfigureAfter(TraceSpringIntegrationAutoConfiguration.class) @ConditionalOnClass(DelegatingWebSocketMessageBrokerConfiguration.class) @ConditionalOnBean(AbstractWebSocketMessageBrokerConfigurer.class) public class TraceWebSocketAutoConfiguration extends AbstractWebSocketMessageBrokerConfigurer { - @Autowired - private Tracer tracer; - - @Autowired - private TraceKeys traceKeys; - - @Autowired - private Random random; + @Autowired Tracer tracer; + @Autowired TraceKeys traceKeys; + @Autowired @Qualifier("stompMessagingSpanExtractor") SpanExtractor spanExtractor; + @Autowired @Qualifier("stompMessagingSpanInjector") SpanInjector spanInjector; @Override public void registerStompEndpoints(StompEndpointRegistry registry) { @@ -46,12 +56,28 @@ public class TraceWebSocketAutoConfiguration @Override public void configureClientOutboundChannel(ChannelRegistration registration) { registration.setInterceptors( - new TraceChannelInterceptor(this.tracer, this.traceKeys, this.random)); + new TraceChannelInterceptor(this.tracer, this.traceKeys, this.spanExtractor, this.spanInjector)); } @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.setInterceptors( - new TraceChannelInterceptor(this.tracer, this.traceKeys, this.random)); + new TraceChannelInterceptor(this.tracer, this.traceKeys, this.spanExtractor, this.spanInjector)); + } + + // TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work + @Bean + @Qualifier("stompMessagingSpanExtractor") + @ConditionalOnProperty(value = "spring.sleuth.integration.websocket.injector.enabled", matchIfMissing = true) + public SpanExtractor stompMessagingSpanExtractor(Random random) { + return new MessagingSpanExtractor(random); + } + + // TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work + @Bean + @Qualifier("stompMessagingSpanInjector") + @ConditionalOnProperty(value = "spring.sleuth.integration.websocket.injector.enabled", matchIfMissing = true) + public SpanInjector stompMessagingSpanInjector(TraceKeys traceKeys) { + return new MessagingSpanInjector(traceKeys); } } \ No newline at end of file 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 new file mode 100644 index 000000000..4bad6e395 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletRequestExtractor.java @@ -0,0 +1,84 @@ +/* + * 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.util.Random; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; + +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 String HTTP_COMPONENT = "http"; + + private final Random random; + private final Pattern skipPattern; + + private UrlPathHelper urlPathHelper = new UrlPathHelper(); + + public HttpServletRequestExtractor(Random random, Pattern skipPattern) { + this.random = random; + this.skipPattern = skipPattern; + } + + @Override + public Span joinTrace(HttpServletRequest carrier) { + String uri = this.urlPathHelper.getPathWithinApplication(carrier); + boolean skip = this.skipPattern.matcher(uri).matches() + || carrier.getHeader(Span.NOT_SAMPLED_NAME) != null; + long traceId = Span + .hexToId(carrier.getHeader(Span.TRACE_ID_NAME)); + long spanId = carrier.getHeader(Span.SPAN_ID_NAME) != null + ? Span.hexToId(carrier.getHeader(Span.SPAN_ID_NAME)) + : this.random.nextLong(); + + 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/HttpServletResponseInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletResponseInjector.java new file mode 100644 index 000000000..e0f7a92fe --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/HttpServletResponseInjector.java @@ -0,0 +1,44 @@ +/* + * 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.HttpServletResponse; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; + +/** + * Span injector that injects tracing info to {@link HttpServletResponse} + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class HttpServletResponseInjector implements SpanInjector { + + @Override + public void inject(Span span, HttpServletResponse carrier) { + if (span == null) { + return; + } + if (!carrier.containsHeader(Span.SPAN_ID_NAME)) { + carrier.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); + carrier.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); + } + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServletUtils.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServletUtils.java new file mode 100644 index 000000000..0fc050556 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/ServletUtils.java @@ -0,0 +1,43 @@ +/* + * 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 javax.servlet.http.HttpServletResponse; + +/** + * Utility class to retrieve data from Servlet + * HTTP request and response + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class ServletUtils { + static boolean hasHeader(HttpServletRequest request, HttpServletResponse response, + String name) { + String value = request.getHeader(name); + return value != null || response.getHeader(name) != null; + } + + static String getHeader(HttpServletRequest request, HttpServletResponse response, + String name) { + String value = request.getHeader(name); + return value != null ? value : response.getHeader(name); + } + +} 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 273f747a3..4450b2064 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 @@ -19,7 +19,6 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; -import java.util.Random; import java.util.regex.Pattern; import javax.servlet.FilterChain; @@ -28,7 +27,8 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Span.SpanBuilder; +import org.springframework.cloud.sleuth.SpanExtractor; +import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.SpanReporter; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; @@ -39,6 +39,7 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UrlPathHelper; +import static org.springframework.cloud.sleuth.instrument.web.ServletUtils.hasHeader; import static org.springframework.util.StringUtils.hasText; /** @@ -66,6 +67,8 @@ import static org.springframework.util.StringUtils.hasText; @Order(Ordered.HIGHEST_PRECEDENCE + 5) public class TraceFilter extends OncePerRequestFilter { + private static final String HTTP_COMPONENT = "http"; + protected static final String TRACE_REQUEST_ATTR = TraceFilter.class.getName() + ".TRACE"; @@ -75,34 +78,36 @@ public class TraceFilter extends OncePerRequestFilter { private final Tracer tracer; private final TraceKeys traceKeys; private final Pattern skipPattern; - private final Random random; private final SpanReporter spanReporter; + private final SpanExtractor spanExtractor; + private final SpanInjector spanInjector; private UrlPathHelper urlPathHelper = new UrlPathHelper(); - public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter) { - this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), new Random(), - spanReporter); + public TraceFilter(Tracer tracer, TraceKeys traceKeys, SpanReporter spanReporter, + SpanExtractor spanExtractor, SpanInjector spanInjector) { + this(tracer, traceKeys, Pattern.compile(DEFAULT_SKIP_PATTERN), spanReporter, + spanExtractor, spanInjector); } public TraceFilter(Tracer tracer, TraceKeys traceKeys, Pattern skipPattern, - Random random, SpanReporter spanReporter) { + SpanReporter spanReporter, SpanExtractor spanExtractor, + SpanInjector spanInjector) { this.tracer = tracer; this.traceKeys = traceKeys; this.skipPattern = skipPattern; - this.random = random; this.spanReporter = spanReporter; + this.spanExtractor = spanExtractor; + this.spanInjector = spanInjector; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String uri = this.urlPathHelper.getPathWithinApplication(request); boolean skip = this.skipPattern.matcher(uri).matches() - || getHeader(request, response, Span.NOT_SAMPLED_NAME) != null; - + || ServletUtils.getHeader(request, response, Span.NOT_SAMPLED_NAME) != null; Span spanFromRequest = (Span) request.getAttribute(TRACE_REQUEST_ATTR); if (spanFromRequest != null) { this.tracer.continueSpan(spanFromRequest); @@ -110,66 +115,15 @@ public class TraceFilter extends OncePerRequestFilter { else if (skip) { addToResponseIfNotPresent(response, Span.NOT_SAMPLED_NAME, ""); } - - String protocol = "http"; - String name = protocol + ":" + uri; - if (spanFromRequest == null) { - if (hasHeader(request, response, Span.TRACE_ID_NAME)) { - long traceId = Span - .hexToId(getHeader(request, response, Span.TRACE_ID_NAME)); - long spanId = hasHeader(request, response, Span.SPAN_ID_NAME) - ? Span.hexToId(getHeader(request, response, Span.SPAN_ID_NAME)) - : this.random.nextLong(); - - SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId); - if (skip) { - span.exportable(false); - } - String processId = getHeader(request, response, Span.PROCESS_ID_NAME); - String parentName = getHeader(request, response, Span.SPAN_NAME_NAME); - if (StringUtils.hasText(parentName)) { - span.name(parentName); - } - else { - span.name(protocol + ":" + "/parent" + uri); - } - if (StringUtils.hasText(processId)) { - span.processId(processId); - } - if (hasHeader(request, response, Span.PARENT_ID_NAME)) { - span.parent(Span - .hexToId(getHeader(request, response, Span.PARENT_ID_NAME))); - } - span.remote(true); - - Span parent = span.build(); - spanFromRequest = this.tracer.createSpan(name, parent); - if (parent != null && parent.isRemote()) { - parent.logEvent(Span.SERVER_RECV); - } - request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); - - } - else { - if (skip) { - spanFromRequest = this.tracer.createSpan(name, NeverSampler.INSTANCE); - } - else { - spanFromRequest = this.tracer.createSpan(name); - } - request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); - } - } - + String name = HTTP_COMPONENT + ":" + uri; + spanFromRequest = createSpan(request, response, skip, spanFromRequest, name); Throwable exception = null; try { - addRequestTags(request); // Add headers before filter chain in case one of the filters flushes the // response... - addResponseHeaders(response, spanFromRequest); + this.spanInjector.inject(spanFromRequest, response); filterChain.doFilter(request, response); - } catch (Throwable e) { exception = e; @@ -199,13 +153,33 @@ public class TraceFilter extends OncePerRequestFilter { } } - private void addResponseHeaders(HttpServletResponse response, Span span) { - if (span != null) { - if (!response.containsHeader(Span.SPAN_ID_NAME)) { - response.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - response.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); - } + /** + * Creates a span and appends it as the current request's attribute + */ + private Span createSpan(HttpServletRequest request, HttpServletResponse response, + boolean skip, Span spanFromRequest, String name) { + if (spanFromRequest != null) { + return spanFromRequest; } + if (hasHeader(request, response, Span.TRACE_ID_NAME)) { + Span parent = this.spanExtractor + .joinTrace(request); + spanFromRequest = this.tracer.createSpan(name, parent); + if (parent != null && parent.isRemote()) { + parent.logEvent(Span.SERVER_RECV); + } + request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); + } + else { + if (skip) { + spanFromRequest = this.tracer.createSpan(name, NeverSampler.INSTANCE); + } + else { + spanFromRequest = this.tracer.createSpan(name); + } + request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); + } + return spanFromRequest; } /** Override to add annotations not defined in {@link TraceKeys}. */ @@ -242,18 +216,6 @@ public class TraceFilter extends OncePerRequestFilter { } } - private boolean hasHeader(HttpServletRequest request, HttpServletResponse response, - String name) { - String value = request.getHeader(name); - return value != null || response.getHeader(name) != null; - } - - private String getHeader(HttpServletRequest request, HttpServletResponse response, - String name) { - String value = request.getHeader(name); - return value != null ? value : response.getHeader(name); - } - private void addToResponseIfNotPresent(HttpServletResponse response, String name, String value) { if (!hasText(response.getHeader(name))) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java index 55f033847..84ebd9332 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java @@ -24,7 +24,6 @@ import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import org.springframework.cloud.sleuth.SpanAccessor; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.async.TraceContinuingCallable; @@ -72,12 +71,10 @@ public class TraceWebAspect { .getLog(TraceWebAspect.class); private final Tracer tracer; - private final SpanAccessor accessor; private final SpanNamer spanNamer; - public TraceWebAspect(Tracer tracer, SpanAccessor accessor, SpanNamer spanNamer) { + public TraceWebAspect(Tracer tracer, SpanNamer spanNamer) { this.tracer = tracer; - this.accessor = accessor; this.spanNamer = spanNamer; } @@ -109,9 +106,9 @@ public class TraceWebAspect { @SuppressWarnings("unchecked") public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { Callable callable = (Callable) pjp.proceed(); - if (this.accessor.isTracing()) { + if (this.tracer.isTracing()) { log.debug("Wrapping callable with span [" - + this.accessor.getCurrentSpan() + "]"); + + this.tracer.getCurrentSpan() + "]"); return new TraceContinuingCallable<>(this.tracer, this.spanNamer, callable); } else { @@ -122,10 +119,10 @@ public class TraceWebAspect { @Around("anyControllerOrRestControllerWithPublicWebAsyncTaskMethod()") public Object wrapWebAsyncTaskWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable { final WebAsyncTask webAsyncTask = (WebAsyncTask) pjp.proceed(); - if (this.accessor.isTracing()) { + if (this.tracer.isTracing()) { try { log.debug("Wrapping callable with span [" - + this.accessor.getCurrentSpan() + "]"); + + this.tracer.getCurrentSpan() + "]"); Field callableField = WebAsyncTask.class.getDeclaredField("callable"); callableField.setAccessible(true); callableField.set(webAsyncTask, new TraceContinuingCallable<>(this.tracer, 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 9f9b4483e..d357cd34f 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 @@ -18,7 +18,10 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.Random; import java.util.regex.Pattern; -import org.springframework.beans.factory.annotation.Autowired; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.autoconfigure.ManagementServerProperties; import org.springframework.boot.autoconfigure.AutoConfigureAfter; @@ -29,7 +32,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClas import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.SpanExtractor; import org.springframework.cloud.sleuth.SpanNamer; import org.springframework.cloud.sleuth.SpanReporter; import org.springframework.cloud.sleuth.TraceKeys; @@ -64,26 +68,36 @@ public class TraceWebAutoConfiguration { @Value("${spring.sleuth.web.skipPattern:}") private String skipPattern; - @Autowired - private Tracer tracer; - - @Autowired - private SpanAccessor accessor; - - @Autowired - private TraceKeys traceKeys; - @Bean - public TraceWebAspect traceWebAspect(SpanNamer spanNamer) { - return new TraceWebAspect(this.tracer, this.accessor, spanNamer); + public TraceWebAspect traceWebAspect(Tracer tracer, SpanNamer spanNamer) { + return new TraceWebAspect(tracer, spanNamer); } @Bean @ConditionalOnMissingBean - public TraceFilter traceFilter(Random random, - SkipPatternProvider skipPatternProvider, SpanReporter spanReporter) { - return new TraceFilter(this.tracer, this.traceKeys, skipPatternProvider.skipPattern(), random, - spanReporter); + public TraceFilter traceFilter(Tracer tracer, TraceKeys traceKeys, + SkipPatternProvider skipPatternProvider, SpanReporter spanReporter, + @Qualifier("httpServletRequestSpanExtractor") SpanExtractor spanExtractor, + @Qualifier("httpServletResponseInjector") SpanInjector spanInjector) { + return new TraceFilter(tracer, traceKeys, skipPatternProvider.skipPattern(), + spanReporter, spanExtractor, spanInjector); + } + + // TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work + @Bean + @Qualifier("httpServletRequestSpanExtractor") + @ConditionalOnProperty(value = "spring.sleuth.web.extractor.enabled", matchIfMissing = true) + public SpanExtractor httpServletRequestSpanExtractor(Random random, + SkipPatternProvider skipPatternProvider) { + return new HttpServletRequestExtractor(random, skipPatternProvider.skipPattern()); + } + + // TODO: Qualifier + ConditionalOnProp cause autowiring generics doesn't work + @Bean + @Qualifier("httpServletResponseInjector") + @ConditionalOnProperty(value = "spring.sleuth.web.injector.enabled", matchIfMissing = true) + public SpanInjector httpServletResponseInjector() { + return new HttpServletResponseInjector(); } @Configuration 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 a7f9e5fb3..6ce1772d2 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 @@ -19,9 +19,9 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.net.URI; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.Tracer; import org.springframework.http.HttpRequest; -import org.springframework.util.StringUtils; /** * Abstraction over classes that interact with Http requests. Allows you @@ -34,38 +34,12 @@ import org.springframework.util.StringUtils; abstract class AbstractTraceHttpRequestInterceptor { protected final Tracer tracer; + protected final SpanInjector spanInjector; - protected AbstractTraceHttpRequestInterceptor(Tracer tracer) { + protected AbstractTraceHttpRequestInterceptor(Tracer tracer, + SpanInjector spanInjector) { this.tracer = tracer; - } - - - private void enrichWithTraceHeaders(HttpRequest request, Span span) { - setIdHeader(request, Span.TRACE_ID_NAME, span.getTraceId()); - setIdHeader(request, Span.SPAN_ID_NAME, span.getSpanId()); - if (!span.isExportable()) { - setHeader(request, Span.NOT_SAMPLED_NAME, "true"); - } - setHeader(request, Span.SPAN_NAME_NAME, span.getName()); - setIdHeader(request, Span.PARENT_ID_NAME, getParentId(span)); - setHeader(request, 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) && - this.tracer.isTracing()) { - request.getHeaders().add(name, value); - } - } - - private void setIdHeader(HttpRequest request, String name, Long value) { - if (value != null) { - setHeader(request, name, Span.idToHex(value)); - } + this.spanInjector = spanInjector; } /** @@ -76,7 +50,7 @@ abstract class AbstractTraceHttpRequestInterceptor { URI uri = request.getURI(); String spanName = uriScheme(uri) + ":" + uri.getPath(); Span newSpan = this.tracer.createSpan(spanName); - enrichWithTraceHeaders(request, newSpan); + this.spanInjector.inject(newSpan, request); newSpan.logEvent(Span.CLIENT_SEND); } 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 new file mode 100644 index 000000000..fcb7f6eac --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/HttpRequestInjector.java @@ -0,0 +1,60 @@ +/* + * 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()); + if (!span.isExportable()) { + setHeader(carrier, Span.NOT_SAMPLED_NAME, "true"); + } + 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/TraceAsyncClientHttpRequestFactoryWrapper.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceAsyncClientHttpRequestFactoryWrapper.java index 9366d8942..7158a8b47 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,9 +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.Tracer; 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; @@ -53,9 +55,9 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp * * @see org.springframework.web.client.AsyncRestTemplate#AsyncRestTemplate(AsyncClientHttpRequestFactory) */ - public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, + public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector spanInjector, AsyncClientHttpRequestFactory asyncDelegate) { - super(tracer); + super(tracer, spanInjector); this.asyncDelegate = asyncDelegate; this.syncDelegate = asyncDelegate instanceof ClientHttpRequestFactory ? (ClientHttpRequestFactory) asyncDelegate : defaultClientHttpRequestFactory(); @@ -65,16 +67,16 @@ public class TraceAsyncClientHttpRequestFactoryWrapper extends AbstractTraceHttp * Default implementation that creates a {@link SimpleClientHttpRequestFactory} that * has a wrapped task executor via the {@link TraceAsyncListenableTaskExecutor} */ - public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer) { - super(tracer); + public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector spanInjector) { + super(tracer, spanInjector); SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = defaultClientHttpRequestFactory(); this.asyncDelegate = simpleClientHttpRequestFactory; this.syncDelegate = simpleClientHttpRequestFactory; } - public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, + public TraceAsyncClientHttpRequestFactoryWrapper(Tracer tracer, SpanInjector spanInjector, AsyncClientHttpRequestFactory asyncDelegate, ClientHttpRequestFactory syncDelegate) { - super(tracer); + super(tracer, spanInjector); this.asyncDelegate = asyncDelegate; this.syncDelegate = syncDelegate; } 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 7d38edbd3..5c559d342 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,6 +18,7 @@ package org.springframework.cloud.sleuth.instrument.web.client; import java.io.IOException; +import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.Tracer; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; @@ -38,8 +39,8 @@ import org.springframework.http.client.ClientHttpResponse; public class TraceRestTemplateInterceptor extends AbstractTraceHttpRequestInterceptor implements ClientHttpRequestInterceptor { - public TraceRestTemplateInterceptor(Tracer tracer) { - super(tracer); + public TraceRestTemplateInterceptor(Tracer tracer, SpanInjector spanInjector) { + super(tracer, spanInjector); } @Override 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 180e3a21d..e82ff8f2a 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,10 +22,12 @@ 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.SpanAccessor; +import org.springframework.cloud.sleuth.SpanInjector; 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.http.HttpRequest; import org.springframework.http.client.AsyncClientHttpRequestFactory; import org.springframework.web.client.AsyncRestTemplate; @@ -47,8 +49,9 @@ public class TraceWebAsyncClientAutoConfiguration { @Bean @ConditionalOnMissingBean - public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory(Tracer tracer) { - return new TraceAsyncClientHttpRequestFactoryWrapper(tracer); + public AsyncClientHttpRequestFactory asyncClientHttpRequestFactory(Tracer tracer, + SpanInjector spanInjector) { + return new TraceAsyncClientHttpRequestFactoryWrapper(tracer, spanInjector); } @Bean 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 73f5aadcf..e86a74007 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 @@ -28,10 +28,12 @@ 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.Tracer; import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration; 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; @@ -52,8 +54,9 @@ public class TraceWebClientAutoConfiguration { @Bean @ConditionalOnMissingBean - public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer) { - return new TraceRestTemplateInterceptor(tracer); + public TraceRestTemplateInterceptor traceRestTemplateInterceptor(Tracer tracer, + SpanInjector spanInjector) { + return new TraceRestTemplateInterceptor(tracer, spanInjector); } @Bean @@ -62,6 +65,11 @@ public class TraceWebClientAutoConfiguration { return new RestTemplate(); } + @Bean + public SpanInjector httpRequestInjector() { + 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/FeignEventPublisher.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignEventPublisher.java index 04f62ddb4..143bd7dd3 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignEventPublisher.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignEventPublisher.java @@ -20,7 +20,7 @@ import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Tracer; /** - * Abstract class for publishing logging the client received event + * Abstract class for logging the client received event * * @author Marcin Grzejszczak * diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTemplateInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTemplateInjector.java new file mode 100644 index 000000000..f605a242b --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignRequestTemplateInjector.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.client.feign; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.util.StringUtils; + +import feign.RequestTemplate; + +/** + * Span injector that injects tracing info to {@link RequestTemplate} + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class FeignRequestTemplateInjector implements SpanInjector { + + @Override + public void inject(Span span, RequestTemplate carrier) { + if (span == null) { + setHeader(carrier, Span.NOT_SAMPLED_NAME, "true"); + return; + } + carrier.header(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); + setHeader(carrier, Span.SPAN_NAME_NAME, span.getName()); + setHeader(carrier, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); + if (!span.isExportable()) { + setHeader(carrier, Span.NOT_SAMPLED_NAME, "true"); + } + Long parentId = getParentId(span); + if (parentId != null) { + setHeader(carrier, Span.PARENT_ID_NAME, Span.idToHex(parentId)); + } + setHeader(carrier, Span.PROCESS_ID_NAME, span.getProcessId()); + } + + private Long getParentId(Span span) { + return !span.getParents().isEmpty() ? span.getParents().get(0) : null; + } + + protected void setHeader(RequestTemplate request, String name, String value) { + if (StringUtils.hasText(value) && !request.headers().containsKey(name)) { + request.header(name, value); + } + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignResponseHeadersHolder.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignResponseHeadersHolder.java new file mode 100644 index 000000000..b08a04e70 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignResponseHeadersHolder.java @@ -0,0 +1,35 @@ +/* + * 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.util.Collection; +import java.util.Map; + +/** + * Mutable holder for Feign Response headers + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class FeignResponseHeadersHolder { + final Map> responseHeaders; + + FeignResponseHeadersHolder(Map> responseHeaders) { + this.responseHeaders = responseHeaders; + } +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignResponseHeadersInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignResponseHeadersInjector.java new file mode 100644 index 000000000..3e9039799 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/FeignResponseHeadersInjector.java @@ -0,0 +1,71 @@ +/* + * 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.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.util.StringUtils; + +import static java.util.Collections.singletonList; + +/** + * Span injector that injects tracing info to + * {@link FeignResponseHeadersHolder#responseHeaders} + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class FeignResponseHeadersInjector implements SpanInjector { + + @Override + public void inject(Span span, FeignResponseHeadersHolder carrier) { + Map> headers = carrier.responseHeaders; + headersWithTraceId(span, headers); + } + + private Map> headersWithTraceId(Span span, + Map> headers) { + Map> newHeaders = new HashMap<>(); + newHeaders.putAll(headers); + if (span == null) { + setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "true"); + return newHeaders; + } + setHeader(newHeaders, Span.TRACE_ID_NAME, span.getTraceId()); + setHeader(newHeaders, Span.SPAN_ID_NAME, span.getSpanId()); + return newHeaders; + } + + void setHeader(Map> headers, String name, + String value) { + if (StringUtils.hasText(value) && !headers.containsKey(name)) { + headers.put(name, singletonList(value)); + } + } + + void setHeader(Map> headers, String name, + Long value) { + if (value != null) { + setHeader(headers, name, Span.idToHex(value)); + } + } +} 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 a9a80ccd2..69f1b32b0 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 @@ -50,7 +50,8 @@ final class TraceFeignClient extends FeignEventPublisher implements Client { @Override public Response execute(Request request, Request.Options options) throws IOException { Response response = this.delegate.execute(request, options); - if (response.body() == null || (response.body() != null && Objects.equals(response.body().length(), 0))) { + if (response.body() == null || (response.body() != null + && Objects.equals(response.body().length(), 0))) { finish(); } return response; diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java index c47299fec..5d182202d 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignClientAutoConfiguration.java @@ -18,9 +18,6 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.io.IOException; import java.lang.reflect.Type; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -32,7 +29,7 @@ import org.springframework.boot.autoconfigure.web.HttpMessageConverters; import org.springframework.cloud.netflix.feign.FeignAutoConfiguration; import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder; import org.springframework.cloud.netflix.feign.support.SpringDecoder; -import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration; @@ -40,7 +37,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Scope; -import org.springframework.util.StringUtils; import com.netflix.hystrix.HystrixCommand; @@ -48,11 +44,10 @@ import feign.Client; import feign.Feign; import feign.FeignException; import feign.RequestInterceptor; +import feign.RequestTemplate; import feign.Response; import feign.codec.Decoder; -import static java.util.Collections.singletonList; - /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} * enables span information propagation when using Feign. @@ -71,34 +66,33 @@ public class TraceFeignClientAutoConfiguration { @Autowired private ObjectFactory messageConverters; - @Autowired - private Tracer tracer; - - private final FeignRequestContext feignRequestContext = FeignRequestContext.getInstance(); - @Bean @Scope("prototype") @ConditionalOnClass(HystrixCommand.class) @ConditionalOnProperty(name = "feign.hystrix.enabled", matchIfMissing = true) - public Feign.Builder feignHystrixBuilder(Tracer tracer, TraceKeys traceKeys) { + Feign.Builder feignHystrixBuilder(Tracer tracer, TraceKeys traceKeys) { return SleuthFeignBuilder.builder(tracer); } @Bean @ConditionalOnProperty(name = "spring.sleuth.feign.processor.enabled", matchIfMissing = true) - public FeignBeanPostProcessor feignBeanPostProcessor(Tracer tracer) { + FeignBeanPostProcessor feignBeanPostProcessor(Tracer tracer) { return new FeignBeanPostProcessor(tracer); } @Bean @Primary - public Decoder feignDecoder(final Tracer tracer) { + Decoder feignDecoder(final Tracer tracer) { return new TraceFeignDecoder(tracer, new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)) { @Override public Object decode(Response response, Type type) throws IOException, FeignException { + FeignRequestContext feignRequestContext = FeignRequestContext.getInstance(); + FeignResponseHeadersHolder feignResponseHeadersHolder = + new FeignResponseHeadersHolder(response.headers()); + feignResponseHeadersInjector().inject(feignRequestContext.getCurrentSpan(), feignResponseHeadersHolder); return super.decode(Response.create(response.status(), - response.reason(), headersWithTraceId(response.headers()), + response.reason(), feignResponseHeadersHolder.responseHeaders, response.body()), type); } }); @@ -110,36 +104,14 @@ public class TraceFeignClientAutoConfiguration { */ @Bean public RequestInterceptor traceIdRequestInterceptor(Tracer tracer) { - return new TraceFeignRequestInterceptor(tracer); + return new TraceFeignRequestInterceptor(tracer, feignRequestTemplateInjector()); } - private Map> headersWithTraceId( - Map> headers) { - Map> newHeaders = new HashMap<>(); - newHeaders.putAll(headers); - Span span = this.feignRequestContext.getCurrentSpan(); - if (span == null) { - setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "true"); - return newHeaders; - } - setHeader(newHeaders, Span.TRACE_ID_NAME, span.getTraceId()); - setHeader(newHeaders, Span.SPAN_ID_NAME, span.getSpanId()); - return newHeaders; + private SpanInjector feignRequestTemplateInjector() { + return new FeignRequestTemplateInjector(); } - public void setHeader(Map> headers, String name, - String value) { - if (StringUtils.hasText(value) && !headers.containsKey(name) - && this.tracer.isTracing()) { - headers.put(name, singletonList(value)); - } + private SpanInjector feignResponseHeadersInjector() { + return new FeignResponseHeadersInjector(); } - - public void setHeader(Map> headers, String name, - Long value) { - if (value != null) { - setHeader(headers, name, Span.idToHex(value)); - } - } - } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignRequestInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignRequestInterceptor.java index d3a7447a6..b48fa9f2e 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignRequestInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/feign/TraceFeignRequestInterceptor.java @@ -19,8 +19,8 @@ package org.springframework.cloud.sleuth.instrument.web.client.feign; import java.net.URI; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.Tracer; -import org.springframework.util.StringUtils; import feign.RequestInterceptor; import feign.RequestTemplate; @@ -36,31 +36,20 @@ import feign.RequestTemplate; final class TraceFeignRequestInterceptor implements RequestInterceptor { private final Tracer tracer; + private final SpanInjector spanInjector; private final FeignRequestContext feignRequestContext = FeignRequestContext.getInstance(); - TraceFeignRequestInterceptor(Tracer tracer) { + TraceFeignRequestInterceptor(Tracer tracer, + SpanInjector spanInjector) { this.tracer = tracer; + this.spanInjector = spanInjector; } @Override public void apply(RequestTemplate template) { String spanName = getSpanName(template); Span span = getSpan(spanName); - if (span == null) { - setHeader(template, Span.NOT_SAMPLED_NAME, "true"); - return; - } - template.header(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); - setHeader(template, Span.SPAN_NAME_NAME, span.getName()); - setHeader(template, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - if (!span.isExportable()) { - setHeader(template, Span.NOT_SAMPLED_NAME, "true"); - } - Long parentId = getParentId(span); - if (parentId != null) { - setHeader(template, Span.PARENT_ID_NAME, Span.idToHex(parentId)); - } - setHeader(template, Span.PROCESS_ID_NAME, span.getProcessId()); + this.spanInjector.inject(span, template); span.logEvent(Span.CLIENT_SEND); } @@ -90,15 +79,4 @@ final class TraceFeignRequestInterceptor implements RequestInterceptor { return uri.getScheme() == null ? "http" : uri.getScheme(); } - private Long getParentId(Span span) { - return !span.getParents().isEmpty() ? span.getParents().get(0) : null; - } - - protected void setHeader(RequestTemplate request, String name, String value) { - if (StringUtils.hasText(value) && !request.headers().containsKey(name) - && this.tracer.isTracing()) { - request.header(name, value); - } - } - } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestBuilderContextInjector.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestBuilderContextInjector.java new file mode 100644 index 000000000..04583249c --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestBuilderContextInjector.java @@ -0,0 +1,61 @@ +/* + * Copyright 2013-2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.instrument.zuul; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; + +import com.netflix.client.http.HttpRequest; +import com.netflix.client.http.HttpRequest.Builder; + +/** + * Span injector that injects tracing info to {@link Builder} + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class RequestBuilderContextInjector implements SpanInjector { + + @Override + public void inject(Span span, Builder carrier) { + if (span == null) { + setHeader(carrier, Span.NOT_SAMPLED_NAME, "true"); + return; + } + 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()); + if (getParentId(span) != null) { + setHeader(carrier, Span.PARENT_ID_NAME, + Span.idToHex(getParentId(span))); + } + setHeader(carrier, Span.PROCESS_ID_NAME, + span.getProcessId()); + } + + private Long getParentId(Span span) { + return !span.getParents().isEmpty() + ? span.getParents().get(0) : null; + } + + public void setHeader(HttpRequest.Builder builder, String name, String value) { + if (value != null) { + builder.header(name, 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 new file mode 100644 index 000000000..a41be18fb --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/RequestContextInjector.java @@ -0,0 +1,68 @@ +/* + * 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 org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.util.StringUtils; + +import com.netflix.zuul.context.RequestContext; + +/** + * Span injector that injects tracing info to {@link RequestContext} + * + * @author Marcin Grzejszczak + * + * @since 1.0.0 + */ +class RequestContextInjector implements SpanInjector { + + @Override + public void inject(Span span, RequestContext carrier) { + Map requestHeaders = carrier.getZuulRequestHeaders(); + if (span == null) { + setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true"); + 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()); + if (!span.isExportable()) { + setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true"); + } + 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/TracePreZuulFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java index 70147c445..4624401ca 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,12 +16,9 @@ package org.springframework.cloud.sleuth.instrument.zuul; -import java.util.Map; - import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanAccessor; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.Tracer; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; @@ -36,10 +33,12 @@ import com.netflix.zuul.context.RequestContext; */ public class TracePreZuulFilter extends ZuulFilter { - private final SpanAccessor accessor; + private final Tracer tracer; + private final SpanInjector spanInjector; - public TracePreZuulFilter(SpanAccessor accessor) { - this.accessor = accessor; + public TracePreZuulFilter(Tracer tracer, SpanInjector spanInjector) { + this.tracer = tracer; + this.spanInjector = spanInjector; } @Override @@ -50,49 +49,17 @@ public class TracePreZuulFilter extends ZuulFilter { @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); - Map requestHeaders = ctx.getZuulRequestHeaders(); Span span = getCurrentSpan(); - if (span == null) { - setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true"); - return null; - } - try { - setHeader(requestHeaders, Span.SPAN_ID_NAME, span.getSpanId()); - setHeader(requestHeaders, Span.TRACE_ID_NAME, span.getTraceId()); - setHeader(requestHeaders, Span.SPAN_NAME_NAME, span.getName()); - if (!span.isExportable()) { - setHeader(requestHeaders, Span.NOT_SAMPLED_NAME, "true"); - } - setHeader(requestHeaders, Span.PARENT_ID_NAME, getParentId(span)); - setHeader(requestHeaders, Span.PROCESS_ID_NAME, span.getProcessId()); - // TODO: the client sent event should come from the client not the filter! - span.logEvent(Span.CLIENT_SEND); - } - catch (Exception ex) { - ReflectionUtils.rethrowRuntimeException(ex); - } + this.spanInjector.inject(span, ctx); + // TODO: the client sent event should come from the client not the filter! + span.logEvent(Span.CLIENT_SEND); return null; } private Span getCurrentSpan() { - return this.accessor.getCurrentSpan(); + return this.tracer.getCurrentSpan(); } - 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) && this.accessor.isTracing()) { - request.put(name, value); - } - } - - public void setHeader(Map request, String name, Long value) { - if (value != null) { - setHeader(request, name, Span.idToHex(value)); - } - } @Override public String filterType() { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java index a52d55eda..3d9effb55 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java @@ -26,7 +26,8 @@ import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonComm import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory; import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.Tracer; import org.springframework.util.MultiValueMap; import com.netflix.client.http.HttpRequest; @@ -43,12 +44,14 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand private static final Log log = LogFactory.getLog(TraceRestClientRibbonCommandFactory.class); - private final SpanAccessor accessor; + private final Tracer tracer; + private final SpanInjector spanInjector; public TraceRestClientRibbonCommandFactory(SpringClientFactory clientFactory, - SpanAccessor accessor) { + Tracer tracer, SpanInjector spanInjector) { super(clientFactory); - this.accessor = accessor; + this.tracer = tracer; + this.spanInjector = spanInjector; } @Override @@ -60,7 +63,7 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand return new TraceRestClientRibbonCommand(context.getServiceId(), restClient, getVerb(context.getVerb()), context.getUri(), context.getRetryable(), context.getHeaders(), context.getParams(), context.getRequestEntity(), - this.accessor); + this.tracer, this.spanInjector); } catch (URISyntaxException e) { log.error("Exception occurred while trying to create the TraceRestClientRibbonCommand", e); @@ -70,52 +73,31 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand class TraceRestClientRibbonCommand extends RestClientRibbonCommand { - private final SpanAccessor accessor; + private final Tracer tracer; + private final SpanInjector spanInjector; @SuppressWarnings("deprecation") public TraceRestClientRibbonCommand(String commandKey, RestClient restClient, HttpRequest.Verb verb, String uri, Boolean retryable, MultiValueMap headers, MultiValueMap params, InputStream requestEntity, - SpanAccessor accessor) + Tracer tracer, SpanInjector spanInjector) throws URISyntaxException { super(commandKey, restClient, verb, uri, retryable, headers, params, requestEntity); - this.accessor = accessor; + this.tracer = tracer; + this.spanInjector = spanInjector; } @Override protected void customizeRequest(HttpRequest.Builder requestBuilder) { Span span = getCurrentSpan(); - if (span == null) { - setHeader(requestBuilder, Span.NOT_SAMPLED_NAME, "true"); - return; - } - setHeader(requestBuilder, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId())); - setHeader(requestBuilder, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId())); - setHeader(requestBuilder, Span.SPAN_NAME_NAME, span.getName()); - if (getParentId(span) != null) { - setHeader(requestBuilder, Span.PARENT_ID_NAME, - Span.idToHex(getParentId(span))); - } - setHeader(requestBuilder, Span.PROCESS_ID_NAME, - span.getProcessId()); + this.spanInjector.inject(span, requestBuilder); span.logEvent(Span.CLIENT_SEND); } - private Long getParentId(Span span) { - return !span.getParents().isEmpty() - ? span.getParents().get(0) : null; - } - - public void setHeader(HttpRequest.Builder builder, String name, String value) { - if (value != null && this.accessor.isTracing()) { - builder.header(name, value); - } - } - private Span getCurrentSpan() { - return this.accessor.getCurrentSpan(); + return this.tracer.getCurrentSpan(); } } 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 e03dd7b9c..fdf8f70f7 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 @@ -23,12 +23,15 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.SpanInjector; 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 com.netflix.client.http.HttpRequest; import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration Auto-configuration} @@ -48,13 +51,14 @@ public class TraceZuulAutoConfiguration { @Bean @ConditionalOnMissingBean - public TracePreZuulFilter tracePreZuulFilter(SpanAccessor accessor) { - return new TracePreZuulFilter(accessor); + public TracePreZuulFilter tracePreZuulFilter(Tracer tracer, SpanInjector spanInjector) { + return new TracePreZuulFilter(tracer, spanInjector); } @Bean - public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory, SpanAccessor accessor) { - return new TraceRestClientRibbonCommandFactory(factory, accessor); + public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory, + Tracer tracer, SpanInjector spanInjector) { + return new TraceRestClientRibbonCommandFactory(factory, tracer, spanInjector); } @Bean @@ -63,4 +67,14 @@ public class TraceZuulAutoConfiguration { return new TracePostZuulFilter(accessor); } + @Bean + SpanInjector requestContextInjector() { + return new RequestContextInjector(); + } + + @Bean + SpanInjector requestBuilderContextInjector() { + return new RequestBuilderContextInjector(); + } + } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java index f03f1881b..b30f749c1 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java @@ -48,9 +48,8 @@ public class DefaultTracer implements Tracer { private final SpanReporter spanReporter; - public DefaultTracer(Sampler defaultSampler, Random random, - SpanNamer spanNamer, SpanLogger spanLogger, - SpanReporter spanReporter) { + public DefaultTracer(Sampler defaultSampler, Random random, SpanNamer spanNamer, + SpanLogger spanLogger, SpanReporter spanReporter) { this.defaultSampler = defaultSampler; this.random = random; this.spanNamer = spanNamer; @@ -227,5 +226,4 @@ public class DefaultTracer implements Tracer { } return runnable; } - } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java index 56dd4108b..6210ea484 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/documentation/SpringCloudSleuthDocTests.java @@ -154,24 +154,24 @@ public class SpringCloudSleuthDocTests { assertThat(initialSpan.logs()).extracting("event").doesNotContain("taxCalculated"); executorService.submit(() -> { - // tag::manual_span_continuation[] - // let's assume that we're in a thread Y and we've received - // the `initialSpan` from thread X - Span continuedSpan = this.tracer.continueSpan(initialSpan); - try { - // ... - // You can tag a span - this.tracer.addTag("taxValue", taxValue); - // ... - // You can log an event on a span - continuedSpan.logEvent("taxCalculated"); - } finally { - // Once done remember to detach the span. That way you'll - // safely remove it from the current thread without closing it - this.tracer.detach(continuedSpan); - } - // end::manual_span_continuation[] - } + // tag::manual_span_continuation[] + // let's assume that we're in a thread Y and we've received + // the `initialSpan` from thread X + Span continuedSpan = this.tracer.continueSpan(initialSpan); + try { + // ... + // You can tag a span + this.tracer.addTag("taxValue", taxValue); + // ... + // You can log an event on a span + continuedSpan.logEvent("taxCalculated"); + } finally { + // Once done remember to detach the span. That way you'll + // safely remove it from the current thread without closing it + this.tracer.detach(continuedSpan); + } + // end::manual_span_continuation[] + } ).get(); this.tracer.close(initialSpan); @@ -191,26 +191,26 @@ public class SpringCloudSleuthDocTests { assertThat(initialSpan.logs()).extracting("event").doesNotContain("commissionCalculated"); executorService.submit(() -> { - // tag::manual_span_joining[] - // let's assume that we're in a thread Y and we've received - // the `initialSpan` from thread X. `initialSpan` will be the parent - // of the `newSpan` - Span newSpan = this.tracer.createSpan("calculateCommission", initialSpan); - try { - // ... - // You can tag a span - this.tracer.addTag("commissionValue", commissionValue); - // ... - // You can log an event on a span - newSpan.logEvent("commissionCalculated"); - } finally { - // Once done remember to close the span. This will allow collecting - // the span to send it to Zipkin. The tags and events set on the - // newSpan will not be present on the parent - this.tracer.close(newSpan); - } - // end::manual_span_joining[] - } + // tag::manual_span_joining[] + // let's assume that we're in a thread Y and we've received + // the `initialSpan` from thread X. `initialSpan` will be the parent + // of the `newSpan` + Span newSpan = this.tracer.createSpan("calculateCommission", initialSpan); + try { + // ... + // You can tag a span + this.tracer.addTag("commissionValue", commissionValue); + // ... + // You can log an event on a span + newSpan.logEvent("commissionCalculated"); + } finally { + // Once done remember to close the span. This will allow collecting + // the span to send it to Zipkin. The tags and events set on the + // newSpan will not be present on the parent + this.tracer.close(newSpan); + } + // end::manual_span_joining[] + } ).get(); this.tracer.close(initialSpan); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java index 1475f2594..0b91f7482 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java @@ -9,6 +9,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.sleuth.instrument.messaging.TraceSpringIntegrationAutoConfiguration; +import org.springframework.cloud.sleuth.instrument.messaging.websocket.TraceWebSocketAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @@ -16,6 +17,7 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy; @Retention(RetentionPolicy.RUNTIME) @EnableAutoConfiguration(exclude = { LoadBalancerAutoConfiguration.class, JmxAutoConfiguration.class, TraceSpringIntegrationAutoConfiguration.class, + TraceWebSocketAutoConfiguration.class, LoadBalancerAutoConfiguration.class }) @EnableAspectJAutoProxy(proxyTargetClass = true) @Configuration diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SpanMessageHeadersTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java similarity index 72% rename from spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SpanMessageHeadersTests.java rename to spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java index f0e6a3de5..e5bd46b68 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/SpanMessageHeadersTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/messaging/MessagingSpanInjectorTests.java @@ -32,26 +32,30 @@ import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.asser * @author Dave Syer * */ -public class SpanMessageHeadersTests { +public class MessagingSpanInjectorTests { private TraceKeys traceKeys = new TraceKeys(); + private MessagingSpanInjector messagingSpanInjector = new MessagingSpanInjector(this.traceKeys); @Test public void spanHeadersAdded() { Span span = Span.builder().name("http:foo").spanId(1L).traceId(2L).build(); Message message = new GenericMessage<>("Hello World"); - message = SpanMessageHeaders.addSpanHeaders(this.traceKeys, message, span); - assertThat(message.getHeaders()).containsKey(Span.SPAN_ID_NAME); + MessageBuilder messageBuilder = MessageBuilder.fromMessage(message); + + this.messagingSpanInjector.inject(span, messageBuilder); + + assertThat(messageBuilder.build().getHeaders()).containsKey(Span.SPAN_ID_NAME); } @Test public void shouldNotOverrideSpanTags() { Span span = spanWithStringPayloadType(); - Message message = messageWithIntegerPayloadType(); + MessageBuilder messageBuilder = messageWithIntegerPayloadType(); - message = SpanMessageHeaders.addSpanHeaders(this.traceKeys, message, span); + this.messagingSpanInjector.inject(span, messageBuilder); - assertThat(message.getHeaders()) + assertThat(messageBuilder.build().getHeaders()) .containsKeys(Span.SPAN_ID_NAME, "message/payload-type"); assertThat(span).hasATag("message/payload-type", "java.lang.String"); } @@ -62,18 +66,22 @@ public class SpanMessageHeadersTests { return span; } - private Message messageWithIntegerPayloadType() { + private MessageBuilder messageWithIntegerPayloadType() { MessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); accessor.setHeader("message/payload-type", "java.lang.Integer"); - return MessageBuilder.createMessage("Hello World", accessor.getMessageHeaders()); + return MessageBuilder.withPayload("Hello World").setHeaders(accessor); } @Test public void nativeSpanHeadersAdded() { Span span = Span.builder().name("http:foo").spanId(1L).traceId(2L).build(); MessageHeaderAccessor accessor = SimpMessageHeaderAccessor.create(); - Message message = MessageBuilder.createMessage("Hello World", accessor.getMessageHeaders()); - message = SpanMessageHeaders.addSpanHeaders(this.traceKeys, message, span); + Message messageToBuild = MessageBuilder.createMessage("Hello World", accessor.getMessageHeaders()); + MessageBuilder messageBuilder = MessageBuilder.fromMessage(messageToBuild); + + this.messagingSpanInjector.inject(span, messageBuilder); + + Message message = messageBuilder.build(); assertThat(message.getHeaders()) .containsKey(NativeMessageHeaderAccessor.NATIVE_HEADERS); MessageHeaderAccessor natives = NativeMessageHeaderAccessor diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java index 1c360ee55..d6799e179 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java @@ -76,7 +76,7 @@ public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegra @Override protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) { mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys, - new NoOpSpanReporter())); + new NoOpSpanReporter(), this.spanExtractor, this.spanInjector)); } private MvcResult whenSentPingWithTraceIdAndNotSampling(Long traceId) 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 77458441d..3436805f3 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 @@ -17,6 +17,7 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.Random; +import java.util.regex.Pattern; import org.junit.Before; import org.junit.Test; @@ -70,7 +71,9 @@ public class TraceFilterMockChainIntegrationTests { @Test public void startsNewTrace() throws Exception { - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter()); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(), + new HttpServletRequestExtractor(new Random(), Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)), + new HttpServletResponseInjector()); filter.doFilter(this.request, this.response, this.filterChain); assertNull(TestSpanContextHolder.getCurrentSpan()); } @@ -80,7 +83,9 @@ public class TraceFilterMockChainIntegrationTests { Random generator = new Random(); 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()); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, new NoOpSpanReporter(), + new HttpServletRequestExtractor(new Random(), Pattern.compile(TraceFilter.DEFAULT_SKIP_PATTERN)), + new HttpServletResponseInjector()); 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 188b1bce0..c8e52135e 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 @@ -17,6 +17,10 @@ package org.springframework.cloud.sleuth.instrument.web; import java.util.Random; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.junit.Before; import org.junit.Test; @@ -24,9 +28,11 @@ import org.mockito.Mock; import org.springframework.cloud.sleuth.DefaultSpanNamer; import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.SpanExtractor; +import org.springframework.cloud.sleuth.SpanInjector; import org.springframework.cloud.sleuth.SpanReporter; -import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.TraceKeys; +import org.springframework.cloud.sleuth.Tracer; import org.springframework.cloud.sleuth.log.SpanLogger; import org.springframework.cloud.sleuth.sampler.AlwaysSampler; import org.springframework.cloud.sleuth.sampler.NeverSampler; @@ -55,6 +61,9 @@ public class TraceFilterTests { @Mock SpanLogger spanLogger; @Mock SpanReporter spanReporter; + SpanExtractor spanExtractor = new HttpServletRequestExtractor(new Random(), Pattern + .compile(TraceFilter.DEFAULT_SKIP_PATTERN)); + SpanInjector spanInjector = new HttpServletResponseInjector(); private Tracer tracer; private TraceKeys traceKeys = new TraceKeys(); @@ -91,7 +100,8 @@ public class TraceFilterTests { @Test public void notTraced() throws Exception { this.sampler = NeverSampler.INSTANCE; - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); this.request = get("/favicon.ico").accept(MediaType.ALL) .buildRequest(new MockServletContext()); @@ -104,7 +114,8 @@ public class TraceFilterTests { @Test public void startsNewTrace() throws Exception { - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); filter.doFilter(this.request, this.response, this.filterChain); verifyHttpTags(); assertNull(TestSpanContextHolder.getCurrentSpan()); @@ -117,7 +128,8 @@ public class TraceFilterTests { .header(Span.TRACE_ID_NAME, Span.idToHex(2L)) .header(Span.PARENT_ID_NAME, Span.idToHex(3L)) .buildRequest(new MockServletContext()); - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); filter.doFilter(this.request, this.response, this.filterChain); @@ -133,7 +145,8 @@ public class TraceFilterTests { // It should have been removed from the thread local context so simulate that TestSpanContextHolder.removeCurrentSpan(); - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); filter.doFilter(this.request, this.response, this.filterChain); verifyHttpTags(); @@ -146,7 +159,8 @@ public class TraceFilterTests { this.request = builder().header(Span.SPAN_ID_NAME, 10L) .header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext()); - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); filter.doFilter(this.request, this.response, this.filterChain); verifyHttpTags(); @@ -160,7 +174,8 @@ public class TraceFilterTests { .header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext()); this.traceKeys.getHttp().getHeaders().add("x-foo"); - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); this.request.addHeader("X-Foo", "bar"); filter.doFilter(this.request, this.response, this.filterChain); @@ -175,7 +190,8 @@ public class TraceFilterTests { .header(Span.TRACE_ID_NAME, 20L).buildRequest(new MockServletContext()); this.traceKeys.getHttp().getHeaders().add("x-foo"); - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); this.request.addHeader("X-Foo", "bar"); this.request.addHeader("X-Foo", "spam"); filter.doFilter(this.request, this.response, this.filterChain); @@ -187,7 +203,8 @@ public class TraceFilterTests { @Test public void catchesException() throws Exception { - TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, spanReporter); + TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys, this.spanReporter, + this.spanExtractor, this.spanInjector); this.filterChain = new MockFilterChain() { @Override public void doFilter(javax.servlet.ServletRequest request, 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 8992cb46b..7764623e8 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 @@ -59,7 +59,7 @@ public class TraceRestTemplateInterceptorIntegrationTests { this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter()); this.template.setInterceptors(Arrays.asList( - new TraceRestTemplateInterceptor(this.tracer))); + new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector()))); 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 d105dcc9f..5772ba3f5 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 @@ -65,7 +65,7 @@ public class TraceRestTemplateInterceptorTests { this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), new DefaultSpanNamer(), new NoOpSpanLogger(), new NoOpSpanReporter()); this.template.setInterceptors(Arrays.asList( - new TraceRestTemplateInterceptor(this.tracer))); + new TraceRestTemplateInterceptor(this.tracer, new HttpRequestInjector()))); TestSpanContextHolder.removeCurrentSpan(); } 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 169af540a..873be11bb 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,7 +1,14 @@ package org.springframework.cloud.sleuth.instrument.web.common; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; +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.util.ExceptionUtils; import org.springframework.context.ApplicationContext; import org.springframework.test.context.web.WebAppConfiguration; @@ -23,9 +30,12 @@ import org.springframework.web.context.WebApplicationContext; @WebAppConfiguration public abstract class AbstractMvcIntegrationTest { - @Autowired - protected WebApplicationContext webApplicationContext; + @Autowired protected WebApplicationContext webApplicationContext; protected MockMvc mockMvc; + @Autowired protected Tracer tracer; + @Autowired protected TraceKeys traceKeys; + @Autowired protected SpanExtractor spanExtractor; + @Autowired protected SpanInjector spanInjector; @Before public void setup() { diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcWiremockIntegrationTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcWiremockIntegrationTest.java index 52ecf4d53..93836e1ab 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcWiremockIntegrationTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/common/AbstractMvcWiremockIntegrationTest.java @@ -3,10 +3,7 @@ package org.springframework.cloud.sleuth.instrument.web.common; import org.junit.Before; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.sleuth.NoOpSpanReporter; -import org.springframework.cloud.sleuth.Tracer; -import org.springframework.cloud.sleuth.TraceKeys; import org.springframework.cloud.sleuth.instrument.web.TraceFilter; -import org.springframework.cloud.sleuth.log.NoOpSpanLogger; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder; @@ -30,8 +27,7 @@ public abstract class AbstractMvcWiremockIntegrationTest extends AbstractMvcInte protected WireMock wireMock; @Autowired protected HttpMockServer httpMockServer; - @Autowired protected Tracer tracer; - @Autowired protected TraceKeys traceKeys; + @Override @Before @@ -56,6 +52,6 @@ public abstract class AbstractMvcWiremockIntegrationTest extends AbstractMvcInte @Override protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) { mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys, - new NoOpSpanReporter())); + new NoOpSpanReporter(), this.spanExtractor, this.spanInjector)); } } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java index 69a4b4c7c..537a0a56f 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/multiple/MultipleHopsIntegrationTests.java @@ -32,13 +32,13 @@ public class MultipleHopsIntegrationTests extends AbstractMvcIntegrationTest { @Autowired Tracer tracer; @Autowired TraceKeys traceKeys; + @Autowired TraceFilter traceFilter; @Autowired ArrayListSpanAccumulator arrayListSpanAccumulator; @Autowired SpanReporter spanReporter; @Override protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) { - mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys, - this.spanReporter)); + mockMvcBuilder.addFilters(this.traceFilter); } @Test 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 ebbcca0ca..0a94387f5 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 @@ -46,7 +46,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); + private TracePreZuulFilter filter = new TracePreZuulFilter(this.tracer, new RequestContextInjector()); @After @Before diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactoryTest.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactoryTest.java index f19d76dcb..cc2f6983a 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactoryTest.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactoryTest.java @@ -25,7 +25,8 @@ import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommand; import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext; import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.SpanAccessor; +import org.springframework.cloud.sleuth.SpanInjector; +import org.springframework.cloud.sleuth.Tracer; import com.netflix.client.http.HttpRequest; import com.netflix.niws.client.http.RestClient; @@ -41,15 +42,16 @@ import static org.mockito.Matchers.anyString; @RunWith(MockitoJUnitRunner.class) public class TraceRestClientRibbonCommandFactoryTest { - @Mock SpanAccessor accessor; + @Mock Tracer tracer; @Mock SpringClientFactory springClientFactory; + SpanInjector spanInjector = new RequestBuilderContextInjector(); TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory; @Before @SuppressWarnings({"deprecation", "unchecked"}) public void setup() { this.traceRestClientRibbonCommandFactory = new TraceRestClientRibbonCommandFactory( - this.springClientFactory, this.accessor); + this.springClientFactory, this.tracer, this.spanInjector); given(this.springClientFactory.getClient(anyString(), any(Class.class))).willReturn(new RestClient()); Span span = Span.builder() .name("name") @@ -58,13 +60,14 @@ public class TraceRestClientRibbonCommandFactoryTest { .parent(3L) .processId("processId") .build(); - given(this.accessor.getCurrentSpan()).willReturn(span); - given(this.accessor.isTracing()).willReturn(true); + given(this.tracer.getCurrentSpan()).willReturn(span); + given(this.tracer.isTracing()).willReturn(true); } @Test public void should_wrap_ribbon_command_in_a_sleuth_representation() throws Exception { - RestClientRibbonCommand restClientRibbonCommand = this.traceRestClientRibbonCommandFactory.create(ribbonCommandContext()); + RestClientRibbonCommand restClientRibbonCommand = + this.traceRestClientRibbonCommandFactory.create(ribbonCommandContext()); then(restClientRibbonCommand) .isInstanceOf(TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand.class); @@ -73,7 +76,8 @@ public class TraceRestClientRibbonCommandFactoryTest { @Test public void should_attach_trace_headers_to_the_sent_request() throws Exception { RestClientRibbonCommand restClientRibbonCommand = this.traceRestClientRibbonCommandFactory.create(ribbonCommandContext()); - TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand traceRestClientRibbonCommand = (TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand) restClientRibbonCommand; + TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand traceRestClientRibbonCommand = + (TraceRestClientRibbonCommandFactory.TraceRestClientRibbonCommand) restClientRibbonCommand; HttpRequest.Builder builder = new HttpRequest.Builder(); traceRestClientRibbonCommand.customizeRequest(builder);