diff --git a/pom.xml b/pom.xml index 519ed27ad..cb4aa5ab7 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,6 @@ spring-cloud-sleuth-zipkin spring-cloud-sleuth-zipkin2 spring-cloud-sleuth-stream - spring-cloud-sleuth-zipkin-stream spring-cloud-starter-sleuth spring-cloud-starter-zipkin spring-cloud-starter-zipkin2 diff --git a/spring-cloud-sleuth-samples/README.adoc b/spring-cloud-sleuth-samples/README.adoc index bc1e918e8..2fdf2c483 100644 --- a/spring-cloud-sleuth-samples/README.adoc +++ b/spring-cloud-sleuth-samples/README.adoc @@ -26,3 +26,4 @@ NOTE: You can see the zipkin spans without the UI (in logs) if you run the sampl image::{github-raw}/docs/src/main/asciidoc/images/zipkin-trace-screenshot.png[Sample Zipkin Screenshot] > The fact that the first trace in says "testSleuthMessaging" seems to be a bug in the UI (it has some annotations from that service, but it originates in the "testSleuthRibbon" service). + diff --git a/spring-cloud-sleuth-zipkin-stream/README.md b/spring-cloud-sleuth-zipkin-stream/README.md deleted file mode 100644 index 5cf51e74c..000000000 --- a/spring-cloud-sleuth-zipkin-stream/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Running Zipkin Server - -There are 3 parts to Zipkin: the instrumented client apps, the backend database and the Zipkin server. The database for this implementation is MySQL. - -> There is a running instance on PWS: http://zipkin-web.cfapps.io. It is backed by a `zipkin-server` with a MySQL backend and RabbitMQ (Spring Cloud Stream) for span transport. - -## Instrumenting Apps - -Depend on [Spring Cloud Sleuth Stream](https://github.com/spring-cloud-spring-cloud-sleuth). Bind to a rabbit service (or redis if you prefer - normal Spring Cloud Stream process). - -## Zipkin Server - -Depend on `spring-cloud-sleuth-zipkin-stream` and enable the server: - -```java -@SpringBootApplication -@EnableZipkinStreamServer -public class ZipkinStreamServerApplication { - - public static void main(String[] args) throws Exception { - SpringApplication.run(ZipkinStreamServerApplication.class, args); - } - -} -``` - -Zipkin has a web UI, which is enabled by default when you depend on `io.zipkin.java:zipkin-autoconfigure-ui`. - -Bind to MySQL and the same Stream service that you did in the apps (rabbit, redis, kafka). Set `spring.datasource.initialize=true` the first time you start to initialize the database. - -Uses the `zipkin-server` library from the [OSS](https://github.com/openzipkin/zipkin-java) as well as `spring-cloud-sleuth-stream`. - -> NOTE: running in the "test" profile you don't need MySQL (the span store is in memory). You could even run in PWS without MySQL. - diff --git a/spring-cloud-sleuth-zipkin-stream/pom.xml b/spring-cloud-sleuth-zipkin-stream/pom.xml deleted file mode 100644 index ecef8c366..000000000 --- a/spring-cloud-sleuth-zipkin-stream/pom.xml +++ /dev/null @@ -1,162 +0,0 @@ - - - 4.0.0 - - spring-cloud-sleuth-zipkin-stream - jar - - spring-cloud-sleuth-zipkin-stream - Spring Boot Zipkin Server - - - org.springframework.cloud - spring-cloud-sleuth - 2.0.0.BUILD-SNAPSHOT - - - - springio - UTF-8 - 1.8 - - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-jdbc - true - - - org.springframework.boot - spring-boot-starter-aop - - - org.springframework.cloud - spring-cloud-sleuth-core - - - org.springframework.cloud - spring-cloud-sleuth-stream - - - org.springframework.integration - spring-integration-jmx - true - - - org.springframework.boot - spring-boot-starter-cloud-connectors - - - io.zipkin.java - zipkin-server - - - org.springframework.boot - * - - - - - io.zipkin.java - zipkin-autoconfigure-storage-mysql - - - org.springframework.boot - * - - - - - com.h2database - h2 - true - - - org.assertj - assertj-core - test - - - org.springframework.cloud - spring-cloud-stream-test-support - test - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - ${project.basedir}/src/main/resources - - - ${project.build.directory}/generated-resources - - - - - maven-resources-plugin - - - - copy-resources - validate - - copy-resources - - - ${basedir}/target/generated-docker - - - src/main/docker - true - - - - - - - - com.spotify - docker-maven-plugin - 0.3.8 - - ${docker.image.prefix}/${project.artifactId} - ${basedir}/target/generated-docker - - - / - ${project.build.directory} - ${project.build.finalName}-exec.jar - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - - - diff --git a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/ConvertToZipkinSpanList.java b/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/ConvertToZipkinSpanList.java deleted file mode 100644 index d2b28a8d7..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/ConvertToZipkinSpanList.java +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.sleuth.zipkin.stream; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.commons.logging.Log; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.stream.Host; -import org.springframework.cloud.sleuth.stream.SleuthSink; -import org.springframework.cloud.sleuth.stream.Spans; -import org.springframework.util.StringUtils; -import zipkin.BinaryAnnotation; -import zipkin.Constants; -import zipkin.Endpoint; -import zipkin.Span.Builder; - -/** - * This converts sleuth spans to zipkin ones, skipping invalid or unsampled. - * - * @author Adrian Cole - * - * @since 1.0.0 - */ -final class ConvertToZipkinSpanList { - private static final List ZIPKIN_START_EVENTS = Arrays - .asList(Constants.CLIENT_RECV, Constants.SERVER_RECV); - - private static final Log log = org.apache.commons.logging.LogFactory - .getLog(ConvertToZipkinSpanList.class); - - static List convert(Spans input) { - Host host = input.getHost(); - List result = new ArrayList<>(input.getSpans().size()); - for (Span span : input.getSpans()) { - if (!span.getName().equals("message:" + SleuthSink.INPUT)) { - result.add(convert(span, host)); - } - else { - log.warn("Message tracing cycle detected for: " + input); - } - } - return result; - } - - /** - * Converts a given Sleuth span to a Zipkin Span. - *
    - *
  • Set ids, etc - *
  • Create timeline annotations based on data from Span object. - *
  • Create binary annotations based on data from Span object. - *
