Big refactor of sleuth core API

Instrumentation should be able to get by with only 2
interfaces: TraceManager and TraceAccessor (the former is
not needed if you aren't starting a new Span). No explicit
access to thread locals or manipulation of thread context
is required (except locally where necessary).

A Span is enclosed by a Trace (actually a view of the complete
Trace that would be constructed remotely).
This commit is contained in:
Dave Syer
2015-09-09 15:00:12 +01:00
parent 8351c9ba2c
commit ffa97c1f20
58 changed files with 1005 additions and 917 deletions

View File

@@ -46,7 +46,7 @@ public class MilliSpan implements Span {
private final List<TimelineAnnotation> timelineAnnotations = new ArrayList<>();
public MilliSpan(long begin, long end, String name, String traceId, List<String> parents, String spanId, boolean remote, String processId) {
this.begin = begin;
this.begin = begin<=0 ? System.currentTimeMillis() : begin;
this.end = end;
this.name = name;
this.traceId = traceId;

View File

@@ -18,95 +18,62 @@ package org.springframework.cloud.sleuth;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import lombok.Value;
import lombok.experimental.NonFinal;
/**
* 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:
* <ul>
* <li>Create a new Span which has this thread's currentSpan as one of its parents.</li>
* <li>Set currentSpan to the new Span.</li>
* <li>Create a TraceSpan object to manage the new Span.</li>
* </ul>
*
* Closing a TraceScope does a few things:
* <ul>
* <li>It closes the span which the scope was managing.</li>
* <li>Set currentSpan to the previous currentSpan (which may be null).</li>
* </ul>
* @author Spencer Gibb
*/
public interface Trace {
@Value
@NonFinal
public class Trace {
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";
String NOT_SAMPLED_NAME = "X-Not-Sampled";
public static final String NOT_SAMPLED_NAME = "X-Not-Sampled";
List<String> HEADERS = Arrays.asList(SPAN_ID_NAME, TRACE_ID_NAME,
SPAN_NAME_NAME, PARENT_ID_NAME, PROCESS_ID_NAME, NOT_SAMPLED_NAME);
public static final String PROCESS_ID_NAME = "X-Process-Id";
public static final String PARENT_ID_NAME = "X-Parent-Id";
public static final String TRACE_ID_NAME = "X-Trace-Id";
public static final String SPAN_NAME_NAME = "X-Span-Name";
public static final String SPAN_ID_NAME = "X-Span-Id";
public static final List<String> HEADERS = Arrays.asList(SPAN_ID_NAME, TRACE_ID_NAME,
SPAN_NAME_NAME, PARENT_ID_NAME, PROCESS_ID_NAME, NOT_SAMPLED_NAME);
/**
* Creates a trace scope wrapping a new span.
* <p/>
* 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.
* the span for this scope
*/
TraceScope startSpan(String name);
private final Span span;
/**
* Creates a new trace scope with a specific parent. The parent might be in another
* process or thread.
* <p/>
* 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.
* the trace that was "current" before this trace was entered
*/
TraceScope startSpan(String name, Span parent);
private final Trace savedTrace;
/**
* 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
*/
<T> TraceScope startSpan(String name, Sampler<T> sampler, T info);
@NonFinal
private boolean detached = false;
/**
* Pick up an existing span from another thread.
*/
TraceScope continueSpan(Span s);
public Trace(Trace saved, Span span) {
this.savedTrace = saved;
this.span = span;
}
/**
* Adds a data annotation to the current span if tracing is currently on.
*/
void addAnnotation(String key, String value);
public Trace(Span span) {
this(null, span);
}
<V> Callable<V> wrap(Callable<V> callable);
public void addAnnotation(String key, String value) {
if (this.span != null && !this.detached) {
this.span.addAnnotation(key, value);
}
}
public void detach() {
this.detached = true;
}
Runnable wrap(Runnable runnable);
}

View File

@@ -0,0 +1,31 @@
/*
* 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;
/**
* Strategy for accessing the current span.
*
* @author Dave Syer
*
*/
public interface TraceAccessor {
Span getCurrentSpan();
boolean isTracing();
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2013-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;
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:
* <ul>
* <li>Create a new Span which has this thread's currentSpan as one of its parents.</li>
* <li>Set currentSpan to the new Span.</li>
* <li>Create a TraceSpan object to manage the new Span.</li>
* </ul>
*
* Closing a TraceScope does a few things:
* <ul>
* <li>It closes the span which the scope was managing.</li>
* <li>Set currentSpan to the previous currentSpan (which may be null).</li>
* </ul>
*/
public interface TraceManager extends TraceAccessor {
/**
* Creates a trace wrapping a new span.
* <p/>
* 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.
*/
Trace startSpan(String name);
/**
* Creates a new trace scope with a specific parent. The parent might be in another
* process or thread.
* <p/>
* 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.
*/
Trace startSpan(String name, Span parent);
/**
* 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
*/
<T> Trace startSpan(String name, Sampler<T> sampler, T info);
/**
* Pick up an existing span from another thread.
*/
Trace continueSpan(Span s);
/**
* Adds a data annotation to the current span if tracing is currently on.
*/
void addAnnotation(String key, String value);
/**
* 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 saved trace if there was one before the trace started (null otherwise)
*/
Trace detach(Trace trace);
Trace close(Trace trace);
<V> Callable<V> wrap(Callable<V> callable);
Runnable wrap(Runnable runnable);
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2013-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;
import java.io.Closeable;
import lombok.SneakyThrows;
import lombok.Value;
import lombok.experimental.NonFinal;
import org.springframework.cloud.sleuth.event.SpanReleasedEvent;
import org.springframework.cloud.sleuth.util.ExceptionUtils;
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 (this.detached) {
ExceptionUtils.error("Tried to detach trace span but "
+ "it has already been detached: " + this.span);
}
this.detached = true;
Span cur = TraceContextHolder.getCurrentSpan();
if (cur != this.span) {
ExceptionUtils.error("Tried to detach trace span but "
+ "it is not the current span for the '"
+ Thread.currentThread().getName() + "' thread: " + this.span
+ ". You have " + "probably forgotten to close or detach " + cur);
}
else {
TraceContextHolder.setCurrentSpan(this.savedSpan);
}
return this.span;
}
@Override
@SneakyThrows
public void close() {
if (this.detached) {
return;
}
this.detached = true;
Span cur = TraceContextHolder.getCurrentSpan();
if (cur != this.span) {
ExceptionUtils.error("Tried to close trace span but "
+ "it is not the current span for the '"
+ Thread.currentThread().getName() + "' thread" + this.span
+ ". You have " + "probably forgotten to close or detach " + cur);
}
else {
this.span.stop();
if (this.savedSpan != null
&& this.span.getParents().contains(this.savedSpan.getSpanId())) {
this.publisher.publishEvent(new SpanReleasedEvent(this, this.savedSpan,
this.span));
}
else {
this.publisher.publishEvent(new SpanReleasedEvent(this, this.span));
}
TraceContextHolder.setCurrentSpan(this.savedSpan);
}
}
}

View File

@@ -14,10 +14,12 @@
* limitations under the License.
*/
package org.springframework.cloud.sleuth;
package org.springframework.cloud.sleuth.autoconfig;
import java.util.UUID;
import org.springframework.cloud.sleuth.IdGenerator;
/**
* @author Spencer Gibb
*/

View File

@@ -19,11 +19,9 @@ package org.springframework.cloud.sleuth.autoconfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.IdGenerator;
import org.springframework.cloud.sleuth.RandomUuidGenerator;
import org.springframework.cloud.sleuth.Sampler;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.sampler.IsTracingSampler;
import org.springframework.cloud.sleuth.trace.DefaultTrace;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -37,20 +35,20 @@ public class TraceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public IdGenerator idGenerator() {
public IdGenerator traceIdGenerator() {
return new RandomUuidGenerator();
}
@Bean
@ConditionalOnMissingBean
public Sampler<Void> defaultSampler() {
public Sampler<Void> defaultTraceSampler() {
return new IsTracingSampler();
}
@Bean
@ConditionalOnMissingBean
public Trace trace(Sampler<Void> sampler, IdGenerator idGenerator,
public DefaultTraceManager traceManager(Sampler<Void> sampler, IdGenerator idGenerator,
ApplicationEventPublisher publisher) {
return new DefaultTrace(sampler, idGenerator, publisher);
return new DefaultTraceManager(sampler, idGenerator, publisher);
}
}

View File

@@ -18,48 +18,36 @@ package org.springframework.cloud.sleuth.instrument;
import java.util.concurrent.Callable;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import lombok.EqualsAndHashCode;
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
@EqualsAndHashCode(callSuper=false)
public class TraceCallable<V> extends TraceDelegate<Callable<V>> implements Callable<V> {
@EqualsAndHashCode(callSuper = false)
public class TraceCallable<V> extends TraceDelegate<Callable<V>>implements Callable<V> {
public TraceCallable(Trace trace, Callable<V> delegate) {
super(trace, delegate);
public TraceCallable(TraceManager traceManager, Callable<V> delegate) {
super(traceManager, delegate);
}
public TraceCallable(Trace trace, Callable<V> delegate, Span parent) {
super(trace, delegate, parent);
}
public TraceCallable(Trace trace, Callable<V> delegate, Span parent, String name) {
super(trace, delegate, parent, name);
public TraceCallable(TraceManager traceManager, Callable<V> delegate, String name) {
super(traceManager, delegate, name);
}
@Override
public V call() throws Exception {
if (this.getParent() != null) {
TraceScope scope = startSpan();
try {
return this.getDelegate().call();
}
finally {
scope.close();
}
}
else {
Trace trace = startSpan();
try {
return this.getDelegate().call();
}
finally {
close(trace);
}
}
}

View File

@@ -16,12 +16,11 @@
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;
import org.springframework.cloud.sleuth.TraceScope;
import org.springframework.cloud.sleuth.TraceManager;
import lombok.Getter;
/**
* @author Spencer Gibb
@@ -29,28 +28,28 @@ import org.springframework.cloud.sleuth.TraceScope;
@Getter
public abstract class TraceDelegate<T> {
private final Trace trace;
private final TraceManager traceManager;
private final T delegate;
private final Span parent;
private final String name;
private final Span parent;
public TraceDelegate(Trace trace, T delegate) {
this(trace, delegate, TraceContextHolder.getCurrentSpan(), null);
public TraceDelegate(TraceManager trace, T delegate) {
this(trace, delegate, null);
}
public TraceDelegate(Trace trace, T delegate, Span parent) {
this(trace, delegate, parent, null);
}
public TraceDelegate(Trace trace, T delegate, Span parent, String name) {
this.trace = trace;
public TraceDelegate(TraceManager traceManager, T delegate, String name) {
this.traceManager = traceManager;
this.delegate = delegate;
this.parent = parent;
this.name = name;
this.parent = traceManager.getCurrentSpan();
}
protected TraceScope startSpan() {
return this.trace.startSpan(getSpanName(), this.parent);
protected void close(Trace scope) {
this.traceManager.close(scope);
}
protected Trace startSpan() {
return this.traceManager.startSpan(getSpanName(), this.parent);
}
protected String getSpanName() {

View File

@@ -16,46 +16,35 @@
package org.springframework.cloud.sleuth.instrument;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import lombok.EqualsAndHashCode;
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
@EqualsAndHashCode(callSuper=false)
public class TraceRunnable extends TraceDelegate<Runnable> implements Runnable {
@EqualsAndHashCode(callSuper = false)
public class TraceRunnable extends TraceDelegate<Runnable>implements Runnable {
public TraceRunnable(Trace trace, Runnable delagate) {
super(trace, delagate);
public TraceRunnable(TraceManager traceManager, Runnable delegate) {
super(traceManager, delegate);
}
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);
public TraceRunnable(TraceManager traceManager, Runnable delegate, String name) {
super(traceManager, delegate, name);
}
@Override
public void run() {
if (this.getParent() != null) {
TraceScope scope = startSpan();
try {
this.getDelegate().run();
}
finally {
scope.close();
}
}
else {
Trace trace = startSpan();
try {
this.getDelegate().run();
}
finally {
close(trace);
}
}
}

View File

@@ -24,7 +24,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
@@ -35,7 +35,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@Configuration
@ConditionalOnMissingBean(AsyncConfigurer.class)
@ConditionalOnProperty(value = "spring.sleuth.async.enabled", matchIfMissing = true)
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceManager.class)
@AutoConfigureAfter(AsyncCustomAutoConfiguration.class)
public class AsyncDefaultAutoConfiguration extends AsyncConfigurerSupport {

View File

@@ -18,13 +18,13 @@ package org.springframework.cloud.sleuth.instrument.async;
import java.util.concurrent.Executor;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
import lombok.RequiredArgsConstructor;
/**
* @author Dave Syer
*
@@ -32,21 +32,21 @@ import org.springframework.cloud.sleuth.instrument.TraceRunnable;
@RequiredArgsConstructor
public class LazyTraceExecutor implements Executor {
private Trace trace;
private TraceManager traceManager;
private final BeanFactory beanFactory;
private final Executor delegate;
@Override
public void execute(Runnable command) {
if (this.trace == null) {
if (this.traceManager == null) {
try {
this.trace = this.beanFactory.getBean(Trace.class);
this.traceManager = this.beanFactory.getBean(TraceManager.class);
}
catch (NoSuchBeanDefinitionException e) {
this.delegate.execute(command);
}
}
this.delegate.execute(new TraceRunnable(this.trace, command));
this.delegate.execute(new TraceRunnable(this.traceManager, command));
}
}

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.concurrent.executor;
package org.springframework.cloud.sleuth.instrument.executor;
import java.util.Collection;
import java.util.List;
@@ -24,7 +24,7 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.TraceCallable;
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
/**
@@ -34,16 +34,16 @@ import org.springframework.cloud.sleuth.instrument.TraceRunnable;
*/
public class TraceableExecutorService implements ExecutorService {
final ExecutorService delegate;
final Trace trace;
public TraceableExecutorService(final ExecutorService delegate, final Trace trace) {
final TraceManager traceManager;
public TraceableExecutorService(final ExecutorService delegate, final TraceManager traceManager) {
this.delegate = delegate;
this.trace = trace;
this.traceManager = traceManager;
}
@Override
public void execute(Runnable command) {
final Runnable r = new TraceRunnable(trace, command);
final Runnable r = new TraceRunnable(this.traceManager, command);
this.delegate.execute(r);
}
@@ -74,19 +74,19 @@ public class TraceableExecutorService implements ExecutorService {
@Override
public <T> Future<T> submit(Callable<T> task) {
Callable<T> c = new TraceCallable<>(this.trace, task);
Callable<T> c = new TraceCallable<>(this.traceManager, task);
return this.delegate.submit(c);
}
@Override
public <T> Future<T> submit(Runnable task, T result) {
Runnable r = new TraceRunnable(trace, task);
Runnable r = new TraceRunnable(this.traceManager, task);
return this.delegate.submit(r, result);
}
@Override
public Future<?> submit(Runnable task) {
Runnable r = new TraceRunnable(trace, task);
Runnable r = new TraceRunnable(this.traceManager, task);
return this.delegate.submit(r);
}

View File

@@ -14,14 +14,14 @@
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.concurrent.executor;
package org.springframework.cloud.sleuth.instrument.executor;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.TraceCallable;
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
@@ -31,38 +31,38 @@ import org.springframework.cloud.sleuth.instrument.TraceRunnable;
*
*/
public class TraceableScheduledExecutorService extends TraceableExecutorService implements ScheduledExecutorService {
public TraceableScheduledExecutorService(final ScheduledExecutorService delegate, final Trace trace) {
super(delegate, trace);
public TraceableScheduledExecutorService(final ScheduledExecutorService delegate, final TraceManager traceManager) {
super(delegate, traceManager);
}
private ScheduledExecutorService getScheduledExecutorService() {
return (ScheduledExecutorService) this.delegate;
}
@Override
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
Runnable r = new TraceRunnable(this.trace, command);
Runnable r = new TraceRunnable(this.traceManager, command);
return getScheduledExecutorService().schedule(r, delay, unit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) {
Callable<V> c = new TraceCallable<>(this.trace,callable);
Callable<V> c = new TraceCallable<>(this.traceManager,callable);
return getScheduledExecutorService().schedule(c, delay, unit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) {
Runnable r = new TraceRunnable(this.trace, command);
Runnable r = new TraceRunnable(this.traceManager, command);
return getScheduledExecutorService().scheduleAtFixedRate(r, initialDelay, period, unit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) {
Runnable r = new TraceRunnable(this.trace, command);
Runnable r = new TraceRunnable(this.traceManager, command);
return getScheduledExecutorService().scheduleWithFixedDelay(r, initialDelay, delay, unit);
}

View File

@@ -14,19 +14,20 @@
* limitations under the License.
*/
package org.springframework.cloud.sleuth.instrument.circuitbreaker;
package org.springframework.cloud.sleuth.instrument.hystrix;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
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
* @see TraceManager
*
* @author Tomasz Nurkiewicz, 4financeIT
* @author Marcin Grzejszczak, 4financeIT
@@ -34,40 +35,40 @@ import org.springframework.cloud.sleuth.TraceScope;
*/
public abstract class TraceCommand<R> extends HystrixCommand<R> {
private Trace trace;
private TraceManager traceManager;
protected TraceCommand(Trace trace, HystrixCommandGroupKey group) {
protected TraceCommand(TraceManager traceManager, HystrixCommandGroupKey group) {
super(group);
this.trace = trace;
this.traceManager = traceManager;
}
protected TraceCommand(Trace trace, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
protected TraceCommand(TraceManager traceManager, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
super(group, threadPool);
this.trace = trace;
this.traceManager = traceManager;
}
protected TraceCommand(Trace trace, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
protected TraceCommand(TraceManager traceManager, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
super(group, executionIsolationThreadTimeoutInMilliseconds);
this.trace = trace;
this.traceManager = traceManager;
}
protected TraceCommand(Trace trace, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
protected TraceCommand(TraceManager traceManager, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
this.trace = trace;
this.traceManager = traceManager;
}
protected TraceCommand(Trace trace, Setter setter) {
protected TraceCommand(TraceManager traceManager, Setter setter) {
super(setter);
this.trace = trace;
this.traceManager = traceManager;
}
@Override
protected R run() throws Exception {
TraceScope scope = trace.startSpan(getCommandKey().name());
Trace trace = this.traceManager.startSpan(getCommandKey().name());
try {
return doRun();
} finally {
scope.close();
this.traceManager.close(trace);
}
}

View File

@@ -16,19 +16,12 @@
package org.springframework.cloud.sleuth.instrument.integration;
import static org.springframework.cloud.sleuth.Trace.HEADERS;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
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 java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
@@ -40,9 +33,9 @@ public class SpanMessageHeaders {
public static Message<?> addSpanHeaders(Message<?> message, Span span) {
if (span == null) {
if (!message.getHeaders().containsKey(NOT_SAMPLED_NAME)) {
if (!message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
return MessageBuilder.fromMessage(message)
.setHeader(NOT_SAMPLED_NAME, "").build();
.setHeader(Trace.NOT_SAMPLED_NAME, "").build();
}
return message;
}
@@ -50,17 +43,17 @@ public class SpanMessageHeaders {
addAnnotations(message, span);
Map<String, String> headers = new HashMap<>();
addHeader(headers, TRACE_ID_NAME, span.getTraceId());
addHeader(headers, SPAN_ID_NAME, span.getSpanId());
addHeader(headers, PARENT_ID_NAME, getFirst(span.getParents()));
addHeader(headers, SPAN_NAME_NAME, span.getName());
addHeader(headers, PROCESS_ID_NAME, span.getProcessId());
addHeader(headers, Trace.TRACE_ID_NAME, span.getTraceId());
addHeader(headers, Trace.SPAN_ID_NAME, span.getSpanId());
addHeader(headers, Trace.PARENT_ID_NAME, getFirst(span.getParents()));
addHeader(headers, Trace.SPAN_NAME_NAME, span.getName());
addHeader(headers, Trace.PROCESS_ID_NAME, span.getProcessId());
return MessageBuilder.fromMessage(message).copyHeaders(headers).build();
}
public static void addAnnotations(Message<?> message, Span span) {
for ( Map.Entry<String, Object> entry : message.getHeaders().entrySet()) {
if (!HEADERS.contains(entry.getKey())) { //filter out trace headers
if (!Trace.HEADERS.contains(entry.getKey())) { //filter out trace headers
String key = "/messaging/headers/" + entry.getKey().toLowerCase();
String value = null;
if (entry.getValue() != null) {

View File

@@ -16,20 +16,12 @@
package org.springframework.cloud.sleuth.instrument.integration;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
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;
import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.MilliSpan.MilliSpanBuilder;
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.TraceManager;
import org.springframework.integration.channel.AbstractMessageChannel;
import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.messaging.Message;
@@ -42,45 +34,38 @@ import org.springframework.messaging.support.ChannelInterceptorAdapter;
*/
public class TraceChannelInterceptor extends ChannelInterceptorAdapter {
private ThreadLocal<TraceScope> traceScopeHolder = new ThreadLocal<TraceScope>();
private ThreadLocal<Trace> traceManagerScopeHolder = new ThreadLocal<Trace>();
private ThreadLocal<Span> spanHolder = new ThreadLocal<Span>();
private final TraceManager traceManager;
private final Trace trace;
public TraceChannelInterceptor(Trace trace) {
this.trace = trace;
public TraceChannelInterceptor(TraceManager traceManager) {
this.traceManager = traceManager;
}
@Override
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
TraceScope traceScope = this.traceScopeHolder.get();
if (traceScope != null) {
traceScope.close();
}
this.traceScopeHolder.remove();
// TODO: Maybe the TraceScope could handle this
TraceContextHolder.setCurrentSpan(this.spanHolder.get());
Trace traceManagerScope = this.traceManagerScopeHolder.get();
this.traceManager.close(traceManagerScope);
this.traceManagerScopeHolder.remove();
}
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
this.spanHolder.set(TraceContextHolder.getCurrentSpan());
if (TraceContextHolder.isTracing()
|| message.getHeaders().containsKey(NOT_SAMPLED_NAME)) {
if (this.traceManager.isTracing()
|| message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
return SpanMessageHeaders.addSpanHeaders(message,
TraceContextHolder.getCurrentSpan());
this.traceManager.getCurrentSpan());
}
String spanId = getHeader(message, SPAN_ID_NAME);
String traceId = getHeader(message, TRACE_ID_NAME);
String spanId = getHeader(message, Trace.SPAN_ID_NAME);
String traceId = getHeader(message, Trace.TRACE_ID_NAME);
String name = "message/" + getChannelName(channel);
TraceScope traceScope;
Trace trace;
if (hasText(spanId) && hasText(traceId)) {
MilliSpanBuilder span = MilliSpan.builder().traceId(traceId).spanId(spanId);
String parentId = getHeader(message, PARENT_ID_NAME);
String processId = getHeader(message, PROCESS_ID_NAME);
String spanName = getHeader(message, SPAN_NAME_NAME);
String parentId = getHeader(message, Trace.PARENT_ID_NAME);
String processId = getHeader(message, Trace.PROCESS_ID_NAME);
String spanName = getHeader(message, Trace.SPAN_NAME_NAME);
if (spanName != null) {
span.name(spanName);
}
@@ -92,14 +77,14 @@ public class TraceChannelInterceptor extends ChannelInterceptorAdapter {
}
span.remote(true);
// TODO: trace description?
traceScope = this.trace.startSpan(name, span.build());
// TODO: traceManager description?
trace = this.traceManager.startSpan(name, span.build());
}
else {
traceScope = this.trace.startSpan(name);
trace = this.traceManager.startSpan(name);
}
this.traceScopeHolder.set(traceScope);
return SpanMessageHeaders.addSpanHeaders(message, traceScope.getSpan());
this.traceManagerScopeHolder.set(trace);
return SpanMessageHeaders.addSpanHeaders(message, trace.getSpan());
}
private String getChannelName(MessageChannel channel) {

View File

@@ -16,20 +16,13 @@
package org.springframework.cloud.sleuth.instrument.integration;
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.removeCurrentSpan;
import static org.springframework.cloud.sleuth.TraceContextHolder.setCurrentSpan;
import java.util.HashMap;
import java.util.Map;
import org.springframework.aop.support.AopUtils;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
@@ -52,18 +45,22 @@ import org.springframework.util.Assert;
* @since 1.0
*/
public class TraceContextPropagationChannelInterceptor extends ChannelInterceptorAdapter
implements ExecutorChannelInterceptor {
implements ExecutorChannelInterceptor {
private final static ThreadLocal<Span> ORIGINAL_CONTEXT = new ThreadLocal<>();
private final TraceManager traceManager;
private final static ThreadLocal<Trace> ORIGINAL_CONTEXT = new ThreadLocal<>();
public TraceContextPropagationChannelInterceptor(TraceManager traceManager) {
this.traceManager = traceManager;
}
@Override
public final Message<?> preSend(Message<?> message, MessageChannel channel) {
if (DirectChannel.class.isAssignableFrom(AopUtils.getTargetClass(channel))) {
return message;
}
Span span = getCurrentSpan();
Span span = this.traceManager.getCurrentSpan();
if (span != null) {
return new MessageWithSpan(message, span);
}
@@ -78,7 +75,6 @@ implements ExecutorChannelInterceptor {
MessageWithSpan messageWithSpan = (MessageWithSpan) message;
Message<?> messageToHandle = messageWithSpan.message;
populatePropagatedContext(messageWithSpan.span, messageToHandle, channel);
return message;
}
return message;
@@ -97,33 +93,21 @@ implements ExecutorChannelInterceptor {
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty() ? span
.getParents().get(0) : null;
return span.getParents() != null && !span.getParents().isEmpty()
? span.getParents().get(0) : null;
}
protected void populatePropagatedContext(Span span, Message<?> message,
MessageChannel channel) {
if (span != null) {
Span currentContext = getCurrentSpan();
ORIGINAL_CONTEXT.set(currentContext);
setCurrentSpan(span);
ORIGINAL_CONTEXT.set(this.traceManager.continueSpan(span).getSavedTrace());
}
}
protected void resetPropagatedContext() {
Span originalContext = ORIGINAL_CONTEXT.get();
try {
if (originalContext == null) {
removeCurrentSpan();
ORIGINAL_CONTEXT.remove();
}
else {
setCurrentSpan(originalContext);
}
}
catch (Throwable t) {// NOSONAR
removeCurrentSpan();
}
Trace originalContext = ORIGINAL_CONTEXT.get();
this.traceManager.detach(originalContext);
ORIGINAL_CONTEXT.remove();
}
private class MessageWithSpan implements Message<Object> {
@@ -143,16 +127,16 @@ implements ExecutorChannelInterceptor {
Map<String, Object> headers = new HashMap<>();
headers.putAll(message.getHeaders());
setHeader(headers, SPAN_ID_NAME, this.span.getSpanId());
setHeader(headers, TRACE_ID_NAME, this.span.getTraceId());
setHeader(headers, SPAN_NAME_NAME, this.span.getName());
String parentId = getParentId(getCurrentSpan());
setHeader(headers, Trace.SPAN_ID_NAME, this.span.getSpanId());
setHeader(headers, Trace.TRACE_ID_NAME, this.span.getTraceId());
setHeader(headers, Trace.SPAN_NAME_NAME, this.span.getName());
String parentId = getParentId(span);
if (parentId != null) {
setHeader(headers, PARENT_ID_NAME, parentId);
setHeader(headers, Trace.PARENT_ID_NAME, parentId);
}
String processId = this.span.getProcessId();
if (processId != null) {
setHeader(headers, PROCESS_ID_NAME, processId);
setHeader(headers, Trace.PROCESS_ID_NAME, processId);
}
this.messageHeaders = new MessageHeaders(headers);
}
@@ -175,8 +159,8 @@ implements ExecutorChannelInterceptor {
@Override
public String toString() {
return "MessageWithSpan{" + "message=" + this.message + ", span="
+ this.span + ", messageHeaders=" + this.messageHeaders + '}';
return "MessageWithSpan{" + "message=" + this.message + ", span=" + this.span
+ ", messageHeaders=" + this.messageHeaders + '}';
}
}

View File

@@ -20,7 +20,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -31,20 +31,21 @@ import org.springframework.integration.config.GlobalChannelInterceptor;
*/
@Configuration
@ConditionalOnClass(GlobalChannelInterceptor.class)
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceManager.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled", matchIfMissing = true)
public class TraceSpringIntegrationAutoConfiguration {
@Bean
@GlobalChannelInterceptor
public TraceContextPropagationChannelInterceptor traceContextPropagationChannelInterceptor() {
return new TraceContextPropagationChannelInterceptor();
public TraceContextPropagationChannelInterceptor traceContextPropagationChannelInterceptor(
TraceManager trace) {
return new TraceContextPropagationChannelInterceptor(trace);
}
@Bean
@GlobalChannelInterceptor
public TraceChannelInterceptor traceChannelInterceptor(Trace trace) {
public TraceChannelInterceptor traceChannelInterceptor(TraceManager trace) {
return new TraceChannelInterceptor(trace);
}

View File

@@ -20,7 +20,7 @@ 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.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Scheduled;
/**
@@ -32,24 +32,24 @@ import org.springframework.scheduling.annotation.Scheduled;
* @author Marcin Grzejszczak, 4financeIT
* @author Spencer Gibb
*
* @see Trace
* @see TraceManager
*/
@Aspect
public class TraceSchedulingAspect {
private final Trace trace;
private final TraceManager trace;
public TraceSchedulingAspect(Trace trace) {
public TraceSchedulingAspect(TraceManager trace) {
this.trace = trace;
}
@Around("execution (@org.springframework.scheduling.annotation.Scheduled * *.*(..))")
public Object traceBackgroundThread(final ProceedingJoinPoint pjp) throws Throwable {
TraceScope scope = this.trace.startSpan(pjp.toShortString());
Trace scope = this.trace.startSpan(pjp.toShortString());
try {
return pjp.proceed();
} finally {
scope.close();
this.trace.close(scope);
}
}
}

View File

@@ -25,7 +25,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,13 +42,13 @@ import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ConditionalOnProperty(value = "spring.sleuth.schedule.enabled", matchIfMissing = true)
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceManager.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class TraceSchedulingAutoConfiguration {
@ConditionalOnClass(ProceedingJoinPoint.class)
@Bean
public TraceSchedulingAspect traceSchedulingAspect(Trace trace) {
public TraceSchedulingAspect traceSchedulingAspect(TraceManager trace) {
return new TraceSchedulingAspect(trace);
}

View File

@@ -15,12 +15,6 @@
*/
package org.springframework.cloud.sleuth.instrument.web;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
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;
import java.io.IOException;
@@ -36,8 +30,7 @@ import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.MilliSpan.MilliSpanBuilder;
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.TraceManager;
import org.springframework.cloud.sleuth.event.ServerReceivedEvent;
import org.springframework.cloud.sleuth.event.ServerSentEvent;
import org.springframework.context.ApplicationEvent;
@@ -53,7 +46,7 @@ import org.springframework.web.util.UrlPathHelper;
* {@link Trace#TRACE_ID_NAME} header from either request or response and uses them to
* create a new span.
*
* @see Trace
* @see TraceManager
*
* @author Jakub Nabrdalik, 4financeIT
* @author Tomasz Nurkiewicz, 4financeIT
@@ -70,19 +63,19 @@ public class TraceFilter extends OncePerRequestFilter implements ApplicationEven
public static final Pattern DEFAULT_SKIP_PATTERN = Pattern
.compile("/api-docs.*|/autoconfig|/configprops|/dump|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/hystrix.stream");
private final Trace trace;
private final TraceManager traceManager;
private final Pattern skipPattern;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private ApplicationEventPublisher publisher;
public TraceFilter(Trace trace) {
this.trace = trace;
public TraceFilter(TraceManager traceManager) {
this.traceManager = traceManager;
this.skipPattern = DEFAULT_SKIP_PATTERN;
}
public TraceFilter(Trace trace, Pattern skipPattern) {
this.trace = trace;
public TraceFilter(TraceManager traceManager, Pattern skipPattern) {
this.traceManager = traceManager;
this.skipPattern = skipPattern;
}
@@ -98,26 +91,26 @@ public class TraceFilter extends OncePerRequestFilter implements ApplicationEven
String uri = this.urlPathHelper.getPathWithinApplication(request);
boolean skip = this.skipPattern.matcher(uri).matches()
|| getHeader(request, response, NOT_SAMPLED_NAME) != null;
|| getHeader(request, response, Trace.NOT_SAMPLED_NAME) != null;
TraceScope traceScope = (TraceScope) request.getAttribute(TRACE_REQUEST_ATTR);
if (traceScope != null) {
this.trace.continueSpan(traceScope.getSpan());
Trace trace = (Trace) request.getAttribute(TRACE_REQUEST_ATTR);
if (trace != null) {
this.traceManager.continueSpan(trace.getSpan());
}
else if (skip) {
addToResponseIfNotPresent(response, NOT_SAMPLED_NAME, "");
addToResponseIfNotPresent(response, Trace.NOT_SAMPLED_NAME, "");
}
else {
String spanId = getHeader(request, response, SPAN_ID_NAME);
String traceId = getHeader(request, response, TRACE_ID_NAME);
String spanId = getHeader(request, response, Trace.SPAN_ID_NAME);
String traceId = getHeader(request, response, Trace.TRACE_ID_NAME);
String name = "http" + uri;
if (hasText(spanId) && hasText(traceId)) {
MilliSpanBuilder span = 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);
String parentId = getHeader(request, response, Trace.PARENT_ID_NAME);
String processId = getHeader(request, response, Trace.PROCESS_ID_NAME);
String parentName = getHeader(request, response, Trace.SPAN_NAME_NAME);
if (parentName != null) {
span.name(parentName);
}
@@ -131,18 +124,18 @@ public class TraceFilter extends OncePerRequestFilter implements ApplicationEven
// TODO: trace description?
Span parent = span.build();
traceScope = this.trace.startSpan(name, parent);
publish(new ServerReceivedEvent(this, parent, traceScope.getSpan()));
request.setAttribute(TRACE_REQUEST_ATTR, traceScope);
trace = this.traceManager.startSpan(name, parent);
publish(new ServerReceivedEvent(this, parent, trace.getSpan()));
request.setAttribute(TRACE_REQUEST_ATTR, trace);
// Send new span id back
addToResponseIfNotPresent(response, TRACE_ID_NAME, traceScope.getSpan()
addToResponseIfNotPresent(response, Trace.TRACE_ID_NAME, trace.getSpan()
.getTraceId());
addToResponseIfNotPresent(response, SPAN_ID_NAME, traceScope.getSpan()
addToResponseIfNotPresent(response, Trace.SPAN_ID_NAME, trace.getSpan()
.getSpanId());
}
else {
traceScope = this.trace.startSpan(name);
request.setAttribute(TRACE_REQUEST_ATTR, traceScope);
trace = this.traceManager.startSpan(name);
request.setAttribute(TRACE_REQUEST_ATTR, trace);
}
}
@@ -157,12 +150,14 @@ public class TraceFilter extends OncePerRequestFilter implements ApplicationEven
// TODO: how to deal with response annotations and async?
return;
}
if (traceScope != null) {
if (trace != null) {
addResponseAnnotations(response);
publish(new ServerSentEvent(this, traceScope.getSavedSpan(), traceScope.getSpan()));
traceScope.close();
if (trace.getSavedTrace()!=null) {
publish(new ServerSentEvent(this, trace.getSavedTrace().getSpan(), trace.getSpan()));
}
// Double close to clean up the parent (remote span as well)
this.traceManager.close(this.traceManager.close(trace));
}
TraceContextHolder.removeCurrentSpan();
}
}
@@ -175,10 +170,10 @@ public class TraceFilter extends OncePerRequestFilter implements ApplicationEven
//TODO: move annotation keys to constants
protected void addRequestAnnotations(HttpServletRequest request) {
String uri = this.urlPathHelper.getPathWithinApplication(request);
this.trace.addAnnotation("/http/request/uri", request.getRequestURL()
this.traceManager.addAnnotation("/http/request/uri", request.getRequestURL()
.toString());
this.trace.addAnnotation("/http/request/endpoint", uri);
this.trace.addAnnotation("/http/request/method", request.getMethod());
this.traceManager.addAnnotation("/http/request/endpoint", uri);
this.traceManager.addAnnotation("/http/request/method", request.getMethod());
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
@@ -187,20 +182,20 @@ public class TraceFilter extends OncePerRequestFilter implements ApplicationEven
while (values.hasMoreElements()) {
String value = values.nextElement();
String key = "/http/request/headers/" + name.toLowerCase();
this.trace.addAnnotation(key, value);
this.traceManager.addAnnotation(key, value);
}
}
}
private void addResponseAnnotations(HttpServletResponse response) {
this.trace.addAnnotation("/http/response/status_code",
this.traceManager.addAnnotation("/http/response/status_code",
String.valueOf(response.getStatus()));
for (String name : response.getHeaderNames()) {
for (String value : response.getHeaders(name)) {
String key = "/http/response/headers/" + name.toLowerCase();
this.trace.addAnnotation(key, value);
this.traceManager.addAnnotation(key, value);
}
}
}

View File

@@ -16,14 +16,14 @@
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;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
/**
* @author Spencer Gibb
*/
@@ -31,31 +31,32 @@ public class TraceHandlerInterceptor implements HandlerInterceptor {
private static final String ATTR_NAME = "__CURRENT_TRACE_HANDLER_TRACE_SCOPE_ATTR___";
private final Trace trace;
private final TraceManager traceManager;
public TraceHandlerInterceptor(Trace trace) {
this.trace = trace;
public TraceHandlerInterceptor(TraceManager traceManager) {
this.traceManager = traceManager;
}
@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);
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
// TODO: get trace data from request?
// TODO: what is the description?
Trace trace = this.traceManager.startSpan("traceHandlerInterceptor");
request.setAttribute(ATTR_NAME, trace);
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
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();
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
Trace trace = Trace.class.cast(request.getAttribute(ATTR_NAME));
this.traceManager.close(trace);
}
}

View File

@@ -18,19 +18,19 @@ 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.TraceAccessor;
import org.springframework.cloud.sleuth.TraceManager;
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;
import lombok.extern.apachecommons.CommonsLog;
/**
* Aspect that adds correlation id to
* <p/>
@@ -47,7 +47,7 @@ import org.springframework.web.client.RestOperations;
* @see Controller
* @see RestOperations
* @see TraceCallable
* @see Trace
* @see TraceManager
*
* @author Tomasz Nurkewicz, 4financeIT
* @author Marcin Grzejszczak, 4financeIT
@@ -58,10 +58,12 @@ import org.springframework.web.client.RestOperations;
@CommonsLog
public class TraceWebAspect {
private final Trace trace;
private final TraceManager traceManager;
private final TraceAccessor accessor;
public TraceWebAspect(Trace trace) {
this.trace = trace;
public TraceWebAspect(TraceManager traceManager, TraceAccessor accessor) {
this.traceManager = traceManager;
this.accessor = accessor;
}
@Pointcut("@within(org.springframework.web.bind.annotation.RestController)")
@@ -84,11 +86,10 @@ public class TraceWebAspect {
@SuppressWarnings("unchecked")
public Object wrapWithCorrelationId(ProceedingJoinPoint pjp) throws Throwable {
Callable<Object> callable = (Callable<Object>) pjp.proceed();
if (TraceContextHolder.isTracing()) {
if (this.accessor.isTracing()) {
log.debug("Wrapping callable with span ["
+ TraceContextHolder.getCurrentSpan() + "]");
return new TraceCallable<>(this.trace, callable);
+ this.accessor.getCurrentSpan() + "]");
return new TraceCallable<>(this.traceManager, callable);
}
else {
return callable;

View File

@@ -24,7 +24,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
@@ -42,7 +43,7 @@ import org.springframework.util.StringUtils;
@Configuration
@ConditionalOnProperty(value = "spring.sleuth.web.enabled", matchIfMissing = true)
@ConditionalOnWebApplication
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceManager.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class TraceWebAutoConfiguration {
@@ -53,18 +54,21 @@ public class TraceWebAutoConfiguration {
private String skipPattern;
@Autowired
private Trace trace;
private TraceManager traceManager;
@Autowired
private TraceAccessor accessor;
@Bean
public TraceWebAspect traceWebAspect() {
return new TraceWebAspect(this.trace);
return new TraceWebAspect(this.traceManager, this.accessor);
}
@Bean
public FilterRegistrationBean traceWebFilter(ApplicationEventPublisher publisher) {
Pattern pattern = StringUtils.hasText(this.skipPattern) ? Pattern.compile(this.skipPattern)
: TraceFilter.DEFAULT_SKIP_PATTERN;
TraceFilter filter = new TraceFilter(this.trace, pattern);
TraceFilter filter = new TraceFilter(this.traceManager, pattern);
filter.setApplicationEventPublisher(publisher);
return new FilterRegistrationBean(filter);
}

View File

@@ -17,9 +17,7 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import static java.util.Collections.singletonList;
import static org.springframework.cloud.sleuth.Trace.*;
import static org.springframework.cloud.sleuth.TraceContextHolder.getCurrentSpan;
import static org.springframework.cloud.sleuth.TraceContextHolder.isTracing;
import static org.springframework.cloud.sleuth.trace.TraceContextHolder.isTracing;
import java.io.IOException;
import java.lang.reflect.Type;
@@ -35,6 +33,8 @@ import org.springframework.boot.autoconfigure.web.HttpMessageConverters;
import org.springframework.cloud.netflix.feign.support.ResponseEntityDecoder;
import org.springframework.cloud.netflix.feign.support.SpringDecoder;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.context.ApplicationEvent;
@@ -67,10 +67,13 @@ public class TraceFeignClientAutoConfiguration {
@Autowired
private ApplicationEventPublisher publisher;
@Autowired
private TraceAccessor accessor;
@Bean
@Primary
public Decoder feignDecoder() {
return new ResponseEntityDecoder(new SpringDecoder(messageConverters)) {
return new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)) {
@Override
public Object decode(Response response, Type type) throws IOException, FeignException {
try {
@@ -93,14 +96,14 @@ public class TraceFeignClientAutoConfiguration {
public void apply(RequestTemplate template) {
Span span = getCurrentSpan();
if (span != null) {
template.header(TRACE_ID_NAME, span.getTraceId());
setHeader(template, SPAN_NAME_NAME, span.getName());
setHeader(template, SPAN_ID_NAME, span.getSpanId());
setHeader(template, PARENT_ID_NAME, getParentId(span));
setHeader(template, PROCESS_ID_NAME, span.getProcessId());
template.header(Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(template, Trace.SPAN_NAME_NAME, span.getName());
setHeader(template, Trace.SPAN_ID_NAME, span.getSpanId());
setHeader(template, Trace.PARENT_ID_NAME, getParentId(span));
setHeader(template, Trace.PROCESS_ID_NAME, span.getProcessId());
publish(new ClientSentEvent(this, span));
} else {
setHeader(template, NOT_SAMPLED_NAME, "");
setHeader(template, Trace.NOT_SAMPLED_NAME, "");
}
}
};
@@ -126,12 +129,12 @@ public class TraceFeignClientAutoConfiguration {
Map<String, Collection<String>> newHeaders = new HashMap<>();
newHeaders.putAll(headers);
if (getCurrentSpan() == null) {
setHeader(newHeaders, NOT_SAMPLED_NAME, "");
setHeader(newHeaders, Trace.NOT_SAMPLED_NAME, "");
return newHeaders;
}
setHeader(newHeaders, TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(newHeaders, SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(newHeaders, PARENT_ID_NAME, getParentId(getCurrentSpan()));
setHeader(newHeaders, Trace.TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(newHeaders, Trace.SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(newHeaders, Trace.PARENT_ID_NAME, getParentId(getCurrentSpan()));
return newHeaders;
}
@@ -140,4 +143,9 @@ public class TraceFeignClientAutoConfiguration {
headers.put(name, singletonList(value));
}
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}
}

View File

@@ -15,19 +15,13 @@
*/
package org.springframework.cloud.sleuth.instrument.web.client;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
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 static org.springframework.cloud.sleuth.trace.TraceContextHolder.isTracing;
import java.io.IOException;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.context.ApplicationEvent;
@@ -43,7 +37,7 @@ import org.springframework.http.client.ClientHttpResponse;
* and sets them if one or both of them are missing.
*
* @see org.springframework.web.client.RestTemplate
* @see Trace
* @see TraceAccessor
*
* @author Marcin Grzejszczak, 4financeIT
* @author Spencer Gibb
@@ -53,6 +47,12 @@ ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
private TraceAccessor accessor;
public TraceRestTemplateInterceptor(TraceAccessor accessor) {
this.accessor = accessor;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
@@ -62,14 +62,14 @@ ApplicationEventPublisherAware {
public ClientHttpResponse intercept(HttpRequest request, byte[] body,
ClientHttpRequestExecution execution) throws IOException {
if (getCurrentSpan() == null) {
setHeader(request, NOT_SAMPLED_NAME, "");
setHeader(request, Trace.NOT_SAMPLED_NAME, "");
return execution.execute(request, body);
}
setHeader(request, SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(request, TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(request, SPAN_NAME_NAME, getCurrentSpan().getName());
setHeader(request, PARENT_ID_NAME, getParentId(getCurrentSpan()));
setHeader(request, PROCESS_ID_NAME, getCurrentSpan().getProcessId());
setHeader(request, Trace.SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(request, Trace.TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(request, Trace.SPAN_NAME_NAME, getCurrentSpan().getName());
setHeader(request, Trace.PARENT_ID_NAME, getParentId(getCurrentSpan()));
setHeader(request, Trace.PROCESS_ID_NAME, getCurrentSpan().getProcessId());
publish(new ClientSentEvent(this, getCurrentSpan()));
return new TraceHttpResponse(this, execution.execute(request, body));
}
@@ -98,4 +98,8 @@ ApplicationEventPublisherAware {
}
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}
}

View File

@@ -28,7 +28,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -41,14 +41,14 @@ import org.springframework.web.client.RestTemplate;
@Configuration
@ConditionalOnProperty(value = "spring.sleuth.client.enabled", matchIfMissing = true)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceAccessor.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class TraceWebClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TraceRestTemplateInterceptor traceRestTemplateInterceptor() {
return new TraceRestTemplateInterceptor();
public TraceRestTemplateInterceptor traceRestTemplateInterceptor(TraceAccessor accessor) {
return new TraceRestTemplateInterceptor(accessor);
}
@Bean

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.instrument.zuul;
import static org.springframework.cloud.sleuth.TraceContextHolder.getCurrentSpan;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
@@ -34,6 +34,12 @@ public class TracePostZuulFilter extends ZuulFilter
private ApplicationEventPublisher publisher;
private final TraceAccessor accessor;
public TracePostZuulFilter(TraceAccessor accessor) {
this.accessor = accessor;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
@@ -67,4 +73,7 @@ public class TracePostZuulFilter extends ZuulFilter
}
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}
}

View File

@@ -16,18 +16,13 @@
package org.springframework.cloud.sleuth.instrument.zuul;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
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 static org.springframework.cloud.sleuth.trace.TraceContextHolder.isTracing;
import java.util.Map;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
@@ -46,6 +41,12 @@ ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
private final TraceAccessor accessor;
public TracePreZuulFilter(TraceAccessor accessor) {
this.accessor = accessor;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
@@ -62,15 +63,15 @@ ApplicationEventPublisherAware {
Map<String, String> response = ctx.getZuulRequestHeaders();
// N.B. this will only work with the simple host filter (not ribbon) unless you set hystrix.execution.isolation.strategy=SEMAPHORE
if (getCurrentSpan() == null) {
setHeader(response, NOT_SAMPLED_NAME, "");
setHeader(response, Trace.NOT_SAMPLED_NAME, "");
return null;
}
try {
setHeader(response, SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(response, TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(response, SPAN_NAME_NAME, getCurrentSpan().getName());
setHeader(response, PARENT_ID_NAME, getParentId(getCurrentSpan()));
setHeader(response, PROCESS_ID_NAME, getCurrentSpan().getProcessId());
setHeader(response, Trace.SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(response, Trace.TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(response, Trace.SPAN_NAME_NAME, getCurrentSpan().getName());
setHeader(response, Trace.PARENT_ID_NAME, getParentId(getCurrentSpan()));
setHeader(response, Trace.PROCESS_ID_NAME, getCurrentSpan().getProcessId());
// TODO: the client sent event should come from the client not the filter!
publish(new ClientSentEvent(this, getCurrentSpan()));
}
@@ -80,6 +81,10 @@ ApplicationEventPublisherAware {
return null;
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty() ? span
.getParents().get(0) : null;

View File

@@ -16,25 +16,18 @@
package org.springframework.cloud.sleuth.instrument.zuul;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
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 static org.springframework.cloud.sleuth.trace.TraceContextHolder.isTracing;
import java.io.InputStream;
import java.net.URISyntaxException;
import lombok.SneakyThrows;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommand;
import org.springframework.cloud.netflix.zuul.filters.route.RestClientRibbonCommandFactory;
import org.springframework.cloud.netflix.zuul.filters.route.RibbonCommandContext;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
@@ -44,16 +37,22 @@ import org.springframework.util.MultiValueMap;
import com.netflix.client.http.HttpRequest;
import com.netflix.niws.client.http.RestClient;
import lombok.SneakyThrows;
/**
* @author Spencer Gibb
*/
public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommandFactory
implements ApplicationEventPublisherAware {
implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
public TraceRestClientRibbonCommandFactory(SpringClientFactory clientFactory) {
private final TraceAccessor accessor;
public TraceRestClientRibbonCommandFactory(SpringClientFactory clientFactory,
TraceAccessor accessor) {
super(clientFactory);
this.accessor = accessor;
}
@Override
@@ -67,34 +66,45 @@ implements ApplicationEventPublisherAware {
public RestClientRibbonCommand create(RibbonCommandContext context) {
RestClient restClient = getClientFactory().getClient(context.getServiceId(),
RestClient.class);
return new TraceRestClientRibbonCommand(
context.getServiceId(), restClient, getVerb(context.getVerb()),
context.getUri(), context.getRetryable(), context.getHeaders(),
context.getParams(), context.getRequestEntity(), this.publisher);
return new TraceRestClientRibbonCommand(context.getServiceId(), restClient,
getVerb(context.getVerb()), context.getUri(), context.getRetryable(),
context.getHeaders(), context.getParams(), context.getRequestEntity(),
this.publisher, this.accessor);
}
class TraceRestClientRibbonCommand extends RestClientRibbonCommand {
private ApplicationEventPublisher publisher;
private final TraceAccessor accessor;
@SuppressWarnings("deprecation")
public TraceRestClientRibbonCommand(String commandKey, RestClient restClient, HttpRequest.Verb verb, String uri, Boolean retryable, MultiValueMap<String, String> headers, MultiValueMap<String, String> params, InputStream requestEntity, ApplicationEventPublisher publisher) throws URISyntaxException {
super(commandKey, restClient, verb, uri, retryable, headers, params, requestEntity);
public TraceRestClientRibbonCommand(String commandKey, RestClient restClient,
HttpRequest.Verb verb, String uri, Boolean retryable,
MultiValueMap<String, String> headers,
MultiValueMap<String, String> params, InputStream requestEntity,
ApplicationEventPublisher publisher, TraceAccessor accessor)
throws URISyntaxException {
super(commandKey, restClient, verb, uri, retryable, headers, params,
requestEntity);
this.publisher = publisher;
this.accessor = accessor;
}
@Override
protected void customizeRequest(HttpRequest.Builder requestBuilder) {
if (getCurrentSpan() == null) {
setHeader(requestBuilder, NOT_SAMPLED_NAME, "");
setHeader(requestBuilder, Trace.NOT_SAMPLED_NAME, "");
return;
}
setHeader(requestBuilder, SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(requestBuilder, TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(requestBuilder, SPAN_NAME_NAME, getCurrentSpan().getName());
setHeader(requestBuilder, PARENT_ID_NAME, getParentId(getCurrentSpan()));
setHeader(requestBuilder, PROCESS_ID_NAME, getCurrentSpan().getProcessId());
setHeader(requestBuilder, Trace.SPAN_ID_NAME, getCurrentSpan().getSpanId());
setHeader(requestBuilder, Trace.TRACE_ID_NAME, getCurrentSpan().getTraceId());
setHeader(requestBuilder, Trace.SPAN_NAME_NAME, getCurrentSpan().getName());
setHeader(requestBuilder, Trace.PARENT_ID_NAME,
getParentId(getCurrentSpan()));
setHeader(requestBuilder, Trace.PROCESS_ID_NAME,
getCurrentSpan().getProcessId());
publish(new ClientSentEvent(this, getCurrentSpan()));
}
@@ -105,8 +115,8 @@ implements ApplicationEventPublisherAware {
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty() ? span
.getParents().get(0) : null;
return span.getParents() != null && !span.getParents().isEmpty()
? span.getParents().get(0) : null;
}
public void setHeader(HttpRequest.Builder builder, String name, String value) {
@@ -114,5 +124,10 @@ implements ApplicationEventPublisherAware {
builder.header(name, value);
}
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}
}
}

View File

@@ -22,7 +22,8 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,25 +39,25 @@ import com.netflix.zuul.ZuulFilter;
@ConditionalOnProperty(value = "spring.sleuth.zuul.enabled", matchIfMissing = true)
@ConditionalOnWebApplication
@ConditionalOnClass(ZuulFilter.class)
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceManager.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class TraceZuulAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public TracePreZuulFilter tracePreZuulFilter() {
return new TracePreZuulFilter();
public TracePreZuulFilter tracePreZuulFilter(TraceAccessor accessor) {
return new TracePreZuulFilter(accessor);
}
@Bean
public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory) {
return new TraceRestClientRibbonCommandFactory(factory);
public TraceRestClientRibbonCommandFactory traceRestClientRibbonCommandFactory(SpringClientFactory factory, TraceAccessor accessor) {
return new TraceRestClientRibbonCommandFactory(factory, accessor);
}
@Bean
@ConditionalOnMissingBean
public TracePostZuulFilter tracePostZuulFilter() {
return new TracePostZuulFilter();
public TracePostZuulFilter tracePostZuulFilter(TraceAccessor accessor) {
return new TracePostZuulFilter(accessor);
}
}

View File

@@ -22,7 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -31,7 +31,7 @@ import org.springframework.context.annotation.Configuration;
* @author Spencer Gibb
*/
@Configuration
@ConditionalOnBean(Trace.class)
@ConditionalOnBean(TraceManager.class)
@AutoConfigureAfter(TraceAutoConfiguration.class)
public class SleuthLogAutoConfiguration {

View File

@@ -16,20 +16,18 @@
package org.springframework.cloud.sleuth.log;
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.SpanContinuedEvent;
import org.springframework.cloud.sleuth.event.SpanAcquiredEvent;
import org.springframework.cloud.sleuth.event.SpanContinuedEvent;
import org.springframework.cloud.sleuth.event.SpanReleasedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import lombok.extern.slf4j.Slf4j;
/**
* @author Spencer Gibb
*/
@@ -69,8 +67,8 @@ public class Slf4jSpanListener {
MDC.put(Trace.SPAN_ID_NAME, event.getParent().getSpanId());
}
else {
MDC.remove(SPAN_ID_NAME);
MDC.remove(TRACE_ID_NAME);
MDC.remove(Trace.SPAN_ID_NAME);
MDC.remove(Trace.TRACE_ID_NAME);
}
}

View File

@@ -17,7 +17,7 @@
package org.springframework.cloud.sleuth.sampler;
import org.springframework.cloud.sleuth.Sampler;
import org.springframework.cloud.sleuth.TraceContextHolder;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
/**
* @author Spencer Gibb
@@ -26,6 +26,6 @@ public class IsTracingSampler implements Sampler<Void> {
@Override
public boolean next(Void info) {
return TraceContextHolder.getCurrentSpan() != null;
return TraceContextHolder.isTracing();
}
}

View File

@@ -0,0 +1,23 @@
/*
* 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.template;
import org.springframework.cloud.sleuth.Trace;
public interface TraceCallback<T> {
T doInTrace(Trace traceScope);
}

View File

@@ -0,0 +1,27 @@
/*
* 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.template;
/**
* @author Dave Syer
*
*/
public interface TraceOperations {
<T> T trace(TraceCallback<T> task);
}

View File

@@ -14,34 +14,34 @@
* limitations under the License.
*/
package org.springframework.cloud.sleuth;
package org.springframework.cloud.sleuth.template;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.TraceDelegate;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
/**
* @author Spencer Gibb
*/
public class TraceTemplate {
public class TraceTemplate implements TraceOperations {
public interface TraceCallback<T> {
T doInTrace(TraceScope traceScope);
}
private final TraceManager trace;
private final Trace trace;
public TraceTemplate(Trace trace) {
public TraceTemplate(TraceManager trace) {
this.trace = trace;
}
@Override
public <T> T trace(final TraceCallback<T> callback) {
DelegateCallback<T> delegate = new DelegateCallback<>(this.trace);
if (delegate.getParent() != null) {
TraceScope traceScope = delegate.startSpan();
if (TraceContextHolder.isTracing()) {
DelegateCallback<T> delegate = new DelegateCallback<>(this.trace);
Trace traceScope = delegate.startSpan();
try {
return callback.doInTrace(traceScope);
} finally {
traceScope.close();
this.trace.close(traceScope);
}
} else {
return callback.doInTrace(null);
@@ -50,12 +50,12 @@ public class TraceTemplate {
class DelegateCallback<T> extends TraceDelegate<TraceCallback<T>> {
public DelegateCallback(Trace trace) {
public DelegateCallback(TraceManager trace) {
super(trace, null);
}
@Override
protected TraceScope startSpan() {
protected Trace startSpan() {
return super.startSpan();
}

View File

@@ -22,22 +22,22 @@ import java.util.concurrent.Callable;
import org.springframework.cloud.sleuth.IdGenerator;
import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.NullScope;
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.TraceScope;
import org.springframework.cloud.sleuth.event.SpanContinuedEvent;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.event.SpanAcquiredEvent;
import org.springframework.cloud.sleuth.event.SpanContinuedEvent;
import org.springframework.cloud.sleuth.event.SpanReleasedEvent;
import org.springframework.cloud.sleuth.instrument.TraceCallable;
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
import org.springframework.cloud.sleuth.util.ExceptionUtils;
import org.springframework.context.ApplicationEventPublisher;
/**
* @author Spencer Gibb
*/
public class DefaultTrace implements Trace {
public class DefaultTraceManager implements TraceManager {
private final Sampler<Void> defaultSampler;
@@ -45,7 +45,7 @@ public class DefaultTrace implements Trace {
private final ApplicationEventPublisher publisher;
public DefaultTrace(Sampler<Void> defaultSampler, IdGenerator idGenerator,
public DefaultTraceManager(Sampler<Void> defaultSampler, IdGenerator idGenerator,
ApplicationEventPublisher publisher) {
this.defaultSampler = defaultSampler;
this.idGenerator = idGenerator;
@@ -53,7 +53,7 @@ public class DefaultTrace implements Trace {
}
@Override
public TraceScope startSpan(String name, Span parent) {
public Trace startSpan(String name, Span parent) {
if (parent == null) {
return startSpan(name);
}
@@ -63,19 +63,16 @@ public class DefaultTrace implements Trace {
+ " tried to start a new Span " + "with parent " + parent.toString()
+ ", but there is already a " + "currentSpan " + currentSpan);
}
if (currentSpan==null) {
TraceContextHolder.setCurrentSpan(parent);
}
return continueSpan(createChild(parent, name));
}
@Override
public TraceScope startSpan(String name) {
public Trace startSpan(String name) {
return this.startSpan(name, this.defaultSampler, null);
}
@Override
public <T> TraceScope startSpan(String name, Sampler<T> s, T info) {
public <T> Trace startSpan(String name, Sampler<T> s, T info) {
Span span = null;
if (TraceContextHolder.isTracing() || s.next(info)) {
span = createChild(getCurrentSpan(), name);
@@ -83,17 +80,75 @@ public class DefaultTrace implements Trace {
return continueSpan(span);
}
@Override
public Trace detach(Trace trace) {
if (trace == null) {
return null;
}
trace.detach();
Span cur = TraceContextHolder.getCurrentSpan();
Span span = trace.getSpan();
if (cur != span) {
ExceptionUtils.error("Tried to detach trace span but "
+ "it is not the current span for the '"
+ Thread.currentThread().getName() + "' thread: " + span
+ ". You have " + "probably forgotten to close or detach " + cur);
}
else {
if (span != NullTrace.INSTANCE) {
TraceContextHolder.setCurrentTrace(trace.getSavedTrace());
}
}
return trace.getSavedTrace();
}
@Override
public Trace close(Trace trace) {
if (trace == null) {
return null;
}
Span cur = TraceContextHolder.getCurrentSpan();
Span span = trace.getSpan();
Trace savedTrace = trace.getSavedTrace();
if (cur != span) {
ExceptionUtils.error("Tried to close trace span but "
+ "it is not the current span for the '"
+ Thread.currentThread().getName() + "' thread" + span
+ ". You have " + "probably forgotten to close or detach " + cur);
}
else {
if (span != NullTrace.INSTANCE) {
span.stop();
if (savedTrace != null
&& span.getParents().contains(savedTrace.getSpan().getSpanId())) {
this.publisher.publishEvent(
new SpanReleasedEvent(this, savedTrace.getSpan(), span));
TraceContextHolder.setCurrentTrace(savedTrace);
}
else {
this.publisher.publishEvent(new SpanReleasedEvent(this, span));
TraceContextHolder.removeCurrentTrace();
}
}
}
return savedTrace;
}
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();
MilliSpan span = MilliSpan.builder().begin(System.currentTimeMillis())
.name(name).traceId(this.idGenerator.create())
.spanId(this.idGenerator.create()).build();
this.publisher.publishEvent(new SpanAcquiredEvent(this, span));
return span;
}
else {
MilliSpan span = MilliSpan.builder().begin(System.currentTimeMillis()).name(name)
.traceId(parent.getTraceId()).parent(parent.getSpanId())
if (TraceContextHolder.getCurrentTrace() == null) {
Trace trace = createTrace(null, parent);
TraceContextHolder.setCurrentTrace(trace);
}
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 SpanAcquiredEvent(this, parent, span));
@@ -102,20 +157,31 @@ public class DefaultTrace implements Trace {
}
@Override
public TraceScope continueSpan(Span span) {
public Trace continueSpan(Span span) {
// Return an empty TraceScope that does nothing on close
if (span == null)
return NullScope.INSTANCE;
Span oldSpan = getCurrentSpan();
TraceContextHolder.setCurrentSpan(span);
if (span == null) {
return NullTrace.INSTANCE;
}
this.publisher.publishEvent(new SpanContinuedEvent(this, span));
return new TraceScope(this.publisher, span, oldSpan);
Trace trace = createTrace(TraceContextHolder.getCurrentTrace(), span);
TraceContextHolder.setCurrentTrace(trace);
return trace;
}
protected Span getCurrentSpan() {
protected Trace createTrace(Trace trace, Span span) {
return new Trace(trace, span);
}
@Override
public Span getCurrentSpan() {
return TraceContextHolder.getCurrentSpan();
}
@Override
public boolean isTracing() {
return TraceContextHolder.isTracing();
}
@Override
public void addAnnotation(String key, String value) {
Span s = getCurrentSpan();
@@ -132,8 +198,7 @@ public class DefaultTrace implements Trace {
@Override
public <V> Callable<V> wrap(Callable<V> callable) {
if (TraceContextHolder.isTracing()) {
return new TraceCallable<>(this, callable,
TraceContextHolder.getCurrentSpan());
return new TraceCallable<>(this, callable);
}
return callable;
}
@@ -146,8 +211,9 @@ public class DefaultTrace implements Trace {
@Override
public Runnable wrap(Runnable runnable) {
if (TraceContextHolder.isTracing()) {
return new TraceRunnable(this, runnable, TraceContextHolder.getCurrentSpan());
return new TraceRunnable(this, runnable);
}
return runnable;
}
}

View File

@@ -14,34 +14,26 @@
* limitations under the License.
*/
package org.springframework.cloud.sleuth;
package org.springframework.cloud.sleuth.trace;
import org.springframework.cloud.sleuth.Trace;
/**
* @author Spencer Gibb
*/
/**
* Singleton instance representing an empty {@link TraceScope}.
*/
public final class NullScope extends TraceScope {
public final class NullTrace extends Trace {
public static final TraceScope INSTANCE = new NullScope();
/**
* Singleton instance representing an empty {@link Trace}.
*/
public static final Trace INSTANCE = new NullTrace();
private NullScope() {
super(null, null, null);
}
@Override
public Span detach() {
return null;
}
@Override
public void close() {
return;
private NullTrace() {
super(null);
}
@Override
public String toString() {
return "NullScope";
return "NullTrace";
}
}

View File

@@ -14,24 +14,31 @@
* limitations under the License.
*/
package org.springframework.cloud.sleuth;
package org.springframework.cloud.sleuth.trace;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.core.NamedThreadLocal;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.core.NamedThreadLocal;
/**
* @author Spencer Gibb
*/
@CommonsLog
public class TraceContextHolder {
private static final ThreadLocal<Span> currentSpan = new NamedThreadLocal<>("Trace Context");
public static Span getCurrentSpan() {
private static final ThreadLocal<Trace> currentSpan = new NamedThreadLocal<>("Trace Context");
public static Trace getCurrentTrace() {
return currentSpan.get();
}
public static void setCurrentSpan(Span span) {
public static Span getCurrentSpan() {
return isTracing() ? currentSpan.get().getSpan() : null;
}
public static void setCurrentTrace(Trace span) {
// backwards compatibility
if (span == null) {
currentSpan.remove();
@@ -43,7 +50,7 @@ public class TraceContextHolder {
currentSpan.set(span);
}
public static void removeCurrentSpan() {
public static void removeCurrentTrace() {
currentSpan.remove();
}

View File

@@ -29,18 +29,19 @@ import java.util.List;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.cloud.sleuth.autoconfig.RandomUuidGenerator;
import org.springframework.cloud.sleuth.event.SpanAcquiredEvent;
import org.springframework.cloud.sleuth.event.SpanReleasedEvent;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.sampler.IsTracingSampler;
import org.springframework.cloud.sleuth.trace.DefaultTrace;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
/**
* @author Spencer Gibb
*/
public class DefaultTraceTests {
public class DefaultTraceManagerTests {
public static final String CREATE_SIMPLE_TRACE = "createSimpleTrace";
public static final String IMPORTANT_WORK_1 = "important work 1";
@@ -51,15 +52,15 @@ public class DefaultTraceTests {
public void tracingWorks() {
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
DefaultTrace trace = new DefaultTrace(new IsTracingSampler(),
DefaultTraceManager traceManager = new DefaultTraceManager(new IsTracingSampler(),
new RandomUuidGenerator(), publisher);
TraceScope scope = trace.startSpan(CREATE_SIMPLE_TRACE, new AlwaysSampler(), null);
Trace scope = traceManager.startSpan(CREATE_SIMPLE_TRACE, new AlwaysSampler(), null);
try {
importantWork1(trace);
importantWork1(traceManager);
}
finally {
scope.close();
traceManager.close(scope);
}
verify(publisher, times(NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
@@ -108,22 +109,22 @@ public class DefaultTraceTests {
return found;
}
private void importantWork1(Trace trace) {
TraceScope cur = trace.startSpan(IMPORTANT_WORK_1);
private void importantWork1(TraceManager traceManager) {
Trace cur = traceManager.startSpan(IMPORTANT_WORK_1);
try {
Thread.sleep((long) (50 * Math.random()));
importantWork2(trace);
importantWork2(traceManager);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
finally {
cur.close();
traceManager.close(cur);
}
}
private void importantWork2(Trace trace) {
TraceScope cur = trace.startSpan(IMPORTANT_WORK_2);
private void importantWork2(TraceManager traceManager) {
Trace cur = traceManager.startSpan(IMPORTANT_WORK_2);
try {
Thread.sleep((long) (50 * Math.random()));
}
@@ -131,7 +132,7 @@ public class DefaultTraceTests {
Thread.currentThread().interrupt();
}
finally {
cur.close();
traceManager.close(cur);
}
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.cloud.sleuth.instrument.concurrent.executor;
package org.springframework.cloud.sleuth.instrument.executor;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
@@ -20,53 +20,52 @@ import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.cloud.sleuth.RandomUuidGenerator;
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.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.RandomUuidGenerator;
import org.springframework.cloud.sleuth.event.SpanAcquiredEvent;
import org.springframework.cloud.sleuth.event.SpanReleasedEvent;
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.DefaultTrace;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
public class TraceableExecutorServiceTests {
private ApplicationEventPublisher publisher;
private ExecutorService traceableExecutorService;
private Trace trace;
private ExecutorService traceManagerableExecutorService;
private TraceManager traceManager;
private ExecutorService executorService;
private int NUM_SPANS = 11;
private int TOTAL_THREADS = 10;
@Before
public void setUp() throws Exception {
this.publisher = Mockito.mock(ApplicationEventPublisher.class);
this.trace = new DefaultTrace(new AlwaysSampler(), new RandomUuidGenerator(), this.publisher);
this.traceManager = new DefaultTraceManager(new AlwaysSampler(), new RandomUuidGenerator(), this.publisher);
ExecutorService es = Executors.newFixedThreadPool(3);
this.traceableExecutorService = new TraceableExecutorService(es, this.trace);
this.traceManagerableExecutorService = new TraceableExecutorService(es, this.traceManager);
this.executorService = Executors.newFixedThreadPool(3);
}
@After
public void tearDown() throws Exception {
this.trace = null;
this.traceableExecutorService.shutdown();
this.traceManager = null;
this.traceManagerableExecutorService.shutdown();
this.executorService.shutdown();
}
@Test
public void test_whenTraceContextOfWorkerThreadIsNotClosed_thenException() {
//THis test case ideally should fail but it is not failing because of the
// https://github.com/spring-cloud/spring-cloud-sleuth/issues/60 comment two
final AtomicInteger counter = new AtomicInteger(0);
final CountDownLatch latch = new CountDownLatch(TOTAL_THREADS);
TraceScope scope = this.trace.startSpan("PARENT");
for (int i = 0; i < TOTAL_THREADS; i++) {
traceableExecutorService.execute(new MyRunnable(counter, latch));
final CountDownLatch latch = new CountDownLatch(this.TOTAL_THREADS);
Trace scope = this.traceManager.startSpan("PARENT");
for (int i = 0; i < this.TOTAL_THREADS; i++) {
this.traceManagerableExecutorService.execute(new MyRunnable(counter, latch));
}
try {
@@ -74,15 +73,15 @@ public class TraceableExecutorServiceTests {
} catch (InterruptedException e) {
e.printStackTrace();
}
scope.close();
verify(this.publisher, times(NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
verify(publisher, times(NUM_SPANS)).publishEvent(isA(SpanReleasedEvent.class));
this.traceManager.close(scope);
verify(this.publisher, times(this.NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
verify(this.publisher, times(this.NUM_SPANS)).publishEvent(isA(SpanReleasedEvent.class));
ArgumentCaptor<ApplicationEvent> captor = ArgumentCaptor
.forClass(ApplicationEvent.class);
verify(publisher, atLeast(NUM_SPANS)).publishEvent(captor.capture());
verify(this.publisher, atLeast(this.NUM_SPANS)).publishEvent(captor.capture());
List<Span> spans = new ArrayList<>();
for (ApplicationEvent event : captor.getAllValues()) {
@@ -91,17 +90,17 @@ public class TraceableExecutorServiceTests {
}
}
assertThat("spans was wrong size", spans.size(), is(NUM_SPANS));
assertThat("spans was wrong size", spans.size(), is(this.NUM_SPANS));
}
@Test
public void test_whenTraceContextOfWorkerThreadIsClosed_thenNoException() {
final AtomicInteger counter = new AtomicInteger(0);
final CountDownLatch latch = new CountDownLatch(TOTAL_THREADS);
TraceScope scope = this.trace.startSpan("PARENT");
for (int i = 0; i < TOTAL_THREADS; i++) {
final TraceRunnableAdapter command = new TraceRunnableAdapter(new TraceRunnable(this.trace, new MyRunnable(counter, latch)));
executorService.execute(command);
final CountDownLatch latch = new CountDownLatch(this.TOTAL_THREADS);
Trace scope = this.traceManager.startSpan("PARENT");
for (int i = 0; i < this.TOTAL_THREADS; i++) {
final Runnable command = new TraceRunnable(this.traceManager, new MyRunnable(counter, latch));
this.executorService.execute(command);
}
try {
@@ -109,15 +108,15 @@ public class TraceableExecutorServiceTests {
} catch (InterruptedException e) {
e.printStackTrace();
}
scope.close();
verify(this.publisher, times(NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
verify(publisher, times(NUM_SPANS)).publishEvent(isA(SpanReleasedEvent.class));
this.traceManager.close(scope);
verify(this.publisher, times(this.NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
verify(this.publisher, times(this.NUM_SPANS)).publishEvent(isA(SpanReleasedEvent.class));
ArgumentCaptor<ApplicationEvent> captor = ArgumentCaptor
.forClass(ApplicationEvent.class);
verify(publisher, atLeast(NUM_SPANS)).publishEvent(captor.capture());
verify(this.publisher, atLeast(this.NUM_SPANS)).publishEvent(captor.capture());
List<Span> spans = new ArrayList<>();
for (ApplicationEvent event : captor.getAllValues()) {
@@ -126,36 +125,18 @@ public class TraceableExecutorServiceTests {
}
}
assertThat("spans was wrong size", spans.size(), is(NUM_SPANS));
}
class TraceRunnableAdapter implements Runnable {
private final Runnable delegate;
public TraceRunnableAdapter(final Runnable delegate) {
this.delegate = delegate;
}
@Override
public void run() {
try {
this.delegate.run();
}
finally {
TraceContextHolder.removeCurrentSpan();
}
}
assertThat("spans was wrong size", spans.size(), is(this.NUM_SPANS));
}
class MyRunnable implements Runnable {
private final AtomicInteger counter;
private final CountDownLatch latch;
MyRunnable(final AtomicInteger counter, final CountDownLatch latch) {
this.counter = counter;
this.latch = latch;
}
@Override
public void run() {
try {
@@ -166,12 +147,12 @@ public class TraceableExecutorServiceTests {
}
}
finally {
counter.incrementAndGet();
latch.countDown();
this.counter.incrementAndGet();
this.latch.countDown();
}
}
}
}

View File

@@ -18,9 +18,6 @@ package org.springframework.cloud.sleuth.instrument.integration;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.springframework.cloud.sleuth.Trace.NOT_SAMPLED_NAME;
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
import org.junit.After;
import org.junit.Before;
@@ -32,10 +29,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceContextHolder;
import org.springframework.cloud.sleuth.TraceScope;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.integration.TraceChannelInterceptorTests.App;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.DirectChannel;
@@ -60,7 +57,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
private DirectChannel channel;
@Autowired
private Trace trace;
private TraceManager trace;
private Message<?> message;
@@ -76,17 +73,17 @@ public class TraceChannelInterceptorTests implements MessageHandler {
@After
public void close() {
TraceContextHolder.removeCurrentSpan();
TraceContextHolder.removeCurrentTrace();
this.channel.unsubscribe(this);
}
@Test
public void testNoSpanCreation() {
this.channel.send(MessageBuilder.withPayload("hi").setHeader(NOT_SAMPLED_NAME, "")
this.channel.send(MessageBuilder.withPayload("hi").setHeader(Trace.NOT_SAMPLED_NAME, "")
.build());
assertNotNull("message was null", this.message);
String spanId = this.message.getHeaders().get(SPAN_ID_NAME, String.class);
String spanId = this.message.getHeaders().get(Trace.SPAN_ID_NAME, String.class);
assertNull("spanId was not null", spanId);
}
@@ -95,28 +92,28 @@ public class TraceChannelInterceptorTests implements MessageHandler {
this.channel.send(MessageBuilder.withPayload("hi").build());
assertNotNull("message was null", this.message);
String spanId = this.message.getHeaders().get(SPAN_ID_NAME, String.class);
String spanId = this.message.getHeaders().get(Trace.SPAN_ID_NAME, String.class);
assertNotNull("spanId was null", spanId);
String traceId = this.message.getHeaders().get(TRACE_ID_NAME, String.class);
String traceId = this.message.getHeaders().get(Trace.TRACE_ID_NAME, String.class);
assertNotNull("traceId was null", traceId);
assertNull(TraceContextHolder.getCurrentSpan());
assertNull(TraceContextHolder.getCurrentTrace());
}
@Test
public void testHeaderCreation() {
TraceScope traceScope = this.trace.startSpan("testSendMessage",
Trace traceScope = this.trace.startSpan("testSendMessage",
new AlwaysSampler(), null);
this.channel.send(MessageBuilder.withPayload("hi").build());
traceScope.close();
this.trace.close(traceScope);
assertNotNull("message was null", this.message);
String spanId = this.message.getHeaders().get(SPAN_ID_NAME, String.class);
String spanId = this.message.getHeaders().get(Trace.SPAN_ID_NAME, String.class);
assertNotNull("spanId was null", spanId);
String traceId = this.message.getHeaders().get(TRACE_ID_NAME, String.class);
String traceId = this.message.getHeaders().get(Trace.TRACE_ID_NAME, String.class);
assertNotNull("traceId was null", traceId);
assertNull(TraceContextHolder.getCurrentSpan());
assertNull(TraceContextHolder.getCurrentTrace());
}
@Configuration

View File

@@ -18,8 +18,6 @@ package org.springframework.cloud.sleuth.instrument.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
import org.junit.After;
import org.junit.Test;
@@ -30,10 +28,10 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceContextHolder;
import org.springframework.cloud.sleuth.TraceScope;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.integration.TraceContextPropagationChannelInterceptorTests.App;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.QueueChannel;
@@ -57,29 +55,29 @@ public class TraceContextPropagationChannelInterceptorTests {
private PollableChannel channel;
@Autowired
private Trace trace;
private TraceManager trace;
@After
public void close() {
TraceContextHolder.removeCurrentSpan();
TraceContextHolder.removeCurrentTrace();
}
@Test
public void testSpanPropagation() {
TraceScope traceScope = this.trace.startSpan("testSendMessage", new AlwaysSampler(), null);
Trace traceScope = this.trace.startSpan("testSendMessage", new AlwaysSampler(), null);
this.channel.send(MessageBuilder.withPayload("hi").build());
String expectedSpanId = traceScope.getSpan().getSpanId();
traceScope.close();
this.trace.close(traceScope);
Message<?> message = this.channel.receive(0);
assertNotNull("message was null", message);
String spanId = message.getHeaders().get(SPAN_ID_NAME, String.class);
String spanId = message.getHeaders().get(Trace.SPAN_ID_NAME, String.class);
assertEquals("spanId was wrong", expectedSpanId, spanId);
String traceId = message.getHeaders().get(TRACE_ID_NAME, String.class);
String traceId = message.getHeaders().get(Trace.TRACE_ID_NAME, String.class);
assertNotNull("traceId was null", traceId);
}

View File

@@ -17,18 +17,16 @@
package org.springframework.cloud.sleuth.instrument.web;
import static org.junit.Assert.assertNull;
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import lombok.SneakyThrows;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.sleuth.RandomUuidGenerator;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceContextHolder;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.RandomUuidGenerator;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.DefaultTrace;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockFilterChain;
@@ -37,6 +35,8 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import lombok.SneakyThrows;
/**
* @author Spencer Gibb
* @author Dave Syer
@@ -45,7 +45,7 @@ public class TraceFilterIntegrationTests {
private StaticApplicationContext context = new StaticApplicationContext();
private Trace trace = new DefaultTrace(new AlwaysSampler(),
private TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new RandomUuidGenerator(), this.context);
private MockHttpServletRequest request;
@@ -55,7 +55,7 @@ public class TraceFilterIntegrationTests {
@Before
@SneakyThrows
public void init() {
TraceContextHolder.removeCurrentSpan();
TraceContextHolder.removeCurrentTrace();
this.context.refresh();
this.request = builder().buildRequest(new MockServletContext());
this.response = new MockHttpServletResponse();
@@ -70,16 +70,16 @@ public class TraceFilterIntegrationTests {
@Test
public void startsNewTrace() throws Exception {
TraceFilter filter = new TraceFilter(this.trace);
TraceFilter filter = new TraceFilter(this.traceManager);
filter.doFilter(this.request, this.response, this.filterChain);
assertNull(TraceContextHolder.getCurrentSpan());
assertNull(TraceContextHolder.getCurrentTrace());
}
@Test
public void continuesSpanFromHeaders() throws Exception {
this.request = builder().header(SPAN_ID_NAME, "myspan")
.header(TRACE_ID_NAME, "mytrace").buildRequest(new MockServletContext());
TraceFilter filter = new TraceFilter(this.trace);
this.request = builder().header(Trace.SPAN_ID_NAME, "myspan")
.header(Trace.TRACE_ID_NAME, "mytraceManager").buildRequest(new MockServletContext());
TraceFilter filter = new TraceFilter(this.traceManager);
filter.doFilter(this.request, this.response, this.filterChain);
assertNull(TraceContextHolder.getCurrentSpan());
}

View File

@@ -16,24 +16,27 @@
package org.springframework.cloud.sleuth.instrument.web;
import static org.mockito.Matchers.anyObject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import lombok.SneakyThrows;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceScope;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.RandomUuidGenerator;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockFilterChain;
@@ -42,18 +45,18 @@ import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import lombok.SneakyThrows;
/**
* @author Spencer Gibb
*/
public class TraceFilterTests {
@Mock
private Trace trace;
private ApplicationEventPublisher publisher;
@Mock
private TraceScope traceScope;
private TraceManager trace;
@Mock
private Span span;
private MockHttpServletRequest request;
@@ -64,97 +67,90 @@ public class TraceFilterTests {
@SneakyThrows
public void init() {
initMocks(this);
request = builder()
.buildRequest(new MockServletContext());
response = new MockHttpServletResponse();
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
filterChain = new MockFilterChain();
this.trace = new DefaultTraceManager(new AlwaysSampler(),
new RandomUuidGenerator(), this.publisher) {
@Override
protected Trace createTrace(Trace trace, Span span) {
TraceFilterTests.this.span= span;
return super.createTrace(trace, span);
}
};
this.request = builder().buildRequest(new MockServletContext());
this.response = new MockHttpServletResponse();
this.response.setContentType(MediaType.APPLICATION_JSON_VALUE);
this.filterChain = new MockFilterChain();
}
public MockHttpServletRequestBuilder builder() {
return get("/")
.accept(MediaType.APPLICATION_JSON)
.header("User-Agent", "MockMvc");
return get("/").accept(MediaType.APPLICATION_JSON).header("User-Agent",
"MockMvc");
}
@Test
public void notTraced() throws Exception {
TraceManager trace = Mockito.mock(TraceManager.class);
TraceFilter filter = new TraceFilter(trace);
when(this.trace.startSpan(anyString())).thenReturn(traceScope);
request = get("/favicon.ico")
.accept(MediaType.ALL)
this.request = get("/favicon.ico").accept(MediaType.ALL)
.buildRequest(new MockServletContext());
filter.doFilter(request, response, filterChain);
filter.doFilter(this.request, this.response, this.filterChain);
verify(this.trace, never()).startSpan(anyString());
verify(this.traceScope, never()).close();
verify(trace, never()).startSpan(anyString());
verify(trace, never()).close(any(Trace.class));
}
@Test
public void startsNewTrace() throws Exception {
TraceFilter filter = new TraceFilter(trace);
when(this.trace.startSpan(anyString())).thenReturn(traceScope);
filter.doFilter(request, response, filterChain);
verify(this.trace).startSpan(anyString());
TraceFilter filter = new TraceFilter(this.trace);
filter.doFilter(this.request, this.response, this.filterChain);
verifyHttpAnnotations();
verify(this.traceScope).close();
assertNull(TraceContextHolder.getCurrentTrace());
}
@Test
public void continuesSpanInRequestAttr() throws Exception {
request.setAttribute(TraceFilter.TRACE_REQUEST_ATTR, this.traceScope);
TraceFilter filter = new TraceFilter(trace);
filter.doFilter(request, response, filterChain);
Trace traceScope = this.trace.startSpan("foo");
this.request.setAttribute(TraceFilter.TRACE_REQUEST_ATTR, traceScope);
verify(this.trace).continueSpan((Span) anyObject());
TraceFilter filter = new TraceFilter(this.trace);
filter.doFilter(this.request, this.response, this.filterChain);
verifyHttpAnnotations();
verify(this.traceScope).close();
assertNull(TraceContextHolder.getCurrentTrace());
}
@Test
public void continuesSpanFromHeaders() throws Exception {
request = builder()
.header(SPAN_ID_NAME, "myspan")
.header(TRACE_ID_NAME, "mytrace")
this.request = builder().header(Trace.SPAN_ID_NAME, "myspan")
.header(Trace.TRACE_ID_NAME, "mytrace")
.buildRequest(new MockServletContext());
when(this.trace.startSpan(anyString(), (Span) anyObject())).thenReturn(traceScope);
when(this.traceScope.getSpan()).thenReturn(this.span);
when(this.span.getSpanId()).thenReturn("myspan");
when(this.span.getTraceId()).thenReturn("mytrace");
TraceFilter filter = new TraceFilter(trace);
filter.doFilter(request, response, filterChain);
verify(this.trace).startSpan(anyString(), (Span) anyObject());
TraceFilter filter = new TraceFilter(this.trace);
filter.doFilter(this.request, this.response, this.filterChain);
verifyHttpAnnotations();
verify(this.traceScope).close();
assertNull(TraceContextHolder.getCurrentTrace());
}
public void verifyHttpAnnotations() {
verify(this.trace).addAnnotation("/http/request/uri", "http://localhost/");
verify(this.trace).addAnnotation("/http/request/endpoint", "/");
verify(this.trace).addAnnotation("/http/request/method", "GET");
verify(this.trace).addAnnotation("/http/request/headers/accept", MediaType.APPLICATION_JSON_VALUE);
verify(this.trace).addAnnotation("/http/request/headers/user-agent", "MockMvc");
hasAnnotation(this.span, "/http/request/uri", "http://localhost/");
hasAnnotation(this.span, "/http/request/endpoint", "/");
hasAnnotation(this.span, "/http/request/method", "GET");
hasAnnotation(this.span, "/http/request/headers/accept",
MediaType.APPLICATION_JSON_VALUE);
hasAnnotation(this.span, "/http/request/headers/user-agent", "MockMvc");
verify(this.trace).addAnnotation("/http/response/status_code", HttpStatus.OK.toString());
verify(this.trace).addAnnotation("/http/response/headers/content-type", MediaType.APPLICATION_JSON_VALUE);
hasAnnotation(this.span, "/http/response/status_code",
HttpStatus.OK.toString());
hasAnnotation(this.span, "/http/response/headers/content-type",
MediaType.APPLICATION_JSON_VALUE);
}
private void hasAnnotation(Span span, String name, String value) {
assertEquals(value, span.getAnnotations().get(name));
}
}

View File

@@ -1,9 +1,6 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.sleuth.Trace.PARENT_ID_NAME;
import static org.springframework.cloud.sleuth.Trace.SPAN_ID_NAME;
import static org.springframework.cloud.sleuth.Trace.TRACE_ID_NAME;
import java.util.ArrayList;
import java.util.Arrays;
@@ -21,10 +18,12 @@ import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.TraceContextHolder;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.cloud.sleuth.instrument.web.TraceWebAutoConfiguration;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -42,8 +41,11 @@ import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { TraceWebAutoConfiguration.class, FeignTraceTest.TestConfiguration.class })
@WebIntegrationTest(value = "spring.application.name=fooservice", randomPort = true)
@SpringApplicationConfiguration(classes = { TraceWebAutoConfiguration.class,
FeignTraceTest.TestConfiguration.class })
// TODO: make it work with default isolation
@WebIntegrationTest(value = { "spring.application.name=fooservice",
"hystrix.command.default.execution.isolation.strategy=SEMAPHORE" }, randomPort = true)
public class FeignTraceTest {
@Autowired
@@ -52,20 +54,23 @@ public class FeignTraceTest {
@Autowired
Listener listener;
@Autowired
TraceManager traceManager;
@After
public void close() {
TraceContextHolder.removeCurrentSpan();
listener.getEvents().clear();
TraceContextHolder.removeCurrentTrace();
this.listener.getEvents().clear();
}
@Test
public void shouldWorkWhenNotTracing() {
// when
ResponseEntity<String> response = testFeignInterface.getNoTrace();
ResponseEntity<String> response = this.testFeignInterface.getNoTrace();
// then
assertThat(getHeader(response, TRACE_ID_NAME)).isNull();
assertThat(listener.getEvents()).isEmpty();
assertThat(getHeader(response, Trace.TRACE_ID_NAME)).isNull();
assertThat(this.listener.getEvents()).isEmpty();
}
@Test
@@ -74,17 +79,17 @@ public class FeignTraceTest {
String currentTraceId = "currentTraceId";
String currentSpanId = "currentSpanId";
String currentParentId = "currentParentId";
TraceContextHolder.setCurrentSpan(
MilliSpan.builder().traceId(currentTraceId).spanId(currentSpanId).parent(currentParentId).build());
this.traceManager.continueSpan(MilliSpan.builder().traceId(currentTraceId)
.spanId(currentSpanId).parent(currentParentId).build());
// when
ResponseEntity<String> response = testFeignInterface.getTraceId();
ResponseEntity<String> response = this.testFeignInterface.getTraceId();
// then
assertThat(getHeader(response, TRACE_ID_NAME)).isEqualTo(currentTraceId);
assertThat(getHeader(response, SPAN_ID_NAME)).isEqualTo(currentSpanId);
assertThat(getHeader(response, PARENT_ID_NAME)).isEqualTo(currentParentId);
assertThat(listener.getEvents().size()).isEqualTo(2);
assertThat(getHeader(response, Trace.TRACE_ID_NAME)).isEqualTo(currentTraceId);
assertThat(getHeader(response, Trace.SPAN_ID_NAME)).isEqualTo(currentSpanId);
assertThat(getHeader(response, Trace.PARENT_ID_NAME)).isEqualTo(currentParentId);
assertThat(this.listener.getEvents().size()).isEqualTo(2);
}
private String getHeader(ResponseEntity<String> response, String name) {
@@ -124,16 +129,16 @@ public class FeignTraceTest {
@EventListener(ClientSentEvent.class)
public void sent(ClientSentEvent event) {
events.add(event);
this.events.add(event);
}
@EventListener(ClientReceivedEvent.class)
public void received(ClientReceivedEvent event) {
events.add(event);
this.events.add(event);
}
public List<ApplicationEvent> getEvents() {
return events;
return this.events;
}
}
@@ -141,14 +146,16 @@ public class FeignTraceTest {
public static class FooController {
@RequestMapping(value = "/notrace", method = RequestMethod.GET)
public String notrace(@RequestHeader(name = TRACE_ID_NAME, required = false) String traceId) {
public String notrace(
@RequestHeader(name = Trace.TRACE_ID_NAME, required = false) String traceId) {
assertThat(traceId).isNull();
return "OK";
}
@RequestMapping(value = "/traceid", method = RequestMethod.GET)
public String traceId(@RequestHeader(TRACE_ID_NAME) String traceId, @RequestHeader(SPAN_ID_NAME) String spanId,
@RequestHeader(PARENT_ID_NAME) String parentId) {
public String traceId(@RequestHeader(Trace.TRACE_ID_NAME) String traceId,
@RequestHeader(Trace.SPAN_ID_NAME) String spanId,
@RequestHeader(Trace.PARENT_ID_NAME) String parentId) {
assertThat(traceId).isNotEmpty();
assertThat(parentId).isNotEmpty();
assertThat(spanId).isNotEmpty();
@@ -165,7 +172,7 @@ public class FeignTraceTest {
@Bean
public ILoadBalancer ribbonLoadBalancer() {
BaseLoadBalancer balancer = new BaseLoadBalancer();
balancer.setServersList(Arrays.asList(new Server("localhost", port)));
balancer.setServersList(Arrays.asList(new Server("localhost", this.port)));
return balancer;
}

View File

@@ -28,7 +28,11 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceContextHolder;
import org.springframework.cloud.sleuth.autoconfig.RandomUuidGenerator;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.test.web.client.MockMvcClientHttpRequestFactory;
@@ -48,28 +52,33 @@ public class TraceRestTemplateInterceptorTests {
private MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new TestController())
.build();
private RestTemplate template = new RestTemplate(new MockMvcClientHttpRequestFactory(
this.mockMvc));
private RestTemplate template = new RestTemplate(
new MockMvcClientHttpRequestFactory(this.mockMvc));
private DefaultTraceManager traces;
private StaticApplicationContext publisher = new StaticApplicationContext();
@Before
public void setup() {
this.template
.setInterceptors(Arrays
.<ClientHttpRequestInterceptor> asList(new TraceRestTemplateInterceptor()));
this.publisher.refresh();
this.traces = new DefaultTraceManager(new AlwaysSampler(),
new RandomUuidGenerator(), this.publisher);
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
new TraceRestTemplateInterceptor(this.traces)));
}
@After
public void clean() {
TraceContextHolder.removeCurrentSpan();
TraceContextHolder.removeCurrentTrace();
}
@Test
public void headersAddedWhenTracing() {
TraceContextHolder.setCurrentSpan(MilliSpan.builder().traceId("foo")
.spanId("bar").build());
this.traces.continueSpan(MilliSpan.builder().traceId("foo").spanId("bar").build());
@SuppressWarnings("unchecked")
Map<String, String> headers = this.template.getForEntity("/", Map.class)
.getBody();
.getBody();
assertEquals("bar", headers.get(Trace.SPAN_ID_NAME));
assertEquals("foo", headers.get(Trace.TRACE_ID_NAME));
}
@@ -78,7 +87,7 @@ public class TraceRestTemplateInterceptorTests {
public void headersNotAddedWhenNotTracing() {
@SuppressWarnings("unchecked")
Map<String, String> headers = this.template.getForEntity("/", Map.class)
.getBody();
.getBody();
assertFalse("Wrong headers: " + headers, headers.containsKey(Trace.SPAN_ID_NAME));
}

View File

@@ -21,7 +21,7 @@ import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
public class SampleBackground {
@Autowired
private Trace trace;
private TraceManager traceManager;
@SneakyThrows
@Async
@@ -40,7 +40,7 @@ public class SampleBackground {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.trace.addAnnotation("background-sleep-millis", String.valueOf(millis));
this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));
}
}

View File

@@ -1,6 +1,10 @@
server:
port: 3382
sample:
zipkin:
enabled: false
spring:
application:
name: testSleuthRibbon

View File

@@ -21,7 +21,7 @@ import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
public class SampleBackground {
@Autowired
private Trace trace;
private TraceManager traceManager;
@SneakyThrows
@Async
@@ -40,7 +40,7 @@ public class SampleBackground {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.trace.addAnnotation("background-sleep-millis", String.valueOf(millis));
this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));
}
}

View File

@@ -19,21 +19,21 @@ package sample;
import java.util.Random;
import java.util.concurrent.Callable;
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.TraceAccessor;
import org.springframework.cloud.sleuth.TraceManager;
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 lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/**
* @author Spencer Gibb
*/
@@ -44,7 +44,9 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@Autowired
private RestTemplate restTemplate;
@Autowired
private Trace trace;
private TraceManager traceManager;
@Autowired
private TraceAccessor accessor;
@Autowired
private SampleBackground controller;
private int port;
@@ -68,8 +70,8 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
SampleController.this.trace.addAnnotation("callable-sleep-millis", String.valueOf(millis));
Span currentSpan = TraceContextHolder.getCurrentSpan();
SampleController.this.traceManager.addAnnotation("callable-sleep-millis", String.valueOf(millis));
Span currentSpan = SampleController.this.accessor.getCurrentSpan();
return "async hi: " + currentSpan;
}
};
@@ -87,24 +89,24 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.trace.addAnnotation("random-sleep-millis", String.valueOf(millis));
this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
return "hi2";
}
@SneakyThrows
@RequestMapping("/traced")
public String traced() {
TraceScope scope = this.trace.startSpan("customTraceEndpoint",
Trace scope = this.traceManager.startSpan("customTraceEndpoint",
new AlwaysSampler(), null);
final Random random = new Random();
int millis = random.nextInt(1000);
log.info("Sleeping for {} millis", millis);
Thread.sleep(millis);
this.trace.addAnnotation("random-sleep-millis", String.valueOf(millis));
this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
String s = this.restTemplate.getForObject("http://localhost:" + this.port
+ "/call", String.class);
scope.close();
this.traceManager.close(scope);
return "traced/" + s;
}
@@ -115,7 +117,7 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
int millis = random.nextInt(1000);
log.info("Sleeping for {} millis", millis);
Thread.sleep(millis);
this.trace.addAnnotation("random-sleep-millis", String.valueOf(millis));
this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
String s = this.restTemplate.getForObject("http://localhost:" + this.port
+ "/call", String.class);

View File

@@ -11,4 +11,4 @@ logging:
sample:
zipkin:
# enabled: false
enabled: false

View File

@@ -21,7 +21,7 @@ import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@@ -32,7 +32,7 @@ import org.springframework.stereotype.Component;
public class SampleBackground {
@Autowired
private Trace trace;
private TraceManager traceManager;
@SneakyThrows
@Async
@@ -40,7 +40,7 @@ public class SampleBackground {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.trace.addAnnotation("background-sleep-millis", String.valueOf(millis));
this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));
}
}

View File

@@ -19,21 +19,21 @@ package sample;
import java.util.Random;
import java.util.concurrent.Callable;
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.TraceAccessor;
import org.springframework.cloud.sleuth.TraceManager;
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 lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
/**
* @author Spencer Gibb
*/
@@ -44,7 +44,9 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@Autowired
private RestTemplate restTemplate;
@Autowired
private Trace trace;
private TraceManager trace;
@Autowired
private TraceAccessor accessor;
@Autowired
private SampleBackground controller;
private int port;
@@ -69,7 +71,7 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
int millis = random.nextInt(1000);
Thread.sleep(millis);
SampleController.this.trace.addAnnotation("callable-sleep-millis", String.valueOf(millis));
Span currentSpan = TraceContextHolder.getCurrentSpan();
Span currentSpan = SampleController.this.accessor.getCurrentSpan();
return "async hi: " + currentSpan;
}
};
@@ -94,7 +96,7 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/traced")
public String traced() {
TraceScope scope = this.trace.startSpan("customTraceEndpoint",
Trace scope = this.trace.startSpan("customTraceEndpoint",
new AlwaysSampler(), null);
final Random random = new Random();
int millis = random.nextInt(1000);
@@ -104,7 +106,7 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
String s = this.restTemplate.getForObject("http://localhost:" + this.port
+ "/call", String.class);
scope.close();
this.trace.close(scope);
return "traced/" + s;
}

View File

@@ -32,15 +32,13 @@ import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.Sampler;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceScope;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.cloud.sleuth.event.ServerReceivedEvent;
import org.springframework.cloud.sleuth.event.ServerSentEvent;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.stream.SleuthStreamAutoConfiguration;
import org.springframework.cloud.sleuth.stream.StreamSpanListener;
import org.springframework.cloud.sleuth.stream.StreamSpanListenerTests.TestConfiguration;
import org.springframework.cloud.stream.binder.local.config.LocalBinderAutoConfiguration;
import org.springframework.cloud.stream.config.ChannelBindingAutoConfiguration;
@@ -59,7 +57,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
public class StreamSpanListenerTests {
@Autowired
private Trace trace;
private TraceManager traceManager;
@Autowired
private ApplicationContext application;
@@ -74,8 +72,8 @@ public class StreamSpanListenerTests {
@Test
public void acquireAndRelease() {
TraceScope context = this.trace.startSpan("foo");
context.close();
Trace context = this.traceManager.startSpan("foo");
this.traceManager.close(context);
assertEquals(1, this.test.spans.size());
}
@@ -83,14 +81,14 @@ public class StreamSpanListenerTests {
public void rpcAnnotations() {
Span parent = MilliSpan.builder().traceId("xxxx").name("parent").remote(true)
.build();
TraceScope context = this.trace.startSpan("child", parent);
Trace context = this.traceManager.startSpan("child", parent);
this.application.publishEvent(new ClientSentEvent(this, context.getSpan()));
this.application
.publishEvent(new ServerReceivedEvent(this, parent, context.getSpan()));
this.application
.publishEvent(new ServerSentEvent(this, parent, context.getSpan()));
this.application.publishEvent(new ClientReceivedEvent(this, context.getSpan()));
context.close();
this.traceManager.close(context);
assertEquals(2, this.test.spans.size());
}

View File

@@ -32,7 +32,7 @@ import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.Sampler;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceScope;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
@@ -58,7 +58,7 @@ import com.github.kristofa.brave.SpanCollector;
public class ZipkinSpanListenerTests {
@Autowired
private Trace trace;
private TraceManager traceManager;
@Autowired
private ApplicationContext application;
@@ -73,20 +73,20 @@ public class ZipkinSpanListenerTests {
@Test
public void acquireAndRelease() {
TraceScope context = this.trace.startSpan("foo");
context.close();
Trace context = this.traceManager.startSpan("foo");
this.traceManager.close(context);
assertEquals(1, this.test.spans.size());
}
@Test
public void rpcAnnotations() {
Span parent = MilliSpan.builder().traceId("xxxx").name("parent").remote(true).build();
TraceScope context = this.trace.startSpan("child", parent);
Trace context = this.traceManager.startSpan("child", parent);
this.application.publishEvent(new ClientSentEvent(this, context.getSpan()));
this.application.publishEvent(new ServerReceivedEvent(this, parent, context.getSpan()));
this.application.publishEvent(new ServerSentEvent(this, parent, context.getSpan()));
this.application.publishEvent(new ClientReceivedEvent(this, context.getSpan()));
context.close();
this.traceManager.close(context);
assertEquals(2, this.test.spans.size());
}