Merge branch 'master' into 2.0.x

This commit is contained in:
Dave Syer
2017-10-18 10:18:09 +01:00
23 changed files with 633 additions and 173 deletions

View File

@@ -27,8 +27,7 @@ public class HeaderBasedMessagingExtractor implements MessagingSpanTextMapExtrac
if (spanIdMissing) {
carrier.put(TraceMessageHeaders.SPAN_ID_NAME, traceId);
}
} else if (!hasHeader(carrier, TraceMessageHeaders.SPAN_ID_NAME)
|| !hasHeader(carrier, TraceMessageHeaders.TRACE_ID_NAME)) {
} else if (spanIdMissing) {
return null;
// TODO: Consider throwing IllegalArgumentException;
}

View File

@@ -22,9 +22,12 @@ import java.util.Map;
import org.springframework.cloud.sleuth.SpanTextMap;
import org.springframework.messaging.Message;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
/**
@@ -35,9 +38,9 @@ import org.springframework.util.StringUtils;
*/
class MessagingTextMap implements SpanTextMap {
private final MessageBuilder delegate;
private final MessageBuilder<?> delegate;
public MessagingTextMap(MessageBuilder delegate) {
public MessagingTextMap(MessageBuilder<?> delegate) {
this.delegate = delegate;
}
@@ -46,7 +49,9 @@ class MessagingTextMap implements SpanTextMap {
Map<String, String> map = new HashMap<>();
for (Map.Entry<String, Object> entry : this.delegate.build().getHeaders()
.entrySet()) {
map.put(entry.getKey(), String.valueOf(entry.getValue()));
if (!NativeMessageHeaderAccessor.NATIVE_HEADERS.equals(entry.getKey())) {
map.put(entry.getKey(), String.valueOf(entry.getValue()));
}
}
return map.entrySet().iterator();
}
@@ -61,10 +66,23 @@ class MessagingTextMap implements SpanTextMap {
MessageHeaderAccessor accessor = MessageHeaderAccessor
.getMutableAccessor(initialMessage);
accessor.setHeader(key, value);
if (accessor instanceof NativeMessageHeaderAccessor) {
NativeMessageHeaderAccessor nativeAccessor = (NativeMessageHeaderAccessor) accessor;
if (accessor instanceof SimpMessageHeaderAccessor) {
SimpMessageHeaderAccessor nativeAccessor = (SimpMessageHeaderAccessor) accessor;
nativeAccessor.setNativeHeader(key, value);
}
else if (accessor.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS) != null) {
if (accessor.getHeader(
NativeMessageHeaderAccessor.NATIVE_HEADERS) instanceof MultiValueMap) {
MultiValueMap<String, String> map = (MultiValueMap<String, String>) accessor
.getHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS);
map.add(key, value);
}
}
else {
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
accessor.setHeader(NativeMessageHeaderAccessor.NATIVE_HEADERS, map);
map.add(key, value);
}
this.delegate.copyHeaders(accessor.toMessageHeaders());
}
}

View File

