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
+ *
+ *
+ *
{@code X-B3-TraceId} 64 encoded bits
+ *
{@code X-B3-SpanId} 64 encoded bits
+ *
{@code X-B3-ParentSpanId} 64 encoded bits
+ *
{@code X-B3-Sampled} Boolean (either “1” or “0”)
+ *
+ *
+ * @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