add SleuthTracer that uses lower level brave classes
to enable it, set `spring.cloud.sleuth.zipkin.braveTracer.enabled=false`
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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(); }
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user