@@ -17,14 +17,15 @@
package org.springframework.cloud.sleuth.instrument.messaging;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.cloud.sleuth.Log;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.sampler.NeverSampler;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.MessageHeaderAccessor;
@@ -46,17 +47,23 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
}
@Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
Span currentSpan = getTracer().getCurrentSpan();
public void afterSendCompletion(Message<?> message, MessageChannel channel,
boolean sent, Exception ex) {
Message<?> retrievedMessage = getMessage(message);
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(retrievedMessage);
Span currentSpan = getTracer().isTracing() ? getTracer().getCurrentSpan()
: buildSpan(new MessagingTextMap(messageBuilder));
if (log.isDebugEnabled()) {
log.debug("Completed sending and current span is " + currentSpan);
}
getTracer().continueSpan(currentSpan);
if (containsServerReceived(currentSpan)) {
if (log.isDebugEnabled()) {
log.debug("Marking span with server send");
}
currentSpan.logEvent(Span.SERVER_SEND);
} else if (currentSpan != null) {
}
else if (currentSpan != null) {
if (log.isDebugEnabled()) {
log.debug("Marking span with client received");
}
@@ -93,6 +100,8 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(retrievedMessage);
Span parentSpan = getTracer().isTracing() ? getTracer().getCurrentSpan()
: buildSpan(new MessagingTextMap(messageBuilder));
// Do not continue the parent (assume that this is handled by caller)
// getTracer().continueSpan(parentSpan);
if (log.isDebugEnabled()) {
log.debug("Parent span is " + parentSpan);
}
@@ -101,12 +110,14 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
log.debug("Name of the span will be [" + name + "]");
}
Span span = startSpan(parentSpan, name, message);
if (message.getHeaders().containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) {
if (message.getHeaders()
.containsKey(TraceMessageHeaders.MESSAGE_SENT_FROM_CLIENT)) {
if (log.isDebugEnabled()) {
log.debug("Marking span with server received");
}
span.logEvent(Span.SERVER_RECV);
} else {
}
else {
if (log.isDebugEnabled()) {
log.debug("Marking span with client send");
}
@@ -119,7 +130,7 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
return new GenericMessage<>(message.getPayload(), headers.getMessageHeaders());
}
private Message getMessage(Message<?> message) {
private Message<?> getMessage(Message<?> message) {
Object payload = message.getPayload();
if (payload instanceof MessagingException) {
MessagingException e = (MessagingException) payload;
@@ -132,7 +143,8 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
if (span != null) {
return getTracer().createSpan(name, span);
}
if (Span.SPAN_NOT_SAMPLED.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) {
if (Span.SPAN_NOT_SAMPLED
.equals(message.getHeaders().get(TraceMessageHeaders.SAMPLED_NAME))) {
return getTracer().createSpan(name, NeverSampler.INSTANCE);
}
return getTracer().createSpan(name);
@@ -141,7 +153,10 @@ public class TraceChannelInterceptor extends AbstractTraceChannelInterceptor {
@Override
public Message<?> beforeHandle(Message<?> message, MessageChannel channel,
MessageHandler handler) {
Span spanFromHeader = getTracer().getCurrentSpan();
Message<?> retrievedMessage = getMessage(message);
MessageBuilder<?> messageBuilder = MessageBuilder.fromMessage(retrievedMessage);
Span spanFromHeader = getTracer().isTracing() ? getTracer().getCurrentSpan()
: buildSpan(new MessagingTextMap(messageBuilder));
if (log.isDebugEnabled()) {
log.debug("Continuing span " + spanFromHeader + " before handling message");
}

View File

@@ -14,6 +14,7 @@ import org.springframework.cloud.sleuth.instrument.messaging.TraceChannelInterce
import org.springframework.cloud.sleuth.instrument.messaging.TraceSpanMessagingAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration;
@@ -53,6 +54,11 @@ public class TraceWebSocketAutoConfiguration
// The user must register their own endpoints
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.configureBrokerChannel().setInterceptors(new TraceChannelInterceptor(this.beanFactory));
}
@Override
public void configureClientOutboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new TraceChannelInterceptor(this.beanFactory));

View File

@@ -6,11 +6,12 @@ import java.util.Iterator;
import java.util.Map;
import org.junit.Test;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanTextMap;
import org.springframework.cloud.sleuth.TraceKeys;
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
import static org.assertj.core.api.BDDAssertions.then;
/**
* @author Marcin Grzejszczak
@@ -19,7 +20,6 @@ public class HeaderBasedMessagingInjectorTests {
HeaderBasedMessagingInjector injector = new HeaderBasedMessagingInjector(new TraceKeys());
@SuppressWarnings("unchecked")
@Test
public void should_not_override_already_existing_headers() throws Exception {
Span span = Span.builder()

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2016-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.instrument.messaging;
import java.util.HashSet;
import java.util.Set;
import org.junit.Test;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.messaging.support.NativeMessageHeaderAccessor;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Dave Syer
*
*/
public class MessagingTextMapTests {
@Test
public void vanilla() {
MessageBuilder<String> builder = MessageBuilder.withPayload("foo");
MessagingTextMap map = new MessagingTextMap(builder);
map.put("foo", "bar");
Set<String> keys = new HashSet<>();
map.forEach(entry -> keys.add(entry.getKey()));
assertThat(keys).contains("foo");
@SuppressWarnings("unchecked")
MultiValueMap<String, String> natives = (MultiValueMap<String, String>) builder.build().getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS);
assertThat(natives).containsKey("foo");
assertThat(keys).doesNotContain(NativeMessageHeaderAccessor.NATIVE_HEADERS);
}
@Test
public void nativeHeadersAlreadyExist() {
MessageBuilder<String> builder = MessageBuilder.withPayload("foo").setHeader(
NativeMessageHeaderAccessor.NATIVE_HEADERS, new LinkedMultiValueMap<>());
MessagingTextMap map = new MessagingTextMap(builder);
map.put("foo", "bar");
Set<String> keys = new HashSet<>();
map.forEach(entry -> keys.add(entry.getKey()));
assertThat(keys).contains("foo");
@SuppressWarnings("unchecked")
MultiValueMap<String, String> natives = (MultiValueMap<String, String>) builder.build().getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS);
assertThat(natives).containsKey("foo");
assertThat(keys).doesNotContain(NativeMessageHeaderAccessor.NATIVE_HEADERS);
}
@Test
public void nativeHeaders() {
Message<String> message = MessageBuilder.withPayload("foo").build();
MessageBuilder<String> builder = MessageBuilder.fromMessage(message)
.setHeaders(NativeMessageHeaderAccessor.getMutableAccessor(message));
MessagingTextMap map = new MessagingTextMap(builder);
map.put("foo", "bar");
Set<String> keys = new HashSet<>();
map.forEach(entry -> keys.add(entry.getKey()));
assertThat(keys).contains("foo");
@SuppressWarnings("unchecked")
MultiValueMap<String, String> natives = (MultiValueMap<String, String>) builder.build().getHeaders().get(NativeMessageHeaderAccessor.NATIVE_HEADERS);
assertThat(natives).containsKey("foo");
assertThat(keys).doesNotContain(NativeMessageHeaderAccessor.NATIVE_HEADERS);
}
}

View File

@@ -18,11 +18,15 @@ package org.springframework.cloud.sleuth.instrument.messaging;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -40,11 +44,11 @@ import org.springframework.cloud.sleuth.util.ExceptionUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.ExecutorChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageDeliveryException;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.support.ChannelInterceptor;
@@ -55,6 +59,7 @@ import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.SerializationUtils;
import static org.assertj.core.api.BDDAssertions.then;
import static org.junit.Assert.assertNotNull;
import static org.springframework.cloud.sleuth.assertions.SleuthAssertions.then;
@@ -72,6 +77,10 @@ public class TraceChannelInterceptorTests implements MessageHandler {
@Qualifier("tracedChannel")
private DirectChannel tracedChannel;
@Autowired
@Qualifier("tracedExecutorChannel")
private ExecutorChannel executorChannel;
@Autowired
@Qualifier("ignoredChannel")
private DirectChannel ignoredChannel;
@@ -88,9 +97,12 @@ public class TraceChannelInterceptorTests implements MessageHandler {
private Message<?> message;
private Span span;
private CountDownLatch latch = new CountDownLatch(1);
@Override
public void handleMessage(Message<?> message) throws MessagingException {
this.latch.countDown();
this.message = message;
this.span = TestSpanContextHolder.getCurrentSpan();
if (message.getHeaders().containsKey("THROW_EXCEPTION")) {
@@ -101,6 +113,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
@Before
public void init() {
this.tracedChannel.subscribe(this);
this.executorChannel.subscribe(this);
this.ignoredChannel.subscribe(this);
this.accumulator.getSpans().clear();
}
@@ -110,6 +123,7 @@ public class TraceChannelInterceptorTests implements MessageHandler {
then(ExceptionUtils.getLastException()).isNull();
TestSpanContextHolder.removeCurrentSpan();
this.tracedChannel.unsubscribe(this);
this.executorChannel.unsubscribe(this);
this.ignoredChannel.unsubscribe(this);
this.accumulator.getSpans().clear();
}
@@ -126,6 +140,19 @@ public class TraceChannelInterceptorTests implements MessageHandler {
then(this.span.isExportable()).isFalse();
}
@Test
public void executableSpanCreation() throws Exception {
this.executorChannel.send(MessageBuilder.withPayload("hi")
.setHeader(TraceMessageHeaders.SAMPLED_NAME, Span.SPAN_NOT_SAMPLED).build());
this.latch.await(1, TimeUnit.SECONDS);
assertNotNull("message was null", this.message);
String spanId = this.message.getHeaders().get(TraceMessageHeaders.SPAN_ID_NAME, String.class);
then(spanId).isNotNull();
then(TestSpanContextHolder.getCurrentSpan()).isNull();
then(this.span.isExportable()).isFalse();
}
@Test
public void messageHeadersStillMutable() {
this.tracedChannel.send(MessageBuilder.withPayload("hi")
@@ -351,6 +378,11 @@ public class TraceChannelInterceptorTests implements MessageHandler {
return new ArrayListSpanAccumulator();
}
@Bean
public ExecutorChannel tracedExecutorChannel() {
return new ExecutorChannel(Executors.newSingleThreadExecutor());
}
@Bean
public DirectChannel tracedChannel() {
return new DirectChannel();

View File

@@ -39,20 +39,23 @@ import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
* @author Marcin Grzejszczak
*/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TraceWebSocketAutoConfigurationTest.Config.class)
public class TraceWebSocketAutoConfigurationTest {
@SpringBootTest(classes = TraceWebSocketAutoConfigurationTests.Config.class)
public class TraceWebSocketAutoConfigurationTests {
@Autowired
DelegatingWebSocketMessageBrokerConfiguration delegatingWebSocketMessageBrokerConfiguration;
@Test
public void should_register_interceptors_for_inbound_and_outbound_channels() {
public void should_register_interceptors_for_all_channels() {
then(this.delegatingWebSocketMessageBrokerConfiguration.clientInboundChannel()
.getInterceptors())
.hasAtLeastOneElementOfType(TraceChannelInterceptor.class);
then(this.delegatingWebSocketMessageBrokerConfiguration.clientOutboundChannel()
.getInterceptors())
.hasAtLeastOneElementOfType(TraceChannelInterceptor.class);
then(this.delegatingWebSocketMessageBrokerConfiguration.brokerChannel()
.getInterceptors())
.hasAtLeastOneElementOfType(TraceChannelInterceptor.class);
}
@EnableAutoConfiguration

View File

@@ -2,7 +2,7 @@
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.cloud.sleuth" level="TRACE"/>
<logger name="org.springframework.boot.autoconfigure.logging" level="DEBUG"/>
<logger name="org.springframework.boot.autoconfigure.logging" level="INFO"/>
<logger name="org.springframework.cloud.sleuth.log" level="DEBUG"/>
<logger name="org.springframework.cloud.sleuth.trace" level="DEBUG"/>
<logger name="org.springframework.cloud.sleuth.instrument.rxjava" level="DEBUG"/>

View File

@@ -14,9 +14,9 @@
<name>spring-cloud-sleuth-dependencies</name>
<description>Spring Cloud Sleuth Dependencies</description>
<properties>
<zipkin.version>2.2.0</zipkin.version>
<zipkin-reporter.version>1.1.1</zipkin-reporter.version>
<zipkin-reporter2.version>2.1.1</zipkin-reporter2.version>
<zipkin.version>2.2.1</zipkin.version>
<zipkin-reporter.version>1.1.2</zipkin-reporter.version>
<zipkin-reporter2.version>2.1.3</zipkin-reporter2.version>
</properties>
<dependencyManagement>
<dependencies>
@@ -105,6 +105,30 @@
<artifactId>zipkin-reporter</artifactId>
<version>${zipkin-reporter2.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-kafka11</artifactId>
<version>${zipkin-reporter2.version}</version>
<exclusions>
<!-- assigned with spring-kafka -->
<exclusion>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-amqp-client</artifactId>
<version>${zipkin-reporter2.version}</version>
<exclusions>
<!-- assigned with spring-rabbit -->
<exclusion>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</dependencyManagement>
<profiles>

View File

@@ -59,17 +59,17 @@
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>io.zipkin.zipkin2</groupId>
<artifactId>zipkin</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin-server</artifactId>
<version>2.2.0</version>
<version>2.2.1</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -73,6 +73,24 @@
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter</artifactId>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-kafka11</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-sender-amqp-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>

View File

@@ -16,25 +16,17 @@
package org.springframework.cloud.sleuth.zipkin2;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.sleuth.Sampler;
@@ -44,13 +36,11 @@ import org.springframework.cloud.sleuth.autoconfig.TraceAutoConfiguration;
import org.springframework.cloud.sleuth.metric.SpanMetricReporter;
import org.springframework.cloud.sleuth.sampler.PercentageBasedSampler;
import org.springframework.cloud.sleuth.sampler.SamplerProperties;
import org.springframework.cloud.sleuth.zipkin2.sender.ZipkinSenderConfigurationImportSelector;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import zipkin2.Span;
import zipkin2.reporter.AsyncReporter;
@@ -77,10 +67,10 @@ import zipkin2.reporter.Sender;
@EnableConfigurationProperties({ZipkinProperties.class, SamplerProperties.class})
@ConditionalOnProperty(value = "spring.zipkin.enabled", matchIfMissing = true)
@AutoConfigureBefore(TraceAutoConfiguration.class)
@Import(ZipkinSenderConfigurationImportSelector.class)
public class ZipkinAutoConfiguration {
@Autowired(required = false) List<SpanAdjuster> spanAdjusters = new ArrayList<>();
@Autowired ZipkinUrlExtractor extractor;
/**
* Accepts a sender so you can plug-in any standard one. Returns a Reporter so you can also
@@ -100,55 +90,6 @@ public class ZipkinAutoConfiguration {
.build(zipkin.getEncoder());
}
@Bean
@ConditionalOnMissingBean
public Sender restTemplateSender(ZipkinProperties zipkin,
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
RestTemplate restTemplate = new ZipkinRestTemplateWrapper(zipkin, this.extractor);
zipkinRestTemplateCustomizer.customize(restTemplate);
return new RestTemplateSender(restTemplate, zipkin.getBaseUrl(), zipkin.getEncoder());
}
@Configuration
@ConditionalOnClass(DiscoveryClient.class)
static class DiscoveryClientZipkinUrlExtractorConfiguration {
@Autowired(required = false) DiscoveryClient discoveryClient;
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
final DiscoveryClient discoveryClient = this.discoveryClient;
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
if (discoveryClient != null) {
URI uri = URI.create(zipkinProperties.getBaseUrl());
String host = uri.getHost();
List<ServiceInstance> instances = discoveryClient.getInstances(host);
if (!instances.isEmpty()) {
return instances.get(0).getUri();
}
}
return URI.create(zipkinProperties.getBaseUrl());
}
};
}
}
@Configuration
@ConditionalOnMissingClass("org.springframework.cloud.client.discovery.DiscoveryClient")
static class DefaultZipkinUrlExtractorConfiguration {
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
return URI.create(zipkinProperties.getBaseUrl());
}
};
}
}
@Bean
@ConditionalOnMissingBean
public ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer(ZipkinProperties zipkinProperties) {
@@ -164,7 +105,7 @@ public class ZipkinAutoConfiguration {
@Bean
public SpanReporter zipkinSpanListener(Reporter<Span> reporter, EndpointLocator endpointLocator,
Environment environment) {
return new ZipkinSpanListener(reporter, endpointLocator, environment, this.spanAdjusters);
return new ZipkinSpanReporter(reporter, endpointLocator, environment, this.spanAdjusters);
}
@Configuration
@@ -220,52 +161,3 @@ public class ZipkinAutoConfiguration {
}
}
}
/**
* Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery client
* then this value will be taken from the properties. Otherwise host will be assumed to
* be a service id.
*/
interface ZipkinUrlExtractor {
URI zipkinUrl(ZipkinProperties zipkinProperties);
}
/**
* Resolves at runtime where the Zipkin server is. If there's no discovery client then
* {@link URI} from the properties is taken. Otherwise service discovery is pinged
* for current Zipkin address.
*/
class ZipkinRestTemplateWrapper extends RestTemplate {
private static final Log log = LogFactory.getLog(ZipkinRestTemplateWrapper.class);
private final ZipkinProperties zipkinProperties;
private final ZipkinUrlExtractor extractor;
ZipkinRestTemplateWrapper(ZipkinProperties zipkinProperties,
ZipkinUrlExtractor extractor) {
this.zipkinProperties = zipkinProperties;
this.extractor = extractor;
}
@Override protected <T> T doExecute(URI originalUrl, HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
URI uri = this.extractor.zipkinUrl(this.zipkinProperties);
URI newUri = resolvedZipkinUri(originalUrl, uri);
return super.doExecute(newUri, method, requestCallback, responseExtractor);
}
private URI resolvedZipkinUri(URI originalUrl, URI resolvedZipkinUri) {
try {
return new URI(resolvedZipkinUri.getScheme(), resolvedZipkinUri.getUserInfo(),
resolvedZipkinUri.getHost(), resolvedZipkinUri.getPort(), originalUrl.getPath(),
originalUrl.getQuery(), originalUrl.getFragment());
} catch (URISyntaxException e) {
if (log.isDebugEnabled()) {
log.debug("Failed to create the new URI from original [" + originalUrl + "] and new one [" + resolvedZipkinUri + "]");
}
return originalUrl;
}
}
}

View File

@@ -32,9 +32,9 @@ import zipkin2.reporter.Reporter;
/**
* Listener of Sleuth events. Reports to Zipkin via {@link Reporter}.
*/
public class ZipkinSpanListener implements SpanReporter {
public class ZipkinSpanReporter implements SpanReporter {
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory
.getLog(ZipkinSpanListener.class);
.getLog(ZipkinSpanReporter.class);
private final Reporter<zipkin2.Span> reporter;
private final Environment environment;
@@ -46,7 +46,7 @@ public class ZipkinSpanListener implements SpanReporter {
// Visible for testing
final EndpointLocator endpointLocator;
public ZipkinSpanListener(Reporter<zipkin2.Span> reporter, EndpointLocator endpointLocator,
public ZipkinSpanReporter(Reporter<zipkin2.Span> reporter, EndpointLocator endpointLocator,
Environment environment, List<SpanAdjuster> spanAdjusters) {
this.reporter = reporter;
this.endpointLocator = endpointLocator;
@@ -202,4 +202,9 @@ public class ZipkinSpanListener implements SpanReporter {
}
}
}
@Override
public String toString(){
return "ZipkinSpanReporter(" + this.reporter + ")";
}
}

View File

@@ -1,4 +1,4 @@
package org.springframework.cloud.sleuth.zipkin2;
package org.springframework.cloud.sleuth.zipkin2.sender;
import java.io.IOException;
import java.net.URI;

View File

@@ -0,0 +1,51 @@
package org.springframework.cloud.sleuth.zipkin2.sender;
import java.util.List;
import java.util.Map;
import org.apache.kafka.common.serialization.ByteArraySerializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import zipkin2.reporter.Sender;
import zipkin2.reporter.kafka11.KafkaSender;
@Configuration
@ConditionalOnClass(ByteArraySerializer.class)
@ConditionalOnBean(KafkaProperties.class)
@ConditionalOnMissingBean(Sender.class)
@Conditional(ZipkinSenderCondition.class)
class ZipkinKafkaSenderConfiguration {
@Value("${spring.zipkin.kafka.topic:zipkin}")
private String topic;
@Bean Sender kafkaSender(KafkaProperties config) {
Map<String, Object> properties = config.buildProducerProperties();
properties.put("key.serializer", ByteArraySerializer.class.getName());
properties.put("value.serializer", ByteArraySerializer.class.getName());
// Kafka expects the input to be a String, but KafkaProperties returns a list
Object bootstrapServers = properties.get("bootstrap.servers");
if (bootstrapServers instanceof List) {
properties.put("bootstrap.servers", join((List) bootstrapServers));
}
return KafkaSender.newBuilder()
.topic(this.topic)
.overrides(properties)
.build();
}
static String join(List<?> parts) {
StringBuilder to = new StringBuilder();
for (int i = 0, length = parts.size(); i < length; i++) {
to.append(parts.get(i));
if (i + 1 < length) {
to.append(',');
}
}
return to.toString();
}
}

View File

@@ -0,0 +1,29 @@
package org.springframework.cloud.sleuth.zipkin2.sender;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import zipkin2.reporter.Sender;
import zipkin2.reporter.amqp.RabbitMQSender;
@Configuration
@ConditionalOnBean(CachingConnectionFactory.class)
@ConditionalOnMissingBean(Sender.class)
@Conditional(ZipkinSenderCondition.class)
class ZipkinRabbitSenderConfiguration {
@Value("${spring.zipkin.rabbitmq.queue:zipkin}")
private String queue;
@Bean Sender rabbitSender(CachingConnectionFactory connectionFactory, RabbitProperties config) {
return RabbitMQSender.newBuilder()
.connectionFactory(connectionFactory.getRabbitConnectionFactory())
.queue(this.queue)
.addresses(config.determineAddresses())
.build();
}
}

View File

@@ -0,0 +1,131 @@
package org.springframework.cloud.sleuth.zipkin2.sender;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.sleuth.zipkin2.ZipkinProperties;
import org.springframework.cloud.sleuth.zipkin2.ZipkinRestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.ResponseExtractor;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import zipkin2.reporter.Sender;
@Configuration
@ConditionalOnMissingBean(Sender.class)
@Conditional(ZipkinSenderCondition.class)
class ZipkinRestTemplateSenderConfiguration {
@Autowired ZipkinUrlExtractor extractor;
@Bean
@ConditionalOnMissingBean
public Sender restTemplateSender(ZipkinProperties zipkin,
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
RestTemplate restTemplate = new ZipkinRestTemplateWrapper(zipkin, this.extractor);
zipkinRestTemplateCustomizer.customize(restTemplate);
return new RestTemplateSender(restTemplate, zipkin.getBaseUrl(), zipkin.getEncoder());
}
@Configuration
@ConditionalOnMissingClass("org.springframework.cloud.client.discovery.DiscoveryClient")
static class DefaultZipkinUrlExtractorConfiguration {
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
return URI.create(zipkinProperties.getBaseUrl());
}
};
}
}
@Configuration
@ConditionalOnClass(DiscoveryClient.class)
static class DiscoveryClientZipkinUrlExtractorConfiguration {
@Autowired(required = false) DiscoveryClient discoveryClient;
@Bean
ZipkinUrlExtractor zipkinUrlExtractor() {
final DiscoveryClient discoveryClient = this.discoveryClient;
return new ZipkinUrlExtractor() {
@Override
public URI zipkinUrl(ZipkinProperties zipkinProperties) {
if (discoveryClient != null) {
URI uri = URI.create(zipkinProperties.getBaseUrl());
String host = uri.getHost();
List<ServiceInstance> instances = discoveryClient.getInstances(host);
if (!instances.isEmpty()) {
return instances.get(0).getUri();
}
}
return URI.create(zipkinProperties.getBaseUrl());
}
};
}
}
}
/**
* Resolves at runtime where the Zipkin server is. If there's no discovery client then {@link URI}
* from the properties is taken. Otherwise service discovery is pinged for current Zipkin address.
*/
class ZipkinRestTemplateWrapper extends RestTemplate {
private static final Log log = LogFactory.getLog(ZipkinRestTemplateWrapper.class);
private final ZipkinProperties zipkinProperties;
private final ZipkinUrlExtractor extractor;
ZipkinRestTemplateWrapper(ZipkinProperties zipkinProperties,
ZipkinUrlExtractor extractor) {
this.zipkinProperties = zipkinProperties;
this.extractor = extractor;
}
@Override protected <T> T doExecute(URI originalUrl, HttpMethod method,
RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
URI uri = this.extractor.zipkinUrl(this.zipkinProperties);
URI newUri = resolvedZipkinUri(originalUrl, uri);
return super.doExecute(newUri, method, requestCallback, responseExtractor);
}
private URI resolvedZipkinUri(URI originalUrl, URI resolvedZipkinUri) {
try {
return new URI(resolvedZipkinUri.getScheme(), resolvedZipkinUri.getUserInfo(),
resolvedZipkinUri.getHost(), resolvedZipkinUri.getPort(), originalUrl.getPath(),
originalUrl.getQuery(), originalUrl.getFragment());
} catch (URISyntaxException e) {
if (log.isDebugEnabled()) {
log.debug("Failed to create the new URI from original ["
+ originalUrl
+ "] and new one ["
+ resolvedZipkinUri
+ "]");
}
return originalUrl;
}
}
}
/**
* Internal interface to provide a way to retrieve Zipkin URI. If there's no discovery client then
* this value will be taken from the properties. Otherwise host will be assumed to be a service id.
*/
interface ZipkinUrlExtractor {
URI zipkinUrl(ZipkinProperties zipkinProperties);
}

View File

@@ -0,0 +1,37 @@
package org.springframework.cloud.sleuth.zipkin2.sender;
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.ClassMetadata;
import static org.springframework.cloud.sleuth.zipkin2.sender.ZipkinSenderConfigurationImportSelector.getType;
/** Attach this to any new sender configuration. */
class ZipkinSenderCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata md) {
String sourceClass = "";
if (md instanceof ClassMetadata) {
sourceClass = ((ClassMetadata) md).getClassName();
}
ConditionMessage.Builder message = ConditionMessage.forCondition("ZipkinSender", sourceClass);
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(
context.getEnvironment(), "spring.zipkin.sender.");
if (!resolver.containsProperty("type")) {
return ConditionOutcome.match(message.because("automatic sender type"));
}
String senderType = getType(((AnnotationMetadata) md).getClassName());
String value = resolver.getProperty("type");
if (value.equals(senderType)) {
return ConditionOutcome.match(message.because(value + " sender type"));
}
return ConditionOutcome.noMatch(message.because(value + " sender type"));
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.cloud.sleuth.zipkin2.sender;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class ZipkinSenderConfigurationImportSelector implements ImportSelector {
static final Map<String, String> MAPPINGS;
// Classes below must be annotated with @Conditional(ZipkinSenderCondition.class)
static {
// Mappings in descending priority (highest is last)
Map<String, String> mappings = new LinkedHashMap<>();
mappings.put("rabbit", ZipkinRabbitSenderConfiguration.class.getName());
mappings.put("kafka", ZipkinKafkaSenderConfiguration.class.getName());
mappings.put("web", ZipkinRestTemplateSenderConfiguration.class.getName());
MAPPINGS = Collections.unmodifiableMap(mappings);
}
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return MAPPINGS.values().toArray(new String[0]);
}
static String getType(String configurationClassName) {
for (Map.Entry<String, String> entry : MAPPINGS.entrySet()) {
if (entry.getValue().equals(configurationClassName)) {
return entry.getKey();
}
}
throw new IllegalStateException("Unknown configuration class " + configurationClassName);
}
}

View File

@@ -23,13 +23,18 @@ import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;
import org.springframework.boot.autoconfigure.kafka.KafkaProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.cloud.sleuth.metric.TraceMetricsAutoConfiguration;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import zipkin2.reporter.amqp.RabbitMQSender;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.BDDAssertions.then;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
@@ -92,4 +97,78 @@ public class ZipkinAutoConfigurationTests {
then(request.getPath()).isEqualTo("/api/v1/spans");
then(request.getBody().readUtf8()).contains("binaryAnnotations");
}
@Test
public void overrideRabbitMQQueue() throws Exception {
context = new AnnotationConfigApplicationContext();
addEnvironment(context, "spring.zipkin.rabbitmq.queue:zipkin2");
context.register(
PropertyPlaceholderAutoConfiguration.class,
TraceMetricsAutoConfiguration.class,
RabbitAutoConfiguration.class,
ZipkinAutoConfiguration.class);
context.refresh();
SpanReporter spanReporter = context.getBean(SpanReporter.class);
assertThat(spanReporter).extracting("reporter.sender.queue")
.contains("zipkin2");
context.close();
}
@Test
public void overrideKafkaTopic() throws Exception {
context = new AnnotationConfigApplicationContext();
addEnvironment(context, "spring.zipkin.kafka.topic:zipkin2");
context.register(
PropertyPlaceholderAutoConfiguration.class,
TraceMetricsAutoConfiguration.class,
KafkaAutoConfiguration.class,
ZipkinAutoConfiguration.class);
context.refresh();
SpanReporter spanReporter = context.getBean(SpanReporter.class);
assertThat(spanReporter).extracting("reporter.sender.topic")
.contains("zipkin2");
context.close();
}
@Test
public void canOverrideBySender() throws Exception {
context = new AnnotationConfigApplicationContext();
addEnvironment(context, "spring.zipkin.sender.type:web");
context.register(
PropertyPlaceholderAutoConfiguration.class,
TraceMetricsAutoConfiguration.class,
RabbitAutoConfiguration.class,
KafkaAutoConfiguration.class,
ZipkinAutoConfiguration.class);
context.refresh();
SpanReporter spanReporter = context.getBean(SpanReporter.class);
assertThat(spanReporter).extracting("reporter.sender").allSatisfy(
s -> assertThat(s.getClass().getSimpleName()).isEqualTo("RestTemplateSender")
);
context.close();
}
@Test
public void rabbitWinsWhenKafkaPresent() throws Exception {
context = new AnnotationConfigApplicationContext();
context.register(
PropertyPlaceholderAutoConfiguration.class,
TraceMetricsAutoConfiguration.class,
RabbitAutoConfiguration.class,
KafkaAutoConfiguration.class,
ZipkinAutoConfiguration.class);
context.refresh();
SpanReporter spanReporter = context.getBean(SpanReporter.class);
assertThat(spanReporter).extracting("reporter.sender")
.allSatisfy(s -> assertThat(s).isInstanceOf(RabbitMQSender.class));
context.close();
}
}

View File

@@ -29,7 +29,9 @@ import zipkin.junit.ZipkinRule;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ZipkinDiscoveryClientTests.Config.class,
properties = {"spring.zipkin.baseUrl=http://zipkin/",
"spring.cloud.discovery.client.composite-indicator.enabled=false"})
"spring.cloud.discovery.client.composite-indicator.enabled=false",
"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();
@@ -112,4 +114,4 @@ class ZipkinDiscoveryClient implements DiscoveryClient {
return Collections.singletonList("zipkin");
}
}
}

View File

@@ -32,7 +32,7 @@ import org.springframework.cloud.sleuth.SpanAdjuster;
import org.springframework.cloud.sleuth.SpanReporter;
import org.springframework.cloud.sleuth.Tracer;
import org.springframework.cloud.sleuth.sampler.AlwaysSampler;
import org.springframework.cloud.sleuth.zipkin2.ZipkinSpanListenerTests.TestConfiguration;
import org.springframework.cloud.sleuth.zipkin2.ZipkinSpanReporterTests.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@@ -52,12 +52,12 @@ import static org.junit.Assert.assertEquals;
*/
@SpringBootTest(classes = TestConfiguration.class)
@RunWith(SpringRunner.class)
public class ZipkinSpanListenerTests {
public class ZipkinSpanReporterTests {
@Autowired Tracer tracer;
@Autowired TestConfiguration test;
@Autowired ZipkinSpanListener spanListener;
@Autowired Reporter<zipkin2.Span> spanReporter;
@Autowired ZipkinSpanReporter spanReporter;
@Autowired Reporter<zipkin2.Span> zipkinReporter;
@Autowired MockEnvironment mockEnvironment;
@Autowired EndpointLocator endpointLocator;
@@ -76,7 +76,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("hystrix/retry"); // System.currentTimeMillis
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.timestamp())
.isEqualTo(span.getBegin() * 1000);
@@ -97,7 +97,7 @@ public class ZipkinSpanListenerTests {
Thread.sleep(20);
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.timestamp()).isEqualTo(span.getBegin() * 1000);
long clientSendTimestamp = span.logs().stream()
@@ -114,7 +114,7 @@ public class ZipkinSpanListenerTests {
@Test
public void doesntSetDurationWhenStillRunning() {
Span span = Span.builder().traceId(1L).name("http:api").build();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.timestamp())
.isGreaterThan(0); // sanity check it did start
@@ -128,16 +128,16 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent("hystrix/retry");
this.parent.tag("spring-boot/version", "1.3.1.RELEASE");
zipkin2.Span result = this.spanListener.convert(this.parent);
zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.localEndpoint())
.isEqualTo(this.spanListener.endpointLocator.local());
.isEqualTo(this.spanReporter.endpointLocator.local());
}
/** zipkin's Endpoint.serviceName should never be null. */
@Test
public void localEndpointIncludesServiceName() {
assertThat(this.spanListener.endpointLocator.local().serviceName())
assertThat(this.spanReporter.endpointLocator.local().serviceName())
.isNotEmpty();
}
@@ -155,7 +155,7 @@ public class ZipkinSpanListenerTests {
Span context = this.tracer.createSpan("http:child", this.parent);
context.logEvent(Span.CLIENT_SEND);
logServerReceived(this.parent);
logServerSent(this.spanListener, this.parent);
logServerSent(this.spanReporter, this.parent);
this.tracer.close(context);
assertEquals(2, this.test.zipkinSpans.size());
}
@@ -178,7 +178,7 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent("hystrix/retry");
this.parent.stop();
zipkin2.Span result = this.spanListener.convert(this.parent);
zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.tags())
.isEmpty();
@@ -190,7 +190,7 @@ public class ZipkinSpanListenerTests {
this.parent.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "fooservice");
this.parent.stop();
zipkin2.Span result = this.spanListener.convert(this.parent);
zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.remoteEndpoint())
.isEqualTo(Endpoint.newBuilder().serviceName("fooservice").build());
@@ -201,7 +201,7 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent("cs");
this.parent.stop();
zipkin2.Span result = this.spanListener.convert(this.parent);
zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.remoteEndpoint())
.isNull();
@@ -211,7 +211,7 @@ public class ZipkinSpanListenerTests {
public void converts128BitTraceId() {
Span span = Span.builder().traceIdHigh(1L).traceId(2L).spanId(3L).name("foo").build();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.traceId())
.isEqualTo("00000000000000010000000000000002");
@@ -223,7 +223,7 @@ public class ZipkinSpanListenerTests {
this.parent.tag(Span.SPAN_PEER_SERVICE_TAG_NAME, "fooservice");
this.parent.stop();
zipkin2.Span result = this.spanListener.convert(this.parent);
zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.remoteEndpoint())
.isEqualTo(Endpoint.newBuilder().serviceName("fooservice").build());
@@ -233,7 +233,7 @@ public class ZipkinSpanListenerTests {
public void shouldNotReportToZipkinWhenSpanIsNotExportable() {
Span span = Span.builder().exportable(false).build();
this.spanListener.report(span);
this.spanReporter.report(span);
assertThat(this.test.zipkinSpans).isEmpty();
}
@@ -243,7 +243,7 @@ public class ZipkinSpanListenerTests {
this.parent.logEvent(Span.CLIENT_SEND);
this.mockEnvironment.setProperty("vcap.application.instance_id", "foo");
zipkin2.Span result = this.spanListener.convert(this.parent);
zipkin2.Span result = this.spanReporter.convert(this.parent);
assertThat(result.tags())
.containsExactly(entry(Span.INSTANCEID, "foo"));
@@ -252,7 +252,7 @@ public class ZipkinSpanListenerTests {
@Test
public void shouldNotAddAnyServiceIdTagWhenSpanContainsRpcEventAndThereIsNoEnvironment() {
this.parent.logEvent(Span.CLIENT_RECV);
ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter,
ZipkinSpanReporter spanListener = new ZipkinSpanReporter(this.zipkinReporter,
this.endpointLocator, null, new ArrayList<>());
zipkin2.Span result = spanListener.convert(this.parent);
@@ -264,7 +264,7 @@ public class ZipkinSpanListenerTests {
@Test
public void should_adjust_span_before_reporting_it() {
this.parent.logEvent(Span.CLIENT_RECV);
ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter,
ZipkinSpanReporter spanListener = new ZipkinSpanReporter(this.zipkinReporter,
this.endpointLocator, null, Collections.<SpanAdjuster>singletonList(
span -> Span.builder().from(span).name("foo").build())) {
@Override String defaultInstanceId() {
@@ -288,7 +288,7 @@ public class ZipkinSpanListenerTests {
.build();
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
assertThat(result.duration()).isNotNull();
assertThat(result.timestamp()).isNotNull();
@@ -302,7 +302,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("ss");
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.SERVER);
then(result.annotations()).isEmpty();
@@ -315,7 +315,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("cr");
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.CLIENT);
then(result.annotations()).isEmpty();
@@ -327,7 +327,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("ms");
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.PRODUCER);
then(result.annotations()).isEmpty();
@@ -339,7 +339,7 @@ public class ZipkinSpanListenerTests {
span.logEvent("mr");
span.stop();
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
then(result.kind()).isEqualTo(zipkin2.Span.Kind.CONSUMER);
then(result.annotations()).isEmpty();
@@ -364,7 +364,7 @@ public class ZipkinSpanListenerTests {
}
// end::service_name[]
zipkin2.Span result = this.spanListener.convert(span);
zipkin2.Span result = this.spanReporter.convert(span);
then(result.remoteEndpoint())
.isEqualTo(Endpoint.newBuilder().serviceName("redis").ip("1.2.3.4").port(1234).build());