- * - * When logging {@link Constants#CLIENT_SEND}, instrumentation should also log the - * {@link Constants#SERVER_ADDR} Check - * Zipkin code for more information - */ - // VisibleForTesting - static zipkin.Span convert(Span span, Host host) { - //TODO: Consider adding support for the debug flag (related to #496) - Builder zipkinSpan = zipkin.Span.builder(); - - Endpoint ep = Endpoint.builder() - .serviceName(host.getServiceName()) - .ipv4(host.getIpv4()) - .port(host.getPort() != null ? host.getPort() : 0).build(); - - // A zipkin span without any annotations cannot be queried, add special "lc" to - // avoid that. - if (notClientOrServer(span)) { - ensureLocalComponent(span, zipkinSpan, ep); - } - ZipkinMessageListener.addZipkinAnnotations(zipkinSpan, span, ep); - ZipkinMessageListener.addZipkinBinaryAnnotations(zipkinSpan, span, ep); - if (hasClientSend(span)) { - ensureServerAddr(span, zipkinSpan); - } - // In the RPC span model, the client owns the timestamp and duration of the span. If we - // were propagated an id, we can assume that we shouldn't report timestamp or duration, - // rather let the client do that. Worst case we were propagated an unreported ID and - // Zipkin backfills timestamp and duration. - if (!span.isRemote()) { - if (Boolean.TRUE.equals(span.isShared())) { - // don't report server-side timestamp on shared spans - zipkinSpan.timestamp(null).duration(null); - } else { - zipkinSpan.timestamp(span.getBegin() * 1000); - if (!span.isRunning()) { // duration is authoritative, only write when the span stopped - zipkinSpan.duration(calculateDurationInMicros(span)); - } - } - } - zipkinSpan.traceIdHigh(span.getTraceIdHigh()); - zipkinSpan.traceId(span.getTraceId()); - if (span.getParents().size() > 0) { - if (span.getParents().size() > 1) { - if (log.isDebugEnabled()) { - log.debug( - "zipkin doesn't support spans with multiple parents. Omitting " - + "other parents for " + span); - } - } - zipkinSpan.parentId(span.getParents().get(0)); - } - zipkinSpan.id(span.getSpanId()); - if (StringUtils.hasText(span.getName())) { - zipkinSpan.name(span.getName()); - } - return zipkinSpan.build(); - } - - private static void ensureLocalComponent(Span span, Builder zipkinSpan, Endpoint ep) { - if (span.tags().containsKey(Constants.LOCAL_COMPONENT)) { - return; - } - String processId = span.getProcessId() != null ? span.getProcessId().toLowerCase() - : ZipkinMessageListener.UNKNOWN_PROCESS_ID; - zipkinSpan.addBinaryAnnotation( - BinaryAnnotation.create(Constants.LOCAL_COMPONENT, processId, ep)); - } - - private static void ensureServerAddr(Span span, Builder zipkinSpan) { - if (span.tags().containsKey(Span.SPAN_PEER_SERVICE_TAG_NAME)) { - Endpoint endpoint = Endpoint.builder().serviceName(span.tags().get( - Span.SPAN_PEER_SERVICE_TAG_NAME)).build(); - zipkinSpan.addBinaryAnnotation( - BinaryAnnotation.address(Constants.SERVER_ADDR, endpoint)); - } - } - - private static boolean notClientOrServer(Span span) { - for (org.springframework.cloud.sleuth.Log log : span.logs()) { - if (ZIPKIN_START_EVENTS.contains(log.getEvent())) { - return false; - } - } - return true; - } - - private static boolean hasClientSend(Span span) { - for (org.springframework.cloud.sleuth.Log log : span.logs()) { - if (Constants.CLIENT_SEND.equals(log.getEvent())) { - return !span.tags().containsKey(Constants.SERVER_ADDR); - } - } - return false; - } - - /** - * There could be instrumentation delay between span creation and the - * semantic start of the span (client send). When there's a difference, - * spans look confusing. Ex users expect duration to be client - * receive - send, but it is a little more than that. Rather than have - * to teach each user about the possibility of instrumentation overhead, - * we truncate absolute duration (span finish - create) to semantic - * duration (client receive - send) - */ - private static long calculateDurationInMicros(Span span) { - org.springframework.cloud.sleuth.Log clientSend = hasLog(Span.CLIENT_SEND, span); - org.springframework.cloud.sleuth.Log clientReceived = hasLog(Span.CLIENT_RECV, span); - if (clientSend != null && clientReceived != null) { - return (clientReceived.getTimestamp() - clientSend.getTimestamp()) * 1000; - } - return span.getAccumulatedMicros(); - } - - private static org.springframework.cloud.sleuth.Log hasLog(String logName, Span span) { - for (org.springframework.cloud.sleuth.Log log : span.logs()) { - if (logName.equals(log.getEvent())) { - return log; - } - } - return null; - } -} diff --git a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/EnableZipkinStreamServer.java b/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/EnableZipkinStreamServer.java deleted file mode 100644 index 7829a73ab..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/EnableZipkinStreamServer.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Copyright 2015 The OpenZipkin Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ -package org.springframework.cloud.sleuth.zipkin.stream; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.cloud.sleuth.stream.SleuthSink; -import org.springframework.cloud.stream.annotation.EnableBinding; -import org.springframework.context.annotation.Import; - -import zipkin.server.EnableZipkinServer; - -/** - * When enabled, instrumented apps will transport spans over a - * Spring Cloud Stream, for example RabbitMQ. - * - * @author Dave Syer - * @since 1.0.0 - * - * @see ZipkinMessageListener - */ -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -@Documented -@EnableBinding(SleuthSink.class) -@EnableZipkinServer -@Import(ZipkinMessageListener.class) -public @interface EnableZipkinStreamServer { - -} diff --git a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/StreamEnvironmentPostProcessor.java b/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/StreamEnvironmentPostProcessor.java deleted file mode 100644 index 18d299085..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/StreamEnvironmentPostProcessor.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.sleuth.zipkin.stream; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.cloud.sleuth.stream.SleuthSink; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertySource; - -/** - * {@link EnvironmentPostProcessor} that sets the default properties for Sleuth Zipkin - * Stream. - * - * @author Dave Syer - * @since 1.0.0 - */ -public class StreamEnvironmentPostProcessor implements EnvironmentPostProcessor { - - private static final String PROPERTY_SOURCE_NAME = "defaultProperties"; - - @Override - public void postProcessEnvironment(ConfigurableEnvironment environment, - SpringApplication application) { - Map map = new HashMap(); - // Clearing the content type on the inbound channel means that the payload - // of inbound messages can be coerced by a `@StreamListener` method to - // its argument type based on the 'contentType' header of the inbound message. - // Necessary to be done explicitly because the property is set by by - // org.springframework.cloud.sleuth.stream.StreamEnvironmentPostProcessor to - // 'application/json' for outbound channels - map.put("spring.cloud.stream.bindings." + SleuthSink.INPUT + ".content-type", - ""); - addOrReplace(environment.getPropertySources(), map); - } - - private void addOrReplace(MutablePropertySources propertySources, - Map map) { - MapPropertySource target = null; - if (propertySources.contains(PROPERTY_SOURCE_NAME)) { - PropertySource source = propertySources.get(PROPERTY_SOURCE_NAME); - if (source instanceof MapPropertySource) { - target = (MapPropertySource) source; - for (String key : map.keySet()) { - target.getSource().put(key, map.get(key)); - } - } - } - if (target == null) { - target = new MapPropertySource(PROPERTY_SOURCE_NAME, map); - } - if (!propertySources.contains(PROPERTY_SOURCE_NAME)) { - propertySources.addLast(target); - } - } - -} diff --git a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinMessageListener.java b/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinMessageListener.java deleted file mode 100644 index c5ce55651..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/main/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinMessageListener.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.springframework.cloud.sleuth.zipkin.stream; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionOutcome; -import org.springframework.boot.autoconfigure.condition.SpringBootCondition; -import org.springframework.cloud.sleuth.Log; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.stream.SleuthSink; -import org.springframework.cloud.sleuth.stream.Spans; -import org.springframework.cloud.sleuth.zipkin.stream.ZipkinMessageListener.NotSleuthStreamClient; -import org.springframework.cloud.stream.annotation.StreamListener; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.context.annotation.Lazy; -import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.Environment; -import org.springframework.core.env.MapPropertySource; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.integration.annotation.MessageEndpoint; -import zipkin.Annotation; -import zipkin.BinaryAnnotation; -import zipkin.BinaryAnnotation.Type; -import zipkin.Endpoint; -import zipkin.Span.Builder; -import zipkin.collector.Collector; -import zipkin.collector.CollectorMetrics; -import zipkin.collector.CollectorSampler; -import zipkin.storage.Callback; -import zipkin.storage.StorageComponent; - -import java.io.UnsupportedEncodingException; -import java.util.Collections; -import java.util.List; -import java.util.Map; - -/** - * A message listener that is turned on if Sleuth Stream is disabled. - * Asynchronously stores the received spans using {@link Collector}. - * - * @author Dave Syer - * @since 1.0.0 - * - * @see NotSleuthStreamClient - */ -@MessageEndpoint -@Conditional(NotSleuthStreamClient.class) -public class ZipkinMessageListener { - - private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory - .getLog(ZipkinMessageListener.class); - static final String UNKNOWN_PROCESS_ID = "unknown"; - final Collector collector; - - /** lazy so transient storage errors don't crash bootstrap */ - @Lazy - @Autowired - ZipkinMessageListener(StorageComponent storage, CollectorSampler sampler, - CollectorMetrics metrics) { - this.collector = Collector.builder(getClass()) - .storage(storage) - .sampler(sampler) - .metrics(metrics.forTransport("stream")).build(); - } - - @StreamListener(SleuthSink.INPUT) - public void sink(Spans input) { - List converted = ConvertToZipkinSpanList.convert(input); - this.collector.accept(converted, Callback.NOOP); - } - - /** - * Add annotations from the sleuth Span. - */ - static void addZipkinAnnotations(Builder zipkinSpan, Span span, Endpoint endpoint) { - for (Log ta : span.logs()) { - Annotation zipkinAnnotation = Annotation.builder() - .endpoint(endpoint) - .timestamp(ta.getTimestamp() * 1000) // Zipkin is in microseconds - .value(ta.getEvent()) - .build(); - zipkinSpan.addAnnotation(zipkinAnnotation); - } - } - - /** - * Adds binary annotations from the sleuth Span - */ - static void addZipkinBinaryAnnotations(Builder zipkinSpan, Span span, - Endpoint endpoint) { - for (Map.Entry e : span.tags().entrySet()) { - BinaryAnnotation.Builder binaryAnn = BinaryAnnotation.builder(); - binaryAnn.type(Type.STRING); - binaryAnn.key(e.getKey()); - try { - binaryAnn.value(e.getValue().getBytes("UTF-8")); - } - catch (UnsupportedEncodingException ex) { - log.error("Error encoding string as UTF-8", ex); - } - binaryAnn.endpoint(endpoint); - zipkinSpan.addBinaryAnnotation(binaryAnn.build()); - } - } - - protected static class NotSleuthStreamClient extends SpringBootCondition { - - @Override - public ConditionOutcome getMatchOutcome(ConditionContext context, - AnnotatedTypeMetadata metadata) { - Environment environment = context.getEnvironment(); - if ("true".equals(environment - .resolvePlaceholders("${spring.sleuth.stream.enabled:}"))) { - return ConditionOutcome - .noMatch("Found spring.sleuth.stream.enabled=true"); - } - if (environment instanceof ConfigurableEnvironment) { - ConfigurableEnvironment configurable = (ConfigurableEnvironment) environment; - configurable.getPropertySources() - .addLast( - new MapPropertySource("spring.sleuth.stream", - Collections.singletonMap( - "spring.sleuth.stream.enabled", - "false"))); - } - return ConditionOutcome.match("Not found: spring.sleuth.stream.enabled"); - } - - } -} diff --git a/spring-cloud-sleuth-zipkin-stream/src/main/resources/META-INF/spring.factories b/spring-cloud-sleuth-zipkin-stream/src/main/resources/META-INF/spring.factories deleted file mode 100644 index cb99f0ea7..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/main/resources/META-INF/spring.factories +++ /dev/null @@ -1,3 +0,0 @@ -# Environment Post Processor -org.springframework.boot.env.EnvironmentPostProcessor=\ -org.springframework.cloud.sleuth.zipkin.stream.StreamEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ConvertToZipkinSpanListTests.java b/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ConvertToZipkinSpanListTests.java deleted file mode 100644 index 15dc2fcb8..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ConvertToZipkinSpanListTests.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 2016 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.cloud.sleuth.zipkin.stream; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Random; - -import org.assertj.core.api.Condition; -import org.junit.Test; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.stream.Host; -import org.springframework.cloud.sleuth.stream.Spans; -import zipkin.Constants; -import zipkin.Endpoint; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ConvertToZipkinSpanListTests { - - Host host = new Host("myservice", "1.2.3.4", 8080); - - @Test - public void skipsInputSpans() { - Spans spans = new Spans(this.host, - Collections.singletonList(span("sleuth"))); - - List result = ConvertToZipkinSpanList.convert(spans); - - assertThat(result).isEmpty(); - } - - @Test - public void nullEndpointPort() { - this.host.setPort(0); - Span span = span("sleuth"); - span.logEvent(Constants.CLIENT_SEND); - zipkin.Span result = ConvertToZipkinSpanList.convert(span, host); - assertThat(result).isNotNull(); - } - - @Test - public void retainsValidSpans() { - Spans spans = new Spans(this.host, - Arrays.asList(span("foo"), span("bar"), span("baz"))); - - List result = ConvertToZipkinSpanList.convert(spans); - - assertThat(result).extracting(s -> s.name).containsExactly( - "message:foo", "message:bar", "message:baz"); - } - - @Test - public void appendsLocalComponentTagIfNoZipkinLogIsPresent() { - Spans spans = new Spans(this.host, Collections.singletonList(span("foo"))); - - List result = ConvertToZipkinSpanList.convert(spans); - - assertThat(result) - .flatExtracting(s -> s.binaryAnnotations) - .extracting(input -> input.key) - .contains(Constants.LOCAL_COMPONENT); - } - - @Test - public void appendServerAddressTagIfClientLogIsPresentWhenPeerServiceIsPresent() { - Span span = span("foo"); - span.logEvent(Constants.CLIENT_SEND); - span.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "myservice"); - Spans spans = new Spans(this.host, Collections.singletonList(span)); - - List result = ConvertToZipkinSpanList.convert(spans); - - assertThat(result) - .hasSize(1) - .flatExtracting(input1 -> input1.binaryAnnotations) - .filteredOn("key", Constants.SERVER_ADDR) - .extracting(input -> input.endpoint) - .hasSize(1) - .has(new Condition>() { - @Override public boolean matches(List value) { - Endpoint endpoint = value.get(0); - return endpoint.serviceName.equals("myservice") && endpoint.ipv4 == 0; - } - }); - } - - @Test - public void doesNotAppendServerAddressTagIfClientLogIsPresent() { - Span span = span("foo"); - span.logEvent(Constants.CLIENT_SEND); - Spans spans = new Spans(this.host, Collections.singletonList(span)); - - List result = ConvertToZipkinSpanList.convert(spans); - - assertThat(result) - .hasSize(1) - .flatExtracting(input1 -> input1.binaryAnnotations) - .filteredOn("key", Constants.SERVER_ADDR) - .isEmpty(); - } - - @Test - public void shouldReuseServerAddressTag() { - Span span = span("foo"); - span.logEvent(Constants.CLIENT_SEND); - span.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "barservice"); - Spans spans = new Spans(this.host, Collections.singletonList(span)); - - List result = ConvertToZipkinSpanList.convert(spans); - - assertThat(result) - .hasSize(1) - .flatExtracting(input1 -> input1.binaryAnnotations) - .filteredOn("key", Constants.SERVER_ADDR) - .extracting(input -> input.endpoint.serviceName) - .contains("barservice"); - } - - /** Sleuth timestamps are millisecond granularity while zipkin is microsecond. */ - @Test - public void convertsTimestampToMicrosecondsAndSetsDurationToAccumulatedMicros() { - long start = System.currentTimeMillis(); - Span span = span("foo"); - span.logEvent(Constants.CLIENT_SEND); - span.stop(); - - Spans spans = new Spans(this.host, Collections.singletonList(span)); - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.timestamp) - .isEqualTo(span.getBegin() * 1000); - assertThat(result.duration) - .isEqualTo(span.getAccumulatedMicros()); - assertThat(result.annotations.get(0).timestamp) - .isGreaterThanOrEqualTo(start * 1000) - .isLessThanOrEqualTo(System.currentTimeMillis() * 1000); - } - - @Test - public void setsTheDurationToTheDifferenceBetweenCRandCS() - throws InterruptedException { - Span span = span("foo"); - span.logEvent(Span.CLIENT_SEND); - Thread.sleep(10); - span.logEvent(Span.CLIENT_RECV); - Thread.sleep(20); - span.stop(); - - Spans spans = new Spans(this.host, Collections.singletonList(span)); - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.timestamp) - .isEqualTo(span.getBegin() * 1000); - long clientSendTimestamp = span.logs().stream().filter(log -> Span.CLIENT_SEND.equals(log.getEvent())) - .findFirst().get().getTimestamp(); - long clientRecvTimestamp = span.logs().stream().filter(log -> Span.CLIENT_RECV.equals(log.getEvent())) - .findFirst().get().getTimestamp(); - assertThat(result.duration) - .isNotEqualTo(span.getAccumulatedMicros()) - .isEqualTo((clientRecvTimestamp - clientSendTimestamp) * 1000); - } - - /** Zipkin's duration should only be set when the span is finished. */ - @Test - public void doesntSetDurationWhenStillRunning() { - Span running = Span.builder().traceId(1L).name("http:child").build(); - Spans spans = new Spans(this.host, Collections.singletonList(running)); - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.timestamp) - .isGreaterThan(0); // sanity check it did start - assertThat(result.duration) - .isNull(); - } - - /** - * In the RPC span model, the client owns the timestamp and duration of the span. If we - * were propagated an id, we can assume that we shouldn't report timestamp or duration, - * rather let the client do that. Worst case we were propagated an unreported ID and - * Zipkin backfills timestamp and duration. - */ - @Test - public void doesntSetTimestampOrDurationWhenRemote() { - Span span = span("foo", true); - Spans spans = new Spans(this.host, Collections.singletonList(span)); - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.timestamp) - .isNull(); - assertThat(result.duration) - .isNull(); - } - - @Test - public void converts128BitTraceId() { - Span span = Span.builder().traceIdHigh(1L).traceId(2L).spanId(3L).name("foo").build(); - - Spans spans = new Spans(this.host, Collections.singletonList(span)); - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.traceIdHigh).isEqualTo(span.getTraceIdHigh()); - assertThat(result.traceId).isEqualTo(span.getTraceId()); - } - - @Test - public void shouldRemoveTimestampAndDurationForNonRemoteSharedSpan() { - Span span = Span.builder() - .name("foo") - .exportable(false) - .remote(false) - .shared(true) - .build(); - Spans spans = new Spans(this.host, Collections.singletonList(span)); - - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.duration).isNull(); - assertThat(result.timestamp).isNull(); - } - - @Test - public void shouldNotRemoveTimestampAndDurationForNonRemoteNonSharedSpan() { - Span span = Span.builder() - .name("foo") - .exportable(false) - .remote(false) - .shared(false) - .build(); - span.stop(); - Spans spans = new Spans(this.host, Collections.singletonList(span)); - - zipkin.Span result = ConvertToZipkinSpanList.convert(spans).get(0); - - assertThat(result.duration).isNotNull(); - assertThat(result.timestamp).isNotNull(); - } - - Span span(String name) { - return span(name, false); - } - - Span span(String name, boolean remote) { - Long id = new Random().nextLong(); - return Span.builder().begin(1).end(3).name("message:" + name).traceId(id).spanId(id) - .remote(remote).processId("process").build(); - } -} diff --git a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinMessageListenerTests.java b/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinMessageListenerTests.java deleted file mode 100644 index d41eebfe1..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinMessageListenerTests.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2013-2015 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.sleuth.zipkin.stream; - -import org.junit.Test; -import org.springframework.cloud.sleuth.Span; -import org.springframework.cloud.sleuth.stream.Host; -import zipkin.BinaryAnnotation; -import zipkin.Endpoint; - -import static org.assertj.core.api.Assertions.assertThat; - -public class ZipkinMessageListenerTests { - Span span = Span.builder().begin(1).end(3).name("http:name").traceId(1L).spanId(2L).remote(true) - .exportable(true).processId("process").build(); - Host host = new Host("myservice", "1.2.3.4", 8080); - Endpoint endpoint = Endpoint.builder() - .serviceName("myservice") - .ipv4(1 << 24 | 2 << 16 | 3 << 8 | 4) - .port(8080).build(); - - /** - * In the RPC span model, the client owns the timestamp and duration of the span. If we - * were propagated an id, we can assume that we shouldn't report timestamp or duration, - * rather let the client do that. Worst case we were propagated an unreported ID and - * Zipkin backfills timestamp and duration. - */ - @Test - public void doesntSetTimestampOrDurationWhenRemote() { - this.span.stop(); - zipkin.Span result = ConvertToZipkinSpanList.convert(this.span, this.host); - - assertThat(result.timestamp) - .isNull(); - assertThat(result.duration) - .isNull(); - } - - /** Sleuth timestamps are millisecond granularity while zipkin is microsecond. */ - @Test - public void convertsTimestampAndDurationToMicroseconds() { - Span span = Span.builder().begin(1).end(3).name("http:name").traceId(1L).spanId(2L).remote(false) - .exportable(true).processId("process").build(); - long start = System.currentTimeMillis(); - span.logEvent("hystrix/retry"); // System.currentTimeMillis - - zipkin.Span result = ConvertToZipkinSpanList.convert(span, this.host); - - assertThat(result.timestamp) - .isEqualTo(span.getBegin() * 1000); - assertThat(result.duration) - .isEqualTo((span.getEnd() - span.getBegin()) * 1000); - assertThat(result.annotations.get(0).timestamp) - .isGreaterThanOrEqualTo(start * 1000) - .isLessThanOrEqualTo(System.currentTimeMillis() * 1000); - } - - /** Sleuth host corresponds to annotation/binaryAnnotation.host in zipkin. */ - @Test - public void annotationsIncludeHost() { - this.span.logEvent("hystrix/retry"); - this.span.tag("spring-boot/version", "1.3.1.RELEASE"); - - zipkin.Span result = ConvertToZipkinSpanList.convert(this.span, this.host); - - assertThat(result.annotations.get(0).endpoint) - .isEqualTo(this.endpoint); - assertThat(result.binaryAnnotations.get(0).endpoint) - .isEqualTo(result.annotations.get(0).endpoint); - } - - /** - * In zipkin, the service context is attached to annotations. Sleuth spans - * that have no annotations will get an "lc" one, which allows them to be - * queryable in zipkin by service name. - */ - @Test - public void spanWithoutAnnotationsLogsComponent() { - zipkin.Span result = ConvertToZipkinSpanList.convert(this.span, this.host); - - assertThat(result.binaryAnnotations).hasSize(1); - assertThat(result.binaryAnnotations.get(0)).isEqualToComparingFieldByField( - BinaryAnnotation.create("lc", this.span.getProcessId(), this.endpoint)); - } - - // TODO: "unknown" bc process id, documented as not nullable, is null in some tests. - @Test - public void nullProcessIdCoercesToUnknownServiceName() { - Span noProcessId = Span.builder().traceId(1L).name("http:parent").remote(true).build(); - - zipkin.Span result = ConvertToZipkinSpanList.convert(noProcessId, this.host); - - assertThat(result.binaryAnnotations) - .containsOnly(BinaryAnnotation.create("lc", "unknown", this.endpoint)); - } -} \ No newline at end of file diff --git a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinServerApplicationTests.java b/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinServerApplicationTests.java deleted file mode 100644 index 6dce55fa3..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/ZipkinServerApplicationTests.java +++ /dev/null @@ -1,110 +0,0 @@ -package org.springframework.cloud.sleuth.zipkin.stream; - -import org.junit.Ignore; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.cloud.sleuth.stream.SleuthSink; -import org.springframework.cloud.sleuth.zipkin.stream.ZipkinServerApplicationTests.ZipkinStreamServerApplication; -import org.springframework.cloud.stream.annotation.EnableBinding; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import zipkin.collector.CollectorMetrics; -import zipkin.collector.CollectorSampler; -import zipkin.internal.V2StorageComponent; -import zipkin.server.ZipkinHttpCollector; -import zipkin.server.ZipkinQueryApiV1; -import zipkin.storage.StorageComponent; -import zipkin2.storage.InMemoryStorage; - -import static org.junit.Assert.assertEquals; - -@RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = ZipkinStreamServerApplication.class, properties = { - "spring.datasource.initialize=true" }, webEnvironment = WebEnvironment.RANDOM_PORT) -@ActiveProfiles("test") -public class ZipkinServerApplicationTests { - - @Autowired - private StorageComponent storage; - - @Test - public void contextLoads() { - int count = this.storage.spanStore().getServiceNames().size(); - assertEquals(0, count); - } - - @SpringBootApplication - @EnableBoot2CompatibleZipkinServer - public static class ZipkinStreamServerApplication { - - public static void main(String[] args) throws Exception { - new SpringApplicationBuilder(ZipkinStreamServerApplication.class) - .profiles("test").properties("spring.datasource.initialize=true") - .run(args); - } - - } -} - -// TODO: Zipkin Server is not Boot 2.0 compatible -//@EnableZipkinStreamServer -@EnableBinding(SleuthSink.class) -@Import({ZipkinMessageListener.class, - Boot2ZipkinCompatibleConfig.class, - ZipkinQueryApiV1.class, - ZipkinHttpCollector.class}) -@interface EnableBoot2CompatibleZipkinServer { -} - -// CollectorMetrics bean definition from `ZipkinServerConfiguration` -// is not Boot 2.0 compatible -@Configuration -class Boot2ZipkinCompatibleConfig { - - @Bean CollectorMetrics collectorMetrics() { - CollectorMetrics mock = Mockito.mock(CollectorMetrics.class); - Mockito.when(mock.forTransport(Mockito.anyString())).thenReturn(mock); - return mock; - } - - @Bean - @ConditionalOnMissingBean(CollectorSampler.class) - CollectorSampler traceIdSampler(@Value("${zipkin.collector.sample-rate:1.0}") float rate) { - return CollectorSampler.create(rate); - } - - /** - * This is a special-case configuration if there's no StorageComponent of any kind. In-Mem can - * supply both read apis, so we add two beans here. - */ - @Configuration - // "matchIfMissing = true" ensures this is used when there's no configured storage type - @ConditionalOnProperty(name = "zipkin.storage.type", havingValue = "mem", matchIfMissing = true) - @ConditionalOnMissingBean(StorageComponent.class) - static class InMemoryConfiguration { - @Bean StorageComponent storage( - @Value("${zipkin.storage.strict-trace-id:true}") boolean strictTraceId, - @Value("${zipkin.storage.mem.max-spans:500000}") int maxSpans) { - return V2StorageComponent.create(InMemoryStorage.newBuilder() - .strictTraceId(strictTraceId) - .maxSpanCount(maxSpans) - .build()); - } - - @Bean InMemoryStorage v2Storage(V2StorageComponent component) { - return (InMemoryStorage) component.delegate(); - } - } -} diff --git a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java b/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java deleted file mode 100644 index 4e1797bd6..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/test/java/org/springframework/cloud/sleuth/zipkin/stream/documentation/Consumer.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2013-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.cloud.sleuth.zipkin.stream.documentation; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cloud.sleuth.zipkin.stream.EnableZipkinStreamServer; - -/** - * Test class to be embedded in the Zipkin Consumer part of - * {@code docs/src/main/asciidoc/spring-cloud-sleuth.adoc} - * - * @author Marcin Grzejszczak - */ -// tag::zipkin_consumer[] -@SpringBootApplication -@EnableZipkinStreamServer -public class Consumer { - public static void main(String[] args) { - SpringApplication.run(Consumer.class, args); - } -} -// end::zipkin_consumer[] \ No newline at end of file diff --git a/spring-cloud-sleuth-zipkin-stream/src/test/resources/drop.sql b/spring-cloud-sleuth-zipkin-stream/src/test/resources/drop.sql deleted file mode 100644 index 507f4f27f..000000000 --- a/spring-cloud-sleuth-zipkin-stream/src/test/resources/drop.sql +++ /dev/null @@ -1,5 +0,0 @@ -drop table zipkin_spans; -drop table zipkin_annotations; -drop table zipkin_binary_annotations; -drop table zipkin_dependencies; -drop table zipkin_dependency_links; \ No newline at end of file