[#127] Added tests for parent-id propagation

fixes #127
This commit is contained in:
Marcin Grzejszczak
2016-02-22 12:03:16 +01:00
parent 7dfe93e02a
commit 08c99667c9
18 changed files with 75 additions and 60 deletions

View File

@@ -18,7 +18,6 @@ package org.springframework.cloud.sleuth;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -50,8 +49,6 @@ public class Span {
public static final String TRACE_ID_NAME = "X-Trace-Id";
public static final String SPAN_NAME_NAME = "X-Span-Name";
public static final String SPAN_ID_NAME = "X-Span-Id";
public static final List<String> HEADERS = Arrays.asList(SPAN_ID_NAME, TRACE_ID_NAME,
SPAN_NAME_NAME, PARENT_ID_NAME, PROCESS_ID_NAME, NOT_SAMPLED_NAME);
public static final String SPAN_EXPORT_NAME = "X-Span-Export";
public static final String SPAN_LOCAL_COMPONENT_TAG_NAME = "lc";
@@ -276,21 +273,21 @@ public class Span {
/**
* Represents given long id as hex string
*/
public static String toHex(long id) {
public static String idToHex(long id) {
return Long.toHexString(id);
}
/**
* Represents hex string as long
*/
public static long fromHex(String hexString) {
public static long hexToId(String hexString) {
Assert.hasText(hexString, "Can't convert empty hex string to long");
return new BigInteger(hexString, 16).longValue();
}
@Override
public String toString() {
return "[Trace: " + toHex(this.traceId) + ", Span: " + toHex(this.spanId) + ", exportable=" + this.exportable + "]";
return "[Trace: " + idToHex(this.traceId) + ", Span: " + idToHex(this.spanId) + ", exportable=" + this.exportable + "]";
}
@Override

View File

@@ -53,9 +53,9 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
return null; // cannot build a span without ids
}
long spanId = hasHeader(message, Span.SPAN_ID_NAME)
? Span.fromHex(getHeader(message, Span.SPAN_ID_NAME))
? Span.hexToId(getHeader(message, Span.SPAN_ID_NAME))
: this.random.nextLong();
long traceId = Span.fromHex(getHeader(message, Span.TRACE_ID_NAME));
long traceId = Span.hexToId(getHeader(message, Span.TRACE_ID_NAME));
Span.SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
if (message.getHeaders().containsKey(Span.NOT_SAMPLED_NAME)) {
span.exportable(false);
@@ -70,7 +70,7 @@ abstract class AbstractTraceChannelInterceptor extends ChannelInterceptorAdapter
span.processId(processId);
}
if (parentId != null) {
span.parent(Span.fromHex(parentId));
span.parent(Span.hexToId(parentId));
}
span.remote(true);
return span.build();

View File

@@ -64,14 +64,14 @@ public class SpanMessageHeaders {
}
Map<String, String> headers = new HashMap<>();
addHeader(headers, Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
addHeader(headers, Span.SPAN_ID_NAME, Span.toHex(span.getSpanId()));
addHeader(headers, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
addHeader(headers, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
if (span.isExportable()) {
addAnnotations(traceKeys, message, span);
Long parentId = getFirst(span.getParents());
if (parentId != null) {
addHeader(headers, Span.PARENT_ID_NAME, Span.toHex(parentId));
addHeader(headers, Span.PARENT_ID_NAME, Span.idToHex(parentId));
}
addHeader(headers, Span.SPAN_NAME_NAME, span.getName());
addHeader(headers, Span.PROCESS_ID_NAME, span.getProcessId());

View File

@@ -121,9 +121,9 @@ public class TraceFilter extends OncePerRequestFilter
if (spanFromRequest == null) {
if (hasHeader(request, response, Span.TRACE_ID_NAME)) {
long traceId = Span
.fromHex(getHeader(request, response, Span.TRACE_ID_NAME));
.hexToId(getHeader(request, response, Span.TRACE_ID_NAME));
long spanId = hasHeader(request, response, Span.SPAN_ID_NAME)
? Span.fromHex(getHeader(request, response, Span.SPAN_ID_NAME))
? Span.hexToId(getHeader(request, response, Span.SPAN_ID_NAME))
: this.random.nextLong();
SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
@@ -143,7 +143,7 @@ public class TraceFilter extends OncePerRequestFilter
}
if (hasHeader(request, response, Span.PARENT_ID_NAME)) {
span.parent(Span
.fromHex(getHeader(request, response, Span.PARENT_ID_NAME)));
.hexToId(getHeader(request, response, Span.PARENT_ID_NAME)));
}
span.remote(true);
@@ -200,8 +200,8 @@ public class TraceFilter extends OncePerRequestFilter
private void addResponseHeaders(HttpServletResponse response, Span span) {
if (span != null) {
response.addHeader(Span.SPAN_ID_NAME, Span.toHex(span.getSpanId()));
response.addHeader(Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
response.addHeader(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
response.addHeader(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
}
}

View File

@@ -75,7 +75,7 @@ abstract class AbstractTraceHttpRequestInterceptor
private void setIdHeader(HttpRequest request, String name, Long value) {
if (value != null) {
setHeader(request, name, Span.toHex(value));
setHeader(request, name, Span.idToHex(value));
}
}

View File

@@ -126,15 +126,15 @@ public class TraceFeignClientAutoConfiguration {
setHeader(template, Span.NOT_SAMPLED_NAME, "true");
return;
}
template.header(Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
template.header(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
setHeader(template, Span.SPAN_NAME_NAME, span.getName());
setHeader(template, Span.SPAN_ID_NAME, Span.toHex(span.getSpanId()));
setHeader(template, Span.SPAN_ID_NAME, Span.idToHex(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));
setHeader(template, Span.PARENT_ID_NAME, Span.idToHex(parentId));
}
setHeader(template, Span.PROCESS_ID_NAME, span.getProcessId());
publish(new ClientSentEvent(this, span));
@@ -185,7 +185,7 @@ public class TraceFeignClientAutoConfiguration {
public void setHeader(Map<String, Collection<String>> headers, String name,
Long value) {
if (value != null) {
setHeader(headers, name, Span.toHex(value));
setHeader(headers, name, Span.idToHex(value));
}
}

View File

@@ -100,7 +100,7 @@ public class TracePreZuulFilter extends ZuulFilter
public void setHeader(Map<String, String> request, String name, Long value) {
if (value != null) {
setHeader(request, name, Span.toHex(value));
setHeader(request, name, Span.idToHex(value));
}
}

View File

@@ -101,11 +101,11 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
setHeader(requestBuilder, Span.NOT_SAMPLED_NAME, "true");
return;
}
setHeader(requestBuilder, Span.TRACE_ID_NAME, Span.toHex(span.getTraceId()));
setHeader(requestBuilder, Span.SPAN_ID_NAME, Span.toHex(span.getSpanId()));
setHeader(requestBuilder, Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
setHeader(requestBuilder, Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
setHeader(requestBuilder, Span.SPAN_NAME_NAME, span.getName());
setHeader(requestBuilder, Span.PARENT_ID_NAME,
Span.toHex(getParentId(span)));
Span.idToHex(getParentId(span)));
setHeader(requestBuilder, Span.PROCESS_ID_NAME,
span.getProcessId());
publish(new ClientSentEvent(this, span));
@@ -129,7 +129,7 @@ public class TraceRestClientRibbonCommandFactory extends RestClientRibbonCommand
}
public void setHeader(HttpRequest.Builder builder, String name, Long value) {
setHeader(builder, name, Span.toHex(value));
setHeader(builder, name, Span.idToHex(value));
}
private Span getCurrentSpan() {

View File

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

View File

@@ -36,7 +36,7 @@ public class SpanTest {
public void should_convert_long_to_hex_string() throws Exception {
long someLong = 123123L;
String hexString = Span.toHex(someLong);
String hexString = Span.idToHex(someLong);
then(hexString).isEqualTo("1e0f3");
}
@@ -45,14 +45,14 @@ public class SpanTest {
public void should_convert_hex_string_to_long() throws Exception {
String hexString = "1e0f3";
long someLong = Span.fromHex(hexString);
long someLong = Span.hexToId(hexString);
then(someLong).isEqualTo(123123L);
}
@Test(expected = IllegalArgumentException.class)
public void should_throw_exception_when_null_string_is_to_be_converted_to_long() throws Exception {
Span.fromHex(null);
Span.hexToId(null);
}
@Test(expected = UnsupportedOperationException.class)

View File

@@ -110,14 +110,14 @@ public class TraceChannelInterceptorTests implements MessageHandler {
@Test
public void parentSpanIncluded() {
this.channel.send(MessageBuilder.withPayload("hi")
.setHeader(Span.TRACE_ID_NAME, Span.toHex(10L))
.setHeader(Span.SPAN_ID_NAME, Span.toHex(20L)).build());
.setHeader(Span.TRACE_ID_NAME, Span.idToHex(10L))
.setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build());
assertNotNull("message was null", this.message);
String spanId = this.message.getHeaders().get(Span.SPAN_ID_NAME, String.class);
assertNotNull("spanId was null", spanId);
long traceId = Span
.fromHex(this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
.hexToId(this.message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
then(traceId).isEqualTo(10L);
then(spanId).isNotEqualTo(20L);
assertEquals(1, this.app.events.size());

View File

@@ -38,6 +38,7 @@ import org.springframework.messaging.PollableChannel;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
@@ -75,12 +76,17 @@ public class TraceContextPropagationChannelInterceptorTests {
assertNotNull("message was null", message);
Long spanId = Span
.fromHex(message.getHeaders().get(Span.SPAN_ID_NAME, String.class));
.hexToId(message.getHeaders().get(Span.SPAN_ID_NAME, String.class));
assertNotEquals("spanId was equal to parent's id", expectedSpanId, spanId);
long traceId = Span
.fromHex(message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
.hexToId(message.getHeaders().get(Span.TRACE_ID_NAME, String.class));
assertNotNull("traceId was null", traceId);
Long parentId = Span
.hexToId(message.getHeaders().get(Span.PARENT_ID_NAME, String.class));
assertEquals("parentId was not equal to parent's id", expectedSpanId, parentId);
}
@Configuration

View File

@@ -95,8 +95,8 @@ public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegra
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()));
.header(headerName, Span.idToHex(correlationId))
.header(Span.SPAN_ID_NAME, Span.idToHex(new Random().nextLong()));
if (!sampling) {
request.header(Span.NOT_SAMPLED_NAME, "true");
}
@@ -104,6 +104,6 @@ public class TraceFilterAlwaysSamplerIntegrationTests extends AbstractMvcIntegra
}
private Long tracingHeaderFrom(MvcResult mvcResult) {
return Span.fromHex(mvcResult.getResponse().getHeader(Span.TRACE_ID_NAME));
return Span.hexToId(mvcResult.getResponse().getHeader(Span.TRACE_ID_NAME));
}
}

View File

@@ -111,12 +111,12 @@ public class TraceFilterIntegrationTests extends AbstractMvcIntegrationTest {
Long correlationId) throws Exception {
return this.mockMvc
.perform(MockMvcRequestBuilders.get(path).accept(MediaType.TEXT_PLAIN)
.header(headerName, Span.toHex(correlationId))
.header(Span.SPAN_ID_NAME, Span.toHex(new Random().nextLong())))
.header(headerName, Span.idToHex(correlationId))
.header(Span.SPAN_ID_NAME, Span.idToHex(new Random().nextLong())))
.andReturn();
}
private Long tracingHeaderFrom(MvcResult mvcResult) {
return Span.fromHex(mvcResult.getResponse().getHeader(Span.TRACE_ID_NAME));
return Span.hexToId(mvcResult.getResponse().getHeader(Span.TRACE_ID_NAME));
}
}

View File

@@ -109,6 +109,22 @@ public class TraceFilterTests {
assertNull(TestSpanContextHolder.getCurrentSpan());
}
@Test
public void startsNewTraceWithParentIdInHeaders() throws Exception {
this.request = builder()
.header(Span.SPAN_ID_NAME, Span.idToHex(1L))
.header(Span.TRACE_ID_NAME, Span.idToHex(2L))
.header(Span.PARENT_ID_NAME, Span.idToHex(3L))
.buildRequest(new MockServletContext());
TraceFilter filter = new TraceFilter(this.tracer, this.traceKeys);
filter.doFilter(this.request, this.response, this.filterChain);
// this creates a child span which is why we'd expect the parents to include 1L)
assertThat(this.span.getParents()).containsOnly(1L);
assertNull(TestSpanContextHolder.getCurrentSpan());
}
@Test
public void continuesSpanInRequestAttr() throws Exception {
Span span = this.tracer.startTrace("http:foo");

View File

@@ -1,7 +1,5 @@
package org.springframework.cloud.sleuth.instrument.web.client;
import static org.assertj.core.api.BDDAssertions.then;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -43,6 +41,8 @@ import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = { FeignTraceTests.TestConfiguration.class })
@WebIntegrationTest(value = { "spring.application.name=fooservice" }, randomPort = true)
@@ -66,25 +66,21 @@ public class FeignTraceTests {
@Test
public void shouldCreateANewSpanWhenNoPreviousTracingWasPresent() {
// when
ResponseEntity<String> response = this.testFeignInterface.getNoTrace();
// then
then(getHeader(response, Span.TRACE_ID_NAME)).isNotNull();
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();
@@ -92,18 +88,17 @@ public class FeignTraceTests {
@Test
public void shouldAttachTraceIdWhenUsingFeignClient() {
// given
Long currentTraceId = 1L;
Long currentParentId = 2L;
this.tracer.continueSpan(Span.builder().traceId(currentTraceId)
.spanId(generatedId()).parent(currentParentId).build());
// when
ResponseEntity<String> response = this.testFeignInterface.getTraceId();
// then
then(Span.fromHex(getHeader(response, Span.TRACE_ID_NAME)))
then(Span.hexToId(getHeader(response, Span.TRACE_ID_NAME)))
.isEqualTo(currentTraceId);
then(Span.hexToId(getHeader(response, Span.PARENT_ID_NAME)))
.isEqualTo(currentParentId);
then(this.listener.getEvents().size()).isEqualTo(2);
}

View File

@@ -76,12 +76,13 @@ public class TraceRestTemplateInterceptorTests {
@Test
public void headersAddedWhenTracing() {
this.traces.continueSpan(Span.builder().traceId(1L).spanId(2L).build());
this.traces.continueSpan(Span.builder().traceId(1L).spanId(2L).parent(3L).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(Long.valueOf(headers.get(Span.PARENT_ID_NAME))).isEqualTo(3L);
}
@Test

View File

@@ -65,8 +65,8 @@ public class RequestSendingRunnable implements Runnable {
private RequestEntity<Void> requestWithTraceId() {
HttpHeaders headers = new HttpHeaders();
headers.add(Span.TRACE_ID_NAME, Span.toHex(this.traceId));
headers.add(Span.SPAN_ID_NAME, Span.toHex(this.spanId));
headers.add(Span.TRACE_ID_NAME, Span.idToHex(this.traceId));
headers.add(Span.SPAN_ID_NAME, Span.idToHex(this.spanId));
URI uri = URI.create(this.url);
RequestEntity<Void> requestEntity = new RequestEntity<>(headers, HttpMethod.GET, uri);
log.info("Request [" + requestEntity + "] is ready");