diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java index 3a361e350..6fc00253f 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/MilliSpan.java @@ -20,50 +20,50 @@ public class MilliSpan implements Span { @NonFinal private long end = 0; private String name; - private String traceId; + private final String traceId; @Singular private List parents; - private String spanId; + private final String spanId; private Map kVAnnotations = new LinkedHashMap<>(); - private String processId; + private final String processId; @Singular private List timelineAnnotations = new ArrayList<>(); @Override public synchronized void stop() { - if (end == 0) { - if (begin == 0) { - throw new IllegalStateException("Span for " + name + if (this.end == 0) { + if (this.begin == 0) { + throw new IllegalStateException("Span for " + this.name + " has not been started"); } - end = System.currentTimeMillis(); + this.end = System.currentTimeMillis(); } } @Override public synchronized long getAccumulatedMillis() { - if (begin == 0) { + if (this.begin == 0) { return 0; } - if (end > 0) { - return end - begin; + if (this.end > 0) { + return this.end - this.begin; } - return System.currentTimeMillis() - begin; + return System.currentTimeMillis() - this.begin; } @Override public synchronized boolean isRunning() { - return begin != 0 && end == 0; + return this.begin != 0 && this.end == 0; } @Override public void addKVAnnotation(String key, String value) { - kVAnnotations.put(key, value); + this.kVAnnotations.put(key, value); } @Override public void addTimelineAnnotation(String msg) { - timelineAnnotations.add(new TimelineAnnotation(System.currentTimeMillis(), msg)); + this.timelineAnnotations.add(new TimelineAnnotation(System.currentTimeMillis(), msg)); } } 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 14117f572..0e9a45e9a 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 @@ -12,6 +12,40 @@ import java.util.Map; * parents.

*/ public interface Span { + + /** + * A human-readable name assigned to this span instance.

+ */ + String getName(); + + /** + * A pseudo-unique (random) number assigned to this span instance.

+ *

+ * The spanId is immutable and cannot be changed. It is safe to access this + * from multiple threads. + */ + String getSpanId(); + + /** + * A pseudo-unique (random) number assigned to the trace associated with this + * span + */ + String getTraceId(); + + /** + * Return a unique id for the process from which this Span originated.

+ *

+ * Will never be null. + */ + String getProcessId(); + + /** + * Returns the parent IDs of the span.

+ *

+ * The collection will be empty if there are no parents. + */ + List getParents(); + /** * The block has completed, stop the clock */ @@ -38,34 +72,6 @@ public interface Span { */ boolean isRunning(); - /** - * Return a textual name of this span.

- *

- * Will never be null. - */ - String getName(); - - /** - * A pseudo-unique (random) number assigned to this span instance.

- *

- * The spanId is immutable and cannot be changed. It is safe to access this - * from multiple threads. - */ - String getSpanId(); - - /** - * A pseudo-unique (random) number assigned to the trace associated with this - * span - */ - String getTraceId(); - - /** - * Returns the parent IDs of the span.

- *

- * The collection will be empty if there are no parents. - */ - List getParents(); - /** * Add a data annotation associated with this span */ @@ -89,11 +95,4 @@ public interface Span { * Will never be null. */ List getTimelineAnnotations(); - - /** - * Return a unique id for the process from which this Span originated.

- *

- * Will never be null. - */ - String getProcessId(); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Trace.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Trace.java index 6c9f04116..601890e87 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Trace.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Trace.java @@ -3,71 +3,76 @@ package org.springframework.cloud.sleuth; import java.util.concurrent.Callable; /** - * The Trace class is the primary way to interact with the library. It provides - * methods to create and manipulate spans. + * The Trace class is the primary way to interact with the library. It provides methods to + * create and manipulate spans. * - * A 'Span' represents a length of time. It has many other attributes such as a - * name, ID, and even potentially a set of key/value strings attached to - * it. + * A 'Span' represents a length of time. It has many other attributes such as a name, ID, + * and even potentially a set of key/value strings attached to it. * - * Each thread in your application has a single currently active currentSpan - * associated with it. When this is non-null, it represents the current - * operation that the thread is doing. Spans are NOT thread-safe, and must - * never be used by multiple threads at once. With care, it is possible to - * safely pass a Span object between threads, but in most cases this is not - * necessary. + * Each thread in your application has a single currently active currentSpan associated + * with it. When this is non-null, it represents the current operation that the thread is + * doing. Spans are NOT thread-safe, and must never be used by multiple threads at once. + * With care, it is possible to safely pass a Span object between threads, but in most + * cases this is not necessary. * - * A 'TraceScope' can either be empty, or contain a Span. TraceScope objects - * implement the Java's Closeable interface. Similar to file descriptors, they - * must be closed after they are created. When a TraceScope contains a Span, - * this span is closed when the scope is closed. + * A 'TraceScope' can either be empty, or contain a Span. TraceScope objects implement the + * Java's Closeable interface. Similar to file descriptors, they must be closed after they + * are created. When a TraceScope contains a Span, this span is closed when the scope is + * closed. * * The 'startSpan' methods in this class do a few things: *

    - *
  • Create a new Span which has this thread's currentSpan as one of its parents.
  • - *
  • Set currentSpan to the new Span.
  • - *
  • Create a TraceSpan object to manage the new Span.
  • + *
  • Create a new Span which has this thread's currentSpan as one of its parents.
  • + *
  • Set currentSpan to the new Span.
  • + *
  • Create a TraceSpan object to manage the new Span.
  • *
* * Closing a TraceScope does a few things: *
    - *
  • It closes the span which the scope was managing.
  • - *
  • Set currentSpan to the previous currentSpan (which may be null).
  • + *
  • It closes the span which the scope was managing.
  • + *
  • Set currentSpan to the previous currentSpan (which may be null).
  • *
*/ public interface Trace { - String SPAN_ID_NAME = "Span-Id"; - String TRACE_ID_NAME = "Trace-Id"; + String SPAN_ID_NAME = "X-Span-Id"; + String TRACE_ID_NAME = "X-Trace-Id"; + String SPAN_NAME_NAME = "X-Span-Name"; + String PARENT_ID_NAME = "X-Parent-Id"; + String PROCESS_ID_NAME = "X-Process-Id"; /** - * Creates a new trace scope. + * Creates a trace scope wrapping a new span. *

- * If this thread has a currently active trace span, the trace scope we create - * here will contain a new span descending from the currently active span. - * If there is no currently active trace span, the trace scope we create will - * be empty. + * If this thread has a currently active span, it will be the parent of the span we + * create here, and the trace scope will contain the new span and the parent. If there + * is no currently active trace span, the trace scope we create will be empty. * * @param name The name field for the new span to create. */ TraceScope startSpan(String name); - TraceScope startSpan(String name, TraceInfo tinfo); - /** - * Creates a new trace scope. + * Creates a new trace scope with a specific parent. The parent might be in another + * process or thread. *

- * If this thread has a currently active trace span, it must be the 'parent' - * span that you pass in here as a parameter. The trace scope we create here - * will contain a new span which is a child of 'parent'. + * If this thread has a currently active trace span, it must be the 'parent' span that + * you pass in here as a parameter. The trace scope we create here will contain a new + * span which is a child of 'parent'. * * @param name The name field for the new span to create. */ TraceScope startSpan(String name, Span parent); - TraceScope startSpan(String name, Sampler s); - - TraceScope startSpan(String name, Sampler s, T info); + /** + * Start a new span if the sampler allows it or if we are already tracing in this + * thread. A sampler can be used to limit the number of traces created. + * + * @param name the name of the span + * @param sampler a sampler to decide whether to create the span or not + * @param info the samplers context information + */ + TraceScope startSpan(String name, Sampler sampler, T info); /** * Pick up an existing span from another thread. diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceInfo.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceInfo.java deleted file mode 100644 index 5f8485eab..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceInfo.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.springframework.cloud.sleuth; - -import lombok.Data; - -/** - * @author Spencer Gibb - */ -@Data -public class TraceInfo { - private final String traceId; - private final String spanId; -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceScope.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceScope.java index 61374d30c..a61df5bf4 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceScope.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceScope.java @@ -5,7 +5,6 @@ import java.io.Closeable; import lombok.SneakyThrows; import lombok.Value; import lombok.experimental.NonFinal; -import lombok.extern.apachecommons.CommonsLog; import org.springframework.cloud.sleuth.event.SpanStoppedEvent; import org.springframework.cloud.sleuth.util.ExceptionUtils; @@ -16,7 +15,6 @@ import org.springframework.context.ApplicationEventPublisher; */ @Value @NonFinal -@CommonsLog public class TraceScope implements Closeable { private final ApplicationEventPublisher publisher; @@ -41,26 +39,27 @@ public class TraceScope implements Closeable { } /** - * Remove this span as the current thread, but don't stop it yet or - * send it for collection. This is useful if the span object is then - * passed to another thread for use with Trace.continueTrace(). + * Remove this span as the current thread, but don't stop it yet or send it for + * collection. This is useful if the span object is then passed to another thread for + * use with Trace.continueTrace(). * * @return the same Span object */ public Span detach() { if (this.detached) { - ExceptionUtils.error("Tried to detach trace span " + this.span + " but " + - "it has already been detached."); + ExceptionUtils.error("Tried to detach trace span " + this.span + " but " + + "it has already been detached."); } this.detached = true; Span cur = TraceContextHolder.getCurrentSpan(); if (cur != this.span) { - ExceptionUtils.error("Tried to detach trace span " + this.span + " but " + - "it is not the current span for the " + - Thread.currentThread().getName() + " thread. You have " + - "probably forgotten to close or detach " + cur); - } else { + ExceptionUtils.error("Tried to detach trace span " + this.span + " but " + + "it is not the current span for the " + + Thread.currentThread().getName() + " thread. You have " + + "probably forgotten to close or detach " + cur); + } + else { TraceContextHolder.setCurrentSpan(this.savedSpan); } return this.span; @@ -75,13 +74,19 @@ public class TraceScope implements Closeable { this.detached = true; Span cur = TraceContextHolder.getCurrentSpan(); if (cur != this.span) { - ExceptionUtils.error("Tried to close trace span " + this.span + " but " + - "it is not the current span for the " + - Thread.currentThread().getName() + " thread. You have " + - "probably forgotten to close or detach " + cur); - } else { + ExceptionUtils.error("Tried to close trace span " + this.span + " but " + + "it is not the current span for the " + + Thread.currentThread().getName() + " thread. You have " + + "probably forgotten to close or detach " + cur); + } + else { this.span.stop(); - this.publisher.publishEvent(new SpanStoppedEvent(this, this.span)); + if (this.savedSpan != null && this.span.getParents().contains(this.savedSpan.getSpanId())) { + this.publisher.publishEvent(new SpanStoppedEvent(this, this.savedSpan, this.span)); + } + else { + this.publisher.publishEvent(new SpanStoppedEvent(this, this.span)); + } TraceContextHolder.setCurrentSpan(this.savedSpan); } } 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 cc8782977..68809faf8 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 @@ -25,13 +25,13 @@ public class TraceAutoConfiguration { @Bean @ConditionalOnMissingBean - public Sampler defaultSampler() { + public Sampler defaultSampler() { return new IsTracingSampler(); } @Bean @ConditionalOnMissingBean - public Trace trace(Sampler sampler, IdGenerator idGenerator, + public Trace trace(Sampler sampler, IdGenerator idGenerator, ApplicationEventPublisher publisher) { return new DefaultTrace(sampler, idGenerator, publisher); } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ClientReceivedEvent.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ClientReceivedEvent.java new file mode 100644 index 000000000..92e3dd657 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ClientReceivedEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.event; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.context.ApplicationEvent; + +/** + * @author Dave Syer + * + */ +@Data +@EqualsAndHashCode(callSuper = false) +@SuppressWarnings("serial") +public class ClientReceivedEvent extends ApplicationEvent { + + private final Span span; + + public ClientReceivedEvent(Object source, Span span) { + super(source); + this.span = span; + } +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ClientSentEvent.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ClientSentEvent.java new file mode 100644 index 000000000..642b7f592 --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/ClientSentEvent.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.sleuth.event; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import org.springframework.cloud.sleuth.Span; +import org.springframework.context.ApplicationEvent; + +/** + * @author Dave Syer + * + */ +@Data +@EqualsAndHashCode(callSuper = false) +@SuppressWarnings("serial") +public class ClientSentEvent extends ApplicationEvent { + + private final Span span; + + public ClientSentEvent(Object source, Span span) { + super(source); + this.span = span; + } +} \ No newline at end of file diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStartedEvent.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStartedEvent.java index 8a0f9314b..abcca95de 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStartedEvent.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStartedEvent.java @@ -1,7 +1,7 @@ package org.springframework.cloud.sleuth.event; +import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.Value; import org.springframework.cloud.sleuth.Span; import org.springframework.context.ApplicationEvent; @@ -9,15 +9,21 @@ import org.springframework.context.ApplicationEvent; /** * @author Spencer Gibb */ -@Value +@Data @EqualsAndHashCode(callSuper=false) @SuppressWarnings("serial") public class SpanStartedEvent extends ApplicationEvent { + private final Span parent; private final Span span; public SpanStartedEvent(Object source, Span span) { + this(source, null, span); + } + + public SpanStartedEvent(Object source, Span parent, Span span) { super(source); + this.parent = parent; this.span = span; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStoppedEvent.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStoppedEvent.java index 1b45c066e..eec98d16b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStoppedEvent.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStoppedEvent.java @@ -1,7 +1,7 @@ package org.springframework.cloud.sleuth.event; +import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.Value; import org.springframework.cloud.sleuth.Span; import org.springframework.context.ApplicationEvent; @@ -9,15 +9,21 @@ import org.springframework.context.ApplicationEvent; /** * @author Spencer Gibb */ -@Value +@Data @EqualsAndHashCode(callSuper=false) @SuppressWarnings("serial") public class SpanStoppedEvent extends ApplicationEvent { private final Span span; + private final Span parent; public SpanStoppedEvent(Object source, Span span) { + this(source, null, span); + } + + public SpanStoppedEvent(Object source, Span parent, Span span) { super(source); + this.parent = parent; this.span = span; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceCallable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceCallable.java index c2aaa1e1f..2946cdcc9 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceCallable.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceCallable.java @@ -2,6 +2,7 @@ package org.springframework.cloud.sleuth.instrument; import java.util.concurrent.Callable; +import lombok.EqualsAndHashCode; import lombok.Value; import org.springframework.cloud.sleuth.Span; @@ -12,27 +13,28 @@ import org.springframework.cloud.sleuth.TraceScope; * @author Spencer Gibb */ @Value +@EqualsAndHashCode(callSuper=false) public class TraceCallable extends TraceDelegate> implements Callable { - public TraceCallable(Trace trace, Callable delagate) { - super(trace, delagate); + public TraceCallable(Trace trace, Callable delegate) { + super(trace, delegate); } - public TraceCallable(Trace trace, Callable delagate, Span parent) { - super(trace, delagate, parent); + public TraceCallable(Trace trace, Callable delegate, Span parent) { + super(trace, delegate, parent); } - public TraceCallable(Trace trace, Callable delagate, Span parent, String name) { - super(trace, delagate, parent, name); + public TraceCallable(Trace trace, Callable delegate, Span parent, String name) { + super(trace, delegate, parent, name); } @Override public V call() throws Exception { - if (this.parent != null) { + if (this.getParent() != null) { TraceScope scope = startSpan(); try { - return this.delagate.call(); + return this.getDelegate().call(); } finally { scope.close(); @@ -40,7 +42,7 @@ public class TraceCallable extends TraceDelegate> implements Call } else { - return this.delagate.call(); + return this.getDelegate().call(); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceDelegate.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceDelegate.java index 458de89d4..074ed1df3 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceDelegate.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceDelegate.java @@ -1,5 +1,7 @@ package org.springframework.cloud.sleuth.instrument; +import lombok.Getter; + import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Trace; import org.springframework.cloud.sleuth.TraceContextHolder; @@ -8,24 +10,25 @@ import org.springframework.cloud.sleuth.TraceScope; /** * @author Spencer Gibb */ +@Getter public abstract class TraceDelegate { - protected final Trace trace; - protected final T delagate; - protected final Span parent; - protected final String name; - - public TraceDelegate(Trace trace, T delagate) { - this(trace, delagate, TraceContextHolder.getCurrentSpan(), null); + private final Trace trace; + private final T delegate; + private final Span parent; + private final String name; + + public TraceDelegate(Trace trace, T delegate) { + this(trace, delegate, TraceContextHolder.getCurrentSpan(), null); } - public TraceDelegate(Trace trace, T delagate, Span parent) { - this(trace, delagate, parent, null); + public TraceDelegate(Trace trace, T delegate, Span parent) { + this(trace, delegate, parent, null); } - public TraceDelegate(Trace trace, T delagate, Span parent, String name) { + public TraceDelegate(Trace trace, T delegate, Span parent, String name) { this.trace = trace; - this.delagate = delagate; + this.delegate = delegate; this.parent = parent; this.name = name; } @@ -35,6 +38,6 @@ public abstract class TraceDelegate { } protected String getSpanName() { - return this.name == null ? Thread.currentThread().getName() : name; + return this.name == null ? Thread.currentThread().getName() : this.name; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceRunnable.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceRunnable.java index 4a46cb32d..56bfdc124 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceRunnable.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceRunnable.java @@ -28,18 +28,18 @@ public class TraceRunnable extends TraceDelegate implements Runnable { @Override public void run() { - if (this.parent != null) { + if (this.getParent() != null) { TraceScope scope = startSpan(); try { - this.delagate.run(); + this.getDelegate().run(); } finally { scope.close(); } } else { - this.delagate.run(); + this.getDelegate().run(); } } } 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 f43913d87..3918fe0ad 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java @@ -15,7 +15,10 @@ */ package org.springframework.cloud.sleuth.instrument.web; +import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME; +import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME; import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME; +import static org.springframework.cloud.sleuth.Trace.SPAN_NAME_NAME; import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME; import static org.springframework.util.StringUtils.hasText; @@ -27,8 +30,9 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.springframework.cloud.sleuth.MilliSpan; +import org.springframework.cloud.sleuth.MilliSpan.MilliSpanBuilder; import org.springframework.cloud.sleuth.Trace; -import org.springframework.cloud.sleuth.TraceInfo; import org.springframework.cloud.sleuth.TraceScope; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -72,19 +76,32 @@ public class TraceFilter extends OncePerRequestFilter { HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { - String uri = hasText(request.getRequestURI()) ? request.getRequestURI() : ""; + String uri = this.urlPathHelper.getPathWithinApplication(request); boolean skip = this.skipPattern.matcher(uri).matches(); TraceScope traceScope = null; if (!skip) { String spanId = getHeader(request, response, SPAN_ID_NAME); String traceId = getHeader(request, response, TRACE_ID_NAME); - String name = this.urlPathHelper.getPathWithinApplication(request); + String name = "http" + uri; if (hasText(spanId) && hasText(traceId)) { - TraceInfo traceInfo = new TraceInfo(traceId, spanId); + MilliSpanBuilder traceInfo = MilliSpan.builder().traceId(traceId).spanId(spanId); + String parentId = getHeader(request, response, PARENT_ID_NAME); + String processId = getHeader(request, response, PROCESS_ID_NAME); + String parentName = getHeader(request, response, SPAN_NAME_NAME); + if (parentName!=null) { + traceInfo.name(parentName); + } + if (processId!=null) { + traceInfo.processId(processId); + } + if (parentId!=null) { + traceInfo.parent(parentId); + } + // TODO: trace description? - traceScope = this.trace.startSpan(name, traceInfo); + traceScope = this.trace.startSpan(name, traceInfo.build()); // Send new span id back addToResponseIfNotPresent(response, SPAN_ID_NAME, traceScope.getSpan() .getSpanId()); 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 3be3db292..32c09aef5 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 @@ -15,22 +15,31 @@ */ package org.springframework.cloud.sleuth.instrument.web.client; +import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME; +import static org.springframework.cloud.sleuth.Trace.PROCESS_ID_NAME; import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME; +import static org.springframework.cloud.sleuth.Trace.SPAN_NAME_NAME; import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME; import static org.springframework.cloud.sleuth.TraceContextHolder.getCurrentSpan; import static org.springframework.cloud.sleuth.TraceContextHolder.isTracing; import java.io.IOException; +import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Trace; +import org.springframework.cloud.sleuth.event.ClientReceivedEvent; +import org.springframework.cloud.sleuth.event.ClientSentEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.ClientHttpResponse; /** - * Interceptor that verifies whether the trance and span id has been set on the - * request and sets them if one or both of them are missing. + * Interceptor that verifies whether the trance and span id has been set on the request + * and sets them if one or both of them are missing. * * @see org.springframework.web.client.RestTemplate * @see Trace @@ -38,14 +47,47 @@ import org.springframework.http.client.ClientHttpResponse; * @author Marcin Grzejszczak, 4financeIT * @author Spencer Gibb */ -public class TraceRestTemplateInterceptor implements ClientHttpRequestInterceptor { +public class TraceRestTemplateInterceptor implements ClientHttpRequestInterceptor, +ApplicationEventPublisherAware { + + private ApplicationEventPublisher publisher; + + @Override + public void setApplicationEventPublisher(ApplicationEventPublisher publisher) { + this.publisher = publisher; + } @Override public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { setHeader(request, SPAN_ID_NAME, getCurrentSpan().getSpanId()); setHeader(request, TRACE_ID_NAME, getCurrentSpan().getTraceId()); - return execution.execute(request, body); + setHeader(request, SPAN_NAME_NAME, getCurrentSpan().getName()); + String parentId = getParentId(getCurrentSpan()); + if (parentId != null) { + setHeader(request, PARENT_ID_NAME, parentId); + } + String processId = getCurrentSpan().getProcessId(); + if (processId != null) { + setHeader(request, PROCESS_ID_NAME, processId); + } + publish(new ClientSentEvent(this, getCurrentSpan())); + try { + return execution.execute(request, body); + } finally { + publish(new ClientReceivedEvent(this, getCurrentSpan())); + } + } + + private void publish(ApplicationEvent event) { + if (this.publisher !=null) { + this.publisher.publishEvent(event); + } + } + + private String getParentId(Span span) { + return span.getParents() != null && !span.getParents().isEmpty() ? span + .getParents().get(0) : null; } public void setHeader(HttpRequest request, String name, String value) { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostFilter.java deleted file mode 100644 index 910dbadb3..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostFilter.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.springframework.cloud.sleuth.instrument.zuul; - -import org.springframework.cloud.sleuth.Trace; -import org.springframework.cloud.sleuth.TraceScope; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; - -/** - * @author Spencer Gibb - */ -public class TracePostFilter extends ZuulFilter { - - private Trace trace; - - public TracePostFilter(Trace trace) { - this.trace = trace; - } - - @Override - public String filterType() { - return "post"; - } - - @Override - public int filterOrder() { - return 0; - } - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - TraceScope traceScope = (TraceScope) RequestContext.getCurrentContext().get( - "traceScope"); - - if (traceScope != null) { - traceScope.close(); - } - - return null; - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreFilter.java deleted file mode 100644 index f48d377f6..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreFilter.java +++ /dev/null @@ -1,65 +0,0 @@ -package org.springframework.cloud.sleuth.instrument.zuul; - -import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME; -import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME; -import static org.springframework.util.StringUtils.hasText; - -import javax.servlet.http.HttpServletRequest; - -import org.springframework.cloud.sleuth.Trace; -import org.springframework.cloud.sleuth.TraceInfo; -import org.springframework.cloud.sleuth.TraceScope; - -import com.netflix.zuul.ZuulFilter; -import com.netflix.zuul.context.RequestContext; - -/** - * @author Spencer Gibb - */ -public class TracePreFilter extends ZuulFilter { - - private Trace trace; - - public TracePreFilter(Trace trace) { - this.trace = trace; - } - - @Override - public String filterType() { - return "pre"; - } - - @Override - public int filterOrder() { - return 0; - } - - @Override - public boolean shouldFilter() { - return true; - } - - @Override - public Object run() { - RequestContext context = RequestContext.getCurrentContext(); - HttpServletRequest request = context.getRequest(); - - String spanId = request.getHeader(SPAN_ID_NAME); - String traceId = request.getHeader(TRACE_ID_NAME); - TraceScope traceScope = null; - if (hasText(spanId) && hasText(traceId)) { - - TraceInfo traceInfo = new TraceInfo(traceId, spanId); - // TODO: trace description? - traceScope = trace.startSpan("traceZuulFilter", traceInfo); - } - else { - traceScope = trace.startSpan("traceZuulFilter"); - - } - - context.set("traceScope", traceScope); - - return null; - } -} 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 deleted file mode 100644 index 18d542435..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.springframework.cloud.sleuth.instrument.zuul; - -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.cloud.sleuth.Trace; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import com.netflix.zuul.ZuulFilter; - -/** - * @author Spencer Gibb - */ -@Configuration -@ConditionalOnClass(ZuulFilter.class) -@ConditionalOnBean(Trace.class) -public class TraceZuulAutoConfiguration { - - @Bean - public TracePreFilter tracePreFilter(Trace trace) { - return new TracePreFilter(trace); - } - - @Bean - public TracePostFilter tracePostFilter(Trace trace) { - return new TracePostFilter(trace); - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/AlwaysSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/AlwaysSampler.java index 087912506..5cfb80e30 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/AlwaysSampler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/AlwaysSampler.java @@ -5,9 +5,9 @@ import org.springframework.cloud.sleuth.Sampler; /** * @author Spencer Gibb */ -public class AlwaysSampler implements Sampler { +public class AlwaysSampler implements Sampler { @Override - public boolean next(Object info) { + public boolean next(Void info) { return true; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/IsTracingSampler.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/IsTracingSampler.java index 3c8eaaf07..b05b772e6 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/IsTracingSampler.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/IsTracingSampler.java @@ -6,10 +6,10 @@ import org.springframework.cloud.sleuth.TraceContextHolder; /** * @author Spencer Gibb */ -public class IsTracingSampler implements Sampler { +public class IsTracingSampler implements Sampler { @Override - public boolean next(Object info) { + public boolean next(Void info) { return TraceContextHolder.getCurrentSpan() != null; } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java index f17d0cba9..63cfce7ea 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/SleuthSlf4jAutoConfiguration.java @@ -13,12 +13,8 @@ import org.springframework.context.annotation.Configuration; public class SleuthSlf4jAutoConfiguration { @Bean - public Slf4jSpanStartedListener slf4jSpanStartedListener() { - return new Slf4jSpanStartedListener(); + public Slf4jSpanListener slf4jSpanStartedListener() { + return new Slf4jSpanListener(); } - @Bean - public Slf4jSpanStoppedListener slf4jSpanStoppedListener() { - return new Slf4jSpanStoppedListener(); - } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanListener.java new file mode 100644 index 000000000..3a6f6a4fe --- /dev/null +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanListener.java @@ -0,0 +1,46 @@ +package org.springframework.cloud.sleuth.slf4j; + +import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME; +import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME; +import lombok.extern.slf4j.Slf4j; + +import org.slf4j.MDC; +import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.Trace; +import org.springframework.cloud.sleuth.event.SpanStartedEvent; +import org.springframework.cloud.sleuth.event.SpanStoppedEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; + +/** + * @author Spencer Gibb + */ +@Slf4j +@Order(Ordered.LOWEST_PRECEDENCE) +public class Slf4jSpanListener { + + @EventListener(SpanStartedEvent.class) + public void start(SpanStartedEvent event) { + Span span = event.getSpan(); + MDC.put(Trace.SPAN_ID_NAME, span.getSpanId()); + MDC.put(Trace.TRACE_ID_NAME, span.getTraceId()); + //TODO: what log level? + log.info("Starting span: {}", span); + if (event.getParent()!=null) { + log.info("Starting parent: {}", event.getParent()); + } + } + + @EventListener(SpanStoppedEvent.class) + public void stop(SpanStoppedEvent event) { + //TODO: what should this log level be? + log.info("Stopped span: {}", event.getSpan()); + if (event.getParent()!=null) { + log.info("Stopped parent: {}", event.getParent()); + } + MDC.remove(SPAN_ID_NAME); + MDC.remove(TRACE_ID_NAME); + } + +} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanStartedListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanStartedListener.java deleted file mode 100644 index 7708340e0..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanStartedListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.springframework.cloud.sleuth.slf4j; - -import lombok.extern.slf4j.Slf4j; - -import org.slf4j.MDC; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.Trace; -import org.springframework.cloud.sleuth.event.SpanStartedEvent; -import org.springframework.context.ApplicationListener; - -/** - * @author Spencer Gibb - */ -@Slf4j -public class Slf4jSpanStartedListener implements ApplicationListener { - - @Override - public void onApplicationEvent(SpanStartedEvent event) { - Span span = event.getSpan(); - MDC.put(Trace.SPAN_ID_NAME, span.getSpanId()); - MDC.put(Trace.TRACE_ID_NAME, span.getTraceId()); - //TODO: what log level? - log.info("Starting span: {}", span); - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanStoppedListener.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanStoppedListener.java deleted file mode 100644 index b5b12d053..000000000 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/slf4j/Slf4jSpanStoppedListener.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.springframework.cloud.sleuth.slf4j; - -import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME; -import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME; - -import lombok.extern.slf4j.Slf4j; - -import org.slf4j.MDC; -import org.springframework.cloud.sleuth.event.SpanStoppedEvent; -import org.springframework.context.ApplicationListener; - -/** - * @author Spencer Gibb - */ -@Slf4j -public class Slf4jSpanStoppedListener implements ApplicationListener { - @Override - public void onApplicationEvent(SpanStoppedEvent event) { - //TODO: what should this log level be? - log.info("Received span: {}", event.getSpan()); - MDC.remove(SPAN_ID_NAME); - MDC.remove(TRACE_ID_NAME); - } -} diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTrace.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTrace.java index 31ebd0d66..e35844a21 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTrace.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTrace.java @@ -11,7 +11,6 @@ import org.springframework.cloud.sleuth.Sampler; import org.springframework.cloud.sleuth.Span; import org.springframework.cloud.sleuth.Trace; import org.springframework.cloud.sleuth.TraceContextHolder; -import org.springframework.cloud.sleuth.TraceInfo; import org.springframework.cloud.sleuth.TraceScope; import org.springframework.cloud.sleuth.event.SpanStartedEvent; import org.springframework.cloud.sleuth.instrument.TraceCallable; @@ -23,102 +22,73 @@ import org.springframework.context.ApplicationEventPublisher; */ public class DefaultTrace implements Trace { - private final Sampler defaultSampler; + private final Sampler defaultSampler; private final IdGenerator idGenerator; private final ApplicationEventPublisher publisher; - public DefaultTrace(Sampler defaultSampler, IdGenerator idGenerator, + public DefaultTrace(Sampler defaultSampler, IdGenerator idGenerator, ApplicationEventPublisher publisher) { this.defaultSampler = defaultSampler; this.idGenerator = idGenerator; this.publisher = publisher; } - @Override - public TraceScope startSpan(String name) { - return this.startSpan(name, this.defaultSampler); - } - - @Override - public TraceScope startSpan(String name, TraceInfo tinfo) { - if (tinfo == null) return doStart(null); - MilliSpan span = MilliSpan.builder() - .begin(System.currentTimeMillis()) - .name(name) - .traceId(tinfo.getTraceId()) - .spanId(this.idGenerator.create()) - .parent(tinfo.getSpanId()) - .build(); - return doStart(span); - } - @Override public TraceScope startSpan(String name, Span parent) { if (parent == null) { return startSpan(name); } Span currentSpan = getCurrentSpan(); - if ((currentSpan != null) && (currentSpan != parent)) { - error("HTrace client error: thread " + - Thread.currentThread().getName() + " tried to start a new Span " + - "with parent " + parent.toString() + ", but there is already a " + - "currentSpan " + currentSpan); + if (currentSpan != null && !parent.equals(currentSpan)) { + error("HTrace client error: thread " + Thread.currentThread().getName() + + " tried to start a new Span " + "with parent " + parent.toString() + + ", but there is already a " + "currentSpan " + currentSpan); } - return doStart(createChild(parent, name)); + if (currentSpan==null) { + TraceContextHolder.setCurrentSpan(parent); + } + return continueSpan(createChild(parent, name)); } @Override - public TraceScope startSpan(String name, Sampler s) { - return startSpan(name, s, null); + public TraceScope startSpan(String name) { + return this.startSpan(name, this.defaultSampler, null); } @Override public TraceScope startSpan(String name, Sampler s, T info) { Span span = null; if (TraceContextHolder.isTracing() || s.next(info)) { - span = createNew(name); - } - return doStart(span); - } - - protected Span createNew(String name) { - Span parent = getCurrentSpan(); - if (parent == null) { - return MilliSpan.builder() - .begin(System.currentTimeMillis()) - .name(name) - .traceId(this.idGenerator.create()) - .spanId(this.idGenerator.create()) - .build(); - } else { - return createChild(parent, name); - } - } - - protected Span createChild(Span parent, String childname) { - return MilliSpan.builder() - .begin(System.currentTimeMillis()) - .name(childname) - .traceId(parent.getTraceId()) - .parent(parent.getSpanId()) - .spanId(this.idGenerator.create()) - .processId(parent.getProcessId()) - .build(); - } - - protected TraceScope doStart(Span span) { - if (span != null) { - this.publisher.publishEvent(new SpanStartedEvent(this, span)); + span = createChild(getCurrentSpan(), name); } return continueSpan(span); } + protected Span createChild(Span parent, String name) { + if (parent == null) { + MilliSpan span = MilliSpan.builder().begin(System.currentTimeMillis()).name(name) + .traceId(this.idGenerator.create()).spanId(this.idGenerator.create()) + .build(); + this.publisher.publishEvent(new SpanStartedEvent(this, span)); + return span; + } + else { + MilliSpan span = MilliSpan.builder().begin(System.currentTimeMillis()).name(name) + .traceId(parent.getTraceId()).parent(parent.getSpanId()) + .spanId(this.idGenerator.create()).processId(parent.getProcessId()) + .build(); + this.publisher.publishEvent(new SpanStartedEvent(this, parent, span)); + return span; + } + } + @Override public TraceScope continueSpan(Span span) { // Return an empty TraceScope that does nothing on close - if (span == null) return NullScope.INSTANCE; + if (span == null) + return NullScope.INSTANCE; Span oldSpan = getCurrentSpan(); TraceContextHolder.setCurrentSpan(span); return new TraceScope(this.publisher, span, oldSpan); @@ -144,7 +114,8 @@ public class DefaultTrace implements Trace { @Override public Callable wrap(Callable callable) { if (TraceContextHolder.isTracing()) { - return new TraceCallable<>(this, callable, TraceContextHolder.getCurrentSpan()); + return new TraceCallable<>(this, callable, + TraceContextHolder.getCurrentSpan()); } return callable; } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceTests.java index ca2444369..efcf56f75 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceTests.java @@ -2,8 +2,8 @@ package org.springframework.cloud.sleuth; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.isA; import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.isA; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -38,7 +38,7 @@ public class DefaultTraceTests { DefaultTrace trace = new DefaultTrace(new IsTracingSampler(), new RandomUuidGenerator(), publisher); - TraceScope scope = trace.startSpan(CREATE_SIMPLE_TRACE, new AlwaysSampler()); + TraceScope scope = trace.startSpan(CREATE_SIMPLE_TRACE, new AlwaysSampler(), null); try { importantWork1(trace); } diff --git a/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleApplication.java b/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleApplication.java index 9a0f08aec..e1f610b65 100644 --- a/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleApplication.java +++ b/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleApplication.java @@ -1,7 +1,5 @@ package org.springframework.cloud.sleuth.sample; -import com.github.kristofa.brave.LoggingSpanCollectorImpl; -import com.github.kristofa.brave.SpanCollector; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.cloud.sleuth.Sampler; @@ -36,6 +34,10 @@ public class SampleApplication { SpringApplication.run(SampleApplication.class, args); } - //@Bean public SpanCollector spanCollector() { return new LoggingSpanCollectorImpl(); } + // Use this for debugging (or if there is no Zipkin collector running on port 9410) + // @Bean + // public SpanCollector spanCollector() { + // return new LoggingSpanCollectorImpl(); + // } } diff --git a/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleController.java b/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleController.java index 1f571e7bb..7cd571df5 100644 --- a/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleController.java +++ b/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleController.java @@ -67,7 +67,7 @@ ApplicationListener { @SneakyThrows @RequestMapping("/traced") public String traced() { - TraceScope scope = this.trace.startSpan("customTraceEndpoint", new AlwaysSampler()); + TraceScope scope = this.trace.startSpan("customTraceEndpoint", new AlwaysSampler(), null); final Random random = new Random(); int millis = random.nextInt(1000); log.info("Sleeping for {} millis", millis); diff --git a/spring-cloud-sleuth-sample/src/main/resources/application.yml b/spring-cloud-sleuth-sample/src/main/resources/application.yml index 7996c7205..ea69ae529 100644 --- a/spring-cloud-sleuth-sample/src/main/resources/application.yml +++ b/spring-cloud-sleuth-sample/src/main/resources/application.yml @@ -7,7 +7,7 @@ spring: logging: pattern: - console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([trace=%X{Trace-Id:-},span=%X{Span-Id:-}]){yellow} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex' + console: '%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} %clr([trace=%X{X-Trace-Id:-},span=%X{X-Span-Id:-}]){yellow} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wex' endpoints: health: diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/SleuthTracer.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/SleuthTracer.java deleted file mode 100644 index 9a9682255..000000000 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/SleuthTracer.java +++ /dev/null @@ -1,206 +0,0 @@ -package org.springframework.cloud.sleuth.zipkin; - -import java.io.UnsupportedEncodingException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import lombok.extern.apachecommons.CommonsLog; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.web.ServerProperties; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.TimelineAnnotation; - -import com.github.kristofa.brave.SpanCollector; -import com.twitter.zipkin.gen.Annotation; -import com.twitter.zipkin.gen.AnnotationType; -import com.twitter.zipkin.gen.BinaryAnnotation; -import com.twitter.zipkin.gen.Endpoint; -import com.twitter.zipkin.gen.zipkinCoreConstants; -import org.springframework.cloud.sleuth.event.SpanStoppedEvent; -import org.springframework.context.event.EventListener; - -/** - * @author Spencer Gibb - */ -@CommonsLog -public class SleuthTracer { - - private SpanCollector spanCollector; - @Value("${spring.application.name:application}") - private String appName; - @Autowired - private ServerProperties serverProperties; - - public SleuthTracer(SpanCollector spanCollector) { - this.spanCollector = spanCollector; - } - - @EventListener - public void start(SpanStoppedEvent event) { - this.spanCollector.collect(convert(event.getSpan())); - } - - /** - * Converts a given HTrace span to a Zipkin Span. - *
    - *
  • First set the start annotation. [CS, SR], depending whether it is a client service or not. - *
  • Set other id's, etc [TraceId's etc] - *
  • Create binary annotations based on data from HTrace Span object. - *
  • Set the last annotation. [SS, CR] - *
- */ - public com.twitter.zipkin.gen.Span convert(Span span) { - com.twitter.zipkin.gen.Span zipkinSpan = new com.twitter.zipkin.gen.Span(); - - String serviceName = getServiceName(span); - int address = getAddress(); - Integer port = getPort(); - - Endpoint ep = new Endpoint(address, port.shortValue(), serviceName); - List annotationList = createZipkinAnnotations(span, ep); - List binaryAnnotationList = createZipkinBinaryAnnotations(span, ep); - zipkinSpan.setTrace_id(hash(span.getTraceId())); - if (span.getParents().size() > 0) { - if (span.getParents().size() > 1) { - log.error("zipkin doesn't support spans with multiple parents. Omitting " + - "other parents for " + span); - } - zipkinSpan.setParent_id(hash(span.getParents().get(0))); - } - zipkinSpan.setId(hash(span.getSpanId())); - zipkinSpan.setName(span.getName()); - zipkinSpan.setAnnotations(annotationList); - zipkinSpan.setBinary_annotations(binaryAnnotationList); - return zipkinSpan; - } - - public Integer getPort() { - Integer port; - if (serverProperties.getPort() != null) { - port = serverProperties.getPort(); - } else { - port = 8080; //TODO: support random port - } - return port; - } - - public int getAddress() { - String address; - if (serverProperties.getAddress() != null) { - address = serverProperties.getAddress().getHostAddress(); - } else { - address = "127.0.0.1"; //TODO: get address from config - } - return ipAddressToInt(address); - } - - public String getServiceName(Span span) { - String serviceName; - if (span.getProcessId() != null) { - serviceName = span.getProcessId().toLowerCase(); - } else { - serviceName = appName; - } - return serviceName; - } - - - private int ipAddressToInt(final String ip) { - InetAddress inetAddress = null; - try { - inetAddress = InetAddress.getByName(ip); - } catch (final UnknownHostException e) { - throw new IllegalArgumentException(e); - } - return ByteBuffer.wrap(inetAddress.getAddress()).getInt(); - } - - /** - * Add annotations from the sleuth Span. - */ - private List createZipkinAnnotations(Span span, - Endpoint ep) { - List annotationList = new ArrayList<>(); - - int duration = (int)(span.getEnd() - span.getBegin()); - - // add first zipkin annotation. - annotationList.add(createZipkinAnnotation(zipkinCoreConstants.CLIENT_SEND, span.getBegin(), 0, ep, true)); - annotationList.add(createZipkinAnnotation(zipkinCoreConstants.SERVER_RECV, span.getBegin(), 0, ep, true)); - // add sleuth time annotation - for (TimelineAnnotation ta : span.getTimelineAnnotations()) { - annotationList.add(createZipkinAnnotation(ta.getMsg(), ta.getTime(), 0, ep, true)); - } - // add last zipkin annotation - annotationList.add(createZipkinAnnotation(zipkinCoreConstants.SERVER_SEND, span.getEnd(), duration, ep, false)); - annotationList.add(createZipkinAnnotation(zipkinCoreConstants.CLIENT_RECV, span.getEnd(), duration, ep, false)); - return annotationList; - } - - /** - * Creates a list of Annotations that are present in sleuth Span object. - * - * @return list of Annotations that could be added to Zipkin Span. - */ - private List createZipkinBinaryAnnotations(Span span, - Endpoint ep) { - List l = new ArrayList<>(); - for (Map.Entry e : span.getKVAnnotations().entrySet()) { - BinaryAnnotation binaryAnn = new BinaryAnnotation(); - binaryAnn.setAnnotation_type(AnnotationType.BYTES); - binaryAnn.setKey(e.getKey()); - try { - binaryAnn.setValue(e.getValue().getBytes("UTF-8")); - } catch (UnsupportedEncodingException ex) { - log.error("Error encoding string as UTF-8", ex); - } - binaryAnn.setHost(ep); - l.add(binaryAnn); - } - return l; - } - - /** - * Create an annotation with the correct times and endpoint. - * - * @param value Annotation value - * @param time timestamp will be extracted - * @param ep the endopint this annotation will be associated with. - * @param sendRequest use the first or last timestamp. - */ - private static Annotation createZipkinAnnotation(String value, long time, int duration, - Endpoint ep, boolean sendRequest) { - Annotation annotation = new Annotation(); - annotation.setHost(ep); - - // Zipkin is in microseconds - if (sendRequest) { - annotation.setTimestamp(time * 1000); - } else { - annotation.setTimestamp(time * 1000); - } - - if (duration > 0) { - annotation.setDuration(duration * 1000); - } - annotation.setValue(value); - return annotation; - } - - private static long hash(String string) { - long h = 1125899906842597L; - int len = string.length(); - - for (int i = 0; i < len; i++) { - h = 31 * h + string.charAt(i); - } - return h; - } - -} diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java index 275a0083b..46394ff16 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinAutoConfiguration.java @@ -17,7 +17,6 @@ import com.github.kristofa.brave.ClientTracerConfig; import com.github.kristofa.brave.EndPointSubmitterConfig; import com.github.kristofa.brave.FixedSampleRateTraceFilter; import com.github.kristofa.brave.ServerSpanThreadBinderConfig; -import com.github.kristofa.brave.ServerTracer; import com.github.kristofa.brave.ServerTracerConfig; import com.github.kristofa.brave.SpanCollector; import com.github.kristofa.brave.TraceFilter; @@ -41,8 +40,8 @@ import com.google.common.base.Optional; public class ZipkinAutoConfiguration { @Bean - @ConditionalOnMissingBean - public SpanCollector spanCollector() { + @ConditionalOnMissingBean(SpanCollector.class) + public ZipkinSpanCollector spanCollector() { return new ZipkinSpanCollector(zipkinProperties().getHost(), zipkinProperties() .getPort()); } @@ -63,16 +62,16 @@ public class ZipkinAutoConfiguration { return new TraceFilters(traceFilters); } - @Bean - @ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", matchIfMissing = true) - public ZipkinSpanListener zipkinTrace(ServerTracer serverTracer) { - return new ZipkinSpanListener(serverTracer); - } + // @Bean + // @ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", matchIfMissing = true) + // public ZipkinSpanListener zipkinTrace(ServerTracer serverTracer, ClientTracer clientTracer) { + // return new ZipkinSpanListener(serverTracer, clientTracer); + // } @Bean - @ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", havingValue = "false") - public SleuthTracer sleuthTracer(SpanCollector spanCollector) { - return new SleuthTracer(spanCollector); + // @ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", havingValue = "false") + public ZipkinSpanListener sleuthTracer(SpanCollector spanCollector) { + return new ZipkinSpanListener(spanCollector); } @Configuration diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java index 18a0702ed..c61a9046c 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java @@ -1,87 +1,228 @@ package org.springframework.cloud.sleuth.zipkin; -import lombok.Data; +import java.io.UnsupportedEncodingException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import lombok.extern.apachecommons.CommonsLog; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.cloud.sleuth.Span; +import org.springframework.cloud.sleuth.TimelineAnnotation; +import org.springframework.cloud.sleuth.event.ClientReceivedEvent; +import org.springframework.cloud.sleuth.event.ClientSentEvent; import org.springframework.cloud.sleuth.event.SpanStartedEvent; import org.springframework.cloud.sleuth.event.SpanStoppedEvent; import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.Order; -import com.github.kristofa.brave.ServerTracer; +import com.github.kristofa.brave.SpanCollector; +import com.twitter.zipkin.gen.Annotation; +import com.twitter.zipkin.gen.AnnotationType; +import com.twitter.zipkin.gen.BinaryAnnotation; +import com.twitter.zipkin.gen.Endpoint; +import com.twitter.zipkin.gen.zipkinCoreConstants; /** * @author Spencer Gibb */ @CommonsLog +@Order(0) public class ZipkinSpanListener { - private final ServerTracer serverTracer; + private SpanCollector spanCollector; + @Value("${spring.application.name:application}") + private String appName; + @Autowired + private ServerProperties serverProperties; - public ZipkinSpanListener(ServerTracer serverTracer) { - this.serverTracer = serverTracer; + public ZipkinSpanListener(SpanCollector spanCollector) { + this.spanCollector = spanCollector; } @EventListener public void start(SpanStartedEvent event) { - preTrace(event.getSpan()); + if (event.getParent()!=null) { + event.getParent().addTimelineAnnotation(zipkinCoreConstants.SERVER_RECV); + } + } + + @EventListener + public void clientSend(ClientSentEvent event) { + event.getSpan().addTimelineAnnotation(zipkinCoreConstants.CLIENT_SEND); + } + + @EventListener + public void clientReceive(ClientReceivedEvent event) { + event.getSpan().addTimelineAnnotation(zipkinCoreConstants.CLIENT_RECV); } @EventListener public void start(SpanStoppedEvent event) { - postTrace(event.getSpan()); - } - - protected void preTrace(Span context) { - final TraceData traceData = getTraceData(context); - - final String spanName = getSpanName(context, traceData); - if (traceData.getTraceId() != null && traceData.getSpanId() != null) { - - log.debug("Received span information as part of request."); - this.serverTracer.setStateCurrentTrace(traceData.getTraceId(), - traceData.getSpanId(), traceData.getParentSpanId(), spanName); + if (event.getParent()!=null) { + event.getParent().addTimelineAnnotation(zipkinCoreConstants.SERVER_SEND); + this.spanCollector.collect(convert(event.getParent())); } - else { - log.debug("Received no span state."); - this.serverTracer.setStateUnknown(spanName); + this.spanCollector.collect(convert(event.getSpan())); + } + + /** + * Converts a given Sleuth span to a Zipkin Span. + *
    + *
  • First set the start annotation. [CS, SR], depending whether it is a client service or not. + *
  • Set other id's, etc [TraceId's etc] + *
  • Create binary annotations based on data from HTrace Span object. + *
  • Set the last annotation. [SS, CR] + *
+ */ + public com.twitter.zipkin.gen.Span convert(Span span) { + com.twitter.zipkin.gen.Span zipkinSpan = new com.twitter.zipkin.gen.Span(); + + String serviceName = getServiceName(span); + int address = getAddress(); + Integer port = getPort(); + + Endpoint ep = new Endpoint(address, port.shortValue(), serviceName); + List annotationList = createZipkinAnnotations(span, ep); + List binaryAnnotationList = createZipkinBinaryAnnotations(span, ep); + zipkinSpan.setTrace_id(hash(span.getTraceId())); + if (span.getParents().size() > 0) { + if (span.getParents().size() > 1) { + log.error("zipkin doesn't support spans with multiple parents. Omitting " + + "other parents for " + span); + } + zipkinSpan.setParent_id(hash(span.getParents().get(0))); } - this.serverTracer.setServerReceived(); + zipkinSpan.setId(hash(span.getSpanId())); + zipkinSpan.setName(span.getName()); + zipkinSpan.setAnnotations(annotationList); + zipkinSpan.setBinary_annotations(binaryAnnotationList); + return zipkinSpan; } - protected TraceData getTraceData(Span context) { - TraceData trace = new TraceData(); - trace.setTraceId(hash(context.getTraceId())); - trace.setSpanId(hash(context.getSpanId())); - trace.setSpanName(context.getName()); - if (!context.getParents().isEmpty()) { - trace.setParentSpanId(hash(context.getParents().iterator().next())); + public Integer getPort() { + Integer port; + if (this.serverProperties.getPort() != null) { + port = this.serverProperties.getPort(); + } else { + port = 8080; //TODO: support random port } - return trace; + return port; } - protected String getSpanName(Span context, TraceData traceData) { - return context.getName(); + public int getAddress() { + String address; + if (this.serverProperties.getAddress() != null) { + address = this.serverProperties.getAddress().getHostAddress(); + } else { + address = "127.0.0.1"; //TODO: get address from config + } + return ipAddressToInt(address); } - protected void postTrace(Span context) { - // We can submit this in any case. When server state is not set or - // we should not trace this request nothing will happen. - log.debug("Sending server send."); + public String getServiceName(Span span) { + String serviceName; + if (span.getProcessId() != null) { + serviceName = span.getProcessId().toLowerCase(); + } else { + serviceName = this.appName; + } + return serviceName; + } + + + private int ipAddressToInt(final String ip) { + InetAddress inetAddress = null; try { - this.serverTracer.setServerSend(); - } - finally { - this.serverTracer.clearCurrentSpan(); + inetAddress = InetAddress.getByName(ip); + } catch (final UnknownHostException e) { + throw new IllegalArgumentException(e); } + return ByteBuffer.wrap(inetAddress.getAddress()).getInt(); } - @Data - private static class TraceData { - private Long traceId; - private Long spanId; - private Long parentSpanId; - private String spanName; + /** + * Add annotations from the sleuth Span. + */ + private List createZipkinAnnotations(Span span, + Endpoint endpoint) { + List annotationList = new ArrayList<>(); + + long srTime = 0, csTime = 0; + // add sleuth time annotation + for (TimelineAnnotation ta : span.getTimelineAnnotations()) { + Annotation zipkinAnnotation = createZipkinAnnotation(ta.getMsg(), ta.getTime(), 0, endpoint, true); + if (zipkinCoreConstants.SERVER_RECV.equals(ta.getMsg())) { + srTime = ta.getTime(); + } + if (zipkinCoreConstants.SERVER_SEND.equals(ta.getMsg()) && srTime!=0) { + zipkinAnnotation.setDuration(new Long(ta.getTime() - srTime).intValue()*1000); + } + if (zipkinCoreConstants.CLIENT_SEND.equals(ta.getMsg())) { + csTime = ta.getTime(); + } + if (zipkinCoreConstants.CLIENT_RECV.equals(ta.getMsg()) && csTime!=0) { + zipkinAnnotation.setDuration(new Long(ta.getTime() - csTime).intValue()*1000); + } + annotationList.add(zipkinAnnotation); + } + return annotationList; + } + + /** + * Creates a list of Annotations that are present in sleuth Span object. + * + * @return list of Annotations that could be added to Zipkin Span. + */ + private List createZipkinBinaryAnnotations(Span span, + Endpoint endpoint) { + List l = new ArrayList<>(); + for (Map.Entry e : span.getKVAnnotations().entrySet()) { + BinaryAnnotation binaryAnn = new BinaryAnnotation(); + binaryAnn.setAnnotation_type(AnnotationType.BYTES); + binaryAnn.setKey(e.getKey()); + try { + binaryAnn.setValue(e.getValue().getBytes("UTF-8")); + } catch (UnsupportedEncodingException ex) { + log.error("Error encoding string as UTF-8", ex); + } + binaryAnn.setHost(endpoint); + l.add(binaryAnn); + } + return l; + } + + /** + * Create an annotation with the correct times and endpoint. + * + * @param value Annotation value + * @param time timestamp will be extracted + * @param endpoint the endpoint this annotation will be associated with. + * @param sendRequest use the first or last timestamp. + */ + private static Annotation createZipkinAnnotation(String value, long time, int duration, + Endpoint endpoint, boolean sendRequest) { + Annotation annotation = new Annotation(); + annotation.setHost(endpoint); + + // Zipkin is in microseconds + if (sendRequest) { + annotation.setTimestamp(time * 1000); + } else { + annotation.setTimestamp(time * 1000); + } + + if (duration > 0) { + annotation.setDuration(duration * 1000); + } + annotation.setValue(value); + return annotation; } private static long hash(String string) {