Add message keys to TraceKeys
User can now configure message headers to be added using spring.sleuth.keys.message.headers. Fixes gh-119 Also a bunch of cosmetic changes (imports etc).
This commit is contained in:
@@ -16,13 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
|
||||
@@ -54,6 +54,39 @@ public class TraceKeys {
|
||||
|
||||
private Http http = new Http();
|
||||
|
||||
private Message message = new Message();
|
||||
|
||||
@Data
|
||||
public static class Message {
|
||||
|
||||
private Payload payload = new Payload();
|
||||
|
||||
@Data
|
||||
public static class Payload {
|
||||
/**
|
||||
* An estimate of the size of the payload if available.
|
||||
*/
|
||||
private String size = "message/payload-size";
|
||||
/**
|
||||
* The type of the payload.
|
||||
*/
|
||||
private String type = "message/payload-type";
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefix for header names if they are added as tags.
|
||||
*/
|
||||
private String prefix = "message/";
|
||||
|
||||
/**
|
||||
* Additional headers that should be added as tags if they exist. If the header
|
||||
* value is not a String it will be converted to a String using its toString()
|
||||
* method.
|
||||
*/
|
||||
private Collection<String> headers = new LinkedHashSet<String>();
|
||||
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Http {
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Value;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.SimpleAsyncTaskExecutor;
|
||||
import org.springframework.scheduling.annotation.AsyncConfigurer;
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceCallable;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceCallable;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceRunnable;
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.MilliSpan;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
import org.springframework.integration.channel.AbstractMessageChannel;
|
||||
import org.springframework.integration.context.IntegrationObjectSupport;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.support.ChannelInterceptorAdapter;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Abstraction over classes related to channel intercepting
|
||||
*
|
||||
@@ -19,15 +20,26 @@ import java.util.Random;
|
||||
*/
|
||||
abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter {
|
||||
|
||||
protected final Tracer tracer;
|
||||
private final Tracer tracer;
|
||||
|
||||
protected final Random random;
|
||||
private final Random random;
|
||||
|
||||
protected AbstractTraceChannelInterceptor(Tracer tracer, Random random) {
|
||||
private final TraceKeys traceKeys;
|
||||
|
||||
protected AbstractTraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, Random random) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
this.random = random;
|
||||
}
|
||||
|
||||
protected Tracer getTracer() {
|
||||
return this.tracer;
|
||||
}
|
||||
|
||||
protected TraceKeys getTraceKeys() {
|
||||
return this.traceKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a span given the message and a channel. Returns null when there was no
|
||||
* trace id passed initially.
|
||||
@@ -37,7 +49,7 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
|
||||
return null; // cannot build a span without ids
|
||||
}
|
||||
long spanId = hasHeader(message, Trace.SPAN_ID_NAME) ?
|
||||
getHeader(message, Trace.SPAN_ID_NAME, Long.class) : random.nextLong();
|
||||
getHeader(message, Trace.SPAN_ID_NAME, Long.class) : this.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);
|
||||
|
||||
@@ -16,16 +16,17 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
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;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility for manipulating message headers related to span data.
|
||||
*
|
||||
@@ -34,7 +35,8 @@ import java.util.Map;
|
||||
*/
|
||||
public class SpanMessageHeaders {
|
||||
|
||||
public static Message<?> addSpanHeaders(Message<?> message, Span span) {
|
||||
public static Message<?> addSpanHeaders(TraceKeys traceKeys, Message<?> message,
|
||||
Span span) {
|
||||
if (span == null) {
|
||||
if (!message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
|
||||
return MessageBuilder.fromMessage(message)
|
||||
@@ -48,40 +50,42 @@ public class SpanMessageHeaders {
|
||||
addHeader(headers, Trace.SPAN_ID_NAME, span.getSpanId());
|
||||
|
||||
if (span.isExportable()) {
|
||||
addAnnotations(message, span);
|
||||
addAnnotations(traceKeys, message, span);
|
||||
addHeader(headers, Trace.PARENT_ID_NAME, getFirst(span.getParents()));
|
||||
addHeader(headers, Trace.SPAN_NAME_NAME, span.getName());
|
||||
addHeader(headers, Trace.PROCESS_ID_NAME, span.getProcessId());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
addHeader(headers, Trace.NOT_SAMPLED_NAME, "");
|
||||
}
|
||||
return MessageBuilder.fromMessage(message).copyHeaders(headers).build();
|
||||
}
|
||||
|
||||
public static void addAnnotations(Message<?> message, Span span) {
|
||||
for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) {
|
||||
if (!Trace.HEADERS.contains(entry.getKey())) { // filter out trace headers
|
||||
String key = "/messaging/headers/" + entry.getKey().toLowerCase();
|
||||
String value = null;
|
||||
if (entry.getValue() != null) {
|
||||
value = entry.getValue().toString(); // TODO: better way to serialize?
|
||||
public static void addAnnotations(TraceKeys traceKeys, Message<?> message,
|
||||
Span span) {
|
||||
for (String name : traceKeys.getMessage().getHeaders()) {
|
||||
if (message.getHeaders().containsKey(name)) {
|
||||
String key = traceKeys.getMessage().getPrefix() + name.toLowerCase();
|
||||
Object value = message.getHeaders().get(name);
|
||||
if (value == null) {
|
||||
value = "null";
|
||||
}
|
||||
span.tag(key, value);
|
||||
span.tag(key, value.toString()); // TODO: better way to serialize?
|
||||
}
|
||||
}
|
||||
addPayloadAnnotations(message.getPayload(), span);
|
||||
addPayloadAnnotations(traceKeys, message.getPayload(), span);
|
||||
}
|
||||
|
||||
static void addPayloadAnnotations(Object payload, Span span) {
|
||||
static void addPayloadAnnotations(TraceKeys traceKeys, Object payload, Span span) {
|
||||
if (payload != null) {
|
||||
span.tag("/messaging/payload/type",
|
||||
span.tag(traceKeys.getMessage().getPayload().getType(),
|
||||
payload.getClass().getCanonicalName());
|
||||
if (payload instanceof String) {
|
||||
span.tag("/messaging/payload/size",
|
||||
span.tag(traceKeys.getMessage().getPayload().getSize(),
|
||||
String.valueOf(((String) payload).length()));
|
||||
}
|
||||
else if (payload instanceof byte[]) {
|
||||
span.tag("/messaging/payload/size",
|
||||
span.tag(traceKeys.getMessage().getPayload().getSize(),
|
||||
String.valueOf(((byte[]) payload).length));
|
||||
}
|
||||
}
|
||||
@@ -94,8 +98,7 @@ public class SpanMessageHeaders {
|
||||
}
|
||||
}
|
||||
|
||||
private static void addHeader(Map<String, String> headers, String name,
|
||||
Long value) {
|
||||
private static void addHeader(Map<String, String> headers, String name, Long value) {
|
||||
if (value != null) {
|
||||
addHeader(headers, name, Span.IdConverter.toHex(value));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
|
||||
@@ -24,18 +28,14 @@ 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;
|
||||
import java.util.TreeMap;
|
||||
|
||||
/**
|
||||
* Builder class to create STOMP message
|
||||
*
|
||||
*
|
||||
* @author Gaurav Rai Mazra
|
||||
*
|
||||
*/
|
||||
public class StompMessageBuilder {
|
||||
|
||||
|
||||
public static StompMessageBuilder fromMessage(Message<?> message) {
|
||||
return new StompMessageBuilder(message);
|
||||
}
|
||||
@@ -89,7 +89,7 @@ public class StompMessageBuilder {
|
||||
headerAccessor.getMessageHeaders());
|
||||
}
|
||||
|
||||
private void pushHeaders(final SimpMessageHeaderAccessor assessor, final String key, final Object value) {
|
||||
private void pushHeaders(final SimpMessageHeaderAccessor accessor, final String key, final Object value) {
|
||||
switch (key) {
|
||||
case SimpMessageHeaderAccessor.DESTINATION_HEADER:
|
||||
case SimpMessageHeaderAccessor.MESSAGE_TYPE_HEADER:
|
||||
@@ -107,10 +107,10 @@ public class StompMessageBuilder {
|
||||
case Trace.SPAN_ID_NAME:
|
||||
case Trace.SPAN_NAME_NAME:
|
||||
case Trace.TRACE_ID_NAME:
|
||||
assessor.setHeader(key, value);
|
||||
accessor.setHeader(key, value);
|
||||
break;
|
||||
default:
|
||||
assessor.setNativeHeader(key, value == null ? null : value.toString());
|
||||
accessor.setNativeHeader(key, value == null ? null : value.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,15 +16,16 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.sampler.IsTracingSampler;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
@@ -33,38 +34,38 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
|
||||
|
||||
private ThreadLocal<Trace> traceHolder = new ThreadLocal<>();
|
||||
|
||||
public TraceChannelInterceptor(Tracer tracer, Random random) {
|
||||
super(tracer, random);
|
||||
public TraceChannelInterceptor(Tracer tracer, TraceKeys traceKeys, Random random) {
|
||||
super(tracer, traceKeys, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
Trace trace = this.traceHolder.get();
|
||||
// Double close to clean up the parent (remote span as well)
|
||||
this.tracer.close(this.tracer.close(trace));
|
||||
getTracer().close(getTracer().close(trace));
|
||||
this.traceHolder.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
if (this.tracer.isTracing()) {
|
||||
return SpanMessageHeaders.addSpanHeaders(message,
|
||||
this.tracer.getCurrentSpan());
|
||||
if (getTracer().isTracing()) {
|
||||
return SpanMessageHeaders.addSpanHeaders(getTraceKeys(), message,
|
||||
getTracer().getCurrentSpan());
|
||||
}
|
||||
String name = getMessageChannelName(channel);
|
||||
Trace trace = startSpan(buildSpan(message), name, message);
|
||||
this.traceHolder.set(trace);
|
||||
return SpanMessageHeaders.addSpanHeaders(message, trace.getSpan());
|
||||
return SpanMessageHeaders.addSpanHeaders(getTraceKeys(), message, trace.getSpan());
|
||||
}
|
||||
|
||||
private Trace startSpan(Span span, String name, Message message) {
|
||||
private Trace startSpan(Span span, String name, Message<?> message) {
|
||||
if (span != null) {
|
||||
return tracer.joinTrace(name, span);
|
||||
return getTracer().joinTrace(name, span);
|
||||
}
|
||||
if (message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
|
||||
return tracer.startTrace(name, IsTracingSampler.INSTANCE);
|
||||
return getTracer().startTrace(name, IsTracingSampler.INSTANCE);
|
||||
}
|
||||
return this.tracer.startTrace(name);
|
||||
return getTracer().startTrace(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,19 +16,20 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
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
|
||||
*/
|
||||
@@ -37,6 +38,7 @@ import java.util.Random;
|
||||
@ConditionalOnBean(Tracer.class)
|
||||
@AutoConfigureAfter(TraceAutoConfiguration.class)
|
||||
@ConditionalOnProperty(value = "spring.sleuth.integration.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties(TraceKeys.class)
|
||||
public class TraceSpringIntegrationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@@ -48,19 +50,21 @@ public class TraceSpringIntegrationAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@GlobalChannelInterceptor
|
||||
public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer, Random random) {
|
||||
return new TraceChannelInterceptor(tracer, random);
|
||||
public TraceChannelInterceptor traceChannelInterceptor(Tracer tracer,
|
||||
TraceKeys traceKeys, Random random) {
|
||||
return new TraceChannelInterceptor(tracer, traceKeys, random);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceStompMessageChannelInterceptor traceStompMessageChannelInterceptor(Tracer tracer, Random random) {
|
||||
return new TraceStompMessageChannelInterceptor(tracer, random);
|
||||
public TraceStompMessageChannelInterceptor traceStompMessageChannelInterceptor(
|
||||
Tracer tracer, TraceKeys traceKeys, Random random) {
|
||||
return new TraceStompMessageChannelInterceptor(tracer, traceKeys, random);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public TraceStompMessageContextPropagationChannelInterceptor traceStompMessageContextPropagationChannelInteceptor(
|
||||
Tracer tracer) {
|
||||
return new TraceStompMessageContextPropagationChannelInterceptor(tracer);
|
||||
Tracer tracer, TraceKeys traceKeys) {
|
||||
return new TraceStompMessageContextPropagationChannelInterceptor(tracer, traceKeys);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,33 +15,34 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
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
|
||||
*
|
||||
*
|
||||
* @author Gaurav Rai Mazra
|
||||
* @author Marcin Grzejszczak
|
||||
*
|
||||
*
|
||||
*/
|
||||
public class TraceStompMessageChannelInterceptor extends AbstractTraceChannelInterceptor implements ChannelInterceptor {
|
||||
private ThreadLocal<Trace> traceScopeHolder = new ThreadLocal<Trace>();
|
||||
|
||||
public TraceStompMessageChannelInterceptor(Tracer tracer, Random random) {
|
||||
super(tracer, random);
|
||||
public TraceStompMessageChannelInterceptor(Tracer tracer, TraceKeys traceKeys, Random random) {
|
||||
super(tracer, traceKeys, random);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
if (tracer.isTracing() || message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
|
||||
return StompMessageBuilder.fromMessage(message).setHeadersFromSpan(tracer.getCurrentSpan()).build();
|
||||
if (getTracer().isTracing() || message.getHeaders().containsKey(Trace.NOT_SAMPLED_NAME)) {
|
||||
return StompMessageBuilder.fromMessage(message).setHeadersFromSpan(getTracer().getCurrentSpan()).build();
|
||||
}
|
||||
String name = getMessageChannelName(channel);
|
||||
Trace trace = startSpan(buildSpan(message), name);
|
||||
@@ -51,16 +52,16 @@ public class TraceStompMessageChannelInterceptor extends AbstractTraceChannelInt
|
||||
|
||||
private Trace startSpan(Span span, String name) {
|
||||
if (span != null) {
|
||||
return tracer.joinTrace(name, span);
|
||||
return getTracer().joinTrace(name, span);
|
||||
}
|
||||
return tracer.startTrace(name);
|
||||
return getTracer().startTrace(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postSend(Message<?> message, MessageChannel channel, boolean sent) {
|
||||
final ThreadLocal<Trace> traceScopeHolder = this.traceScopeHolder;
|
||||
Trace traceInScope = traceScopeHolder.get();
|
||||
this.tracer.close(traceInScope);
|
||||
getTracer().close(traceInScope);
|
||||
traceScopeHolder.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -43,9 +41,11 @@ public class TraceStompMessageContextPropagationChannelInterceptor extends Chann
|
||||
|
||||
private final Tracer tracer;
|
||||
private final static ThreadLocal<Trace> ORIGINAL_CONTEXT = new ThreadLocal<>();
|
||||
private TraceKeys traceKeys;
|
||||
|
||||
public TraceStompMessageContextPropagationChannelInterceptor(Tracer tracer) {
|
||||
public TraceStompMessageContextPropagationChannelInterceptor(Tracer tracer, TraceKeys traceKeys) {
|
||||
this.tracer = tracer;
|
||||
this.traceKeys = traceKeys;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -55,12 +55,23 @@ public class TraceStompMessageContextPropagationChannelInterceptor extends Chann
|
||||
}
|
||||
Span span = this.tracer.getCurrentSpan();
|
||||
if (span != null) {
|
||||
return new MessageWithSpan(message, span);
|
||||
return createMessageWithSpan(message, span);
|
||||
} else {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
private Message<?> createMessageWithSpan(Message<?> message, Span span) {
|
||||
MessageWithSpan output = new MessageWithSpan(message, span);
|
||||
addAnnotationsToSpanFromMessage(output, span);
|
||||
return output;
|
||||
}
|
||||
|
||||
private void addAnnotationsToSpanFromMessage(Message<?> message, Span span) {
|
||||
SpanMessageHeaders.addAnnotations(this.traceKeys, message, span);
|
||||
SpanMessageHeaders.addPayloadAnnotations(this.traceKeys, message.getPayload(), span);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Message<?> postReceive(Message<?> message, MessageChannel channel) {
|
||||
if (message instanceof MessageWithSpan) {
|
||||
@@ -103,18 +114,6 @@ public class TraceStompMessageContextPropagationChannelInterceptor extends Chann
|
||||
Assert.notNull(span, "span can not be null");
|
||||
this.span = span;
|
||||
this.message = StompMessageBuilder.fromMessage(message).setHeadersFromSpan(this.span).build();
|
||||
addAnnotationsToSpanFromMessage(this.message, this.span);
|
||||
}
|
||||
|
||||
private void addAnnotationsToSpanFromMessage(Message<?> message, Span span) {
|
||||
for (Map.Entry<String, Object> entry : message.getHeaders().entrySet()) {
|
||||
if (!Trace.HEADERS.contains(entry.getKey())) {
|
||||
String key = "/messaging/headers/" + entry.getKey().toLowerCase();
|
||||
String value = entry.getValue() == null ? null : entry.getValue().toString();
|
||||
span.tag(key, value);
|
||||
}
|
||||
}
|
||||
SpanMessageHeaders.addPayloadAnnotations(message.getPayload(), span);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,7 +26,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.springframework.cloud.sleuth.TraceAccessor;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceCallable;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -21,7 +21,6 @@ import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.TraceCommand;
|
||||
|
||||
@@ -38,11 +37,11 @@ import feign.Target;
|
||||
*/
|
||||
final class SleuthHystrixInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final Target target;
|
||||
private final Target<?> target;
|
||||
private final Map<Method, MethodHandler> dispatch;
|
||||
private final Tracer tracer;
|
||||
|
||||
SleuthHystrixInvocationHandler(Target target, Map<Method, MethodHandler> dispatch, Tracer tracer) {
|
||||
SleuthHystrixInvocationHandler(Target<?> target, Map<Method, MethodHandler> dispatch, Tracer tracer) {
|
||||
this.tracer = checkNotNull(tracer, "traceManager");
|
||||
this.target = checkNotNull(target, "target");
|
||||
this.dispatch = checkNotNull(dispatch, "dispatch");
|
||||
@@ -56,7 +55,7 @@ final class SleuthHystrixInvocationHandler implements InvocationHandler {
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
|
||||
.andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
|
||||
|
||||
HystrixCommand<Object> hystrixCommand = new TraceCommand<Object>(tracer, setter) {
|
||||
HystrixCommand<Object> hystrixCommand = new TraceCommand<Object>(this.tracer, setter) {
|
||||
@Override
|
||||
public Object doRun() throws Exception {
|
||||
try {
|
||||
@@ -84,8 +83,8 @@ final class SleuthHystrixInvocationHandler implements InvocationHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvocationHandler create(Target target, Map<Method, MethodHandler> dispatch) {
|
||||
return new SleuthHystrixInvocationHandler(target, dispatch, tracer);
|
||||
public InvocationHandler create(@SuppressWarnings("rawtypes") Target target, Map<Method, MethodHandler> dispatch) {
|
||||
return new SleuthHystrixInvocationHandler(target, dispatch, this.tracer);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import com.netflix.hystrix.HystrixCommand;
|
||||
import feign.*;
|
||||
import feign.codec.Decoder;
|
||||
import feign.hystrix.HystrixFeign;
|
||||
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 org.springframework.beans.factory.ObjectFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
@@ -35,7 +39,6 @@ import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.TraceAccessor;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
|
||||
import org.springframework.cloud.sleuth.event.ClientSentEvent;
|
||||
import org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixAutoConfiguration;
|
||||
@@ -48,13 +51,16 @@ import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.context.annotation.Scope;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
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 static java.util.Collections.singletonList;
|
||||
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;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -23,7 +23,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.springframework.cloud.sleuth.sampler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.sleuth.Sampler;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.TraceAccessor;
|
||||
@@ -21,7 +20,6 @@ import org.springframework.cloud.sleuth.TraceAccessor;
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
@Slf4j
|
||||
public class PercentageBasedSampler implements Sampler<Void> {
|
||||
|
||||
private final SamplerConfiguration configuration;
|
||||
@@ -34,8 +32,8 @@ public class PercentageBasedSampler implements Sampler<Void> {
|
||||
|
||||
@Override
|
||||
public boolean next() {
|
||||
Span currentSpan = traceAccessor.getCurrentSpan();
|
||||
long threshold = Math.abs(Long.MAX_VALUE * (int) (configuration.getPercentage() * 100)); // drops fractional percentage.
|
||||
Span currentSpan = this.traceAccessor.getCurrentSpan();
|
||||
long threshold = Math.abs(Long.MAX_VALUE * (int) (this.configuration.getPercentage() * 100)); // drops fractional percentage.
|
||||
if (currentSpan == null || threshold == 0L) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ package org.springframework.cloud.sleuth.template;
|
||||
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceDelegate;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package org.springframework.cloud.sleuth.instrument;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -7,19 +13,11 @@ import org.mockito.Mockito;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
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 {
|
||||
|
||||
|
||||
@@ -1,6 +1,17 @@
|
||||
package org.springframework.cloud.sleuth.instrument.executor;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
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;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -15,17 +26,7 @@ import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
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;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TraceableExecutorServiceTests {
|
||||
@@ -39,34 +40,34 @@ public class TraceableExecutorServiceTests {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
tracer = new DefaultTracer(new AlwaysSampler(), new Random(), publisher);
|
||||
traceManagerableExecutorService = new TraceableExecutorService(executorService, tracer);
|
||||
this.tracer = new DefaultTracer(new AlwaysSampler(), new Random(), this.publisher);
|
||||
this.traceManagerableExecutorService = new TraceableExecutorService(this.executorService, this.tracer);
|
||||
TraceContextHolder.removeCurrentTrace();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
tracer = null;
|
||||
traceManagerableExecutorService.shutdown();
|
||||
executorService.shutdown();
|
||||
this.tracer = null;
|
||||
this.traceManagerableExecutorService.shutdown();
|
||||
this.executorService.shutdown();
|
||||
TraceContextHolder.removeCurrentTrace();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SneakyThrows
|
||||
public void should_propagate_trace_id_and_set_new_span_when_traceable_executor_service_is_executed() {
|
||||
Trace trace = tracer.startTrace("PARENT");
|
||||
Trace trace = this.tracer.startTrace("PARENT");
|
||||
CompletableFuture.allOf(runnablesExecutedViaTraceManagerableExecutorService()).get();
|
||||
tracer.close(trace);
|
||||
this.tracer.close(trace);
|
||||
|
||||
then(spanVerifyingRunnable.traceIds.stream().distinct().collect(toList())).containsOnly(trace.getSpan().getTraceId());
|
||||
then(spanVerifyingRunnable.spanIds.stream().distinct().collect(toList())).hasSize(TOTAL_THREADS);
|
||||
then(this.spanVerifyingRunnable.traceIds.stream().distinct().collect(toList())).containsOnly(trace.getSpan().getTraceId());
|
||||
then(this.spanVerifyingRunnable.spanIds.stream().distinct().collect(toList())).hasSize(TOTAL_THREADS);
|
||||
}
|
||||
|
||||
private CompletableFuture[] runnablesExecutedViaTraceManagerableExecutorService() {
|
||||
List<CompletableFuture> futures = new ArrayList<>();
|
||||
private CompletableFuture<?>[] runnablesExecutedViaTraceManagerableExecutorService() {
|
||||
List<CompletableFuture<?>> futures = new ArrayList<>();
|
||||
for (int i = 0; i < TOTAL_THREADS; i++) {
|
||||
futures.add(CompletableFuture.runAsync(spanVerifyingRunnable, traceManagerableExecutorService));
|
||||
futures.add(CompletableFuture.runAsync(this.spanVerifyingRunnable, this.traceManagerableExecutorService));
|
||||
}
|
||||
return futures.toArray(new CompletableFuture[futures.size()]);
|
||||
}
|
||||
@@ -79,8 +80,8 @@ public class TraceableExecutorServiceTests {
|
||||
@Override
|
||||
public void run() {
|
||||
Span span = TraceContextHolder.getCurrentSpan();
|
||||
traceIds.add(span.getTraceId());
|
||||
spanIds.add(span.getSpanId());
|
||||
this.traceIds.add(span.getTraceId());
|
||||
this.spanIds.add(span.getSpanId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -14,10 +16,8 @@ import org.springframework.messaging.support.ExecutorSubscribableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
abstract class AbstractTraceStompIntegrationTests {
|
||||
public abstract class AbstractTraceStompIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
@Qualifier("executorSubscribableChannel")
|
||||
@@ -29,17 +29,17 @@ abstract class AbstractTraceStompIntegrationTests {
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
this.channel.subscribe(stompMessageHandler);
|
||||
this.channel.subscribe(this.stompMessageHandler);
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
TraceContextHolder.removeCurrentTrace();
|
||||
this.channel.unsubscribe(stompMessageHandler);
|
||||
this.channel.unsubscribe(this.stompMessageHandler);
|
||||
}
|
||||
|
||||
Trace givenALocallyStartedSpan() {
|
||||
return tracer.startTrace("testSendMessage", sampler);
|
||||
return this.tracer.startTrace("testSendMessage", this.sampler);
|
||||
}
|
||||
|
||||
Message<?> givenMessageToBeSampled() {
|
||||
@@ -48,7 +48,7 @@ abstract class AbstractTraceStompIntegrationTests {
|
||||
|
||||
void whenTheMessageWasSent(Message<?> message) {
|
||||
this.channel.send(message);
|
||||
then(stompMessageHandler.message).isNotNull();
|
||||
then(this.stompMessageHandler.message).isNotNull();
|
||||
}
|
||||
|
||||
Long thenSpanIdFromHeadersIsNotEmpty() {
|
||||
@@ -64,6 +64,6 @@ abstract class AbstractTraceStompIntegrationTests {
|
||||
}
|
||||
|
||||
<T> T getValueFromHeaders(String headerName, Class<T> type) {
|
||||
return stompMessageHandler.message.getHeaders().get(headerName, type);
|
||||
return this.stompMessageHandler.message.getHeaders().get(headerName, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -18,7 +22,6 @@ import org.springframework.cloud.sleuth.MilliSpan;
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.Tracer;
|
||||
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
|
||||
import org.springframework.cloud.sleuth.event.ClientSentEvent;
|
||||
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
|
||||
@@ -35,12 +38,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
||||
import com.netflix.loadbalancer.ILoadBalancer;
|
||||
import com.netflix.loadbalancer.Server;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(classes = { FeignTraceTests.TestConfiguration.class })
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -23,7 +31,6 @@ import org.springframework.cloud.sleuth.MilliSpan;
|
||||
import org.springframework.cloud.sleuth.Trace;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.DefaultTracer;
|
||||
import org.springframework.cloud.sleuth.trace.TraceContextHolder;
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -36,14 +43,6 @@ 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
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user