Ensure clients set X-Not-Sampled header for non-exportable Span
It also turns out that there's a bug in Spring MVC which makes an empty header blow up in RequestHeaderMapMethodArgumentResolver so we set the header to "true" instead of empty. Fixes gh-136
This commit is contained in:
@@ -299,7 +299,7 @@ public class Span {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[Trace: " + toHex(this.traceId) + ", Span: " + toHex(this.spanId) + "]";
|
||||
return "[Trace: " + toHex(this.traceId) + ", Span: " + toHex(this.spanId) + ", exportable=" + this.exportable + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -52,7 +52,7 @@ public class SpanMessageHeaders {
|
||||
if (span == null) {
|
||||
if (!message.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) {
|
||||
return MessageBuilder.fromMessage(message)
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "").build();
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "true").build();
|
||||
}
|
||||
return message;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public class SpanMessageHeaders {
|
||||
addHeader(headers, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
}
|
||||
else {
|
||||
addHeader(headers, Span.NOT_SAMPLED_NAME, "");
|
||||
addHeader(headers, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
return MessageBuilder.fromMessage(message).copyHeaders(headers)
|
||||
.setHeader(SPAN_HEADER, span).build();
|
||||
|
||||
@@ -166,9 +166,6 @@ public class TraceFilter extends OncePerRequestFilter
|
||||
request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.tracer.continueSpan(spanFromRequest);
|
||||
}
|
||||
|
||||
Throwable exception = null;
|
||||
try {
|
||||
|
||||
@@ -122,12 +122,15 @@ public class TraceFeignClientAutoConfiguration {
|
||||
public void apply(RequestTemplate template) {
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(template, Span.NOT_SAMPLED_NAME, "");
|
||||
setHeader(template, Span.NOT_SAMPLED_NAME, "true");
|
||||
return;
|
||||
}
|
||||
template.header(Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
|
||||
setHeader(template, Span.SPAN_NAME_NAME, span.getName());
|
||||
setHeader(template, Span.SPAN_ID_NAME, Span.toHex(span.getSpanId()));
|
||||
if (!span.isExportable()) {
|
||||
setHeader(template, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
Long parentId = getParentId(span);
|
||||
if (parentId != null) {
|
||||
setHeader(template, Span.PARENT_ID_NAME, Span.toHex(parentId));
|
||||
@@ -167,7 +170,7 @@ public class TraceFeignClientAutoConfiguration {
|
||||
newHeaders.putAll(headers);
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "");
|
||||
setHeader(newHeaders, Span.NOT_SAMPLED_NAME, "true");
|
||||
return newHeaders;
|
||||
}
|
||||
setHeader(newHeaders, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.cloud.sleuth.instrument.web.client;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.event.ClientReceivedEvent;
|
||||
@@ -26,9 +28,6 @@ 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
|
||||
@@ -40,8 +39,8 @@ import java.io.IOException;
|
||||
* @author Marcin Grzejszczak, 4financeIT
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
public class TraceRestTemplateInterceptor implements ClientHttpRequestInterceptor,
|
||||
ApplicationEventPublisherAware {
|
||||
public class TraceRestTemplateInterceptor
|
||||
implements ClientHttpRequestInterceptor, ApplicationEventPublisherAware {
|
||||
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@@ -61,11 +60,14 @@ ApplicationEventPublisherAware {
|
||||
ClientHttpRequestExecution execution) throws IOException {
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(request, Span.NOT_SAMPLED_NAME, "");
|
||||
setHeader(request, Span.NOT_SAMPLED_NAME, "true");
|
||||
return execution.execute(request, body);
|
||||
}
|
||||
setHeader(request, Span.TRACE_ID_NAME, span.getTraceId());
|
||||
setHeader(request, Span.SPAN_ID_NAME, span.getSpanId());
|
||||
if (!span.isExportable()) {
|
||||
setHeader(request, Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
setHeader(request, Span.SPAN_NAME_NAME, span.getName());
|
||||
setHeader(request, Span.PARENT_ID_NAME, getParentId(span));
|
||||
setHeader(request, Span.PROCESS_ID_NAME, span.getProcessId());
|
||||
@@ -87,12 +89,11 @@ ApplicationEventPublisherAware {
|
||||
}
|
||||
|
||||
private Long getParentId(Span span) {
|
||||
return !span.getParents().isEmpty() ? span
|
||||
.getParents().get(0) : null;
|
||||
return !span.getParents().isEmpty() ? span.getParents().get(0) : null;
|
||||
}
|
||||
|
||||
public void setHeader(HttpRequest request, String name, String value) {
|
||||
if (StringUtils.hasText(value) && !request.getHeaders().containsKey(name) && this.accessor.isTracing()) {
|
||||
if (value!=null && !request.getHeaders().containsKey(name) && this.accessor.isTracing()) {
|
||||
request.getHeaders().add(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.zuul;
|
||||
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.cloud.sleuth.Span;
|
||||
import org.springframework.cloud.sleuth.SpanAccessor;
|
||||
import org.springframework.cloud.sleuth.event.ClientSentEvent;
|
||||
@@ -26,7 +26,8 @@ import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisherAware;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import java.util.Map;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
@@ -60,7 +61,7 @@ ApplicationEventPublisherAware {
|
||||
// N.B. this will only work with the simple host filter (not ribbon) unless you set hystrix.execution.isolation.strategy=SEMAPHORE
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(response, Span.NOT_SAMPLED_NAME, "");
|
||||
setHeader(response, Span.NOT_SAMPLED_NAME, "true");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
||||
@@ -92,7 +92,7 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
|
||||
protected void customizeRequest(HttpRequest.Builder requestBuilder) {
|
||||
Span span = getCurrentSpan();
|
||||
if (span == null) {
|
||||
setHeader(requestBuilder, Span.NOT_SAMPLED_NAME, "");
|
||||
setHeader(requestBuilder, Span.NOT_SAMPLED_NAME, "true");
|
||||
return;
|
||||
}
|
||||
setHeader(requestBuilder, Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
|
||||
|
||||
@@ -69,9 +69,9 @@ public class DefaultTracer implements Tracer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Span startTrace(String name, Sampler s) {
|
||||
public Span startTrace(String name, Sampler sampler) {
|
||||
Span span;
|
||||
if (isTracing() || s.isSampled()) {
|
||||
if (isTracing() || sampler.isSampled()) {
|
||||
span = createChild(getCurrentSpan(), name);
|
||||
}
|
||||
else {
|
||||
@@ -144,12 +144,13 @@ public class DefaultTracer implements Tracer {
|
||||
}
|
||||
else {
|
||||
if (SpanContextHolder.getCurrentSpan() == null) {
|
||||
Span span = createSpan(null, parent);
|
||||
Span span = createSpan(parent, null);
|
||||
SpanContextHolder.setCurrentSpan(span);
|
||||
}
|
||||
Span span = Span.builder().begin(System.currentTimeMillis()).name(name)
|
||||
.traceId(parent.getTraceId()).parent(parent.getSpanId()).spanId(id)
|
||||
.processId(parent.getProcessId()).build();
|
||||
.processId(parent.getProcessId()).exportable(parent.isExportable())
|
||||
.build();
|
||||
this.publisher.publishEvent(new SpanAcquiredEvent(this, parent, span));
|
||||
return span;
|
||||
}
|
||||
@@ -164,12 +165,12 @@ public class DefaultTracer implements Tracer {
|
||||
if (span != null) {
|
||||
this.publisher.publishEvent(new SpanContinuedEvent(this, span));
|
||||
}
|
||||
Span newSpan = createSpan(SpanContextHolder.getCurrentSpan(), span);
|
||||
Span newSpan = createSpan(span, SpanContextHolder.getCurrentSpan());
|
||||
SpanContextHolder.setCurrentSpan(newSpan);
|
||||
return newSpan;
|
||||
}
|
||||
|
||||
protected Span createSpan(Span saved, Span span) {
|
||||
protected Span createSpan(Span span, Span saved) {
|
||||
if (saved == null && span.getSavedSpan() != null) {
|
||||
saved = span.getSavedSpan();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,18 @@
|
||||
|
||||
package org.springframework.cloud.sleuth;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.mockito.Matchers.isA;
|
||||
import static org.mockito.Mockito.atLeast;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -29,18 +41,6 @@ import org.springframework.cloud.sleuth.trace.SpanContextHolder;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
|
||||
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.atLeast;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
*/
|
||||
@@ -50,10 +50,12 @@ public class DefaultTraceManagerTests {
|
||||
public static final String IMPORTANT_WORK_1 = "important work 1";
|
||||
public static final String IMPORTANT_WORK_2 = "important work 2";
|
||||
public static final int NUM_SPANS = 3;
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
SpanContextHolder.removeCurrentSpan();
|
||||
this.publisher = mock(ApplicationEventPublisher.class);
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -63,24 +65,23 @@ public class DefaultTraceManagerTests {
|
||||
|
||||
@Test
|
||||
public void tracingWorks() {
|
||||
ApplicationEventPublisher publisher = mock(ApplicationEventPublisher.class);
|
||||
|
||||
DefaultTracer traceManager = new DefaultTracer(new IsTracingSampler(), new Random(), publisher);
|
||||
DefaultTracer tracer = new DefaultTracer(new IsTracingSampler(), new Random(), this.publisher);
|
||||
|
||||
Span span = traceManager.startTrace(CREATE_SIMPLE_TRACE, new AlwaysSampler());
|
||||
Span span = tracer.startTrace(CREATE_SIMPLE_TRACE, new AlwaysSampler());
|
||||
try {
|
||||
importantWork1(traceManager);
|
||||
importantWork1(tracer);
|
||||
}
|
||||
finally {
|
||||
traceManager.close(span);
|
||||
tracer.close(span);
|
||||
}
|
||||
|
||||
verify(publisher, times(NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
|
||||
verify(publisher, times(NUM_SPANS)).publishEvent(isA(SpanReleasedEvent.class));
|
||||
verify(this.publisher, times(NUM_SPANS)).publishEvent(isA(SpanAcquiredEvent.class));
|
||||
verify(this.publisher, times(NUM_SPANS)).publishEvent(isA(SpanReleasedEvent.class));
|
||||
|
||||
ArgumentCaptor<ApplicationEvent> captor = ArgumentCaptor
|
||||
.forClass(ApplicationEvent.class);
|
||||
verify(publisher, atLeast(NUM_SPANS)).publishEvent(captor.capture());
|
||||
verify(this.publisher, atLeast(NUM_SPANS)).publishEvent(captor.capture());
|
||||
|
||||
List<Span> spans = new ArrayList<>();
|
||||
for (ApplicationEvent event : captor.getAllValues()) {
|
||||
@@ -99,6 +100,22 @@ public class DefaultTraceManagerTests {
|
||||
assertThat("gen4 was non-empty", gen4.isEmpty(), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonExportable() {
|
||||
DefaultTracer tracer = new DefaultTracer(new IsTracingSampler(), new Random(), this.publisher);
|
||||
Span span = tracer.startTrace(CREATE_SIMPLE_TRACE);
|
||||
assertThat(span.isExportable(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exportableInheritedFromParent() {
|
||||
DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), this.publisher);
|
||||
Span span = tracer.startTrace(CREATE_SIMPLE_TRACE, new IsTracingSampler());
|
||||
assertThat(span.isExportable(), is(false));
|
||||
Span child = tracer.joinTrace(CREATE_SIMPLE_TRACE + "/child", span);
|
||||
assertThat(child.isExportable(), is(false));
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
@@ -10,10 +10,8 @@ import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.integration.TraceSpringIntegrationAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -21,7 +19,6 @@ import org.springframework.context.annotation.Import;
|
||||
JmxAutoConfiguration.class, TraceSpringIntegrationAutoConfiguration.class,
|
||||
ArchaiusAutoConfiguration.class, LoadBalancerAutoConfiguration.class })
|
||||
@EnableAspectJAutoProxy(proxyTargetClass = true)
|
||||
@Import(AlwaysSampler.class)
|
||||
@Configuration
|
||||
public @interface DefaultTestAutoConfiguration {
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.sleuth.instrument.integration;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
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;
|
||||
|
||||
@@ -46,12 +52,6 @@ import org.springframework.messaging.MessagingException;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
|
||||
@Test
|
||||
public void nonExportableSpanCreation() {
|
||||
this.channel.send(MessageBuilder.withPayload("hi")
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "").build());
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "true").build());
|
||||
assertNotNull("message was null", this.message);
|
||||
|
||||
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package org.springframework.cloud.sleuth.instrument.web;
|
||||
|
||||
import static org.assertj.core.api.BDDAssertions.then;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
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.Tracer;
|
||||
import org.springframework.cloud.sleuth.instrument.DefaultTestAutoConfiguration;
|
||||
import org.springframework.cloud.sleuth.instrument.TraceKeys;
|
||||
import org.springframework.cloud.sleuth.instrument.web.common.AbstractMvcIntegrationTest;
|
||||
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
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.MockHttpServletRequestBuilder;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@SpringApplicationConfiguration(TraceFilterAlwaysSamplerIntegrationTests.class)
|
||||
@DefaultTestAutoConfiguration
|
||||
@RestController
|
||||
@Configuration
|
||||
@Import(AlwaysSampler.class)
|
||||
public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
|
||||
private static Log logger = LogFactory
|
||||
.getLog(TraceFilterAlwaysSamplerIntegrationTests.class);
|
||||
|
||||
@Autowired
|
||||
Tracer tracer;
|
||||
@Autowired
|
||||
TraceKeys traceKeys;
|
||||
|
||||
static Span span;
|
||||
|
||||
@RequestMapping("/ping")
|
||||
public String ping() {
|
||||
logger.info("ping");
|
||||
span = this.tracer.getCurrentSpan();
|
||||
return "ping";
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_always_sampler_is_used_span_is_exportable() throws Exception {
|
||||
Long expectedTraceId = new Random().nextLong();
|
||||
|
||||
MvcResult mvcResult = whenSentPingWithTraceId(expectedTraceId);
|
||||
|
||||
then(tracingHeaderFrom(mvcResult)).isEqualTo(expectedTraceId);
|
||||
then(span.isExportable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void when_not_sampling_header_present_span_is_not_exportable() throws Exception {
|
||||
Long expectedTraceId = new Random().nextLong();
|
||||
|
||||
MvcResult mvcResult = whenSentPingWithTraceIdAndNotSampling(expectedTraceId);
|
||||
|
||||
then(tracingHeaderFrom(mvcResult)).isEqualTo(expectedTraceId);
|
||||
then(span.isExportable()).isFalse();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureMockMvcBuilder(DefaultMockMvcBuilder mockMvcBuilder) {
|
||||
mockMvcBuilder.addFilters(new TraceFilter(this.tracer, this.traceKeys));
|
||||
}
|
||||
|
||||
private MvcResult whenSentPingWithTraceIdAndNotSampling(Long traceId)
|
||||
throws Exception {
|
||||
return sendPingWithTraceId(Span.TRACE_ID_NAME, traceId, false);
|
||||
}
|
||||
|
||||
private MvcResult whenSentPingWithTraceId(Long traceId) throws Exception {
|
||||
return sendPingWithTraceId(Span.TRACE_ID_NAME, traceId);
|
||||
}
|
||||
|
||||
private MvcResult sendPingWithTraceId(String headerName, Long correlationId)
|
||||
throws Exception {
|
||||
return sendPingWithTraceId(headerName, correlationId, true);
|
||||
}
|
||||
|
||||
private MvcResult sendPingWithTraceId(String headerName, Long correlationId,
|
||||
boolean sampling) throws Exception {
|
||||
MockHttpServletRequestBuilder request = MockMvcRequestBuilders.get("/ping")
|
||||
.accept(MediaType.TEXT_PLAIN)
|
||||
.header(headerName, Span.toHex(correlationId))
|
||||
.header(Span.SPAN_ID_NAME, Span.toHex(new Random().nextLong()));
|
||||
if (!sampling) {
|
||||
request.header(Span.NOT_SAMPLED_NAME, "true");
|
||||
}
|
||||
return this.mockMvc.perform(request).andReturn();
|
||||
}
|
||||
|
||||
private Long tracingHeaderFrom(MvcResult mvcResult) {
|
||||
return Span.fromHex(mvcResult.getResponse().getHeader(Span.TRACE_ID_NAME));
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,12 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
|
||||
@Autowired
|
||||
TraceKeys traceKeys;
|
||||
|
||||
static Span span;
|
||||
|
||||
@RequestMapping("/ping")
|
||||
public String ping() {
|
||||
logger.info("ping");
|
||||
span = this.tracer.getCurrentSpan();
|
||||
return "ping";
|
||||
}
|
||||
|
||||
|
||||
@@ -72,8 +72,8 @@ public class TraceFilterTests {
|
||||
this.tracer = new DefaultTracer(new DelegateSampler(), new Random(),
|
||||
this.publisher) {
|
||||
@Override
|
||||
protected Span createSpan(Span saved, Span span) {
|
||||
TraceFilterTests.this.span = super.createSpan(saved, span);
|
||||
protected Span createSpan(Span span, Span saved) {
|
||||
TraceFilterTests.this.span = super.createSpan(span, saved);
|
||||
return TraceFilterTests.this.span;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
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.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -23,6 +29,7 @@ import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -32,12 +39,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 })
|
||||
@@ -70,6 +74,22 @@ public class FeignTraceTests {
|
||||
then(this.listener.getEvents()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldPropagateNotSamplingHeader() {
|
||||
// given
|
||||
Long currentTraceId = 1L;
|
||||
Long currentParentId = 2L;
|
||||
this.tracer.continueSpan(Span.builder().traceId(currentTraceId)
|
||||
.spanId(generatedId()).exportable(false).parent(currentParentId).build());
|
||||
// when
|
||||
ResponseEntity<Map<String, String>> response = this.testFeignInterface.headers();
|
||||
|
||||
// then
|
||||
then(response.getBody().get(Span.TRACE_ID_NAME)).isNotNull();
|
||||
then(response.getBody().get(Span.NOT_SAMPLED_NAME)).isNotNull();
|
||||
then(this.listener.getEvents()).isNotEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAttachTraceIdWhenUsingFeignClient() {
|
||||
// given
|
||||
@@ -82,7 +102,8 @@ public class FeignTraceTests {
|
||||
ResponseEntity<String> response = this.testFeignInterface.getTraceId();
|
||||
|
||||
// then
|
||||
then(Span.fromHex(getHeader(response, Span.TRACE_ID_NAME))).isEqualTo(currentTraceId);
|
||||
then(Span.fromHex(getHeader(response, Span.TRACE_ID_NAME)))
|
||||
.isEqualTo(currentTraceId);
|
||||
then(this.listener.getEvents().size()).isEqualTo(2);
|
||||
}
|
||||
|
||||
@@ -102,6 +123,9 @@ public class FeignTraceTests {
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/notrace")
|
||||
ResponseEntity<String> getNoTrace();
|
||||
|
||||
@RequestMapping(method = RequestMethod.GET, value = "/")
|
||||
ResponseEntity<Map<String, String>> headers();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -159,6 +183,16 @@ public class FeignTraceTests {
|
||||
then(spanId).isNotEmpty();
|
||||
return traceId;
|
||||
}
|
||||
|
||||
@RequestMapping("/")
|
||||
public Map<String, String> home(@RequestHeader HttpHeaders headers) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
for (String key : headers.keySet()) {
|
||||
map.put(key, headers.getFirst(key));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
||||
@@ -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;
|
||||
@@ -34,14 +42,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
|
||||
*
|
||||
@@ -82,6 +82,17 @@ public class TraceRestTemplateInterceptorTests {
|
||||
then(Long.valueOf(headers.get(Span.SPAN_ID_NAME))).isEqualTo(2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notSampledHeaderAddedWhenNotExportable() {
|
||||
this.traces.continueSpan(Span.builder().traceId(1L).spanId(2L).exportable(false).build());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> headers = this.template.getForEntity("/", Map.class)
|
||||
.getBody();
|
||||
then(Long.valueOf(headers.get(Span.TRACE_ID_NAME))).isEqualTo(1L);
|
||||
then(Long.valueOf(headers.get(Span.SPAN_ID_NAME))).isEqualTo(2L);
|
||||
then(headers.get(Span.NOT_SAMPLED_NAME)).isEqualTo("true");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headersNotAddedWhenNotTracing() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -96,7 +107,7 @@ public class TraceRestTemplateInterceptorTests {
|
||||
public Map<String, String> home(@RequestHeader HttpHeaders headers) {
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
addHeaders(map, headers, Span.SPAN_ID_NAME, Span.TRACE_ID_NAME,
|
||||
Span.PARENT_ID_NAME);
|
||||
Span.PARENT_ID_NAME, Span.NOT_SAMPLED_NAME);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ public class SleuthStreamAutoConfiguration {
|
||||
@Override
|
||||
public Message<?> preSend(Message<?> message, MessageChannel channel) {
|
||||
return MessageBuilder.fromMessage(message)
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "").build();
|
||||
.setHeader(Span.NOT_SAMPLED_NAME, "true").build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user