Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uses latest zipkin library in support of simplified json format #43

Merged
merged 1 commit into from Aug 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -42,7 +42,7 @@
</scm>

<properties>
<zipkin.version>1.22.0</zipkin.version>
<zipkin.version>1.30.2</zipkin.version>
<cloud.trace.sdk.version>0.3.2</cloud.trace.sdk.version>
<grpc.version>1.0.2</grpc.version>
</properties>
Expand All @@ -53,7 +53,7 @@
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.4.2.RELEASE</version>
<version>1.5.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
Expand Down
46 changes: 45 additions & 1 deletion translation/pom.xml
Expand Up @@ -35,5 +35,49 @@
</dependency>

</dependencies>

<build>
<plugins>
<!-- Repackage internal zipkin classes until Span2 is a public type -->
<plugin>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
<createDependencyReducedPom>false</createDependencyReducedPom>
<relocations>
<relocation>
<pattern>zipkin.internal</pattern>
<shadedPattern>com.google.cloud.trace.zipkin.translation.internal.zipkin</shadedPattern>
</relocation>
</relocations>
<artifactSet>
<includes>
<include>io.zipkin.java:zipkin</include>
</includes>
</artifactSet>
<filters>
<filter>
<!-- Shade references to span2 until zipkin2 is out -->
<artifact>io.zipkin.java:zipkin</artifact>
<includes>
<include>zipkin/internal/*Span2*.class</include>
<include>zipkin/internal/Util.class</include>
</includes>
<excludes>
<exclude>*</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Expand Up @@ -17,16 +17,14 @@
package com.google.cloud.trace.zipkin.translation;

import com.google.common.collect.ImmutableMap;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import zipkin.Annotation;
import zipkin.BinaryAnnotation;
import zipkin.Span;
import zipkin.internal.Span2;
import zipkin.internal.Span2Converter;

/**
* LabelExtractor extracts the set of Stackdriver Span labels equivalent to the annotations in a given Zipkin Span.
Expand Down Expand Up @@ -60,27 +58,33 @@ public LabelExtractor(Map<String, String> renamedLabels) {
* @return A map of the Stackdriver span labels equivalent to the Zipkin annotations.
*/
public Map<String, String> extract(Span zipkinSpan) {
Map<String, String> labels = new HashMap<>();
for (BinaryAnnotation annotation : zipkinSpan.binaryAnnotations) {
labels.put(getLabelName(annotation.key), readBinaryAnnotation(annotation));
Map<String, String> result = new LinkedHashMap<>();
for (Span2 span2 : Span2Converter.fromSpan(zipkinSpan)) {
result.putAll(extract(span2));
}
return result;
}

Map<String, String> extract(Span2 zipkinSpan) { // not exposed until Span2 is a formal type
Map<String, String> result = new LinkedHashMap<>();
for (Map.Entry<String, String> tag : zipkinSpan.tags().entrySet()) {
result.put(getLabelName(tag.getKey()), tag.getValue());
}

for (Annotation annotation : zipkinSpan.annotations()) {
result.put(getLabelName(annotation.value), formatTimestamp(annotation.timestamp));
}

for (Annotation annotation : zipkinSpan.annotations) {
labels.put(getLabelName(annotation.value), formatTimestamp(annotation.timestamp));
if ("cs".equals(annotation.value) || "sr".equals(annotation.value)) {
// Consistently grab the serviceName from a specific annotation.
if (annotation.endpoint != null && annotation.endpoint.serviceName != null) {
labels.put(kComponentLabelKey, annotation.endpoint.serviceName);
}
}
if (zipkinSpan.localEndpoint() != null && !zipkinSpan.localEndpoint().serviceName.isEmpty()) {
result.put(kComponentLabelKey, zipkinSpan.localEndpoint().serviceName);
}

if (zipkinSpan.parentId == null) {
String agentName = System.getProperty("stackdriver.trace.zipkin.agent", "zipkin-java");
labels.put(kAgentLabelKey, agentName);
if (zipkinSpan.parentId() == null) {
String agentName = System.getProperty("stackdriver.trace.zipkin.agent", "zipkin-java");
result.put(kAgentLabelKey, agentName);
}

return labels;
return result;
}

private String getLabelName(String zipkinName) {
Expand All @@ -91,26 +95,6 @@ private String getLabelName(String zipkinName) {
}
}

private String readBinaryAnnotation(BinaryAnnotation annotation) {
// The value of a BinaryAnnotation is encoded in big endian order.
ByteBuffer buffer = ByteBuffer.wrap(annotation.value).order(ByteOrder.BIG_ENDIAN);
switch (annotation.type) {
case BOOL:
return annotation.value[0] == 1 ? "true" : "false";
case I16:
return Short.toString(buffer.getShort());
case I32:
return Integer.toString(buffer.getInt());
case I64:
return Long.toString(buffer.getLong());
case DOUBLE:
return Double.toString(buffer.getDouble());
case STRING:
default:
return new String(annotation.value, Charset.forName("UTF-8"));
}
}

private String formatTimestamp(long microseconds) {
long milliseconds = microseconds / 1000;
Date date = new Date(milliseconds);
Expand Down
Expand Up @@ -16,18 +16,15 @@

package com.google.cloud.trace.zipkin.translation;

import static zipkin.Constants.CLIENT_RECV;
import static zipkin.Constants.CLIENT_SEND;
import static zipkin.Constants.SERVER_RECV;
import static zipkin.Constants.SERVER_SEND;

import com.google.devtools.cloudtrace.v1.TraceSpan;
import com.google.devtools.cloudtrace.v1.TraceSpan.SpanKind;
import com.google.protobuf.Timestamp;
import java.util.HashMap;
import java.util.Map;
import zipkin.Annotation;
import javax.annotation.Nullable;
import zipkin.Span;
import zipkin.internal.Span2;
import zipkin.internal.Span2Converter;

/**
* SpanTranslator converts a Zipkin Span to a Stackdriver Trace Span.
Expand Down Expand Up @@ -61,69 +58,55 @@ public SpanTranslator() {
* @return A Stackdriver Trace Span.
*/
public TraceSpan translate(Span zipkinSpan) {
Map<String, Annotation> annotations = getAnnotations(zipkinSpan);
TraceSpan.Builder spanBuilder = TraceSpan.newBuilder();
TraceSpan.Builder builder = TraceSpan.newBuilder();
for (Span2 span : Span2Converter.fromSpan(zipkinSpan)) {
translate(builder, span);
}
return builder.build();
}

spanBuilder.setName(zipkinSpan.name);
SpanKind kind = getSpanKind(annotations);
TraceSpan.Builder translate(TraceSpan.Builder spanBuilder, Span2 zipkinSpan) {
spanBuilder.setName(zipkinSpan.name());
SpanKind kind = getSpanKind(zipkinSpan.kind());
spanBuilder.setKind(kind);
rewriteIds(zipkinSpan, spanBuilder, kind);
writeBestTimestamp(zipkinSpan, spanBuilder, annotations);
spanBuilder.putAllLabels(labelExtractor.extract(zipkinSpan));
return spanBuilder.build();
}

private void writeBestTimestamp(Span zipkinSpan, TraceSpan.Builder spanBuilder, Map<String, Annotation> annotations) {
if (zipkinSpan.timestamp != null) {
// Span.timestamp is the authoritative value if it's present.
spanBuilder.setStartTime(createTimestamp(zipkinSpan.timestamp));
spanBuilder.setEndTime(createTimestamp(zipkinSpan.timestamp + zipkinSpan.duration));
} else if (annotations.containsKey(CLIENT_SEND) && annotations.containsKey(CLIENT_RECV)) {
// Client timestamps are more authoritative than server timestamps.
spanBuilder.setStartTime(
createTimestamp(annotations.get(CLIENT_SEND).timestamp)
);
spanBuilder.setEndTime(
createTimestamp(annotations.get(CLIENT_RECV).timestamp)
);
} else if (annotations.containsKey(SERVER_RECV) && annotations.containsKey(SERVER_SEND)) {
spanBuilder.setStartTime(
createTimestamp(annotations.get(SERVER_RECV).timestamp)
);
spanBuilder.setEndTime(
createTimestamp(annotations.get(SERVER_SEND).timestamp)
);
if (zipkinSpan.timestamp() != null) {
spanBuilder.setStartTime(createTimestamp(zipkinSpan.timestamp()));
if (zipkinSpan.duration() != null) {
Timestamp endTime = createTimestamp(zipkinSpan.timestamp() + zipkinSpan.duration());
spanBuilder.setEndTime(endTime);
}
}
spanBuilder.putAllLabels(labelExtractor.extract(zipkinSpan));
return spanBuilder;
}

/**
* Rewrite Span IDs to split multi-host Zipkin spans into multiple single-host Stackdriver spans.
*/
private void rewriteIds(Span zipkinSpan, TraceSpan.Builder builder, SpanKind kind) {
private void rewriteIds(Span2 zipkinSpan, TraceSpan.Builder builder, SpanKind kind) {
// Change the spanId of RPC_CLIENT spans.
if (kind == SpanKind.RPC_CLIENT) {
builder.setSpanId(rewriteId(zipkinSpan.id));
builder.setSpanId(rewriteId(zipkinSpan.id()));
} else {
builder.setSpanId(zipkinSpan.id);
builder.setSpanId(zipkinSpan.id());
}

// Change the parentSpanId of RPC_SERVER spans to use the rewritten spanId of the RPC_CLIENT spans.
if (kind == SpanKind.RPC_SERVER) {
if (zipkinSpan.timestamp != null ) {
// The timestamp field should only be written by instrumentation whenever it "owns" a span.
// Because this field is here, we know that the server "owns" this span which implies this
// is a single-host span.
// This means the parent RPC_CLIENT span was a separate span with id=zipkinSpan.parentId. When
// that span fragment was converted, it would have had id=rewriteId(zipkinSpan.parentId)
builder.setParentSpanId(rewriteId(zipkinSpan.parentId));
} else {
if (Boolean.TRUE.equals(zipkinSpan.shared())) {
// This is a multi-host span.
// This means the parent client-side span has the same id as this span. When that fragment of
// the span was converted, it would have had id rewriteId(zipkinSpan.id)
builder.setParentSpanId(rewriteId(zipkinSpan.id));
builder.setParentSpanId(rewriteId(zipkinSpan.id()));
} else {
// This span isn't shared: the server "owns" this span and it is a single-host span.
// This means the parent RPC_CLIENT span was a separate span with id=zipkinSpan.parentId. When
// that span fragment was converted, it would have had id=rewriteId(zipkinSpan.parentId)
builder.setParentSpanId(rewriteId(zipkinSpan.parentId()));
}
} else {
long parentId = zipkinSpan.parentId == null ? 0 : zipkinSpan.parentId;
long parentId = zipkinSpan.parentId() == null ? 0 : zipkinSpan.parentId();
builder.setParentSpanId(parentId);
}
}
Expand All @@ -137,22 +120,15 @@ private long rewriteId(Long id) {
return id ^ pad;
}

private SpanKind getSpanKind(Map<String, Annotation> annotations) {
if (annotations.containsKey(CLIENT_SEND) || annotations.containsKey(CLIENT_RECV)) {
private SpanKind getSpanKind(@Nullable Span2.Kind zipkinKind) {
if (zipkinKind == null) return SpanKind.SPAN_KIND_UNSPECIFIED;
if (zipkinKind == Span2.Kind.CLIENT) {
return SpanKind.RPC_CLIENT;
}
if (annotations.containsKey(SERVER_SEND) || annotations.containsKey(SERVER_RECV)) {
if (zipkinKind == Span2.Kind.SERVER) {
return SpanKind.RPC_SERVER;
}
return SpanKind.SPAN_KIND_UNSPECIFIED;
}

private Map<String, Annotation> getAnnotations(Span zipkinSpan) {
Map<String, Annotation> annotations = new HashMap<>();
for (Annotation annotation : zipkinSpan.annotations) {
annotations.put(annotation.value, annotation);
}
return annotations;
return SpanKind.UNRECOGNIZED;
}

private Timestamp createTimestamp(long microseconds) {
Expand Down