Merge branch 'master' into 2.0.x

This commit is contained in:
Adrian Cole
2017-10-27 22:33:34 +08:00
17 changed files with 148 additions and 260 deletions

View File

@@ -14,7 +14,9 @@
<name>spring-cloud-sleuth-dependencies</name>
<description>Spring Cloud Sleuth Dependencies</description>
<properties>
<zipkin.version>2.2.1</zipkin.version>
<!-- misaligned intentionally https://github.com/spring-projects/spring-boot/issues/10778-->
<zipkin.version>2.2.0</zipkin.version>
<zipkin2.version>2.2.1</zipkin2.version>
<zipkin-reporter.version>1.1.2</zipkin-reporter.version>
<zipkin-reporter2.version>2.1.3</zipkin-reporter2.version>
</properties>
@@ -73,7 +75,7 @@
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
<version>${zipkin.version}</version>
<version>${zipkin2.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>

View File

@@ -54,21 +54,11 @@
<artifactId>spring-cloud-sleuth-sample-test-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -68,7 +68,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -22,10 +22,10 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import org.springframework.context.annotation.Bean;
import zipkin.Span;
import zipkin2.Span;
import zipkin2.reporter.Reporter;
/**
* @author Spencer Gibb
@@ -43,8 +43,8 @@ public class SampleFeignApplication {
// Use this for debugging (or if there is no Zipkin server running on port 9411)
@Bean
@ConditionalOnProperty(value = "sample.zipkin.enabled", havingValue = "false")
public ZipkinSpanReporter spanCollector() {
return new ZipkinSpanReporter() {
public Reporter<Span> spanReporter() {
return new Reporter<Span>() {
@Override
public void report(Span span) {
logger.info(span);

View File

@@ -82,7 +82,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -20,16 +20,16 @@ import java.util.LinkedList;
import java.util.List;
import org.apache.commons.logging.Log;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import zipkin.Span;
import zipkin2.Span;
import zipkin2.reporter.Reporter;
/**
* Span Collector that logs spans and adds Spans to a list
*
* @author Marcin Grzejszczak
*/
public class IntegrationTestZipkinSpanReporter implements ZipkinSpanReporter {
public class IntegrationTestZipkinSpanReporter implements Reporter<Span> {
private static final Log log = org.apache.commons.logging.LogFactory
.getLog(IntegrationTestZipkinSpanReporter.class);

View File

@@ -15,12 +15,6 @@
*/
package integration;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Random;
@@ -31,7 +25,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
@@ -41,8 +34,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import integration.MessagingApplicationTests.IntegrationSpanCollectorConfig;
import sample.SampleMessagingApplication;
import tools.AbstractIntegrationTest;
import zipkin.Constants;
import zipkin.Span;
import zipkin2.Span;
import zipkin2.reporter.Reporter;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { IntegrationSpanCollectorConfig.class, SampleMessagingApplication.class },
@@ -82,7 +78,7 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
httpMessageWithTraceIdInHeadersIsSuccessfullySent(sampleAppUrl + "/", traceId, spanId).run()
);
await().atMost(5, SECONDS).untilAsserted(() -> {
await().atMost(10, SECONDS).untilAsserted(() -> {
thenAllSpansHaveTraceIdEqualTo(traceId);
thenTheSpansHaveProperParentStructure();
});
@@ -98,79 +94,75 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
await().atMost(5, SECONDS).untilAsserted(() -> {
thenAllSpansHaveTraceIdEqualTo(traceId);
thenThereIsAtLeastOneBinaryAnnotationWithKey("background-sleep-millis");
thenThereIsAtLeastOneTagWithKey("background-sleep-millis");
});
}
private void thenThereIsAtLeastOneBinaryAnnotationWithKey(String binaryAnnotationKey) {
then(new ArrayList<>(this.integrationTestSpanCollector.hashedSpans).stream()
.map(s -> s.binaryAnnotations)
.flatMap(Collection::stream)
.anyMatch(b -> b.key.equals(binaryAnnotationKey))).isTrue();
private void thenThereIsAtLeastOneTagWithKey(String key) {
then(this.integrationTestSpanCollector.hashedSpans.stream()
.map(Span::tags)
.flatMap(m -> m.keySet().stream())
.anyMatch(b -> b.equals(key))).isTrue();
}
private void thenAllSpansHaveTraceIdEqualTo(long traceId) {
then(new ArrayList<>(this.integrationTestSpanCollector.hashedSpans).stream()
.allMatch(span -> span.traceId == traceId)).describedAs("All spans have same trace id").isTrue();
String traceIdHex = Long.toHexString(traceId);
then(this.integrationTestSpanCollector.hashedSpans.stream()
.allMatch(span -> span.traceId().equals(traceIdHex))).describedAs("All spans have same trace id").isTrue();
}
private void thenTheSpansHaveProperParentStructure() {
Optional<Span> firstHttpSpan = findFirstHttpRequestSpan();
List<Span> eventSpans = findAllEventRelatedSpans();
Optional<Span> eventSentSpan = findSpanWithAnnotation(Constants.SERVER_SEND);
Optional<Span> eventReceivedSpan = findSpanWithAnnotation(Constants.CLIENT_RECV);
Optional<Span> eventSentSpan = findSpanWithKind(Span.Kind.SERVER);
Optional<Span> eventReceivedSpan = findSpanWithKind(Span.Kind.CLIENT);
Optional<Span> lastHttpSpansParent = findLastHttpSpansParent();
// "http:/parent/" -> "message:messages" -> "http:/foo" (CS + CR) -> "http:/foo" (SS)
ArrayList<Span> spans = new ArrayList<>(
this.integrationTestSpanCollector.hashedSpans);
Collections.sort(spans);
thenAllSpansArePresent(spans, firstHttpSpan, eventSpans, lastHttpSpansParent, eventSentSpan, eventReceivedSpan);
then(spans).as("There were 4 spans").hasSize(4);
thenAllSpansArePresent(firstHttpSpan, eventSpans, lastHttpSpansParent, eventSentSpan, eventReceivedSpan);
then(this.integrationTestSpanCollector.hashedSpans).as("There were 4 spans").hasSize(4);
log.info("Checking the parent child structure");
List<Optional<Span>> parentChild = spans.stream()
.filter(span -> span.parentId != null)
.map(span -> spans.stream().filter(span1 -> span1.id == span.parentId).findAny()
List<Optional<Span>> parentChild = this.integrationTestSpanCollector.hashedSpans.stream()
.filter(span -> span.parentId() != null)
.map(span -> this.integrationTestSpanCollector.hashedSpans.stream().filter(span1 -> span1.id().equals(span.parentId())).findAny()
).collect(Collectors.toList());
log.info("List of parents and children " + parentChild);
then(parentChild.stream().allMatch(Optional::isPresent)).isTrue();
}
private Optional<Span> findLastHttpSpansParent() {
return new ArrayList<>(this.integrationTestSpanCollector.hashedSpans).stream()
.filter(span -> "http:/foo".equals(span.name) && !span.annotations.isEmpty()).findFirst();
return this.integrationTestSpanCollector.hashedSpans.stream()
.filter(span -> "http:/foo".equals(span.name()) && span.kind() != null).findFirst();
}
private Optional<Span> findSpanWithAnnotation(String annotationName) {
return new ArrayList<>(this.integrationTestSpanCollector.hashedSpans).stream()
.filter(span -> span.annotations.stream()
.filter(annotation -> annotationName
.equals(annotation.value)).findFirst().isPresent())
private Optional<Span> findSpanWithKind(Span.Kind kind) {
return this.integrationTestSpanCollector.hashedSpans.stream()
.filter(span -> kind.equals(span.kind()))
.findFirst();
}
private List<Span> findAllEventRelatedSpans() {
return new ArrayList<>(this.integrationTestSpanCollector.hashedSpans).stream()
.filter(span -> "message:messages".equals(span.name) && span.parentId != null).collect(
return this.integrationTestSpanCollector.hashedSpans.stream()
.filter(span -> "message:messages".equals(span.name()) && span.parentId() != null).collect(
Collectors.toList());
}
private Optional<Span> findFirstHttpRequestSpan() {
return new ArrayList<>(this.integrationTestSpanCollector.hashedSpans).stream()
return this.integrationTestSpanCollector.hashedSpans.stream()
// home is the name of the method
.filter(span -> span.binaryAnnotations.stream()
.anyMatch(binaryAnnotation -> new String(binaryAnnotation.value).equals("home"))).findFirst();
.filter(span -> span.tags().values().stream()
.anyMatch("home"::equals)).findFirst();
}
private void thenAllSpansArePresent(ArrayList<Span> spans, Optional<Span> firstHttpSpan,
List<Span> eventSpans, Optional<Span> lastHttpSpan, Optional<Span> eventSentSpan,
Optional<Span> eventReceivedSpan) {
private void thenAllSpansArePresent(Optional<Span> firstHttpSpan,
List<Span> eventSpans, Optional<Span> lastHttpSpan,
Optional<Span> eventSentSpan, Optional<Span> eventReceivedSpan) {
log.info("Found following spans");
log.info("First http span " + firstHttpSpan);
log.info("Event spans " + eventSpans);
log.info("Event sent span " + eventSentSpan);
log.info("Event received span " + eventReceivedSpan);
log.info("Last http span " + lastHttpSpan);
log.info("All found spans \n" + spans
log.info("All found spans \n" + this.integrationTestSpanCollector.hashedSpans
.stream().map(Span::toString).collect(Collectors.joining("\n")));
then(firstHttpSpan.isPresent()).isTrue();
then(eventSpans).isNotEmpty();
@@ -182,9 +174,8 @@ public class MessagingApplicationTests extends AbstractIntegrationTest {
@Configuration
public static class IntegrationSpanCollectorConfig {
@Bean
ZipkinSpanReporter integrationTestZipkinSpanReporter() {
Reporter<Span> integrationTestZipkinSpanReporter() {
return new IntegrationTestZipkinSpanReporter();
}
}
}

View File

@@ -70,7 +70,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -22,12 +22,12 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
import org.springframework.cloud.sleuth.zipkin.ZipkinSpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.client.RestTemplate;
import zipkin.Span;
import zipkin2.Span;
import zipkin2.reporter.Reporter;
/**
* @author Spencer Gibb
@@ -51,8 +51,8 @@ public class SampleRibbonApplication {
// Use this for debugging (or if there is no Zipkin server running on port 9411)
@Bean
@ConditionalOnProperty(value = "sample.zipkin.enabled", havingValue = "false")
public ZipkinSpanReporter spanCollector() {
return new ZipkinSpanReporter() {
public Reporter<Span> spanReporter() {
return new Reporter<Span>() {
@Override
public void report(Span span) {
logger.info(span);

View File

@@ -106,7 +106,7 @@
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
</dependency>
</dependencies>

View File

@@ -16,14 +16,6 @@
package tools;
import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.awaitility.Awaitility;
@@ -31,17 +23,9 @@ import org.awaitility.core.ConditionFactory;
import org.junit.After;
import org.junit.Before;
import org.springframework.cloud.sleuth.trace.IntegrationTestSpanContextHolder;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import zipkin.Codec;
import zipkin.Span;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
@@ -64,57 +48,6 @@ public abstract class AbstractIntegrationTest {
IntegrationTestSpanContextHolder.removeCurrentSpan();
}
public static ConditionFactory await() {
return Awaitility.await().pollInterval(POLL_INTERVAL, SECONDS).atMost(TIMEOUT, SECONDS);
}
protected Runnable zipkinServerIsUp() {
return checkServerHealth(this::endpointToCheckZipkinServerHealth);
}
protected Runnable checkServerHealth(RequestExchanger requestExchanger) {
return () -> {
ResponseEntity<String> response = requestExchanger.exchange();
log.info(String.format("Response from the [%s] health endpoint is [%s]", "Zipkin Stream Server", response));
then(response.getStatusCode()).isEqualTo(HttpStatus.OK);
log.info(String.format("[%s] is up!", "Zipkin Stream Server"));
};
}
private interface RequestExchanger {
ResponseEntity<String> exchange();
}
protected ResponseEntity<String> endpointToCheckZipkinServerHealth() {
URI uri = URI.create("http://localhost:" + getZipkinServerPort() + "/zipkin/");
log.info(String.format("Sending request to the Zipkin Server [%s]", uri));
return exchangeRequest(uri);
}
protected int getZipkinServerPort() {
return 9411;
}
protected ResponseEntity<String> checkStateOfTheTraceId(long traceId) {
URI uri = URI.create(getZipkinTraceQueryUrl() + Long.toHexString(traceId));
log.info(String.format("Sending request to the Zipkin query service [%s]. "
+ "Checking presence of trace id [%d]", uri, traceId));
return exchangeRequest(uri);
}
protected ResponseEntity<String> exchangeRequest(URI uri) {
return this.restTemplate.exchange(
new RequestEntity<>(new HttpHeaders(), HttpMethod.GET, uri), String.class
);
}
protected String getZipkinTraceQueryUrl() {
return "http://localhost:"+getZipkinServerPort()+"/api/v1/trace/";
}
protected String getZipkinServicesQueryUrl() {
return "http://localhost:"+getZipkinServerPort()+"/api/v1/services";
}
protected Runnable httpMessageWithTraceIdInHeadersIsSuccessfullySent(String endpoint, long traceId) {
return new RequestSendingRunnable(this.restTemplate, endpoint, traceId, null);
@@ -124,68 +57,7 @@ public abstract class AbstractIntegrationTest {
return new RequestSendingRunnable(this.restTemplate, endpoint, traceId, spanId);
}
protected Runnable allSpansWereRegisteredInZipkinWithTraceIdEqualTo(long traceId) {
return () -> {
ResponseEntity<String> response = checkStateOfTheTraceId(traceId);
log.info(String.format("Response from the Zipkin query service about the "
+ "trace id [%s] for trace with id [%d]", response, traceId));
then(response.getStatusCode()).isEqualTo(HttpStatus.OK);
then(response.hasBody()).isTrue();
List<Span> spans = Codec.JSON.readSpans(response.getBody().getBytes());
List<String> serviceNamesNotFoundInZipkin = serviceNamesNotFoundInZipkin(spans);
List<String> spanNamesNotFoundInZipkin = annotationsNotFoundInZipkin(spans);
log.info(String.format("The following services were not found in Zipkin [%s]", serviceNamesNotFoundInZipkin));
log.info(String.format("The following annotations were not found in Zipkin [%s]", spanNamesNotFoundInZipkin));
then(serviceNamesNotFoundInZipkin).isEmpty();
then(spanNamesNotFoundInZipkin).isEmpty();
log.info("Zipkin tracing is working! Sleuth is working! Let's be happy!");
};
public static ConditionFactory await() {
return Awaitility.await().pollInterval(POLL_INTERVAL, SECONDS).atMost(TIMEOUT, SECONDS);
}
protected List<String> serviceNamesNotFoundInZipkin(List<zipkin.Span> spans) {
List<String> serviceNamesFoundInAnnotations = spans.stream()
.filter(span -> span.annotations != null)
.map(span -> span.annotations)
.flatMap(Collection::stream)
.filter(span -> span.endpoint != null)
.map(annotation -> annotation.endpoint)
.map(endpoint -> endpoint.serviceName)
.distinct()
.collect(Collectors.toList());
List<String> serviceNamesFoundInBinaryAnnotations = spans.stream()
.filter(span -> span.binaryAnnotations != null)
.map(span -> span.binaryAnnotations)
.flatMap(Collection::stream)
.filter(span -> span.endpoint != null)
.map(annotation -> annotation.endpoint)
.map(endpoint -> endpoint.serviceName)
.distinct()
.collect(Collectors.toList());
List<String> names = new ArrayList<>();
names.addAll(serviceNamesFoundInAnnotations);
names.addAll(serviceNamesFoundInBinaryAnnotations);
return names.contains(getAppName()) ? Collections.emptyList() : names;
}
protected String getAppName() {
return "unknown";
}
protected List<String> annotationsNotFoundInZipkin(List<zipkin.Span> spans) {
String binaryAnnotationName = getRequiredBinaryAnnotationName();
Optional<String> names = spans.stream()
.filter(span -> span.binaryAnnotations != null)
.map(span -> span.binaryAnnotations)
.flatMap(Collection::stream)
.filter(span -> span.endpoint != null)
.map(annotation -> annotation.key)
.filter(binaryAnnotationName::equals)
.findFirst();
return names.isPresent() ? Collections.emptyList() : Collections.singletonList(binaryAnnotationName);
}
protected String getRequiredBinaryAnnotationName() {
return "random-sleep-millis";
}
}

View File

@@ -82,7 +82,7 @@
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-sleuth-zipkin</artifactId>
<artifactId>spring-cloud-sleuth-zipkin2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@@ -97,20 +97,9 @@
<artifactId>spring-cloud-sleuth-sample-test-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-junit</artifactId>
<scope>test</scope>
</dependency>
<!-- otherwise spring boot's version of okhttp kicks zipkin-junit's deps out of alignment -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<artifactId>mockwebserver</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>

View File

@@ -16,10 +16,17 @@
package integration;
import integration.ZipkinTests.WaitUntilZipkinIsUpConfig;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import sample.SampleZipkinApplication;
import tools.AbstractIntegrationTest;
import zipkin.junit.ZipkinRule;
import zipkin.server.EnableZipkinServer;
import java.net.URI;
import java.util.Random;
@@ -29,8 +36,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.context.annotation.Bean;
@@ -38,16 +43,18 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import zipkin2.Span;
import zipkin2.codec.SpanBytesDecoder;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = { WaitUntilZipkinIsUpConfig.class, SampleZipkinApplication.class },
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource(properties = {"sample.zipkin.enabled=true"})
public class ZipkinTests extends AbstractIntegrationTest {
@ClassRule public static final ZipkinRule zipkin = new ZipkinRule();
@ClassRule public static final MockWebServer zipkin = new MockWebServer();
private static final String APP_NAME = "testsleuthzipkin";
@Value("${local.server.port}")
@@ -55,7 +62,7 @@ public class ZipkinTests extends AbstractIntegrationTest {
private String sampleAppUrl = "http://localhost:" + this.port;
@Autowired ZipkinProperties zipkinProperties;
@Override protected int getZipkinServerPort() {
int getZipkinServerPort() {
return getPortFromProps();
}
@@ -64,21 +71,20 @@ public class ZipkinTests extends AbstractIntegrationTest {
}
@Test
public void should_propagate_spans_to_zipkin() {
public void should_propagate_spans_to_zipkin() throws Exception {
zipkin.enqueue(new MockResponse());
long traceId = new Random().nextLong();
await().atMost(10, SECONDS).untilAsserted(() ->
httpMessageWithTraceIdInHeadersIsSuccessfullySent(
this.sampleAppUrl + "/hi2", traceId).run()
this.sampleAppUrl + "/hi2", traceId).run()
);
await().atMost(10, SECONDS).untilAsserted(() ->
allSpansWereRegisteredInZipkinWithTraceIdEqualTo(traceId).run()
);
spansSentToZipkin(zipkin, traceId);
}
@Override
protected String getAppName() {
String getAppName() {
return APP_NAME;
}
@@ -89,16 +95,63 @@ public class ZipkinTests extends AbstractIntegrationTest {
@Primary
ZipkinProperties testZipkinProperties() {
ZipkinProperties zipkinProperties = new ZipkinProperties();
zipkinProperties.setBaseUrl(zipkin.httpUrl());
zipkinProperties.setBaseUrl(zipkin.url("/").toString());
return zipkinProperties;
}
}
@SpringBootApplication
@EnableZipkinServer
protected static class ZipkinServer {
public static void main(String[] args) {
SpringApplication.run(ZipkinServer.class, args);
}
void spansSentToZipkin(MockWebServer zipkin, long traceId)
throws InterruptedException {
RecordedRequest request = zipkin.takeRequest();
List<Span> spans = SpanBytesDecoder.JSON_V2.decodeList(request.getBody().readByteArray());
List<String> traceIdsNotFoundInZipkin = traceIdsNotFoundInZipkin(spans, traceId);
List<String> serviceNamesNotFoundInZipkin = serviceNamesNotFoundInZipkin(spans);
List<String> tagsNotFoundInZipkin = hasRequiredTag(spans);
log.info(String.format("The following trace IDs were not found in Zipkin [%s]", traceIdsNotFoundInZipkin));
log.info(String.format("The following services were not found in Zipkin [%s]", serviceNamesNotFoundInZipkin));
log.info(String.format("The following tags were not found in Zipkin [%s]", tagsNotFoundInZipkin));
then(traceIdsNotFoundInZipkin).isEmpty();
then(serviceNamesNotFoundInZipkin).isEmpty();
then(tagsNotFoundInZipkin).isEmpty();
log.info("Zipkin tracing is working! Sleuth is working! Let's be happy!");
}
List<String> traceIdsNotFoundInZipkin(List<Span> spans, long traceId) {
String traceIdString = Long.toHexString(traceId);
Optional<String> traceIds = spans.stream()
.map(Span::traceId)
.filter(traceIdString::equals)
.findFirst();
return traceIds.isPresent() ? Collections.emptyList() : Collections.singletonList(traceIdString);
}
List<String> serviceNamesNotFoundInZipkin(List<Span> spans) {
List<String> localServiceNames = spans.stream()
.map(Span::localServiceName)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
List<String> remoteServiceNames = spans.stream()
.map(Span::remoteServiceName)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
List<String> names = new ArrayList<>();
names.addAll(localServiceNames);
names.addAll(remoteServiceNames);
return names.contains(getAppName()) ? Collections.emptyList() : names;
}
List<String> hasRequiredTag(List<Span> spans) {
String key = getRequiredTagKey();
Optional<String> keys = spans.stream()
.flatMap(span -> span.tags().keySet().stream())
.filter(key::equals)
.findFirst();
return keys.isPresent() ? Collections.emptyList() : Collections.singletonList(key);
}
String getRequiredTagKey() {
return "random-sleep-millis";
}
}

View File

@@ -79,6 +79,10 @@
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter</groupId>
<artifactId>zipkin-reporter</artifactId>

View File

@@ -111,23 +111,11 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<!-- otherwise spring boot's version of okhttp kicks zipkin-junit's deps out of alignment -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>mockwebserver</artifactId>

View File

@@ -1,9 +1,12 @@
package org.springframework.cloud.sleuth.zipkin2;
import static org.assertj.core.api.BDDAssertions.then;
import java.io.IOException;
import java.net.URI;
import java.util.Map;
import okhttp3.mockwebserver.MockWebServer;
import org.awaitility.Awaitility;
import org.junit.ClassRule;
import org.junit.Test;
@@ -19,19 +22,15 @@ import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.junit4.SpringRunner;
import zipkin.junit.ZipkinRule;
import static org.assertj.core.api.BDDAssertions.then;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class,
properties = {"spring.zipkin.baseUrl=http://zipkin/",
"spring.cloud.discovery.client.composite-indicator.enabled=false",
@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class, properties = {
"spring.zipkin.baseUrl=http://zipkin/",
"spring.zipkin.sender.type=web" // override default priority which picks rabbit due to classpath
})
public class ZipkinDiscoveryClientTests {
@ClassRule public static ZipkinRule ZIPKIN_RULE = new ZipkinRule();
@ClassRule public static MockWebServer ZIPKIN_RULE = new MockWebServer();
@Autowired SpanReporter spanReporter;
@@ -42,7 +41,7 @@ public class ZipkinDiscoveryClientTests {
this.spanReporter.report(span);
Awaitility.await().untilAsserted(() -> then(ZIPKIN_RULE.httpRequestCount()).isGreaterThan(0));
Awaitility.await().untilAsserted(() -> then(ZIPKIN_RULE.getRequestCount()).isGreaterThan(0));
}
@Configuration
@@ -81,7 +80,7 @@ public class ZipkinDiscoveryClientTests {
@Override
public int getPort() {
return URI.create(ZIPKIN_RULE.httpUrl()).getPort();
return ZIPKIN_RULE.url("/").port();
}
@Override
@@ -91,7 +90,7 @@ public class ZipkinDiscoveryClientTests {
@Override
public URI getUri() {
return URI.create(ZIPKIN_RULE.httpUrl());
return ZIPKIN_RULE.url("/").uri();
}
@Override
@@ -103,4 +102,4 @@ public class ZipkinDiscoveryClientTests {
};
}
}
}
}