Merge branch 'master' into 2.0.x
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.zipkin.java</groupId>
|
||||
<groupId>io.zipkin.zipkin2</groupId>
|
||||
<artifactId>zipkin</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 {
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user