+ * This would trace 50% of all gets, 75% of all puts and would not trace any other requests.
+ */
+public interface Sampler {
+ boolean next(T info);
+}
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
new file mode 100644
index 000000000..14117f572
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java
@@ -0,0 +1,99 @@
+package org.springframework.cloud.sleuth;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base interface for gathering and reporting statistics about a block of
+ * execution.
+ *
+ * Spans should form a directed acyclic graph structure. It should be possible
+ * to keep following the parents of a span until you arrive at a span with no
+ * parents.
+ */
+public interface Span {
+ /**
+ * The block has completed, stop the clock
+ */
+ void stop();
+
+ /**
+ * Get the start time, in milliseconds
+ */
+ long getBegin();
+
+ /**
+ * Get the stop time, in milliseconds
+ */
+ long getEnd();
+
+ /**
+ * Return the total amount of time elapsed since start was called, if running,
+ * or difference between stop and start
+ */
+ long getAccumulatedMillis();
+
+ /**
+ * Has the span been started and not yet stopped?
+ */
+ 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
+ */
+ void addKVAnnotation(String key, String value);
+
+ /**
+ * Add a timeline annotation associated with this span
+ */
+ void addTimelineAnnotation(String msg);
+
+ /**
+ * Get data associated with this span (read only)
+ *
+ * Will never be null.
+ */
+ Map getKVAnnotations();
+
+ /**
+ * Get any timeline annotations (read only)
+ *
+ * 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/TimelineAnnotation.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TimelineAnnotation.java
new file mode 100644
index 000000000..7c31d6845
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TimelineAnnotation.java
@@ -0,0 +1,12 @@
+package org.springframework.cloud.sleuth;
+
+import lombok.Data;
+
+/**
+ * @author Spencer Gibb
+ */
+@Data
+public class TimelineAnnotation {
+ private final long time;
+ private final String msg;
+}
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
new file mode 100644
index 000000000..6c9f04116
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Trace.java
@@ -0,0 +1,85 @@
+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.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ *
+ * 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).
+ *
+ */
+public interface Trace {
+
+ String SPAN_ID_NAME = "Span-Id";
+ String TRACE_ID_NAME = "Trace-Id";
+
+ /**
+ * Creates a new trace scope.
+ *
+ * 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.
+ *
+ * @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.
+ *
+ * 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);
+
+ /**
+ * Pick up an existing span from another thread.
+ */
+ TraceScope continueSpan(Span s);
+
+ /**
+ * Adds a data annotation to the current span if tracing is currently on.
+ */
+ void addKVAnnotation(String key, String value);
+
+ Callable wrap(Callable callable);
+
+ Runnable wrap(Runnable runnable);
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceAutoConfiguration.java
new file mode 100644
index 000000000..84ff1e3b6
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceAutoConfiguration.java
@@ -0,0 +1,33 @@
+package org.springframework.cloud.sleuth;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.cloud.sleuth.sampler.IsTracingSampler;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @author Spencer Gibb
+ */
+@Configuration
+public class TraceAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public IdGenerator idGenerator() {
+ return new RandomUuidGenerator();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public Sampler defaultSampler() {
+ return new IsTracingSampler();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ 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/TraceContextHolder.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceContextHolder.java
new file mode 100644
index 000000000..f544c1a9d
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceContextHolder.java
@@ -0,0 +1,27 @@
+package org.springframework.cloud.sleuth;
+
+import lombok.extern.apachecommons.CommonsLog;
+import org.springframework.core.NamedThreadLocal;
+
+/**
+ * @author Spencer Gibb
+ */
+@CommonsLog
+public class TraceContextHolder {
+ private static final ThreadLocal currentSpan = new NamedThreadLocal<>("Trace Context");
+
+ public static Span getCurrentSpan() {
+ return currentSpan.get();
+ }
+
+ public static void setCurrentSpan(Span span) {
+ if (log.isTraceEnabled()) {
+ log.trace("Setting current span " + span);
+ }
+ currentSpan.set(span);
+ }
+
+ public static boolean isTracing() {
+ return currentSpan.get() != null;
+ }
+}
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
new file mode 100644
index 000000000..5f8485eab
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceInfo.java
@@ -0,0 +1,12 @@
+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
new file mode 100644
index 000000000..695f913b0
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/TraceScope.java
@@ -0,0 +1,86 @@
+package org.springframework.cloud.sleuth;
+
+import java.io.Closeable;
+
+import lombok.SneakyThrows;
+import lombok.Value;
+import lombok.experimental.NonFinal;
+
+import org.springframework.cloud.sleuth.event.SpanStoppedEvent;
+import org.springframework.context.ApplicationEventPublisher;
+
+/**
+ * @author Spencer Gibb
+ */
+@Value
+@NonFinal
+public class TraceScope implements Closeable {
+
+ private final ApplicationEventPublisher publisher;
+
+ /**
+ * the span for this scope
+ */
+ private final Span span;
+
+ /**
+ * the span that was "current" before this scope was entered
+ */
+ private final Span savedSpan;
+
+ @NonFinal
+ private boolean detached = false;
+
+ public TraceScope(ApplicationEventPublisher publisher, Span span, Span savedSpan) {
+ this.publisher = publisher;
+ this.span = span;
+ this.savedSpan = savedSpan;
+ }
+
+ /**
+ * 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 (detached) {
+ Utils.error("Tried to detach trace span " + span + " but " +
+ "it has already been detached.");
+ }
+ detached = true;
+
+ Span cur = TraceContextHolder.getCurrentSpan();
+ if (cur != span) {
+ Utils.error("Tried to detach trace span " + 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(savedSpan);
+ }
+ return span;
+ }
+
+ @Override
+ @SneakyThrows
+ public void close() {
+ if (detached) {
+ return;
+ }
+ detached = true;
+ Span cur = TraceContextHolder.getCurrentSpan();
+ if (cur != span) {
+ Utils.error("Tried to close trace span " + span + " but " +
+ "it is not the current span for the " +
+ Thread.currentThread().getName() + " thread. You have " +
+ "probably forgotten to close or detach " + cur);
+ } else {
+ span.stop();
+ this.publisher.publishEvent(new SpanStoppedEvent(this, span));
+ TraceContextHolder.setCurrentSpan(savedSpan);
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Utils.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Utils.java
new file mode 100644
index 000000000..09c4dbac5
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Utils.java
@@ -0,0 +1,14 @@
+package org.springframework.cloud.sleuth;
+
+import lombok.extern.apachecommons.CommonsLog;
+
+/**
+ * @author Spencer Gibb
+ */
+@CommonsLog
+public abstract class Utils {
+ public static void error(String msg) {
+ log.error(msg);
+ throw new RuntimeException(msg);
+ }
+}
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
new file mode 100644
index 000000000..49e5a7cdb
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStartedEvent.java
@@ -0,0 +1,19 @@
+package org.springframework.cloud.sleuth.event;
+
+import lombok.Value;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author Spencer Gibb
+ */
+@Value
+public class SpanStartedEvent extends ApplicationEvent {
+
+ private final Span span;
+
+ public SpanStartedEvent(Object source, Span span) {
+ super(source);
+ 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
new file mode 100644
index 000000000..5272bfc92
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/event/SpanStoppedEvent.java
@@ -0,0 +1,19 @@
+package org.springframework.cloud.sleuth.event;
+
+import lombok.Value;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.context.ApplicationEvent;
+
+/**
+ * @author Spencer Gibb
+ */
+@Value
+public class SpanStoppedEvent extends ApplicationEvent {
+
+ private final Span span;
+
+ public SpanStoppedEvent(Object source, Span span) {
+ super(source);
+ 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
new file mode 100644
index 000000000..c2aaa1e1f
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceCallable.java
@@ -0,0 +1,47 @@
+package org.springframework.cloud.sleuth.instrument;
+
+import java.util.concurrent.Callable;
+
+import lombok.Value;
+
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceScope;
+
+/**
+ * @author Spencer Gibb
+ */
+@Value
+public class TraceCallable extends TraceDelegate> implements Callable {
+
+ public TraceCallable(Trace trace, Callable delagate) {
+ super(trace, delagate);
+ }
+
+ public TraceCallable(Trace trace, Callable delagate, Span parent) {
+ super(trace, delagate, parent);
+ }
+
+ public TraceCallable(Trace trace, Callable delagate, Span parent, String name) {
+ super(trace, delagate, parent, name);
+ }
+
+ @Override
+ public V call() throws Exception {
+ if (this.parent != null) {
+ TraceScope scope = startSpan();
+
+ try {
+ return this.delagate.call();
+ }
+ finally {
+ scope.close();
+ }
+
+ }
+ else {
+ return this.delagate.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
new file mode 100644
index 000000000..458de89d4
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceDelegate.java
@@ -0,0 +1,40 @@
+package org.springframework.cloud.sleuth.instrument;
+
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceContextHolder;
+import org.springframework.cloud.sleuth.TraceScope;
+
+/**
+ * @author Spencer Gibb
+ */
+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);
+ }
+
+ public TraceDelegate(Trace trace, T delagate, Span parent) {
+ this(trace, delagate, parent, null);
+ }
+
+ public TraceDelegate(Trace trace, T delagate, Span parent, String name) {
+ this.trace = trace;
+ this.delagate = delagate;
+ this.parent = parent;
+ this.name = name;
+ }
+
+ protected TraceScope startSpan() {
+ return this.trace.startSpan(getSpanName(), this.parent);
+ }
+
+ protected String getSpanName() {
+ return this.name == null ? Thread.currentThread().getName() : 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
new file mode 100644
index 000000000..259360f6f
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/TraceRunnable.java
@@ -0,0 +1,43 @@
+package org.springframework.cloud.sleuth.instrument;
+
+import lombok.Value;
+
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceScope;
+
+/**
+ * @author Spencer Gibb
+ */
+@Value
+public class TraceRunnable extends TraceDelegate implements Runnable {
+
+ public TraceRunnable(Trace trace, Runnable delagate) {
+ super(trace, delagate);
+ }
+
+ public TraceRunnable(Trace trace, Runnable delagate, Span parent) {
+ super(trace, delagate, parent);
+ }
+
+ public TraceRunnable(Trace trace, Runnable delagate, Span parent, String name) {
+ super(trace, delagate, parent, name);
+ }
+
+ @Override
+ public void run() {
+ if (this.parent != null) {
+ TraceScope scope = startSpan();
+
+ try {
+ this.delagate.run();
+ }
+ finally {
+ scope.close();
+ }
+ }
+ else {
+ this.delagate.run();
+ }
+ }
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCommand.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCommand.java
new file mode 100644
index 000000000..60fb09713
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/circuitbreaker/TraceCommand.java
@@ -0,0 +1,59 @@
+package org.springframework.cloud.sleuth.instrument.circuitbreaker;
+
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.HystrixCommandGroupKey;
+import com.netflix.hystrix.HystrixThreadPoolKey;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceScope;
+
+/**
+ * Abstraction over {@code HystrixCommand} that wraps command execution with Trace setting
+ *
+ * @see HystrixCommand
+ * @see Trace
+ *
+ * @author Tomasz Nurkiewicz, 4financeIT
+ * @author Marcin Grzejszczak, 4financeIT
+ * @author Spencer Gibb
+ */
+public abstract class TraceCommand extends HystrixCommand {
+
+ private Trace trace;
+
+ protected TraceCommand(Trace trace, HystrixCommandGroupKey group) {
+ super(group);
+ this.trace = trace;
+ }
+
+ protected TraceCommand(Trace trace, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
+ super(group, threadPool);
+ this.trace = trace;
+ }
+
+ protected TraceCommand(Trace trace, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
+ super(group, executionIsolationThreadTimeoutInMilliseconds);
+ this.trace = trace;
+ }
+
+ protected TraceCommand(Trace trace, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
+ super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
+ this.trace = trace;
+ }
+
+ protected TraceCommand(Trace trace, Setter setter) {
+ super(setter);
+ this.trace = trace;
+ }
+
+ @Override
+ protected R run() throws Exception {
+ TraceScope scope = trace.startSpan(getCommandKey().name());
+ try {
+ return doRun();
+ } finally {
+ scope.close();
+ }
+ }
+
+ public abstract R doRun() throws Exception;
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java
new file mode 100644
index 000000000..cfa788e7f
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAspect.java
@@ -0,0 +1,39 @@
+package org.springframework.cloud.sleuth.instrument.scheduling;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceScope;
+import org.springframework.scheduling.annotation.Scheduled;
+
+/**
+ * Aspect that creates a new Span for running threads executing methods annotated with {@link Scheduled} annotation.
+ * For every execution of scheduled method a new trace will be started.
+ *
+ * @author Tomasz Nurkewicz, 4financeIT
+ * @author Michal Chmielarz, 4financeIT
+ * @author Marcin Grzejszczak, 4financeIT
+ * @author Spencer Gibb
+ *
+ * @see Trace
+ */
+@Aspect
+public class TraceSchedulingAspect {
+
+ private final Trace trace;
+
+ public TraceSchedulingAspect(Trace trace) {
+ this.trace = trace;
+ }
+
+ @Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..))")
+ public Object traceSceduledThread(final ProceedingJoinPoint pjp) throws Throwable {
+ TraceScope scope = trace.startSpan(pjp.toShortString());
+ try {
+ return pjp.proceed();
+ } finally {
+ scope.close();
+ }
+ }
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java
new file mode 100644
index 000000000..8444b4793
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/scheduling/TraceSchedulingAutoConfiguration.java
@@ -0,0 +1,33 @@
+package org.springframework.cloud.sleuth.instrument.scheduling;
+
+/**
+ * @author Spencer Gibb
+ */
+
+import org.aspectj.lang.ProceedingJoinPoint;
+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 org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableScheduling;
+
+/**
+ * Registers beans related to task scheduling.
+ *
+ * @see TraceSchedulingAspect
+ *
+ * @author Michal Chmielarz, 4financeIT
+ * @author Spencer Gibb
+ */
+@Configuration
+@EnableScheduling
+@EnableAspectJAutoProxy
+@ConditionalOnClass(ProceedingJoinPoint.class)
+public class TraceSchedulingAutoConfiguration {
+
+ @Bean
+ public TraceSchedulingAspect traceSchedulingAspect(Trace trace) {
+ return new TraceSchedulingAspect(trace);
+ }
+}
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
new file mode 100644
index 000000000..b6dee3321
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2012-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.instrument.web;
+
+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 java.io.IOException;
+import java.util.regex.Pattern;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceInfo;
+import org.springframework.cloud.sleuth.TraceScope;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+/**
+ * Filter that takes the value of the {@link Trace#SPAN_ID_NAME} and
+ * {@link Trace#TRACE_ID_NAME} header from either request or response and uses them to
+ * create a new span.
+ *
+ * @see Trace
+ *
+ * @author Jakub Nabrdalik, 4financeIT
+ * @author Tomasz Nurkiewicz, 4financeIT
+ * @author Marcin Grzejszczak, 4financeIT
+ * @author Spencer Gibb
+ */
+public class TraceFilter extends OncePerRequestFilter {
+
+ public static final Pattern DEFAULT_SKIP_PATTERN = Pattern
+ .compile("/api-docs.*|/autoconfig|/configprops|/dump|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html");
+
+ private final Trace trace;
+ private final Pattern skipPattern;
+
+ public TraceFilter(Trace trace) {
+ this.trace = trace;
+ this.skipPattern = DEFAULT_SKIP_PATTERN;
+ }
+
+ public TraceFilter(Trace trace, Pattern skipPattern) {
+ this.trace = trace;
+ this.skipPattern = skipPattern;
+ }
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request,
+ HttpServletResponse response, FilterChain filterChain)
+ throws ServletException, IOException {
+
+ String uri = hasText(request.getRequestURI()) ? request.getRequestURI() : "";
+ boolean skip = 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);
+ if (hasText(spanId) && hasText(traceId)) {
+
+ TraceInfo traceInfo = new TraceInfo(traceId, spanId);
+ // TODO: trace description?
+ traceScope = trace.startSpan("traceFilter", traceInfo);
+ // Send new span id back
+ addToResponseIfNotPresent(response, SPAN_ID_NAME, traceScope.getSpan()
+ .getSpanId());
+ }
+ else {
+ traceScope = trace.startSpan("traceFilter");
+ }
+ }
+
+ try {
+ filterChain.doFilter(request, response);
+ }
+ finally {
+ if (traceScope != null) {
+ traceScope.close();
+ }
+ }
+ }
+
+ private String getHeader(HttpServletRequest request, HttpServletResponse response,
+ String name) {
+ String value = request.getHeader(name);
+ return hasText(value) ? value : response.getHeader(name);
+ }
+
+ private void addToResponseIfNotPresent(HttpServletResponse response, String name,
+ String value) {
+ if (!hasText(response.getHeader(name))) {
+ response.addHeader(name, value);
+ }
+ }
+
+ @Override
+ protected boolean shouldNotFilterAsyncDispatch() {
+ return false;
+ }
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHandlerInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHandlerInterceptor.java
new file mode 100644
index 000000000..51c46efd8
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceHandlerInterceptor.java
@@ -0,0 +1,45 @@
+package org.springframework.cloud.sleuth.instrument.web;
+
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceScope;
+import org.springframework.web.servlet.HandlerInterceptor;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author Spencer Gibb
+ */
+public class TraceHandlerInterceptor implements HandlerInterceptor {
+
+ private static final String ATTR_NAME = "__CURRENT_TRACE_HANDLER_TRACE_SCOPE_ATTR___";
+
+ private final Trace trace;
+
+ public TraceHandlerInterceptor(Trace trace) {
+ this.trace = trace;
+ }
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ //TODO: get trace data from request?
+ //TODO: what is the description?
+ TraceScope scope = trace.startSpan("traceHandlerInterceptor");
+ request.setAttribute(ATTR_NAME, scope);
+ return true;
+ }
+
+ @Override
+ public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
+
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ TraceScope scope = TraceScope.class.cast(request.getAttribute(ATTR_NAME));
+ if (scope != null) {
+ scope.close();
+ }
+ }
+}
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
new file mode 100644
index 000000000..7ffe04d2b
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAspect.java
@@ -0,0 +1,82 @@
+package org.springframework.cloud.sleuth.instrument.web;
+
+import java.util.concurrent.Callable;
+
+import lombok.extern.apachecommons.CommonsLog;
+
+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.Trace;
+import org.springframework.cloud.sleuth.TraceContextHolder;
+import org.springframework.cloud.sleuth.instrument.TraceCallable;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestOperations;
+
+/**
+ * Aspect that adds correlation id to
+ *
+ *
+ *
{@link RestController} annotated classes with public {@link Callable} methods
+ *
{@link Controller} annotated classes with public {@link Callable} methods
+ *
+ *
+ * For controllers an around aspect is created that wraps the {@link Callable#call()}
+ * method execution in {@link TraceCallable}
+ *
+ *
+ * @see RestController
+ * @see Controller
+ * @see RestOperations
+ * @see TraceCallable
+ * @see Trace
+ *
+ * @author Tomasz Nurkewicz, 4financeIT
+ * @author Marcin Grzejszczak, 4financeIT
+ * @author Michal Chmielarz, 4financeIT
+ * @author Spencer Gibb
+ */
+@Aspect
+@CommonsLog
+public class TraceWebAspect {
+
+ private final Trace trace;
+
+ public TraceWebAspect(Trace trace) {
+ this.trace = trace;
+ }
+
+ @Pointcut("@target(org.springframework.web.bind.annotation.RestController)")
+ private void anyRestControllerAnnotated() {
+ }
+
+ @Pointcut("@target(org.springframework.stereotype.Controller)")
+ private void anyControllerAnnotated() {
+ }
+
+ @Pointcut("execution(public java.util.concurrent.Callable *(..))")
+ private void anyPublicMethodReturningCallable() {
+ }
+
+ @Pointcut("(anyRestControllerAnnotated() || anyControllerAnnotated()) && anyPublicMethodReturningCallable()")
+ private void anyControllerOrRestControllerWithPublicAsyncMethod() {
+ }
+
+ @Around("anyControllerOrRestControllerWithPublicAsyncMethod()")
+ @SuppressWarnings("unchecked")
+ public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
+ Callable callable = (Callable) pjp.proceed();
+ if (TraceContextHolder.isTracing()) {
+ log.debug("Wrapping callable with span ["
+ + TraceContextHolder.getCurrentSpan() + "]");
+
+ return new TraceCallable(this.trace, callable);
+ }
+ else {
+ return callable;
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java
new file mode 100644
index 000000000..321c75dd2
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceWebAutoConfiguration.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012-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.instrument.web;
+
+import java.util.regex.Pattern;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.context.embedded.FilterRegistrationBean;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.StringUtils;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+/**
+ * Registers beans that add tracing to requests
+ *
+ * @author Tomasz Nurkewicz, 4financeIT
+ * @author Marcin Grzejszczak, 4financeIT
+ * @author Michal Chmielarz, 4financeIT
+ * @author Spencer Gibb
+ */
+@Configuration
+@ConditionalOnProperty(value = "spring.cloud.sleuth.trace.web.enabled", matchIfMissing = true)
+@ConditionalOnWebApplication
+public class TraceWebAutoConfiguration {
+
+ /**
+ * Pattern for URLs that should be skipped in tracing
+ */
+ @Value("${spring.cloud.sleuth.instrument.web.skipPattern:}")
+ private String skipPattern;
+
+ @Autowired
+ private Trace trace;
+
+ @Bean
+ @ConditionalOnMissingBean
+ public TraceWebAspect traceWebAspect() {
+ return new TraceWebAspect(trace);
+ }
+
+ //TODO: I don't think TraceHandlerInterceptor is needed with TraceFilter
+ /*@Bean
+ @ConditionalOnMissingBean
+ public TraceHandlerInterceptor traceHandlerInterceptor() {
+ return new TraceHandlerInterceptor(trace);
+ }
+
+ @Bean
+ public WebMvcConfigurerAdapter webMvcConfigurerAdapter(
+ TraceHandlerInterceptor handlerInterceptor) {
+ return new TraceWebConfigurer(handlerInterceptor);
+ }
+
+ protected static class TraceWebConfigurer extends WebMvcConfigurerAdapter {
+ private TraceHandlerInterceptor interceptor;
+
+ public TraceWebConfigurer(TraceHandlerInterceptor interceptor) {
+ this.interceptor = interceptor;
+ }
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ registry.addInterceptor(interceptor).addPathPatterns("/**");
+ }
+ }*/
+
+ @Bean
+ @ConditionalOnMissingBean
+ public FilterRegistrationBean traceFilter() {
+ Pattern pattern = StringUtils.hasText(skipPattern) ? Pattern.compile(skipPattern)
+ : TraceFilter.DEFAULT_SKIP_PATTERN;
+ return new FilterRegistrationBean(new TraceFilter(trace, pattern));
+ }
+
+}
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
new file mode 100644
index 000000000..3be3db292
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2012-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.instrument.web.client;
+
+import static org.springframework.cloud.sleuth.Trace.SPAN_ID_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.Trace;
+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.
+ *
+ * @see org.springframework.web.client.RestTemplate
+ * @see Trace
+ *
+ * @author Marcin Grzejszczak, 4financeIT
+ * @author Spencer Gibb
+ */
+public class TraceRestTemplateInterceptor implements ClientHttpRequestInterceptor {
+
+ @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);
+ }
+
+ public void setHeader(HttpRequest request, String name, String value) {
+ if (!request.getHeaders().containsKey(name) && isTracing()) {
+ request.getHeaders().add(name, value);
+ }
+ }
+
+}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java
new file mode 100644
index 000000000..c45cabd06
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceWebClientAutoConfiguration.java
@@ -0,0 +1,50 @@
+package org.springframework.cloud.sleuth.instrument.web.client;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.client.RestTemplate;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * @author Spencer Gibb
+ */
+@Configuration
+@ConditionalOnProperty(value = "spring.cloud.sleuth.trace.web.client.enabled", matchIfMissing = true)
+@ConditionalOnClass(RestTemplate.class)
+public class TraceWebClientAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ public TraceRestTemplateInterceptor traceRestTemplateInterceptor() {
+ return new TraceRestTemplateInterceptor();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean
+ public RestTemplate restTemplate() {
+ return new RestTemplate();
+ }
+
+ @Configuration
+ protected static class TraceInterceptorConfiguration {
+
+ @Autowired(required = false)
+ private RestTemplate restTemplate;
+
+ @Autowired
+ private TraceRestTemplateInterceptor traceRestTemplateInterceptor;
+
+ @PostConstruct
+ public void init() {
+ if (restTemplate != null) {
+ restTemplate.getInterceptors().add(traceRestTemplateInterceptor);
+ }
+ }
+ }
+}
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
new file mode 100644
index 000000000..910dbadb3
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePostFilter.java
@@ -0,0 +1,46 @@
+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
new file mode 100644
index 000000000..f48d377f6
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreFilter.java
@@ -0,0 +1,65 @@
+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
new file mode 100644
index 000000000..18d542435
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceZuulAutoConfiguration.java
@@ -0,0 +1,28 @@
+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/resttemplate/DefaultRestTemplateConfigurer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/DefaultRestTemplateConfigurer.java
deleted file mode 100644
index f7e33be80..000000000
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/DefaultRestTemplateConfigurer.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright 2012-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.resttemplate;
-
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.List;
-
-/**
- * Default configure of {@code RestTemplate} that adds a list {@code ClientHttpRequestInterceptor}
- * to {@code RestTemplate}
- *
- * @see ClientHttpRequestInterceptor
- * @see RestTemplate
- *
- * @author Marcin Grzejszczak, 4financeIT
- */
-public class DefaultRestTemplateConfigurer implements RestTemplateConfigurer {
-
- private final List clientHttpRequestInterceptors;
-
- public DefaultRestTemplateConfigurer(List clientHttpRequestInterceptors) {
- this.clientHttpRequestInterceptors = clientHttpRequestInterceptors;
- }
-
- @Override
- public void modifyRestTemplate(RestTemplate restTemplate) {
- for (ClientHttpRequestInterceptor clientHttpRequestInterceptor : clientHttpRequestInterceptors) {
- restTemplate.getInterceptors().add(clientHttpRequestInterceptor);
- }
- }
-}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/RestTemplateConfigurer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/RestTemplateConfigurer.java
deleted file mode 100644
index 54b643d40..000000000
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/RestTemplateConfigurer.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright 2012-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.resttemplate;
-
-import org.springframework.web.client.RestTemplate;
-
-/**
- * Interface that allows to modify the {@code RestTemplate} parameters
- *
- * @see RestTemplate
- *
- * @author Marcin Grzejszczak, 4financeIT
- */
-public interface RestTemplateConfigurer {
-
- void modifyRestTemplate(RestTemplate restTemplate);
-}
diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/SleuthRestTemplateAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/SleuthRestTemplateAutoConfiguration.java
deleted file mode 100644
index 662176624..000000000
--- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/resttemplate/SleuthRestTemplateAutoConfiguration.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2012-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.resttemplate;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.http.client.ClientHttpRequestInterceptor;
-import org.springframework.web.client.RestTemplate;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- *
- * Autoconfiguration that sets up a {@code RestTemplate} as a bean if one is not present
- * and performs its additional modification via a {@code RestTemplateConfigurer}.
- *
- * @author Marcin Grzejszczak, 4financeIT
- */
-@Configuration
-@ConditionalOnWebApplication
-@ConditionalOnProperty(value = "spring.cloud.sleuth.resttemplate.enabled", matchIfMissing = true)
-public class SleuthRestTemplateAutoConfiguration {
-
- @Configuration
- protected static class RestTemplateConfig {
-
- @Autowired
- private List clientHttpRequestInterceptors = new ArrayList<>();
-
- @Bean
- @ConditionalOnMissingBean
- public RestTemplate restTemplate() {
- return new RestTemplate();
- }
-
- @Bean
- @ConditionalOnMissingBean
- public RestTemplateConfigurer restTemplateConfigurer(RestTemplate restTemplate) {
- DefaultRestTemplateConfigurer configurer = new DefaultRestTemplateConfigurer(clientHttpRequestInterceptors);
- configurer.modifyRestTemplate(restTemplate);
- return configurer;
- }
- }
-
-}
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
new file mode 100644
index 000000000..087912506
--- /dev/null
+++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/sampler/AlwaysSampler.java
@@ -0,0 +1,13 @@
+package org.springframework.cloud.sleuth.sampler;
+
+import org.springframework.cloud.sleuth.Sampler;
+
+/**
+ * @author Spencer Gibb
+ */
+public class AlwaysSampler implements Samplerorg.springframework.cloud
- spring-cloud-sleuth-zipkin
+ spring-cloud-sleuth-core
+
+
+ org.springframework.boot
+ spring-boot-starter-aoporg.projectlombok
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 ace29542f..8897123cf 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,64 +1,45 @@
package org.springframework.cloud.sleuth.sample;
-import java.util.Random;
-
-import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
-import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
-import org.springframework.context.ApplicationListener;
+import org.springframework.cloud.sleuth.Sampler;
+import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
+import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-import org.springframework.web.client.RestTemplate;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.scheduling.annotation.EnableAsync;
/**
* @author Spencer Gibb
*/
@Configuration
@EnableAutoConfiguration
-@RestController
+@EnableAspectJAutoProxy(proxyTargetClass = true)
+@EnableAsync
@Slf4j
-public class SampleApplication implements ApplicationListener {
+public class SampleApplication {
public static final String CLIENT_NAME = "testApp";
- @Autowired
- private RestTemplate restTemplate;
- private int port;
-
- @SneakyThrows
- @RequestMapping("/")
- public String hi() {
- final Random random = new Random();
- Thread.sleep(random.nextInt(1000));
-
- String s = restTemplate.getForObject("http://localhost:" + port + "/hi2", String.class);
- return "hi/"+s;
+ @Bean
+ public Sampler defaultSampler() {
+ return new AlwaysSampler();
}
- @SneakyThrows
- @RequestMapping("/hi2")
- public String hi2() {
- final Random random = new Random();
- Thread.sleep(random.nextInt(1000));
- return "hi2";
- }
+ @Bean
+ public SampleController sampleController() {
+ return new SampleController();
+ }
public static void main(String[] args) {
SpringApplication.run(SampleApplication.class, args);
}
- /*@Bean
- public SpanCollector spanCollector() {
- return new LoggingSpanCollectorImpl();
- }*/
+ /*
+ * @Bean public SpanCollector spanCollector() { return new LoggingSpanCollectorImpl();
+ * }
+ */
- @Override
- public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
- port = event.getEmbeddedServletContainer().getPort();
- }
}
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
new file mode 100644
index 000000000..242ce39e0
--- /dev/null
+++ b/spring-cloud-sleuth-sample/src/main/java/org/springframework/cloud/sleuth/sample/SampleController.java
@@ -0,0 +1,82 @@
+package org.springframework.cloud.sleuth.sample;
+
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.context.embedded.EmbeddedServletContainerInitializedEvent;
+import org.springframework.cloud.sleuth.Span;
+import org.springframework.cloud.sleuth.Trace;
+import org.springframework.cloud.sleuth.TraceContextHolder;
+import org.springframework.cloud.sleuth.TraceScope;
+import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
+import org.springframework.context.ApplicationListener;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.client.RestTemplate;
+
+import java.util.Random;
+import java.util.concurrent.Callable;
+
+/**
+ * @author Spencer Gibb
+ */
+@Slf4j
+@RestController
+class SampleController implements
+ ApplicationListener {
+ @Autowired
+ private RestTemplate restTemplate;
+ @Autowired
+ private Trace trace;
+ private int port;
+
+ @SneakyThrows
+ @RequestMapping("/")
+ public String hi() {
+ final Random random = new Random();
+ Thread.sleep(random.nextInt(1000));
+
+ String s = restTemplate.getForObject("http://localhost:" + port + "/hi2",
+ String.class);
+ return "hi/" + s;
+ }
+
+ @RequestMapping("/call")
+ public Callable call() {
+ return new Callable() {
+ @Override
+ public String call() throws Exception {
+ Span currentSpan = TraceContextHolder.getCurrentSpan();
+ return "async hi: "+currentSpan;
+ }
+ };
+ }
+
+
+ @SneakyThrows
+ @RequestMapping("/hi2")
+ public String hi2() {
+ final Random random = new Random();
+ Thread.sleep(random.nextInt(1000));
+ return "hi2";
+ }
+
+ @SneakyThrows
+ @RequestMapping("/traced")
+ public String traced() {
+ TraceScope scope = trace.startSpan("customTraceEndpoint", new AlwaysSampler());
+ final Random random = new Random();
+ int millis = random.nextInt(1000);
+ log.info("Sleeping for {} millis", millis);
+ Thread.sleep(millis);
+
+ String s = restTemplate.getForObject("http://localhost:" + port + "/hi2", String.class);
+ scope.close();
+ return "hi/" + s;
+ }
+
+ @Override
+ public void onApplicationEvent(EmbeddedServletContainerInitializedEvent event) {
+ port = event.getEmbeddedServletContainer().getPort();
+ }
+}
diff --git a/spring-cloud-sleuth-sample/src/main/resources/logback.xml b/spring-cloud-sleuth-sample/src/main/resources/logback.xml
new file mode 100644
index 000000000..1698a3b29
--- /dev/null
+++ b/spring-cloud-sleuth-sample/src/main/resources/logback.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file