[#106] Converted UUID to Long

- Changed Random instantiation to a shared Random
- Changed the name of the converter
- Changed generator into random
- Span id is now non-nullable.
    - it gets generated in the http filter if it's not there
    - it's generated in the spring-integration channels if it wasn't set
This commit is contained in:
Marcin Grzejszczak
2016-01-18 14:38:09 +01:00
parent 483737fdc1
commit 82ed9f78a1
68 changed files with 685 additions and 888 deletions

View File

@@ -35,10 +35,10 @@ public class MilliSpan implements Span {
private final long begin;
private long end = 0;
private final String name;
private final String traceId;
private final long traceId;
@Singular
private List<String> parents = new ArrayList<>();
private final String spanId;
private List<Long> parents = new ArrayList<>();
private final long spanId;
private boolean remote = false;
private boolean exportable = true;
private final Map<String, String> tags = new LinkedHashMap<>();
@@ -50,7 +50,7 @@ public class MilliSpan implements Span {
return new MilliSpan().toBuilder();
}
public MilliSpan(long begin, long end, String name, String traceId, List<String> parents, String spanId, boolean remote, boolean exportable, String processId) {
public MilliSpan(long begin, long end, String name, long traceId, List<Long> parents, long spanId, boolean remote, boolean exportable, String processId) {
this.begin = begin<=0 ? System.currentTimeMillis() : begin;
this.end = end;
this.name = name;
@@ -66,9 +66,10 @@ public class MilliSpan implements Span {
private MilliSpan() {
this.begin = 0;
this.name = null;
this.traceId = null;
this.spanId = null;
this.traceId = 0;
this.spanId = 0;
this.processId = null;
this.parents = new ArrayList<>();
}
@Override

View File

@@ -16,6 +16,9 @@
package org.springframework.cloud.sleuth;
import org.springframework.util.Assert;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
@@ -41,18 +44,18 @@ public interface Span {
* The spanId is immutable and cannot be changed. It is safe to access this from
* multiple threads.
*/
String getSpanId();
long getSpanId();
/**
* A pseudo-unique (random) number assigned to the trace associated with this span
*/
String getTraceId();
long getTraceId();
/**
* Return a unique id for the process from which this Span originated.
* <p/>
* <p/>
* Will never be null.
* // TODO: Check when this is going to be null (cause it may be null)
*/
String getProcessId();
@@ -62,7 +65,7 @@ public interface Span {
* <p/>
* The collection will be empty if there are no parents.
*/
List<String> getParents();
List<Long> getParents();
/**
* Flag that tells us whether the span was started in another process. Useful in RPC
@@ -127,4 +130,26 @@ public interface Span {
* Will never be null.
*/
List<Log> logs();
/**
* Class used for conversions of long ids to their String representation
*/
class IdConverter {
/**
* Represents given long id as hex string
*/
public static String toHex(long id) {
return Long.toHexString(id);
}
/**
* Represents hex string as long
*/
public static long fromHex(String hexString) {
Assert.hasText(hexString, "Can't convert empty hex string to long");
return new BigInteger(hexString, 16).longValue();
}
}
}

View File

@@ -19,15 +19,13 @@ 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.Sampler;
import org.springframework.cloud.sleuth.sampler.DefaultStringToUuidConverter;
import org.springframework.cloud.sleuth.sampler.IsTracingSampler;
import org.springframework.cloud.sleuth.sampler.StringToUuidConverter;
import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.IdGenerator;
import org.springframework.util.JdkIdGenerator;
import java.util.Random;
/**
* @author Spencer Gibb
@@ -37,9 +35,8 @@ import org.springframework.util.JdkIdGenerator;
public class TraceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public IdGenerator traceIdGenerator() {
return new JdkIdGenerator();
public Random random() {
return new Random();
}
@Bean
@@ -50,14 +47,8 @@ public class TraceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public StringToUuidConverter stringToUuidConverter() {
return new DefaultStringToUuidConverter();
}
@Bean
@ConditionalOnMissingBean
public DefaultTraceManager traceManager(Sampler<Void> sampler, IdGenerator idGenerator,
public DefaultTraceManager traceManager(Sampler<Void> sampler,
ApplicationEventPublisher publisher) {
return new DefaultTraceManager(sampler, idGenerator, publisher);
return new DefaultTraceManager(sampler, random(), publisher);
}
}

View File

@@ -9,7 +9,8 @@ import org.springframework.integration.context.IntegrationObjectSupport;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.util.StringUtils;
import java.util.Random;
/**
* Abstraction over classes related to channel intercepting
@@ -20,8 +21,11 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
protected final TraceManager traceManager;
protected AbstractTraceChannelInterceptor(TraceManager traceManager) {
protected final Random random;
protected AbstractTraceChannelInterceptor(TraceManager traceManager, Random random) {
this.traceManager = traceManager;
this.random = random;
}
/**
@@ -29,33 +33,42 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
* trace id passed initially.
*/
Span buildSpan(Message<?> message) {
String spanId = getHeader(message, Trace.SPAN_ID_NAME);
String traceId = getHeader(message, Trace.TRACE_ID_NAME);
if (StringUtils.hasText(traceId)) {
MilliSpan.MilliSpanBuilder span = MilliSpan.builder().traceId(traceId).spanId(spanId);
String parentId = getHeader(message, Trace.PARENT_ID_NAME);
if (message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
span.exportable(false);
}
String processId = getHeader(message, Trace.PROCESS_ID_NAME);
String spanName = getHeader(message, Trace.SPAN_NAME_NAME);
if (spanName != null) {
span.name(spanName);
}
if (processId != null) {
span.processId(processId);
}
if (parentId != null) {
span.parent(parentId);
}
span.remote(true);
return span.build();
if (!hasHeader(message, Trace.TRACE_ID_NAME) || !hasHeader(message, Trace.SPAN_ID_NAME)) {
return null; // cannot build a span without ids
}
return null;
long spanId = hasHeader(message, Trace.SPAN_ID_NAME) ?
getHeader(message, Trace.SPAN_ID_NAME, Long.class) : random.nextLong();
long traceId = getHeader(message, Trace.TRACE_ID_NAME, Long.class);
MilliSpan.MilliSpanBuilder span = MilliSpan.builder().traceId(traceId).spanId(spanId);
Long parentId = getHeader(message, Trace.PARENT_ID_NAME, Long.class);
if (message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
span.exportable(false);
}
String processId = getHeader(message, Trace.PROCESS_ID_NAME);
String spanName = getHeader(message, Trace.SPAN_NAME_NAME);
if (spanName != null) {
span.name(spanName);
}
if (processId != null) {
span.processId(processId);
}
if (parentId != null) {
span.parent(parentId);
}
span.remote(true);
return span.build();
}
String getHeader(Message<?> message, String name) {
return (String) message.getHeaders().get(name);
return getHeader(message, name, String.class);
}
<T> T getHeader(Message<?> message, String name, Class<T> type) {
return message.getHeaders().get(name, type);
}
boolean hasHeader(Message<?> message, String name) {
return message.getHeaders().containsKey(name);
}
String getChannelName(MessageChannel channel) {

View File

@@ -20,6 +20,7 @@ import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.List;
@@ -88,13 +89,20 @@ public class SpanMessageHeaders {
private static void addHeader(Map<String, String> headers, String name,
String value) {
if (value != null) {
if (StringUtils.hasText(value)) {
headers.put(name, value);
}
}
private static String getFirst(List<String> parents) {
return parents == null || parents.isEmpty() ? null : parents.get(0);
private static void addHeader(Map<String, String> headers, String name,
Long value) {
if (value != null) {
addHeader(headers, name, Span.IdConverter.toHex(value));
}
}
private static Long getFirst(List<Long> parents) {
return parents.isEmpty() ? null : parents.get(0);
}
}

View File

@@ -22,6 +22,7 @@ import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.simp.SimpMessageType;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Map;
@@ -64,12 +65,12 @@ public class StompMessageBuilder {
setHeaderIfAbsent(Trace.SPAN_ID_NAME, span.getSpanId());
setHeaderIfAbsent(Trace.TRACE_ID_NAME, span.getTraceId());
setHeaderIfAbsent(Trace.SPAN_NAME_NAME, span.getName());
String parentId = getParentId(TraceContextHolder.getCurrentSpan());
Long parentId = getParentId(TraceContextHolder.getCurrentSpan());
if (parentId != null)
setHeaderIfAbsent(Trace.PARENT_ID_NAME, parentId);
String processId = span.getProcessId();
if (processId != null)
if (StringUtils.hasText(processId))
setHeaderIfAbsent(Trace.PROCESS_ID_NAME, processId);
}
return this;
@@ -113,8 +114,8 @@ public class StompMessageBuilder {
}
}
private String getParentId(final Span currentSpan) {
List<String> parents = currentSpan.getParents();
return parents == null || parents.isEmpty() ? null : parents.get(0);
private Long getParentId(final Span currentSpan) {
List<Long> parents = currentSpan.getParents();
return parents.isEmpty() ? null : parents.get(0);
}
}

View File

@@ -23,6 +23,8 @@ import org.springframework.cloud.sleuth.sampler.IsTracingSampler;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import java.util.Random;
/**
* @author Dave Syer
*
@@ -31,8 +33,8 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
private ThreadLocal<Trace> traceHolder = new ThreadLocal<>();
public TraceChannelInterceptor(TraceManager traceManager) {
super(traceManager);
public TraceChannelInterceptor(TraceManager traceManager, Random random) {
super(traceManager, random);
}
@Override

View File

@@ -16,9 +16,6 @@
package org.springframework.cloud.sleuth.instrument.integration;
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;
@@ -31,6 +28,10 @@ import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.messaging.support.ExecutorChannelInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
/**
* The {@link ExecutorChannelInterceptor} implementation responsible for the {@link Span}
@@ -92,8 +93,8 @@ public class TraceContextPropagationChannelInterceptor extends ChannelIntercepto
return postReceive(message, channel);
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty()
private Long getParentId(Span span) {
return !span.getParents().isEmpty()
? span.getParents().get(0) : null;
}
@@ -130,12 +131,12 @@ public class TraceContextPropagationChannelInterceptor extends ChannelIntercepto
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);
Long parentId = getParentId(span);
if (parentId != null) {
setHeader(headers, Trace.PARENT_ID_NAME, parentId);
}
String processId = this.span.getProcessId();
if (processId != null) {
String processId = span.getProcessId();
if (StringUtils.hasText(processId)) {
setHeader(headers, Trace.PROCESS_ID_NAME, processId);
}
this.messageHeaders = new MessageHeaders(headers);
@@ -146,6 +147,9 @@ public class TraceContextPropagationChannelInterceptor extends ChannelIntercepto
headers.put(name, value);
}
}
public void setHeader(Map<String, Object> headers, String name, long value) {
setHeader(headers, name, Span.IdConverter.toHex(value));
}
@Override
public Object getPayload() {

View File

@@ -26,6 +26,8 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.config.GlobalChannelInterceptor;
import java.util.Random;
/**
* @author Spencer Gibb
*/
@@ -45,13 +47,13 @@ public class TraceSpringIntegrationAutoConfiguration {
@Bean
@GlobalChannelInterceptor
public TraceChannelInterceptor traceChannelInterceptor(TraceManager traceManager) {
return new TraceChannelInterceptor(traceManager);
public TraceChannelInterceptor traceChannelInterceptor(TraceManager traceManager, Random random) {
return new TraceChannelInterceptor(traceManager, random);
}
@Bean
public TraceStompMessageChannelInterceptor traceStompMessageChannelInterceptor(TraceManager traceManager) {
return new TraceStompMessageChannelInterceptor(traceManager);
public TraceStompMessageChannelInterceptor traceStompMessageChannelInterceptor(TraceManager traceManager, Random random) {
return new TraceStompMessageChannelInterceptor(traceManager, random);
}
@Bean

View File

@@ -22,6 +22,8 @@ import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.support.ChannelInterceptor;
import java.util.Random;
/**
* Interceptor for Stomp Messages sent over websocket
*
@@ -32,8 +34,8 @@ import org.springframework.messaging.support.ChannelInterceptor;
public class TraceStompMessageChannelInterceptor extends AbstractTraceChannelInterceptor implements ChannelInterceptor {
private ThreadLocal<Trace> traceScopeHolder = new ThreadLocal<Trace>();
public TraceStompMessageChannelInterceptor(final TraceManager traceManager) {
super(traceManager);
public TraceStompMessageChannelInterceptor(TraceManager traceManager, Random random) {
super(traceManager, random);
}
@Override

View File

@@ -19,6 +19,7 @@ import static org.springframework.util.StringUtils.hasText;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Random;
import java.util.regex.Pattern;
import javax.servlet.FilterChain;
@@ -40,6 +41,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.UrlPathHelper;
@@ -68,18 +70,22 @@ public class TraceFilter extends OncePerRequestFilter
private final TraceManager traceManager;
private final Pattern skipPattern;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private final Random random;
private UrlPathHelper urlPathHelper = new UrlPathHelper();
private ApplicationEventPublisher publisher;
public TraceFilter(TraceManager traceManager) {
this.traceManager = traceManager;
this.skipPattern = DEFAULT_SKIP_PATTERN;
this.random = new Random();
}
public TraceFilter(TraceManager traceManager, Pattern skipPattern) {
public TraceFilter(TraceManager traceManager, Pattern skipPattern, Random random) {
this.traceManager = traceManager;
this.skipPattern = skipPattern;
this.random = random;
}
@Override
@@ -105,28 +111,28 @@ public class TraceFilter extends OncePerRequestFilter
addToResponseIfNotPresent(response, Trace.NOT_SAMPLED_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(traceId)) {
if (hasHeader(request, response, Trace.TRACE_ID_NAME)) {
long traceId = Span.IdConverter.fromHex(getHeader(request, response, Trace.TRACE_ID_NAME));
long spanId = hasHeader(request, response, Trace.SPAN_ID_NAME) ?
Span.IdConverter.fromHex(getHeader(request, response, Trace.SPAN_ID_NAME)) : random.nextLong();
MilliSpanBuilder span = MilliSpan.builder().traceId(traceId).spanId(spanId);
if (skip) {
span.exportable(false);
}
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) {
if (StringUtils.hasText(parentName)) {
span.name(parentName);
} else {
span.name("parent/" + name);
}
if (processId != null) {
if (StringUtils.hasText(processId)) {
span.processId(processId);
}
if (parentId != null) {
span.parent(parentId);
if (hasHeader(request, response, Trace.PARENT_ID_NAME)) {
span.parent(Span.IdConverter.fromHex(getHeader(request, response, Trace.PARENT_ID_NAME)));
}
span.remote(true);
@@ -181,8 +187,8 @@ public class TraceFilter extends OncePerRequestFilter
private void addResponseHeaders(HttpServletResponse response, Span span) {
if (span != null) {
response.addHeader(Trace.SPAN_ID_NAME, span.getSpanId());
response.addHeader(Trace.TRACE_ID_NAME, span.getTraceId());
response.addHeader(Trace.SPAN_ID_NAME, Span.IdConverter.toHex(span.getSpanId()));
response.addHeader(Trace.TRACE_ID_NAME, Span.IdConverter.toHex(span.getTraceId()));
}
}
@@ -233,6 +239,12 @@ public class TraceFilter extends OncePerRequestFilter
}
}
private boolean hasHeader(HttpServletRequest request, HttpServletResponse response,
String name) {
String value = request.getHeader(name);
return value != null || response.getHeader(name) != null;
}
private String getHeader(HttpServletRequest request, HttpServletResponse response,
String name) {
String value = request.getHeader(name);

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.cloud.sleuth.instrument.web;
import java.util.Random;
import java.util.regex.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
@@ -65,10 +66,10 @@ public class TraceWebAutoConfiguration {
}
@Bean
public FilterRegistrationBean traceWebFilter(ApplicationEventPublisher publisher) {
public FilterRegistrationBean traceWebFilter(ApplicationEventPublisher publisher, Random random) {
Pattern pattern = StringUtils.hasText(this.skipPattern) ? Pattern.compile(this.skipPattern)
: TraceFilter.DEFAULT_SKIP_PATTERN;
TraceFilter filter = new TraceFilter(this.traceManager, pattern);
TraceFilter filter = new TraceFilter(this.traceManager, pattern, random);
filter.setApplicationEventPublisher(publisher);
return new FilterRegistrationBean(filter);
}

View File

@@ -16,14 +16,10 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import static java.util.Collections.singletonList;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import com.netflix.hystrix.HystrixCommand;
import feign.*;
import feign.codec.Decoder;
import feign.hystrix.HystrixFeign;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -49,17 +45,15 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;
import org.springframework.util.StringUtils;
import com.netflix.hystrix.HystrixCommand;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import feign.Client;
import feign.Feign;
import feign.FeignException;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Response;
import feign.codec.Decoder;
import feign.hystrix.HystrixFeign;
import static java.util.Collections.singletonList;
/**
*
@@ -125,12 +119,7 @@ public class TraceFeignClientAutoConfiguration {
setHeader(template, Trace.NOT_SAMPLED_NAME, "");
return;
}
if (span.getSpanId() == null) {
setHeader(template, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(template, Trace.NOT_SAMPLED_NAME, "");
return;
}
template.header(Trace.TRACE_ID_NAME, span.getTraceId());
template.header(Trace.TRACE_ID_NAME, Span.IdConverter.toHex(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));
@@ -146,8 +135,8 @@ public class TraceFeignClientAutoConfiguration {
}
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty()
private Long getParentId(Span span) {
return !span.getParents().isEmpty()
? span.getParents().get(0) : null;
}
@@ -158,6 +147,12 @@ public class TraceFeignClientAutoConfiguration {
}
}
public void setHeader(RequestTemplate request, String name, Long value) {
if (value != null) {
setHeader(request, name, Span.IdConverter.toHex(value));
}
}
private Map<String, Collection<String>> headersWithTraceId(
Map<String, Collection<String>> headers) {
Map<String, Collection<String>> newHeaders = new HashMap<>();
@@ -167,11 +162,6 @@ public class TraceFeignClientAutoConfiguration {
setHeader(newHeaders, Trace.NOT_SAMPLED_NAME, "");
return newHeaders;
}
if (span.getSpanId() == null) {
setHeader(newHeaders, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(newHeaders, Trace.NOT_SAMPLED_NAME, "");
return newHeaders;
}
setHeader(newHeaders, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(newHeaders, Trace.SPAN_ID_NAME, span.getSpanId());
setHeader(newHeaders, Trace.PARENT_ID_NAME, getParentId(span));
@@ -180,10 +170,16 @@ public class TraceFeignClientAutoConfiguration {
public void setHeader(Map<String, Collection<String>> headers, String name,
String value) {
if (value != null && !headers.containsKey(name) && this.accessor.isTracing()) {
if (StringUtils.hasText(value) && !headers.containsKey(name) && this.accessor.isTracing()) {
headers.put(name, singletonList(value));
}
}
public void setHeader(Map<String, Collection<String>> headers, String name,
Long value) {
if (value != null ){
setHeader(headers, name, Span.IdConverter.toHex(value));
}
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();

View File

@@ -15,8 +15,6 @@
*/
package org.springframework.cloud.sleuth.instrument.web.client;
import java.io.IOException;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
@@ -29,6 +27,9 @@ import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StringUtils;
import java.io.IOException;
/**
* Interceptor that verifies whether the trance and span id has been set on the request
@@ -64,11 +65,6 @@ ApplicationEventPublisherAware {
setHeader(request, Trace.NOT_SAMPLED_NAME, "");
return execution.execute(request, body);
}
if (span.getSpanId()==null) {
setHeader(request, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(request, Trace.NOT_SAMPLED_NAME, "");
return execution.execute(request, body);
}
setHeader(request, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(request, Trace.SPAN_ID_NAME, span.getSpanId());
setHeader(request, Trace.SPAN_NAME_NAME, span.getName());
@@ -91,17 +87,23 @@ ApplicationEventPublisherAware {
}
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty() ? span
private Long getParentId(Span span) {
return !span.getParents().isEmpty() ? span
.getParents().get(0) : null;
}
public void setHeader(HttpRequest request, String name, String value) {
if (value != null && !request.getHeaders().containsKey(name) && this.accessor.isTracing()) {
if (StringUtils.hasText(value) && !request.getHeaders().containsKey(name) && this.accessor.isTracing()) {
request.getHeaders().add(name, value);
}
}
public void setHeader(HttpRequest request, String name, Long value) {
if (value != null) {
setHeader(request, name, Span.IdConverter.toHex(value));
}
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}

View File

@@ -16,8 +16,8 @@
package org.springframework.cloud.sleuth.instrument.zuul;
import java.util.Map;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceAccessor;
@@ -27,8 +27,7 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.util.ReflectionUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import java.util.Map;
/**
* @author Dave Syer
@@ -65,11 +64,6 @@ ApplicationEventPublisherAware {
setHeader(response, Trace.NOT_SAMPLED_NAME, "");
return null;
}
if (span.getSpanId()==null) {
setHeader(response, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(response, Trace.NOT_SAMPLED_NAME, "");
return null;
}
try {
setHeader(response, Trace.SPAN_ID_NAME, span.getSpanId());
setHeader(response, Trace.TRACE_ID_NAME, span.getTraceId());
@@ -89,8 +83,8 @@ ApplicationEventPublisherAware {
return this.accessor.getCurrentSpan();
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty() ? span
private Long getParentId(Span span) {
return !span.getParents().isEmpty() ? span
.getParents().get(0) : null;
}
@@ -99,6 +93,9 @@ ApplicationEventPublisherAware {
request.put(name, value);
}
}
public void setHeader(Map<String, String> request, String name, Long value) {
setHeader(request, name, Span.IdConverter.toHex(value));
}
@Override
public String filterType() {

View File

@@ -16,9 +16,9 @@
package org.springframework.cloud.sleuth.instrument.zuul;
import java.io.InputStream;
import java.net.URISyntaxException;
import com.netflix.client.http.HttpRequest;
import com.netflix.niws.client.http.RestClient;
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;
@@ -32,10 +32,8 @@ import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.util.MultiValueMap;
import com.netflix.client.http.HttpRequest;
import com.netflix.niws.client.http.RestClient;
import lombok.SneakyThrows;
import java.io.InputStream;
import java.net.URISyntaxException;
/**
* @author Spencer Gibb
@@ -96,12 +94,6 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
setHeader(requestBuilder, Trace.NOT_SAMPLED_NAME, "");
return;
}
if (span.getSpanId()==null) {
setHeader(requestBuilder, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(requestBuilder, Trace.NOT_SAMPLED_NAME, "");
return;
}
setHeader(requestBuilder, Trace.TRACE_ID_NAME, span.getTraceId());
setHeader(requestBuilder, Trace.SPAN_ID_NAME, span.getSpanId());
setHeader(requestBuilder, Trace.SPAN_NAME_NAME, span.getName());
@@ -118,8 +110,8 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
}
}
private String getParentId(Span span) {
return span.getParents() != null && !span.getParents().isEmpty()
private Long getParentId(Span span) {
return !span.getParents().isEmpty()
? span.getParents().get(0) : null;
}
@@ -129,6 +121,10 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
}
}
public void setHeader(HttpRequest.Builder builder, String name, Long value) {
setHeader(builder, name, Span.IdConverter.toHex(value));
}
private Span getCurrentSpan() {
return this.accessor.getCurrentSpan();
}

View File

@@ -38,9 +38,9 @@ public class Slf4jSpanListener {
@Order(Ordered.LOWEST_PRECEDENCE)
public void start(SpanAcquiredEvent event) {
Span span = event.getSpan();
MDC.put(Trace.SPAN_ID_NAME, span.getSpanId());
MDC.put(Trace.SPAN_ID_NAME, Span.IdConverter.toHex(span.getSpanId()));
MDC.put(Trace.SPAN_EXPORT_NAME, String.valueOf(span.isExportable()));
MDC.put(Trace.TRACE_ID_NAME, span.getTraceId());
MDC.put(Trace.TRACE_ID_NAME, Span.IdConverter.toHex(span.getTraceId()));
log.trace("Starting span: {}", span);
if (event.getParent() != null) {
log.trace("With parent: {}", event.getParent());
@@ -51,8 +51,8 @@ public class Slf4jSpanListener {
@Order(Ordered.LOWEST_PRECEDENCE)
public void continued(SpanContinuedEvent event) {
Span span = event.getSpan();
MDC.put(Trace.SPAN_ID_NAME, span.getSpanId());
MDC.put(Trace.TRACE_ID_NAME, span.getTraceId());
MDC.put(Trace.SPAN_ID_NAME, Span.IdConverter.toHex(span.getSpanId()));
MDC.put(Trace.TRACE_ID_NAME, Span.IdConverter.toHex(span.getTraceId()));
MDC.put(Trace.SPAN_EXPORT_NAME, String.valueOf(span.isExportable()));
log.trace("Continued span: {}", event.getSpan());
}
@@ -63,7 +63,7 @@ public class Slf4jSpanListener {
log.trace("Stopped span: {}", event.getSpan());
if (event.getParent() != null) {
log.trace("With parent: {}", event.getParent());
MDC.put(Trace.SPAN_ID_NAME, event.getParent().getSpanId());
MDC.put(Trace.SPAN_ID_NAME, Span.IdConverter.toHex(event.getParent().getSpanId()));
MDC.put(Trace.SPAN_EXPORT_NAME, String.valueOf(event.getParent().isExportable()));
}
else {

View File

@@ -1,44 +0,0 @@
package org.springframework.cloud.sleuth.sampler;
import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicLong;
/**
* Default implementation that converts String into UUID
* On parse exceptions a null is returned.
*
* @author Marcin Grzejszczak
* @author Adrian Cole
*/
@Slf4j
public class DefaultStringToUuidConverter implements StringToUuidConverter {
/** Returns a UUID parsed from the input, or null if failed for any reason. */
@Override
public UUID convert(String source) {
try {
UUID uuid = UUID.fromString(source);
incrementSuccess();
return uuid;
} catch (IllegalArgumentException e) {
log.debug("Exception occurred while trying to parse String to UUID", e);
incrementFailures();
return null;
}
}
/**
* Override to increment your counter.
*/
protected void incrementSuccess() {
}
/**
* Override to increment your counter.
*/
protected void incrementFailures() {
}
}

View File

@@ -26,22 +26,22 @@ public class PercentageBasedSampler implements Sampler<Void> {
private final SamplerConfiguration configuration;
private final TraceAccessor traceAccessor;
private final StringToUuidConverter converter;
public PercentageBasedSampler(SamplerConfiguration configuration, TraceAccessor traceAccessor, StringToUuidConverter converter) {
public PercentageBasedSampler(SamplerConfiguration configuration, TraceAccessor traceAccessor) {
this.configuration = configuration;
this.traceAccessor = traceAccessor;
this.converter = converter;
}
@Override
public boolean next(Void info) {
Span currentSpan = traceAccessor.getCurrentSpan();
if (currentSpan == null) {
long threshold = Math.abs(Long.MAX_VALUE * (int) (configuration.getPercentage() * 100)); // drops fractional percentage.
if (currentSpan == null || threshold == 0L) {
return false;
}
return new UuidTraceIdToThresholdComparable(configuration.getPercentage(), converter)
.compareTo(currentSpan.getTraceId()) <= 0;
long traceId = currentSpan.getTraceId();
Long mod = Math.abs(traceId % 100);
return mod.compareTo(threshold) <= 0;
}
}

View File

@@ -1,68 +0,0 @@
package org.springframework.cloud.sleuth.sampler;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.UUID;
/**
* Given the absolute value of a random 128 bit trace id, we expect inputs to be balanced across
* 0-MAX. Threshold is the range of inputs between 0-MAX that we retain.
*
* This decodes a trace id in UUID format into a 128 bit number, then compares it against a threshold.
*
* @author Marcin Grzejszczak
* @author Adrian Cole
*/
final class UuidTraceIdToThresholdComparable implements Comparable<String> {
private static final int SIXTY_FOUR_BITS = 64;
private static final int GREATER_THAN_THRESHOLD = 1;
/**
* 0111....1111 ('0' - for the sign and then 127 times '1')
*/
static final BigInteger MAX_128 = max_128signed();
private final BigInteger threshold;
private final StringToUuidConverter stringToUuidConverter;
UuidTraceIdToThresholdComparable(float rate, StringToUuidConverter converter) {
threshold = MAX_128
.multiply(BigInteger.valueOf((int) (rate * 100))) // drops fractional percentage.
.divide(BigInteger.valueOf(100));
stringToUuidConverter = converter;
}
UuidTraceIdToThresholdComparable(float rate) {
this(rate, new DefaultStringToUuidConverter());
}
/**
* Compares the given Trace Id to the provided threshold
* @param traceId - the UUID version of Trace Id
*/
@Override
public int compareTo(String traceId) {
UUID uuid = stringToUuidConverter.convert(traceId);
if (uuid == null) {
return GREATER_THAN_THRESHOLD;
}
BigInteger asInteger = BigInteger.valueOf(uuid.getMostSignificantBits())
.shiftLeft(SIXTY_FOUR_BITS)
.add(BigInteger.valueOf(uuid.getLeastSignificantBits()))
.abs();
return asInteger.compareTo(threshold);
}
/**
* The Long.MAX_VALUE in binary 0 followed by 63 1s.
*
* We simulate a 128bit long, by doing the same, except following by 127 1s
*/
static BigInteger max_128signed() {
byte[] max_128signed = new byte[16];
Arrays.fill(max_128signed, (byte) -1); // initialize to 11111111
max_128signed[0] = (byte) 127; // reset MSBs to 01111111
return new BigInteger(max_128signed);
}
}

View File

@@ -18,6 +18,7 @@ package org.springframework.cloud.sleuth.trace;
import static org.springframework.cloud.sleuth.util.ExceptionUtils.warn;
import java.util.Random;
import java.util.concurrent.Callable;
import org.springframework.cloud.sleuth.MilliSpan;
@@ -32,7 +33,6 @@ 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;
import org.springframework.util.IdGenerator;
/**
* @author Spencer Gibb
@@ -41,14 +41,14 @@ public class DefaultTraceManager implements TraceManager {
private final Sampler<Void> defaultSampler;
private final IdGenerator idGenerator;
private final ApplicationEventPublisher publisher;
public DefaultTraceManager(Sampler<Void> defaultSampler, IdGenerator idGenerator,
ApplicationEventPublisher publisher) {
private final Random random;
public DefaultTraceManager(Sampler<Void> defaultSampler,
Random random, ApplicationEventPublisher publisher) {
this.defaultSampler = defaultSampler;
this.idGenerator = idGenerator;
this.random = random;
this.publisher = publisher;
}
@@ -79,7 +79,7 @@ public class DefaultTraceManager implements TraceManager {
}
else {
// Non-exportable so we keep the trace but not other data
String id = createId();
long id = createId();
span = MilliSpan.builder().begin(System.currentTimeMillis()).name(name)
.traceId(id).spanId(id).exportable(false).build();
this.publisher.publishEvent(new SpanAcquiredEvent(this, span));
@@ -149,7 +149,7 @@ public class DefaultTraceManager implements TraceManager {
}
protected Span createChild(Span parent, String name) {
String id = createId();
long id = createId();
if (parent == null) {
MilliSpan span = MilliSpan.builder().begin(System.currentTimeMillis())
.name(name).traceId(id).spanId(id).build();
@@ -169,8 +169,8 @@ public class DefaultTraceManager implements TraceManager {
}
}
private String createId() {
return this.idGenerator.generateId().toString();
private long createId() {
return random.nextLong();
}
@Override

View File

@@ -28,15 +28,18 @@ import org.springframework.cloud.sleuth.trace.DefaultTraceManager;
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.util.JdkIdGenerator;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* @author Spencer Gibb
@@ -62,8 +65,7 @@ public class DefaultTraceManagerTests {
public void tracingWorks() {
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
DefaultTraceManager traceManager = new DefaultTraceManager(new IsTracingSampler(),
new JdkIdGenerator(), publisher);
DefaultTraceManager traceManager = new DefaultTraceManager(new IsTracingSampler(), new Random(), publisher);
Trace trace = traceManager.startSpan(CREATE_SIMPLE_TRACE, new AlwaysSampler(), null);
try {
@@ -97,7 +99,7 @@ public class DefaultTraceManagerTests {
assertThat("gen4 was non-empty", gen4.isEmpty(), is(true));
}
private Span assertSpan(List<Span> spans, String parentId, String name) {
private Span assertSpan(List<Span> spans, Long parentId, String name) {
List<Span> found = findSpans(spans, parentId);
assertThat("more than one span with parentId " + parentId, found.size(), is(1));
Span span = found.get(0);
@@ -106,7 +108,7 @@ public class DefaultTraceManagerTests {
return span;
}
private List<Span> findSpans(List<Span> spans, String parentId) {
private List<Span> findSpans(List<Span> spans, Long parentId) {
List<Span> found = new ArrayList<>();
for (Span span : spans) {
if (parentId == null && span.getParents().isEmpty()) {

View File

@@ -16,10 +16,10 @@
package org.springframework.cloud.sleuth;
import java.util.Collections;
import org.junit.Test;
import java.util.Collections;
/**
* @author Rob Winch
* @author Spencer Gibb
@@ -28,7 +28,7 @@ public class MilliSpanTests {
@Test(expected = UnsupportedOperationException.class)
public void getAnnotationsReadOnly() {
MilliSpan span = new MilliSpan(1, 2, "name", "traceId", Collections.<String>emptyList(), "spanId", true, true, "processId");
MilliSpan span = new MilliSpan(1, 2, "name", 1L, Collections.<Long>emptyList(), 2L, true, true, "process");
span.tags().put("a", "b");
}
@@ -36,7 +36,7 @@ public class MilliSpanTests {
@Test(expected = UnsupportedOperationException.class)
public void getTimelineAnnotationsReadOnly() {
MilliSpan span = new MilliSpan(1, 2, "name", "traceId", Collections.<String>emptyList(), "spanId", true, true, "processId");
MilliSpan span = new MilliSpan(1, 2, "name", 1L, Collections.<Long>emptyList(), 2L, true, true, "process");
span.logs().add(new Log(1, "1"));
}

View File

@@ -0,0 +1,34 @@
package org.springframework.cloud.sleuth;
import org.junit.Test;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
*/
public class SpanTest {
@Test
public void should_convert_long_to_hex_string() throws Exception {
long someLong = 123123L;
String hexString = Span.IdConverter.toHex(someLong);
then(hexString).isEqualTo("1e0f3");
}
@Test
public void should_convert_hex_string_to_long() throws Exception {
String hexString = "1e0f3";
long someLong = Span.IdConverter.fromHex(hexString);
then(someLong).isEqualTo(123123L);
}
@Test(expected = IllegalArgumentException.class)
public void should_throw_exception_when_null_string_is_to_be_converted_to_long() throws Exception {
Span.IdConverter.fromHex(null);
}
}

View File

@@ -1,33 +1,39 @@
package org.springframework.cloud.sleuth.assertions;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.assertj.core.api.AbstractAssert;
import org.springframework.cloud.sleuth.Span;
import java.util.Objects;
@Slf4j
public class SpanAssert extends AbstractAssert<SpanAssert, Span> {
public SpanAssert(Span actual) {
super(actual, SpanAssert.class);
}
public SpanAssert(Span actual) {
super(actual, SpanAssert.class);
}
public static SpanAssert then(Span actual) {
return new SpanAssert(actual);
}
public static SpanAssert then(Span actual) {
return new SpanAssert(actual);
}
public SpanAssert hasTraceIdEqualTo(String traceId) {
isNotNull();
if (!Objects.equals(actual.getTraceId(), traceId)) {
failWithMessage("Expected span's traceId to be <%s> but was <%s>", traceId, actual.getTraceId());
}
return this;
}
public SpanAssert hasTraceIdEqualTo(long traceId) {
isNotNull();
if (!Objects.equals(actual.getTraceId(), traceId)) {
String message = String.format("Expected span's traceId to be <%s> but was <%s>", traceId, actual.getTraceId());
log.error(message);
failWithMessage(message);
}
return this;
}
public SpanAssert hasNameNotEqualTo(String name) {
isNotNull();
if (Objects.equals(actual.getName(), name)) {
failWithMessage("Expected span's name not to be <%s> but was <%s>", name, actual.getName());
}
return this;
}
public SpanAssert hasNameNotEqualTo(String name) {
isNotNull();
if (Objects.equals(actual.getName(), name)) {
String message = String.format("Expected span's name not to be <%s> but was <%s>", name, actual.getName());
log.error(message);
failWithMessage(message);
}
return this;
}
}

View File

@@ -1,11 +1,5 @@
package org.springframework.cloud.sleuth.instrument;
import static org.assertj.core.api.BDDAssertions.then;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -17,14 +11,20 @@ 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.util.JdkIdGenerator;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(MockitoJUnitRunner.class)
public class TraceCallableTests {
ExecutorService executor = Executors.newSingleThreadExecutor();
TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new JdkIdGenerator(), Mockito.mock(ApplicationEventPublisher.class));
new Random(), Mockito.mock(ApplicationEventPublisher.class));
@After
public void clean() {

View File

@@ -1,10 +1,5 @@
package org.springframework.cloud.sleuth.instrument;
import static org.assertj.core.api.BDDAssertions.then;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -16,14 +11,19 @@ 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.util.JdkIdGenerator;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(MockitoJUnitRunner.class)
public class TraceRunnableTests {
ExecutorService executor = Executors.newSingleThreadExecutor();
TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new JdkIdGenerator(), Mockito.mock(ApplicationEventPublisher.class));
new Random(), Mockito.mock(ApplicationEventPublisher.class));
@After
public void cleanup() {

View File

@@ -14,11 +14,11 @@ 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.util.JdkIdGenerator;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
@@ -39,8 +39,7 @@ public class TraceableExecutorServiceTests {
@Before
public void setup() {
//traceManager = new DefaultTraceManager(new AlwaysSampler(), new RandomLongSpanIdGenerator(), publisher);
traceManager = new DefaultTraceManager(new AlwaysSampler(), new JdkIdGenerator(), publisher);
traceManager = new DefaultTraceManager(new AlwaysSampler(), new Random(), publisher);
traceManagerableExecutorService = new TraceableExecutorService(executorService, traceManager);
TraceContextHolder.removeCurrentTrace();
}
@@ -74,8 +73,8 @@ public class TraceableExecutorServiceTests {
class SpanVerifyingRunnable implements Runnable {
Queue<String> traceIds = new ConcurrentLinkedQueue<>();
Queue<String> spanIds = new ConcurrentLinkedQueue<>();
Queue<Long> traceIds = new ConcurrentLinkedQueue<>();
Queue<Long> spanIds = new ConcurrentLinkedQueue<>();
@Override
public void run() {

View File

@@ -83,11 +83,9 @@ public class SpanPassingForHystrixViaAnnotationsIntegrationTests {
spanCaughtFromHystrixThread = new AtomicReference<>(TraceContextHolder.getCurrentSpan());
}
public String getTraceId() {
public Long getTraceId() {
if (spanCaughtFromHystrixThread == null ||
spanCaughtFromHystrixThread.get() == null ||
(spanCaughtFromHystrixThread.get() != null &&
spanCaughtFromHystrixThread.get().getTraceId() == null)) {
spanCaughtFromHystrixThread.get() == null) {
return null;
}
return spanCaughtFromHystrixThread.get().getTraceId();

View File

@@ -1,9 +1,7 @@
package org.springframework.cloud.sleuth.instrument.hystrix;
import static com.netflix.hystrix.HystrixCommand.Setter.withGroupKey;
import static com.netflix.hystrix.HystrixCommandGroupKey.Factory.asKey;
import static org.assertj.core.api.BDDAssertions.then;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -15,16 +13,18 @@ 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.util.JdkIdGenerator;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixThreadPoolProperties;
import java.util.Random;
import static com.netflix.hystrix.HystrixCommand.Setter.withGroupKey;
import static com.netflix.hystrix.HystrixCommandGroupKey.Factory.asKey;
import static org.assertj.core.api.BDDAssertions.then;
public class TraceCommandTests {
static final String EXPECTED_TRACE_ID = "A";
static final long EXPECTED_TRACE_ID = 1L;
TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new JdkIdGenerator(), Mockito.mock(ApplicationEventPublisher.class));
new Random(), Mockito.mock(ApplicationEventPublisher.class));
@Before
public void setup() {

View File

@@ -50,19 +50,19 @@ abstract class AbstractTraceStompIntegrationTests {
then(stompMessageHandler.message).isNotNull();
}
String thenSpanIdFromHeadersIsNotEmpty() {
String header = getValueFromHeaders(Trace.SPAN_ID_NAME);
then(header).as("Span id should not be empty").isNotEmpty();
Long thenSpanIdFromHeadersIsNotEmpty() {
Long header = getValueFromHeaders(Trace.SPAN_ID_NAME, Long.class);
then(header).as("Span id should not be empty").isNotNull();
return header;
}
String thenTraceIdFromHeadersIsNotEmpty() {
String header = getValueFromHeaders(Trace.TRACE_ID_NAME);
then(header).as("Trace id should not be empty").isNotEmpty();
Long thenTraceIdFromHeadersIsNotEmpty() {
Long header = getValueFromHeaders(Trace.TRACE_ID_NAME, Long.class);
then(header).as("Trace id should not be empty").isNotNull();
return header;
}
String getValueFromHeaders(String headerName) {
return stompMessageHandler.message.getHeaders().get(headerName, String.class);
<T> T getValueFromHeaders(String headerName, Class<T> type) {
return stompMessageHandler.message.getHeaders().get(headerName, type);
}
}

View File

@@ -16,14 +16,6 @@
package org.springframework.cloud.sleuth.instrument.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.util.ArrayList;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -52,6 +44,12 @@ import org.springframework.messaging.MessagingException;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.BDDAssertions.then;
import static org.junit.Assert.*;
/**
* @author Dave Syer
*/
@@ -109,14 +107,16 @@ public class TraceChannelInterceptorTests implements MessageHandler {
@Test
public void parentSpanIncluded() {
this.channel.send(MessageBuilder.withPayload("hi").setHeader(Trace.TRACE_ID_NAME, "parent")
this.channel.send(MessageBuilder.withPayload("hi").setHeader(Trace.TRACE_ID_NAME, 10L)
.setHeader(Trace.SPAN_ID_NAME, 20L)
.build());
assertNotNull("message was null", this.message);
String spanId = this.message.getHeaders().get(Trace.SPAN_ID_NAME, String.class);
assertNotNull("spanId was null", spanId);
String traceId = this.message.getHeaders().get(Trace.TRACE_ID_NAME, String.class);
assertEquals("parent", traceId);
long traceId = Span.IdConverter.fromHex(this.message.getHeaders().get(Trace.TRACE_ID_NAME, String.class));
then(traceId).isEqualTo(10L);
then(spanId).isNotEqualTo(20L);
assertNull(TraceContextHolder.getCurrentTrace());
assertEquals(1, this.app.events.size());
}

View File

@@ -16,9 +16,6 @@
package org.springframework.cloud.sleuth.instrument.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -27,6 +24,7 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.integration.TraceContextPropagationChannelInterceptorTests.App;
@@ -41,6 +39,9 @@ import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* @author Spencer Gibb
*/
@@ -67,17 +68,17 @@ public class TraceContextPropagationChannelInterceptorTests {
Trace trace = this.traceManager.startSpan("testSendMessage", new AlwaysSampler(), null);
this.channel.send(MessageBuilder.withPayload("hi").build());
String expectedSpanId = trace.getSpan().getSpanId();
Long expectedSpanId = trace.getSpan().getSpanId();
this.traceManager.close(trace);
Message<?> message = this.channel.receive(0);
assertNotNull("message was null", message);
String spanId = message.getHeaders().get(Trace.SPAN_ID_NAME, String.class);
Long spanId = Span.IdConverter.fromHex(message.getHeaders().get(Trace.SPAN_ID_NAME, String.class));
assertEquals("spanId was wrong", expectedSpanId, spanId);
String traceId = message.getHeaders().get(Trace.TRACE_ID_NAME, String.class);
long traceId = Span.IdConverter.fromHex(message.getHeaders().get(Trace.TRACE_ID_NAME, String.class));
assertNotNull("traceId was null", traceId);
}

View File

@@ -55,8 +55,8 @@ public class TraceStompMessageChannelInterceptorTests extends AbstractTraceStomp
whenTheMessageWasSent(message);
this.traceManager.close(trace);
String spanId = thenSpanIdFromHeadersIsNotEmpty();
String traceId = thenTraceIdFromHeadersIsNotEmpty();
Long spanId = thenSpanIdFromHeadersIsNotEmpty();
long traceId = thenTraceIdFromHeadersIsNotEmpty();
then(traceId).isEqualTo(trace.getSpan().getTraceId());
then(spanId).isEqualTo(trace.getSpan().getSpanId());
then(TraceContextHolder.getCurrentTrace()).isNull();
@@ -67,7 +67,7 @@ public class TraceStompMessageChannelInterceptorTests extends AbstractTraceStomp
}
private String thenSpanIdFromHeadersIsEmpty() {
String header = getValueFromHeaders(Trace.SPAN_ID_NAME);
String header = getValueFromHeaders(Trace.SPAN_ID_NAME, String.class);
then(header).as("Span id should be empty").isNullOrEmpty();
return header;
}

View File

@@ -32,11 +32,11 @@ public class TraceStompMessageContextPropagationChannelInterceptorTests extends
Message<?> m = givenMessageToBeSampled();
whenTheMessageWasSent(m);
String expectedTraceId = trace.getSpan().getTraceId();
Long expectedTraceId = trace.getSpan().getTraceId();
this.traceManager.close(trace);
thenReceivedMessageIsNotNull();
String traceId = thenTraceIdFromHeadersIsNotEmpty();
long traceId = thenTraceIdFromHeadersIsNotEmpty();
then(traceId).isEqualTo(expectedTraceId);
thenSpanIdFromHeadersIsNotEmpty();
}

View File

@@ -122,7 +122,7 @@ public class RestTemplateTraceAspectIntegrationTests extends AbstractMvcWiremock
};
private String callWiremockAndReturnOk() {
this.restTemplate.getForObject("http://localhost:"+this.httpMockServer.port(), String.class);
this.restTemplate.getForObject("http://localhost:" + this.httpMockServer.port(), String.class);
return "OK";
}
}

View File

@@ -1,10 +1,7 @@
package org.springframework.cloud.sleuth.instrument.web;
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
import java.util.concurrent.atomic.AtomicReference;
import com.jayway.awaitility.Awaitility;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -20,7 +17,9 @@ import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.jayway.awaitility.Awaitility;
import java.util.concurrent.atomic.AtomicReference;
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = {
@@ -79,24 +78,22 @@ public class TraceAsyncIntegrationTests {
static class ClassPerformingAsyncLogic {
AtomicReference<Span> span;
AtomicReference<Span> span = new AtomicReference<>();
@Async
public void invokeAsynchronousLogic() {
span = new AtomicReference<>(TraceContextHolder.getCurrentSpan());
span.set(TraceContextHolder.getCurrentSpan());
}
public String getTraceId() {
if (span == null || (span.get() != null
&& span.get().getTraceId() == null)) {
public Long getTraceId() {
if (span.get() == null) {
return null;
}
return span.get().getTraceId();
}
public String getSpanName() {
if (span == null
|| (span.get() != null && span.get().getName() == null)) {
if (span.get() != null && span.get().getName() == null) {
return null;
}
return span.get().getName();

View File

@@ -1,74 +0,0 @@
package org.springframework.cloud.sleuth.instrument.web;
import static org.assertj.core.api.BDDAssertions.then;
import java.util.UUID;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(TraceFilterIntegartionTests.class)
@DefaultTestAutoConfiguration
public class TraceFilterIntegartionTests extends AbstractMvcIntegrationTest {
@Autowired
TraceManager traceManager;
@Test
public void should_create_and_return_trace_in_HTTP_header() throws Exception {
MvcResult mvcResult = whenSentPingWithoutTracingData();
then(tracingHeaderFrom(mvcResult)).isNotNull().isNotEmpty();
}
@Test
public void when_correlationId_is_sent_should_not_create_a_new_one_but_return_the_existing_one_instead()
throws Exception {
String expectedTraceId = "passedCorId";
MvcResult mvcResult = whenSentPingWithTraceId(expectedTraceId);
then(tracingHeaderFrom(mvcResult)).isEqualTo(expectedTraceId);
}
@Override
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
mockMvcBuilder.addFilters(new TraceFilter(this.traceManager));
}
private MvcResult whenSentPingWithoutTracingData() throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN))
.andReturn();
}
private MvcResult whenSentPingWithTraceId(String passedCorrelationId)
throws Exception {
return sendPingWithTraceId(Trace.TRACE_ID_NAME, passedCorrelationId);
}
private MvcResult sendPingWithTraceId(String headerName, String passedCorrelationId)
throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN)
.header(headerName, passedCorrelationId)
.header(Trace.SPAN_ID_NAME, UUID.randomUUID().toString()))
.andReturn();
}
private String tracingHeaderFrom(MvcResult mvcResult) {
return mvcResult.getResponse().getHeader(Trace.TRACE_ID_NAME);
}
}

View File

@@ -1,87 +1,75 @@
/*
* 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.instrument.web;
import static org.junit.Assert.assertNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
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.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.util.JdkIdGenerator;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
import lombok.SneakyThrows;
import java.util.Random;
/**
* @author Spencer Gibb
* @author Dave Syer
*/
public class TraceFilterIntegrationTests {
import static org.assertj.core.api.BDDAssertions.then;
private StaticApplicationContext context = new StaticApplicationContext();
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(TraceFilterIntegrationTests.class)
@DefaultTestAutoConfiguration
public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
private TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new JdkIdGenerator(), this.context);
@Autowired
TraceManager traceManager;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockFilterChain filterChain;
@Test
public void should_create_and_return_trace_in_HTTP_header() throws Exception {
MvcResult mvcResult = whenSentPingWithoutTracingData();
@Before
@SneakyThrows
public void init() {
TraceContextHolder.removeCurrentTrace();
this.context.refresh();
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");
then(tracingHeaderFrom(mvcResult)).isNotNull();
}
@Test
public void startsNewTrace() throws Exception {
TraceFilter filter = new TraceFilter(this.traceManager);
filter.doFilter(this.request, this.response, this.filterChain);
assertNull(TraceContextHolder.getCurrentTrace());
public void when_correlationId_is_sent_should_not_create_a_new_one_but_return_the_existing_one_instead()
throws Exception {
Long expectedTraceId = new Random().nextLong();
MvcResult mvcResult = whenSentPingWithTraceId(expectedTraceId);
then(tracingHeaderFrom(mvcResult)).isEqualTo(expectedTraceId);
}
@Test
public void continuesSpanFromHeaders() throws Exception {
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());
@Override
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
mockMvcBuilder.addFilters(new TraceFilter(this.traceManager));
}
private MvcResult whenSentPingWithoutTracingData() throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN))
.andReturn();
}
private MvcResult whenSentPingWithTraceId(Long passedTraceId)
throws Exception {
return sendPingWithTraceId(Trace.TRACE_ID_NAME, passedTraceId);
}
private MvcResult sendPingWithTraceId(String headerName, Long passedCorrelationId)
throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get("/ping").accept(MediaType.TEXT_PLAIN)
.header(headerName, Span.IdConverter.toHex(passedCorrelationId))
.header(Trace.SPAN_ID_NAME, Span.IdConverter.toHex(new Random().nextLong())))
.andReturn();
}
private Long tracingHeaderFrom(MvcResult mvcResult) {
return Span.IdConverter.fromHex(mvcResult.getResponse().getHeader(Trace.TRACE_ID_NAME));
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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.instrument.web;
import lombok.SneakyThrows;
import org.junit.Before;
import org.junit.Test;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
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.MediaType;
import org.springframework.mock.web.MockFilterChain;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import java.util.Random;
import static org.junit.Assert.assertNull;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
* @author Spencer Gibb
* @author Dave Syer
*/
public class TraceFilterMockChainIntegrationTests {
private StaticApplicationContext context = new StaticApplicationContext();
private TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new Random(), this.context);
private MockHttpServletRequest request;
private MockHttpServletResponse response;
private MockFilterChain filterChain;
@Before
@SneakyThrows
public void init() {
TraceContextHolder.removeCurrentTrace();
this.context.refresh();
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");
}
@Test
public void startsNewTrace() throws Exception {
TraceFilter filter = new TraceFilter(this.traceManager);
filter.doFilter(this.request, this.response, this.filterChain);
assertNull(TraceContextHolder.getCurrentTrace());
}
@Test
public void continuesSpanFromHeaders() throws Exception {
Random generator = new Random();
this.request = builder().header(Trace.SPAN_ID_NAME, generator.nextLong())
.header(Trace.TRACE_ID_NAME, generator.nextLong()).buildRequest(new MockServletContext());
TraceFilter filter = new TraceFilter(this.traceManager);
filter.doFilter(this.request, this.response, this.filterChain);
assertNull(TraceContextHolder.getCurrentSpan());
}
}

View File

@@ -16,12 +16,7 @@
package org.springframework.cloud.sleuth.instrument.web;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.MockitoAnnotations.initMocks;
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;
@@ -41,9 +36,14 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.util.JdkIdGenerator;
import lombok.SneakyThrows;
import java.util.Random;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.MockitoAnnotations.initMocks;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
/**
* @author Spencer Gibb
@@ -66,8 +66,7 @@ public class TraceFilterTests {
@SneakyThrows
public void init() {
initMocks(this);
this.traceManager = new DefaultTraceManager(new DelegateSampler(),
new JdkIdGenerator(), this.publisher) {
this.traceManager = new DefaultTraceManager(new DelegateSampler(), new Random(), this.publisher) {
@Override
protected Trace createTrace(Trace trace, Span span) {
TraceFilterTests.this.span = span;
@@ -123,8 +122,8 @@ public class TraceFilterTests {
@Test
public void continuesSpanFromHeaders() throws Exception {
this.request = builder().header(Trace.SPAN_ID_NAME, "myspan")
.header(Trace.TRACE_ID_NAME, "mytrace")
this.request = builder().header(Trace.SPAN_ID_NAME, 10L)
.header(Trace.TRACE_ID_NAME, 20L)
.buildRequest(new MockServletContext());
TraceFilter filter = new TraceFilter(this.traceManager);

View File

@@ -1,11 +1,8 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import static org.assertj.core.api.BDDAssertions.then;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -18,6 +15,7 @@ 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.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
@@ -31,15 +29,17 @@ import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.JdkIdGenerator;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { FeignTraceTests.TestConfiguration.class })
@@ -75,8 +75,8 @@ public class FeignTraceTests {
@Test
public void shouldAttachTraceIdWhenUsingFeignClient() {
// given
String currentTraceId = "currentTraceId";
String currentParentId = "currentParentId";
Long currentTraceId = 1L;
Long currentParentId = 2L;
this.traceManager.continueSpan(MilliSpan.builder().traceId(currentTraceId)
.spanId(generatedId()).parent(currentParentId).build());
@@ -84,12 +84,12 @@ public class FeignTraceTests {
ResponseEntity<String> response = this.testFeignInterface.getTraceId();
// then
then(getHeader(response, Trace.TRACE_ID_NAME)).isEqualTo(currentTraceId);
then(Span.IdConverter.fromHex(getHeader(response, Trace.TRACE_ID_NAME))).isEqualTo(currentTraceId);
then(this.listener.getEvents().size()).isEqualTo(2);
}
private String generatedId() {
return new JdkIdGenerator().generateId().toString();
private Long generatedId() {
return new Random().nextLong();
}
private String getHeader(ResponseEntity<String> response, String name) {

View File

@@ -16,13 +16,6 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,12 +30,19 @@ import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.test.web.client.MockMvcClientHttpRequestFactory;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.util.JdkIdGenerator;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import static org.assertj.core.api.BDDAssertions.then;
import static org.junit.Assert.assertFalse;
/**
* @author Dave Syer
*
@@ -62,8 +62,7 @@ public class TraceRestTemplateInterceptorTests {
@Before
public void setup() {
this.publisher.refresh();
this.traces = new DefaultTraceManager(new AlwaysSampler(),
new JdkIdGenerator(), this.publisher);
this.traces = new DefaultTraceManager(new AlwaysSampler(), new Random(), this.publisher);
this.template.setInterceptors(Arrays.<ClientHttpRequestInterceptor>asList(
new TraceRestTemplateInterceptor(this.traces)));
TraceContextHolder.removeCurrentTrace();
@@ -76,12 +75,12 @@ public class TraceRestTemplateInterceptorTests {
@Test
public void headersAddedWhenTracing() {
this.traces.continueSpan(MilliSpan.builder().traceId("foo").spanId("bar").build());
this.traces.continueSpan(MilliSpan.builder().traceId(1L).spanId(2L).build());
@SuppressWarnings("unchecked")
Map<String, String> headers = this.template.getForEntity("/", Map.class)
.getBody();
assertEquals("bar", headers.get(Trace.SPAN_ID_NAME));
assertEquals("foo", headers.get(Trace.TRACE_ID_NAME));
then(Long.valueOf(headers.get(Trace.TRACE_ID_NAME))).isEqualTo(1L);
then(Long.valueOf(headers.get(Trace.SPAN_ID_NAME))).isEqualTo(2L);
}
@Test

View File

@@ -40,9 +40,9 @@ public class JsonLogSpanListenerTests {
JsonLogSpanListener listener = new JsonLogSpanListener();
Span span = MilliSpan.builder()
.name("testSpan")
.spanId("spanId1")
.parent("parentId1")
.traceId("traceId1")
.spanId(1L)
.parent(2L)
.traceId(3L)
.begin(1)
.end(10)
.build();

View File

@@ -1,31 +0,0 @@
package org.springframework.cloud.sleuth.sampler;
import org.junit.Test;
import java.util.UUID;
import static org.assertj.core.api.BDDAssertions.then;
public class DefaultStringToUuidConverterTests {
DefaultStringToUuidConverter defaultStringToUuidConverter = new DefaultStringToUuidConverter();
@Test
public void should_successfully_convert_string_to_uuid_and_increment_success_counter() throws Exception {
UUID expectedUuid = UUID.randomUUID();
String uuidAsString = expectedUuid.toString();
UUID convertedUuid = defaultStringToUuidConverter.convert(uuidAsString);
then(convertedUuid).isEqualTo(expectedUuid);
}
@Test
public void should_fail_to_convert_string_to_uuid_and_increment_failures_counter() throws Exception {
String invalidUuidString = "non UUID format string";
UUID convertedUuid = defaultStringToUuidConverter.convert(invalidUuidString);
then(convertedUuid).isNull();
}
}

View File

@@ -9,18 +9,21 @@ import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.TraceAccessor;
import org.springframework.util.JdkIdGenerator;
import java.util.Random;
import java.util.stream.Stream;
public class PercentageBasedSamplerTests {
SamplerConfiguration samplerConfiguration = new SamplerConfiguration();
TraceAccessor traceAccessor = traceReturningSpanWithUuid();
StringToUuidConverter stringToUuidConverter = new DefaultStringToUuidConverter();
private static Random RANDOM = new Random();
@Test
public void should_pass_all_samples_when_config_has_1_percentage() throws Exception {
this.samplerConfiguration.setPercentage(1f);
for (int i = 0; i < 10; i++) {
then(new PercentageBasedSampler(this.samplerConfiguration, this.traceAccessor, this.stringToUuidConverter).next(null)).isTrue();
then(new PercentageBasedSampler(this.samplerConfiguration, this.traceAccessor).next(null)).isTrue();
}
}
@@ -30,19 +33,10 @@ public class PercentageBasedSamplerTests {
this.samplerConfiguration.setPercentage(0f);
for (int i = 0; i < 10; i++) {
then(new PercentageBasedSampler(this.samplerConfiguration, this.traceAccessor, this.stringToUuidConverter).next(null)).isFalse();
then(new PercentageBasedSampler(this.samplerConfiguration, this.traceAccessor).next(null)).isFalse();
}
}
@Test
public void should_reject_sample_when_trace_id_is_invalid() throws Exception {
this.samplerConfiguration.setPercentage(1f);
boolean passed = new PercentageBasedSampler(this.samplerConfiguration, traceReturningSpanWithInvalidUuid(), this.stringToUuidConverter).next(null);
then(passed).isFalse();
}
@Test
public void should_pass_given_percent_of_samples() throws Exception {
int numberOfIterations = 10000;
@@ -57,7 +51,7 @@ public class PercentageBasedSamplerTests {
private int countNumberOfSampledElements(int numberOfIterations) {
int passedCounter = 0;
for (int i = 0; i < numberOfIterations; i++) {
boolean passed = new PercentageBasedSampler(this.samplerConfiguration, traceReturningSpanWithUuid(), this.stringToUuidConverter).next(null);
boolean passed = new PercentageBasedSampler(this.samplerConfiguration, traceReturningSpanWithUuid()).next(null);
passedCounter = passedCounter + (passed ? 1 : 0);
}
return passedCounter;
@@ -67,21 +61,7 @@ public class PercentageBasedSamplerTests {
return new TraceAccessor() {
@Override
public Span getCurrentSpan() {
return MilliSpan.builder().traceId(new JdkIdGenerator().generateId().toString()).build();
}
@Override
public boolean isTracing() {
return true;
}
};
}
private TraceAccessor traceReturningSpanWithInvalidUuid() {
return new TraceAccessor() {
@Override
public Span getCurrentSpan() {
return MilliSpan.builder().traceId("invalid uuid").build();
return MilliSpan.builder().traceId(RANDOM.nextLong()).build();
}
@Override

View File

@@ -1,53 +0,0 @@
package org.springframework.cloud.sleuth.sampler;
import org.junit.Test;
import java.util.UUID;
import java.util.stream.Stream;
import static org.assertj.core.api.BDDAssertions.then;
import static org.assertj.core.data.Percentage.withPercentage;
public class UuidTraceIdToThresholdComparableTests {
final UUID[] traceIds = createRandomUuids();
@Test
public void should_retain_10_percent() {
float sampleRate = 0.1f;
UuidTraceIdToThresholdComparable sampler = new UuidTraceIdToThresholdComparable(sampleRate);
long passCount = Stream.of(traceIds).filter(uuid -> smallerThanThreshold(sampler, uuid)).count();
then(passCount)
.isCloseTo((long) (traceIds.length * sampleRate), withPercentage(3));
}
@Test
public void should_be_idempotent() {
UuidTraceIdToThresholdComparable sampler1 = new UuidTraceIdToThresholdComparable(0.1f);
UuidTraceIdToThresholdComparable sampler2 = new UuidTraceIdToThresholdComparable(0.1f);
then(Stream.of(traceIds).filter(uuid -> smallerThanThreshold(sampler1, uuid)).toArray())
.containsExactly(Stream.of(traceIds).filter(uuid -> smallerThanThreshold(sampler2, uuid)).toArray());
}
@Test
public void should_have_127_bit_number_as_max_long() {
then(UuidTraceIdToThresholdComparable.MAX_128.toString(2)).matches("^1+$").hasSize(127);
}
private boolean smallerThanThreshold(UuidTraceIdToThresholdComparable comparable, UUID traceId) {
return comparable.compareTo(traceId.toString()) == -1;
}
private UUID[] createRandomUuids() {
UUID[] traceIds;
traceIds = new UUID[100000];
for (int i = 0; i < traceIds.length; i++) {
traceIds[i] = UUID.randomUUID();
}
return traceIds;
}
}

View File

@@ -8,13 +8,15 @@ 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.util.JdkIdGenerator;
import java.util.Random;
import static org.assertj.core.api.BDDAssertions.then;
public class TraceTemplateTests {
TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(), new JdkIdGenerator(), Mockito.mock(ApplicationEventPublisher.class));
TraceManager traceManager = new DefaultTraceManager(new AlwaysSampler(),
new Random(), Mockito.mock(ApplicationEventPublisher.class));
@Test
public void should_pass_trace_to_the_callback_if_tracing_is_active() {

View File

@@ -32,11 +32,12 @@ public class SampleBackground {
@Autowired
private TraceManager traceManager;
@Autowired
private Random random;
@SneakyThrows
@Async
public void background() {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));

View File

@@ -27,11 +27,11 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.JdkIdGenerator;
import sample.SampleMessagingApplication;
import tools.AbstractIntegrationTest;
import java.util.Collection;
import java.util.Random;
import static org.assertj.core.api.BDDAssertions.then;
@@ -52,7 +52,7 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
@Test
public void should_propagate_spans_for_messaging() {
String traceId = new JdkIdGenerator().generateId().toString();
long traceId = new Random().nextLong();
await().until(httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId));
@@ -63,7 +63,7 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
@Test
public void should_propagate_spans_for_messaging_with_async() {
String traceId = new JdkIdGenerator().generateId().toString();
long traceId = new Random().nextLong();
await().until(httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/xform", traceId));
@@ -80,8 +80,8 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
.anyMatch(b -> b.key.equals(binaryAnnotationKey))).isTrue();
}
private void thenAllSpansHaveTraceIdEqualTo(String traceId) {
then(integrationTestSpanCollector.hashedSpans.stream().allMatch(span -> span.traceId == zipkinHashedTraceId(traceId))).isTrue();
private void thenAllSpansHaveTraceIdEqualTo(long traceId) {
then(this.integrationTestSpanCollector.hashedSpans.stream().allMatch(span -> span.traceId == traceId)).isTrue();
}
@Configuration

View File

@@ -16,15 +16,14 @@
package sample;
import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.Random;
/**
* @author Spencer Gibb
* @author Dave Syer
@@ -34,11 +33,12 @@ public class SampleController {
@Autowired
private RestTemplate restTemplate;
@Autowired
private Random random;
@SneakyThrows
@RequestMapping("/")
public String hi() {
final Random random = new Random();
Thread.sleep(random.nextInt(1000));
String s = this.restTemplate.getForObject("http://zipkin/hi2", String.class);
return "hi/" + s;

View File

@@ -16,9 +16,8 @@
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;
@@ -31,8 +30,8 @@ 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;
import java.util.Random;
import java.util.concurrent.Callable;
/**
* @author Spencer Gibb
@@ -49,12 +48,13 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
private TraceAccessor accessor;
@Autowired
private SampleBackground controller;
@Autowired
private Random random;
private int port;
@SneakyThrows
@RequestMapping("/")
public String hi() {
final Random random = new Random();
Thread.sleep(random.nextInt(1000));
String s = this.restTemplate.getForObject("http://localhost:" + this.port
@@ -67,7 +67,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
return new Callable<String>() {
@Override
public String call() throws Exception {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
SampleController.this.traceManager.addAnnotation("callable-sleep-millis", String.valueOf(millis));
@@ -86,7 +85,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/hi2")
public String hi2() {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
@@ -98,7 +96,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
public String traced() {
Trace trace = 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);
@@ -113,7 +110,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/start")
public String start() {
final Random random = new Random();
int millis = random.nextInt(1000);
log.info("Sleeping for {} millis", millis);
Thread.sleep(millis);

View File

@@ -15,30 +15,20 @@
*/
package tools;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import com.jayway.awaitility.Awaitility;
import com.jayway.awaitility.core.ConditionFactory;
import io.zipkin.Codec;
import io.zipkin.Span;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
@@ -47,31 +37,13 @@ import lombok.extern.slf4j.Slf4j;
public abstract class AbstractIntegrationTest {
protected static int pollInterval = 1;
protected static int timeout = 120;
protected static int timeout = 20;
protected RestTemplate restTemplate = new AssertingRestTemplate();
public static ConditionFactory await() {
return Awaitility.await().pollInterval(pollInterval, SECONDS).atMost(timeout, SECONDS);
}
protected long zipkinHashedTraceId(String string) {
long h = 1125899906842597L;
if (string == null) {
return h;
}
int len = string.length();
for (int i = 0; i < len; i++) {
h = 31 * h + string.charAt(i);
}
return h;
}
protected String zipkinHashedHexStringTraceId(String traceId) {
long hashedTraceId = zipkinHashedTraceId(traceId);
return Long.toHexString(hashedTraceId);
}
protected Runnable zipkinQueryServerIsUp() {
return checkServerHealth("Zipkin Query Server", this::endpointToCheckZipkinQueryHealth);
}
@@ -109,10 +81,9 @@ public abstract class AbstractIntegrationTest {
return 9411;
}
protected ResponseEntity<String> checkStateOfTheTraceId(String traceId) {
String hexTraceId = zipkinHashedHexStringTraceId(traceId);
URI uri = URI.create(getZipkinTraceQueryUrl() + hexTraceId);
log.info("Sending request to the Zipkin query service [{}]. Checking presence of trace id [{}] and its hex version [{}]", uri, traceId, hexTraceId);
protected ResponseEntity<String> checkStateOfTheTraceId(long traceId) {
URI uri = URI.create(getZipkinTraceQueryUrl() + Long.toHexString(traceId));
log.info("Sending request to the Zipkin query service [{}]. Checking presence of trace id [{}]", uri, traceId);
return exchangeRequest(uri);
}
@@ -130,11 +101,11 @@ public abstract class AbstractIntegrationTest {
return "http://localhost:"+getZipkinServerPort()+"/api/v1/services";
}
protected Runnable httpMessageWithTraceIdInHeadersIsSuccessfullySent(String endpoint, String traceId) {
protected Runnable httpMessageWithTraceIdInHeadersIsSuccessfullySent(String endpoint, long traceId) {
return new RequestSendingRunnable(this.restTemplate, endpoint, traceId);
}
protected Runnable allSpansWereRegisteredInZipkinWithTraceIdEqualTo(String traceId) {
protected Runnable allSpansWereRegisteredInZipkinWithTraceIdEqualTo(long traceId) {
return () -> {
ResponseEntity<String> response = checkStateOfTheTraceId(traceId);
log.info("Response from the Zipkin query service about the trace id [{}] for trace with id [{}]", response, traceId);

View File

@@ -16,6 +16,7 @@
package tools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Trace;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
@@ -34,9 +35,9 @@ import static org.assertj.core.api.BDDAssertions.then;
public class RequestSendingRunnable implements Runnable {
private final RestTemplate restTemplate;
private final String url;
private final String traceId;
private final long traceId;
public RequestSendingRunnable(RestTemplate restTemplate, String url, String traceId) {
public RequestSendingRunnable(RestTemplate restTemplate, String url, long traceId) {
this.restTemplate = restTemplate;
this.url = url;
this.traceId = traceId;
@@ -50,9 +51,9 @@ public class RequestSendingRunnable implements Runnable {
log.info("Received the following response [{}]", responseEntity);
}
private RequestEntity requestWithTraceId(String traceId) {
private RequestEntity requestWithTraceId(long traceId) {
HttpHeaders headers = new HttpHeaders();
headers.add(Trace.TRACE_ID_NAME, traceId);
headers.add(Trace.TRACE_ID_NAME, Span.IdConverter.toHex(traceId));
URI uri = URI.create(url);
RequestEntity requestEntity = new RequestEntity<>(headers, HttpMethod.GET, uri);
log.info("Request [" + requestEntity + "] is ready");

View File

@@ -15,8 +15,8 @@
*/
package integration;
import java.util.Arrays;
import example.ZipkinStreamServerApplication;
import lombok.SneakyThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
@@ -34,12 +34,11 @@ import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.JdkIdGenerator;
import example.ZipkinStreamServerApplication;
import lombok.SneakyThrows;
import tools.AbstractIntegrationTest;
import java.util.Collections;
import java.util.Random;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { TestSupportBinderAutoConfiguration.class,
ZipkinStreamServerApplication.class })
@@ -60,13 +59,13 @@ public class ZipkinStreamTests extends AbstractIntegrationTest {
await().until(zipkinServerIsUp());
String traceId = new JdkIdGenerator().generateId().toString();
long traceId = new Random().nextLong();
Span span = MilliSpan.builder().traceId(traceId).spanId(traceId).name("test")
.build();
span.tag(getRequiredBinaryAnnotationName(), "10131");
this.input.send(MessageBuilder.withPayload(
new Spans(new Host(getAppName(), "127.0.0.1", 8080), Arrays.asList(span)))
new Spans(new Host(getAppName(), "127.0.0.1", 8080), Collections.singletonList(span)))
.build());
await().until(allSpansWereRegisteredInZipkinWithTraceIdEqualTo(traceId));

View File

@@ -16,15 +16,14 @@
package sample;
import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.Random;
/**
* @author Spencer Gibb
*/
@@ -33,11 +32,12 @@ public class SampleBackground {
@Autowired
private TraceManager traceManager;
@Autowired
private Random random;
@SneakyThrows
@Async
public void background() {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));

View File

@@ -16,9 +16,8 @@
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;
@@ -31,8 +30,8 @@ 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;
import java.util.Random;
import java.util.concurrent.Callable;
/**
* @author Spencer Gibb
@@ -49,12 +48,13 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
private TraceAccessor accessor;
@Autowired
private SampleBackground controller;
@Autowired
private Random random;
private int port;
@SneakyThrows
@RequestMapping("/")
public String hi() {
final Random random = new Random();
Thread.sleep(random.nextInt(1000));
String s = this.restTemplate.getForObject("http://localhost:" + this.port
@@ -67,7 +67,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
return new Callable<String>() {
@Override
public String call() throws Exception {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
SampleController.this.traceManager.addAnnotation("callable-sleep-millis", String.valueOf(millis));
@@ -86,7 +85,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/hi2")
public String hi2() {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
@@ -98,7 +96,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
public String traced() {
Trace trace = 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);
@@ -113,7 +110,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/start")
public String start() {
final Random random = new Random();
int millis = random.nextInt(1000);
log.info("Sleeping for {} millis", millis);
Thread.sleep(millis);

View File

@@ -31,10 +31,11 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.JdkIdGenerator;
import sample.SampleZipkinApplication;
import tools.AbstractIntegrationTest;
import java.util.Random;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { WaitUntilZipkinIsUpConfig.class,
SampleZipkinApplication.class })
@@ -53,9 +54,8 @@ public class ZipkinTests extends AbstractIntegrationTest {
}
@Test
@SneakyThrows
public void should_propagate_spans_to_zipkin() {
String traceId = new JdkIdGenerator().generateId().toString();
long traceId = new Random().nextLong();
await().until(httpMessageWithTraceIdInHeadersIsSuccessfullySent(
sampleAppUrl + "/hi2", traceId));

View File

@@ -16,15 +16,14 @@
package sample;
import java.util.Random;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.sleuth.TraceManager;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.Random;
/**
* @author Spencer Gibb
*/
@@ -33,11 +32,12 @@ public class SampleBackground {
@Autowired
private TraceManager traceManager;
@Autowired
private Random random;
@SneakyThrows
@Async
public void background() {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.traceManager.addAnnotation("background-sleep-millis", String.valueOf(millis));

View File

@@ -16,9 +16,8 @@
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;
@@ -31,8 +30,8 @@ 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;
import java.util.Random;
import java.util.concurrent.Callable;
/**
* @author Spencer Gibb
@@ -49,12 +48,13 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
private TraceAccessor accessor;
@Autowired
private SampleBackground controller;
@Autowired
private Random random;
private int port;
@SneakyThrows
@RequestMapping("/")
public String hi() {
final Random random = new Random();
Thread.sleep(random.nextInt(1000));
String s = this.restTemplate.getForObject("http://localhost:" + this.port
@@ -67,7 +67,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
return new Callable<String>() {
@Override
public String call() throws Exception {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
SampleController.this.traceManager.addAnnotation("callable-sleep-millis", String.valueOf(millis));
@@ -86,7 +85,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/hi2")
public String hi2() {
final Random random = new Random();
int millis = random.nextInt(1000);
Thread.sleep(millis);
this.traceManager.addAnnotation("random-sleep-millis", String.valueOf(millis));
@@ -98,7 +96,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
public String traced() {
Trace trace = 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);
@@ -113,7 +110,6 @@ ApplicationListener<EmbeddedServletContainerInitializedEvent> {
@SneakyThrows
@RequestMapping("/start")
public String start() {
final Random random = new Random();
int millis = random.nextInt(1000);
log.info("Sleeping for {} millis", millis);
Thread.sleep(millis);

View File

@@ -80,8 +80,8 @@ public class ServerPropertiesHostLocator implements HostLocator {
private String getServiceName(Span span) {
String serviceName;
if (span.getProcessId() != null) { // TODO: javadocs say this isn't nullable!
serviceName = span.getProcessId().toLowerCase();
if (span.getProcessId() != null) {
serviceName = span.getProcessId();
}
else {
serviceName = this.appName;

View File

@@ -16,17 +16,18 @@
package org.springframework.cloud.sleuth.stream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import org.junit.Test;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.sleuth.MilliSpan;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
public class ServerPropertiesHostLocatorTests {
MilliSpan span = new MilliSpan(1, 3, "name", "traceId", Collections.<String>emptyList(), "spanId", true, true, "processId");
MilliSpan span = new MilliSpan(1, 3, "name", 1L, Collections.<Long>emptyList(), 2L, true, true, "process");
@Test
public void portDefaultsTo8080() {

View File

@@ -85,7 +85,7 @@ public class StreamSpanListenerTests {
@Test
public void rpcAnnotations() {
Span parent = MilliSpan.builder().traceId("xxxx").name("parent").remote(true)
Span parent = MilliSpan.builder().traceId(1L).name("parent").remote(true)
.build();
Trace context = this.traceManager.startSpan("child", parent);
this.application.publishEvent(new ClientSentEvent(this, context.getSpan()));

View File

@@ -1,13 +1,9 @@
package org.springframework.cloud.sleuth.zipkin.stream;
import io.zipkin.Sampler;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import javax.sql.DataSource;
import io.zipkin.*;
import io.zipkin.BinaryAnnotation.Type;
import io.zipkin.Span.Builder;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
@@ -16,17 +12,13 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.Cloud;
import org.springframework.cloud.CloudFactory;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.Log;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.stream.Host;
import org.springframework.cloud.sleuth.stream.SleuthSink;
import org.springframework.cloud.sleuth.stream.Spans;
import org.springframework.cloud.sleuth.zipkin.stream.ZipkinMessageListener.NotSleuthStreamClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.*;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
@@ -35,20 +27,19 @@ import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.util.StringUtils;
import io.zipkin.Annotation;
import io.zipkin.BinaryAnnotation;
import io.zipkin.BinaryAnnotation.Type;
import io.zipkin.Constants;
import io.zipkin.Endpoint;
import io.zipkin.Span.Builder;
import io.zipkin.SpanStore;
import lombok.extern.apachecommons.CommonsLog;
import javax.sql.DataSource;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
@MessageEndpoint
@CommonsLog
@Conditional(NotSleuthStreamClient.class)
public class ZipkinMessageListener {
private static final String UNKNOWN_PROCESS_ID = "unknown";
@Autowired
SpanStore spanStore;
@@ -80,10 +71,9 @@ public class ZipkinMessageListener {
// A zipkin span without any annotations cannot be queried, add special "lc" to avoid that.
if (span.logs().isEmpty() && span.tags().isEmpty()) {
// TODO: javadocs say this isn't nullable!
String processId = span.getProcessId() != null
? span.getProcessId().toLowerCase()
: "unknown";
: UNKNOWN_PROCESS_ID;
zipkinSpan.addBinaryAnnotation(
BinaryAnnotation.create(Constants.LOCAL_COMPONENT, processId, ep)
);
@@ -94,15 +84,15 @@ public class ZipkinMessageListener {
zipkinSpan.timestamp(span.getBegin() * 1000);
zipkinSpan.duration((span.getEnd() - span.getBegin()) * 1000);
zipkinSpan.traceId(hash(span.getTraceId()));
zipkinSpan.traceId(span.getTraceId());
if (span.getParents().size() > 0) {
if (span.getParents().size() > 1) {
log.error("zipkin doesn't support spans with multiple parents. Omitting "
+ "other parents for " + span);
}
zipkinSpan.parentId(hash(span.getParents().get(0)));
zipkinSpan.parentId(span.getParents().get(0));
}
zipkinSpan.id(hash(span.getSpanId()));
zipkinSpan.id(span.getSpanId());
if (StringUtils.hasText(span.getName())) {
zipkinSpan.name(span.getName());
}
@@ -145,19 +135,6 @@ public class ZipkinMessageListener {
}
}
private static long hash(String string) {
long h = 1125899906842597L;
if (string == null) {
return h;
}
int len = string.length();
for (int i = 0; i < len; i++) {
h = 31 * h + string.charAt(i);
}
return h;
}
protected static class NotSleuthStreamClient extends SpringBootCondition {
@Override

View File

@@ -16,17 +16,18 @@
package org.springframework.cloud.sleuth.zipkin.stream;
import io.zipkin.Sampler;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Test;
import org.springframework.cloud.sleuth.MilliSpan;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.stream.Host;
import org.springframework.cloud.sleuth.stream.Spans;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
public class SamplingZipkinSpanIteratorTests {
@@ -69,7 +70,7 @@ public class SamplingZipkinSpanIteratorTests {
}
Span span(String name) {
String id = UUID.randomUUID().toString();
return new MilliSpan(1, 3, name, id, Collections.<String>emptyList(), id, true, true, "proc");
Long id = new Random().nextLong();
return new MilliSpan(1, 3, name, id, Collections.<Long>emptyList(), id, true, true, "process");
}
}

View File

@@ -19,8 +19,6 @@ package org.springframework.cloud.sleuth.zipkin.stream;
import io.zipkin.BinaryAnnotation;
import io.zipkin.Endpoint;
import java.util.Collections;
import java.util.Comparator;
import java.util.Objects;
import org.junit.Test;
import org.springframework.cloud.sleuth.MilliSpan;
@@ -29,7 +27,7 @@ import org.springframework.cloud.sleuth.stream.Host;
import static org.assertj.core.api.Assertions.assertThat;
public class ZipkinMessageListenerTests {
MilliSpan span = new MilliSpan(1, 3, "name", "traceId", Collections.<String>emptyList(), "spanId", true, true, "processId");
MilliSpan span = new MilliSpan(1, 3, "name", 1L, Collections.<Long>emptyList(), 2L, true, true, "process");
Host host = new Host("myservice", "1.2.3.4", 8080);
Endpoint endpoint = Endpoint.create("myservice", 1 << 24 | 2 << 16 | 3 << 8 | 4, 8080);
@@ -75,13 +73,13 @@ public class ZipkinMessageListenerTests {
assertThat(result.binaryAnnotations).hasSize(1);
assertThat(result.binaryAnnotations.get(0)).isEqualToComparingFieldByField(
BinaryAnnotation.create("lc", span.getProcessId().toLowerCase(), endpoint));
BinaryAnnotation.create("lc", span.getProcessId(), endpoint));
}
// TODO: "unknown" bc process id, documented as not nullable, is null in some tests.
@Test
public void nullProcessIdCoercesToUnknownServiceName() {
MilliSpan noProcessId = MilliSpan.builder().traceId("xxxx").name("parent").remote(true).build();
MilliSpan noProcessId = MilliSpan.builder().traceId(1L).name("parent").remote(true).build();
io.zipkin.Span result = ZipkinMessageListener.convert(noProcessId, host);

View File

@@ -16,11 +16,13 @@
package org.springframework.cloud.sleuth.zipkin;
import java.nio.charset.Charset;
import java.util.Map;
import org.springframework.cloud.sleuth.Span;
import io.zipkin.Annotation;
import io.zipkin.BinaryAnnotation;
import io.zipkin.Constants;
import io.zipkin.Endpoint;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.cloud.sleuth.Log;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
import org.springframework.cloud.sleuth.event.ClientSentEvent;
import org.springframework.cloud.sleuth.event.ServerReceivedEvent;
@@ -31,12 +33,8 @@ import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;
import io.zipkin.Annotation;
import io.zipkin.BinaryAnnotation;
import io.zipkin.Constants;
import io.zipkin.Endpoint;
import lombok.extern.apachecommons.CommonsLog;
import java.nio.charset.Charset;
import java.util.Map;
/**
* @author Spencer Gibb
@@ -126,7 +124,6 @@ public class ZipkinSpanListener {
// A zipkin span without any annotations cannot be queried, add special "lc" to avoid that.
if (span.logs().isEmpty() && span.tags().isEmpty()) {
// TODO: javadocs say this isn't nullable!
byte[] processId = span.getProcessId() != null
? span.getProcessId().toLowerCase().getBytes(UTF_8)
: UNKNOWN_BYTES;
@@ -143,15 +140,15 @@ public class ZipkinSpanListener {
zipkinSpan.timestamp(span.getBegin() * 1000L);
zipkinSpan.duration((span.getEnd() - span.getBegin()) * 1000L);
zipkinSpan.traceId(hash(span.getTraceId()));
zipkinSpan.traceId(span.getTraceId());
if (span.getParents().size() > 0) {
if (span.getParents().size() > 1) {
log.error("Zipkin doesn't support spans with multiple parents. Omitting "
+ "other parents for " + span);
}
zipkinSpan.parentId(hash(span.getParents().get(0)));
zipkinSpan.parentId(span.getParents().get(0));
}
zipkinSpan.id(hash(span.getSpanId()));
zipkinSpan.id(span.getSpanId());
if (StringUtils.hasText(span.getName())) {
zipkinSpan.name(span.getName());
}
@@ -189,17 +186,4 @@ public class ZipkinSpanListener {
}
}
private static long hash(String string) {
long h = 1125899906842597L;
if (string == null) {
return h;
}
int len = string.length();
for (int i = 0; i < len; i++) {
h = 31 * h + string.charAt(i);
}
return h;
}
}

View File

@@ -72,7 +72,7 @@ public class ZipkinSpanListenerTests {
this.test.spans.clear();
}
Span parent = MilliSpan.builder().traceId("xxxx").name("parent").remote(true).build();
Span parent = MilliSpan.builder().traceId(1L).name("parent").remote(true).build();
/** Sleuth timestamps are millisecond granularity while zipkin is microsecond. */
@Test