diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java index c508c59c6..5552aea70 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java @@ -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 diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/integration/SpanMessageHeaders.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/integration/SpanMessageHeaders.java index a431c8a4d..4bd471632 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/integration/SpanMessageHeaders.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/integration/SpanMessageHeaders.java @@ -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(); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java index fc891aaba..7baa373a0 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/TraceFilter.java @@ -166,9 +166,6 @@ public class TraceFilter extends OncePerRequestFilter request.setAttribute(TRACE_REQUEST_ATTR, spanFromRequest); } } - else { - this.tracer.continueSpan(spanFromRequest); - } Throwable exception = null; try { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java index c0ec51f15..de14de94b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceFeignClientAutoConfiguration.java @@ -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()); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java index 078d5acfb..0f2b4c648 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptor.java @@ -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); } } diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java index 1f16b0c70..45f5e4118 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TracePreZuulFilter.java @@ -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 { diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java index 20a379102..29537849b 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/zuul/TraceRestClientRibbonCommandFactory.java @@ -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())); diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java index d549f74f5..e780f0a06 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java @@ -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(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceManagerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceManagerTests.java index 7ccfd59eb..e2ec06156 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceManagerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/DefaultTraceManagerTests.java @@ -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 captor = ArgumentCaptor .forClass(ApplicationEvent.class); - verify(publisher, atLeast(NUM_SPANS)).publishEvent(captor.capture()); + verify(this.publisher, atLeast(NUM_SPANS)).publishEvent(captor.capture()); List 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 spans, Long parentId, String name) { List found = findSpans(spans, parentId); assertThat("more than one span with parentId " + parentId, found.size(), is(1)); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java index 5a5409921..07f8c8598 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/DefaultTestAutoConfiguration.java @@ -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 { } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/integration/TraceChannelInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/integration/TraceChannelInterceptorTests.java index 2deaf5669..407e1cf98 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/integration/TraceChannelInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/integration/TraceChannelInterceptorTests.java @@ -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); diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java new file mode 100644 index 000000000..6ef3aa35b --- /dev/null +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterAlwaysSamplerIntegrationTests.java @@ -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)); + } +} diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java index 8d0db6b5c..189c515fe 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterIntegrationTests.java @@ -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"; } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java index 9346e4ecd..6655a23ae 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/TraceFilterTests.java @@ -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; } }; diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/FeignTraceTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/FeignTraceTests.java index e065b2c48..e5eac4ee6 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/FeignTraceTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/FeignTraceTests.java @@ -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> 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 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 getNoTrace(); + + @RequestMapping(method = RequestMethod.GET, value = "/") + ResponseEntity> headers(); } @Configuration @@ -159,6 +183,16 @@ public class FeignTraceTests { then(spanId).isNotEmpty(); return traceId; } + + @RequestMapping("/") + public Map home(@RequestHeader HttpHeaders headers) { + Map map = new HashMap(); + for (String key : headers.keySet()) { + map.put(key, headers.getFirst(key)); + } + return map; + } + } @Configuration diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java index 1c9c8c19a..864b1e0d4 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/web/client/TraceRestTemplateInterceptorTests.java @@ -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 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 home(@RequestHeader HttpHeaders headers) { Map map = new HashMap(); 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; } diff --git a/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java b/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java index 0c6b1a65c..fafa8d8fe 100644 --- a/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java +++ b/spring-cloud-sleuth-stream/src/main/java/org/springframework/cloud/sleuth/stream/SleuthStreamAutoConfiguration.java @@ -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