add SleuthTracer that uses lower level brave classes

to enable it, set `spring.cloud.sleuth.zipkin.braveTracer.enabled=false`
This commit is contained in:
Spencer Gibb
2015-07-28 14:41:30 -06:00
parent 7a1ad19097
commit 0f84a5738d
7 changed files with 243 additions and 35 deletions

View File

@@ -1,5 +1,7 @@
package org.springframework.cloud.sleuth;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -22,10 +24,10 @@ public class MilliSpan implements Span {
@Singular
private List<String> parents;
private String spanId;
private Map<String, String> kVAnnotations;
private Map<String, String> kVAnnotations = new LinkedHashMap<>();
private String processId;
@Singular
private List<TimelineAnnotation> timelineAnnotations;
private List<TimelineAnnotation> timelineAnnotations = new ArrayList<>();
@Override
public synchronized void stop() {

View File

@@ -51,7 +51,7 @@ import org.springframework.web.util.UrlPathHelper;
public class TraceFilter extends OncePerRequestFilter {
public static final Pattern DEFAULT_SKIP_PATTERN = Pattern
.compile("/api-docs.*|/autoconfig|/configprops|/dump|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html");
.compile("/api-docs.*|/autoconfig|/configprops|/dump|/info|/metrics.*|/mappings|/trace|/swagger.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico");
private final Trace trace;
private final Pattern skipPattern;
@@ -79,7 +79,7 @@ public class TraceFilter extends OncePerRequestFilter {
if (!skip) {
String spanId = getHeader(request, response, SPAN_ID_NAME);
String traceId = getHeader(request, response, TRACE_ID_NAME);
String name = "traceFilter" + this.urlPathHelper.getPathWithinApplication(request);
String name = this.urlPathHelper.getPathWithinApplication(request);
if (hasText(spanId) && hasText(traceId)) {
TraceInfo traceInfo = new TraceInfo(traceId, spanId);

View File

@@ -56,6 +56,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>

View File

@@ -1,5 +1,7 @@
package org.springframework.cloud.sleuth.sample;
import com.github.kristofa.brave.LoggingSpanCollectorImpl;
import com.github.kristofa.brave.SpanCollector;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cloud.sleuth.Sampler;
@@ -34,9 +36,6 @@ public class SampleApplication {
SpringApplication.run(SampleApplication.class, args);
}
/*
* @Bean public SpanCollector spanCollector() { return new LoggingSpanCollectorImpl();
* }
*/
//@Bean public SpanCollector spanCollector() { return new LoggingSpanCollectorImpl(); }
}

View File

@@ -0,0 +1,206 @@
package org.springframework.cloud.sleuth.zipkin;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import lombok.extern.apachecommons.CommonsLog;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.cloud.sleuth.Span;
import org.springframework.cloud.sleuth.TimelineAnnotation;
import com.github.kristofa.brave.SpanCollector;
import com.twitter.zipkin.gen.Annotation;
import com.twitter.zipkin.gen.AnnotationType;
import com.twitter.zipkin.gen.BinaryAnnotation;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.zipkinCoreConstants;
import org.springframework.cloud.sleuth.event.SpanStoppedEvent;
import org.springframework.context.event.EventListener;
/**
* @author Spencer Gibb
*/
@CommonsLog
public class SleuthTracer {
private SpanCollector spanCollector;
@Value("${spring.application.name:application}")
private String appName;
@Autowired
private ServerProperties serverProperties;
public SleuthTracer(SpanCollector spanCollector) {
this.spanCollector = spanCollector;
}
@EventListener
public void start(SpanStoppedEvent event) {
this.spanCollector.collect(convert(event.getSpan()));
}
/**
* Converts a given HTrace span to a Zipkin Span.
* <ul>
* <li>First set the start annotation. [CS, SR], depending whether it is a client service or not.
* <li>Set other id's, etc [TraceId's etc]
* <li>Create binary annotations based on data from HTrace Span object.
* <li>Set the last annotation. [SS, CR]
* </ul>
*/
public com.twitter.zipkin.gen.Span convert(Span span) {
com.twitter.zipkin.gen.Span zipkinSpan = new com.twitter.zipkin.gen.Span();
String serviceName = getServiceName(span);
int address = getAddress();
Integer port = getPort();
Endpoint ep = new Endpoint(address, port.shortValue(), serviceName);
List<Annotation> annotationList = createZipkinAnnotations(span, ep);
List<BinaryAnnotation> binaryAnnotationList = createZipkinBinaryAnnotations(span, ep);
zipkinSpan.setTrace_id(hash(span.getTraceId()));
if (span.getParents().size() > 0) {
if (span.getParents().size() > 1) {
log.error("zipkin doesn't support spans with multiple parents. Omitting " +
"other parents for " + span);
}
zipkinSpan.setParent_id(hash(span.getParents().get(0)));
}
zipkinSpan.setId(hash(span.getSpanId()));
zipkinSpan.setName(span.getName());
zipkinSpan.setAnnotations(annotationList);
zipkinSpan.setBinary_annotations(binaryAnnotationList);
return zipkinSpan;
}
public Integer getPort() {
Integer port;
if (serverProperties.getPort() != null) {
port = serverProperties.getPort();
} else {
port = 8080; //TODO: support random port
}
return port;
}
public int getAddress() {
String address;
if (serverProperties.getAddress() != null) {
address = serverProperties.getAddress().getHostAddress();
} else {
address = "127.0.0.1"; //TODO: get address from config
}
return ipAddressToInt(address);
}
public String getServiceName(Span span) {
String serviceName;
if (span.getProcessId() != null) {
serviceName = span.getProcessId().toLowerCase();
} else {
serviceName = appName;
}
return serviceName;
}
private int ipAddressToInt(final String ip) {
InetAddress inetAddress = null;
try {
inetAddress = InetAddress.getByName(ip);
} catch (final UnknownHostException e) {
throw new IllegalArgumentException(e);
}
return ByteBuffer.wrap(inetAddress.getAddress()).getInt();
}
/**
* Add annotations from the sleuth Span.
*/
private List<Annotation> createZipkinAnnotations(Span span,
Endpoint ep) {
List<Annotation> annotationList = new ArrayList<>();
int duration = (int)(span.getEnd() - span.getBegin());
// add first zipkin annotation.
annotationList.add(createZipkinAnnotation(zipkinCoreConstants.CLIENT_SEND, span.getBegin(), 0, ep, true));
annotationList.add(createZipkinAnnotation(zipkinCoreConstants.SERVER_RECV, span.getBegin(), 0, ep, true));
// add sleuth time annotation
for (TimelineAnnotation ta : span.getTimelineAnnotations()) {
annotationList.add(createZipkinAnnotation(ta.getMsg(), ta.getTime(), 0, ep, true));
}
// add last zipkin annotation
annotationList.add(createZipkinAnnotation(zipkinCoreConstants.SERVER_SEND, span.getEnd(), duration, ep, false));
annotationList.add(createZipkinAnnotation(zipkinCoreConstants.CLIENT_RECV, span.getEnd(), duration, ep, false));
return annotationList;
}
/**
* Creates a list of Annotations that are present in sleuth Span object.
*
* @return list of Annotations that could be added to Zipkin Span.
*/
private List<BinaryAnnotation> createZipkinBinaryAnnotations(Span span,
Endpoint ep) {
List<BinaryAnnotation> l = new ArrayList<>();
for (Map.Entry<String, String> e : span.getKVAnnotations().entrySet()) {
BinaryAnnotation binaryAnn = new BinaryAnnotation();
binaryAnn.setAnnotation_type(AnnotationType.BYTES);
binaryAnn.setKey(e.getKey());
try {
binaryAnn.setValue(e.getValue().getBytes("UTF-8"));
} catch (UnsupportedEncodingException ex) {
log.error("Error encoding string as UTF-8", ex);
}
binaryAnn.setHost(ep);
l.add(binaryAnn);
}
return l;
}
/**
* Create an annotation with the correct times and endpoint.
*
* @param value Annotation value
* @param time timestamp will be extracted
* @param ep the endopint this annotation will be associated with.
* @param sendRequest use the first or last timestamp.
*/
private static Annotation createZipkinAnnotation(String value, long time, int duration,
Endpoint ep, boolean sendRequest) {
Annotation annotation = new Annotation();
annotation.setHost(ep);
// Zipkin is in microseconds
if (sendRequest) {
annotation.setTimestamp(time * 1000);
} else {
annotation.setTimestamp(time * 1000);
}
if (duration > 0) {
annotation.setDuration(duration * 1000);
}
annotation.setValue(value);
return annotation;
}
private static long hash(String string) {
long h = 1125899906842597L;
int len = string.length();
for (int i = 0; i < len; i++) {
h = 31 * h + string.charAt(i);
}
return h;
}
}

View File

@@ -4,6 +4,7 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
@@ -64,10 +65,17 @@ public class ZipkinAutoConfiguration {
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", matchIfMissing = true)
public ZipkinSpanListener zipkinTrace(ServerTracer serverTracer) {
return new ZipkinSpanListener(serverTracer);
}
@Bean
@ConditionalOnProperty(value = "spring.cloud.sleuth.zipkin.braveTracer.enabled", havingValue = "false")
public SleuthTracer sleuthTracer(SpanCollector spanCollector) {
return new SleuthTracer(spanCollector);
}
@Configuration
protected static class InterceptorConfig {

View File

@@ -35,37 +35,30 @@ public class ZipkinSpanListener {
protected void preTrace(Span context) {
final TraceData traceData = getTraceData(context);
if (Boolean.FALSE.equals(traceData.getShouldBeSampled())) {
this.serverTracer.setStateNoTracing();
log.debug("Received indication that we should NOT trace.");
final String spanName = getSpanName(context, traceData);
if (traceData.getTraceId() != null && traceData.getSpanId() != null) {
log.debug("Received span information as part of request.");
this.serverTracer.setStateCurrentTrace(traceData.getTraceId(),
traceData.getSpanId(), traceData.getParentSpanId(), spanName);
}
else {
final String spanName = getSpanName(context, traceData);
if (traceData.getTraceId() != null && traceData.getSpanId() != null) {
log.debug("Received span information as part of request.");
this.serverTracer.setStateCurrentTrace(traceData.getTraceId(),
traceData.getSpanId(), traceData.getParentSpanId(), spanName);
}
else {
log.debug("Received no span state.");
this.serverTracer.setStateUnknown(spanName);
}
this.serverTracer.setServerReceived();
log.debug("Received no span state.");
this.serverTracer.setStateUnknown(spanName);
}
this.serverTracer.setServerReceived();
}
protected TraceData getTraceData(Span context) {
TraceData trace = new TraceData();
trace.setTraceId(hash(context.getTraceId()));
trace.setSpanId(hash(context.getSpanId()));
trace.setShouldBeSampled(true);
trace.setSpanName(context.getName());
if (!context.getParents().isEmpty()) {
trace.setParentSpanId(hash(context.getParents().iterator().next()));
}
return trace;
};
}
protected String getSpanName(Span context, TraceData traceData) {
return context.getName();
@@ -83,8 +76,12 @@ public class ZipkinSpanListener {
}
}
protected ServerTracer getServerTracer() {
return this.serverTracer;
@Data
private static class TraceData {
private Long traceId;
private Long spanId;
private Long parentSpanId;
private String spanName;
}
private static long hash(String string) {
@@ -97,12 +94,4 @@ public class ZipkinSpanListener {
return h;
}
@Data
private static class TraceData {
private Long traceId;
private Long spanId;
private Long parentSpanId;
private Boolean shouldBeSampled;
private String spanName;
}
}