Skip to content

Commit

Permalink
Propagates logs and reports 128-bit trace IDs
Browse files Browse the repository at this point in the history
This supports 128-bit traces via a new field traceIdHigh, which matches
other zipkin implementations. In encoded form, the trace ID is simply
twice as long (32 hex characters).

With this change in, a 128-bit trace propagated will not be downgraded
to 64-bits when sending downstream, reporting to Zipkin or adding to
the logging context.

This will be followed by a change to support initiating 128-bit traces.

See openzipkin/b3-propagation#6
  • Loading branch information
Adrian Cole committed Nov 17, 2016
1 parent c943e4c commit cbca519
Show file tree
Hide file tree
Showing 18 changed files with 311 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ public class Span implements SpanContext {
private final long begin;
private long end = 0;
private final String name;
private final long traceIdHigh;
private final long traceId;
private List<Long> parents = new ArrayList<>();
private final long spanId;
Expand Down Expand Up @@ -169,6 +170,7 @@ public Span(Span current, Span savedSpan) {
this.begin = current.getBegin();
this.end = current.getEnd();
this.name = current.getName();
this.traceIdHigh = current.getTraceIdHigh();
this.traceId = current.getTraceId();
this.parents = current.getParents();
this.spanId = current.getSpanId();
Expand All @@ -183,37 +185,63 @@ public Span(Span current, Span savedSpan) {
this.savedSpan = savedSpan;
}

/**
* @deprecated please use {@link SpanBuilder}
*/
@Deprecated
public Span(long begin, long end, String name, long traceId, List<Long> parents,
long spanId, boolean remote, boolean exportable, String processId) {
this(begin, end, name, traceId, parents, spanId, remote, exportable, processId,
null);
}

/**
* @deprecated please use {@link SpanBuilder}
*/
@Deprecated
public Span(long begin, long end, String name, long traceId, List<Long> parents,
long spanId, boolean remote, boolean exportable, String processId,
Span savedSpan) {
if (begin > 0) { // conventionally, 0 indicates unset
this(new SpanBuilder()
.begin(begin)
.end(end)
.name(name)
.traceId(traceId)
.parents(parents)
.spanId(spanId)
.remote(remote)
.exportable(exportable)
.processId(processId)
.savedSpan(savedSpan));
}

Span(SpanBuilder builder) {
if (builder.begin > 0) { // conventionally, 0 indicates unset
this.startNanos = null; // don't know the start tick
this.begin = begin;
this.begin = builder.begin;
} else {
this.startNanos = nanoTime();
this.begin = System.currentTimeMillis();
}
if (end > 0) {
this.end = end;
this.durationMicros = (end - begin) * 1000;
if (builder.end > 0) {
this.end = builder.end;
this.durationMicros = (this.end - this.begin) * 1000;
}
this.name = name != null ? name : "";
this.traceId = traceId;
this.parents = parents;
this.spanId = spanId;
this.remote = remote;
this.exportable = exportable;
this.processId = processId;
this.savedSpan = savedSpan;
this.name = builder.name != null ? builder.name : "";
this.traceIdHigh = builder.traceIdHigh;
this.traceId = builder.traceId;
this.parents.addAll(builder.parents);
this.spanId = builder.spanId;
this.remote = builder.remote;
this.exportable = builder.exportable;
this.processId = builder.processId;
this.savedSpan = builder.savedSpan;
this.tags = new ConcurrentHashMap<>();
this.tags.putAll(builder.tags);
this.logs = new ConcurrentLinkedQueue<>();
this.logs.addAll(builder.logs);
this.baggage = new ConcurrentHashMap<>();
this.baggage.putAll(builder.baggage);
}

public static SpanBuilder builder() {
Expand Down Expand Up @@ -397,7 +425,30 @@ public long getSpanId() {
}

/**
* A pseudo-unique (random) number assigned to the trace associated with this span
* When non-zero, the trace containing this span uses 128-bit trace identifiers.
*
* <p>{@code traceIdHigh} corresponds to the high bits in big-endian format and
* {@link #getTraceId()} corresponds to the low bits.
*
* <p>Ex. to convert the two fields to a 128bit opaque id array, you'd use code like below.
* <pre>{@code
* ByteBuffer traceId128 = ByteBuffer.allocate(16);
* traceId128.putLong(span.getTraceIdHigh());
* traceId128.putLong(span.getTraceId());
* traceBytes = traceId128.array();
* }</pre>
*
* @see #traceIdString()
* @since 1.2.0
*/
public long getTraceIdHigh() {
return this.traceIdHigh;
}

/**
* Unique 8-byte identifier for a trace, set on all spans within it.
*
* @see #getTraceIdHigh() for notes about 128-bit trace identifiers
*/
public long getTraceId() {
return this.traceId;
Expand Down Expand Up @@ -452,8 +503,27 @@ public boolean isExportable() {
return this.exportable;
}

/**
* Returns the 16 or 32 character hex representation of the span's trace ID
*
* @since 1.2.0
*/
public String traceIdString() {
if (this.traceIdHigh != 0) {
char[] result = new char[32];
writeHexLong(result, 0, this.traceIdHigh);
writeHexLong(result, 16, this.traceId);
return new String(result);
}
char[] result = new char[16];
writeHexLong(result, 0, this.traceId);
return new String(result);
}

/**
* Represents given long id as 16-character lower-hex string
*
* @see #traceIdString()
*/
public static String idToHex(long id) {
char[] data = new char[16];
Expand Down Expand Up @@ -488,29 +558,40 @@ static void writeHexByte(char[] data, int pos, byte b) {
public static long hexToId(String hexString) {
Assert.hasText(hexString, "Can't convert empty hex string to long");
int length = hexString.length();
if (length < 1 || length > 32) throw new IllegalArgumentException("Malformed id");
if (length < 1 || length > 32) throw new IllegalArgumentException("Malformed id: " + hexString);

// trim off any high bits
int i = length > 16 ? length - 16 : 0;
int beginIndex = length > 16 ? length - 16 : 0;

return hexToId(hexString, beginIndex);
}

/**
* Parses a 16 character lower-hex string with no prefix into an unsigned long, starting at the
* specified index.
*
* @since 1.2.0
*/
public static long hexToId(String lowerHex, int index) {
Assert.hasText(lowerHex, "Can't convert empty hex string to long");
long result = 0;
for (; i < length; i++) {
char c = hexString.charAt(i);
for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {
char c = lowerHex.charAt(index);
result <<= 4;
if (c >= '0' && c <= '9') {
result |= c - '0';
} else if (c >= 'a' && c <= 'f') {
result |= c - 'a' + 10;
} else {
throw new IllegalArgumentException("Malformed id");
throw new IllegalArgumentException("Malformed id: " + lowerHex);
}
}
return result;
}

@Override
public String toString() {
return "[Trace: " + idToHex(this.traceId) + ", Span: " + idToHex(this.spanId)
return "[Trace: " + traceIdString() + ", Span: " + idToHex(this.spanId)
+ ", Parent: " + getParentIdIfPresent() + ", exportable:" + this.exportable + "]";
}

Expand All @@ -520,31 +601,36 @@ private String getParentIdIfPresent() {

@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (this.spanId ^ (this.spanId >>> 32));
result = prime * result + (int) (this.traceId ^ (this.traceId >>> 32));
return result;
int h = 1;
h *= 1000003;
h ^= (this.traceIdHigh >>> 32) ^ this.traceIdHigh;
h *= 1000003;
h ^= (this.traceId >>> 32) ^ this.traceId;
h *= 1000003;
h ^= (this.spanId >>> 32) ^ this.spanId;
h *= 1000003;
return h;
}

@Override
public boolean equals(Object obj) {
if (this == obj)
public boolean equals(Object o) {
if (o == this) {
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Span other = (Span) obj;
if (this.spanId != other.spanId)
return false;
return this.traceId == other.traceId;
}
if (o instanceof Span) {
Span that = (Span) o;
return (this.traceIdHigh == that.traceIdHigh)
&& (this.traceId == that.traceId)
&& (this.spanId == that.spanId);
}
return false;
}

public static class SpanBuilder {
private long begin;
private long end;
private String name;
private long traceIdHigh;
private long traceId;
private ArrayList<Long> parents = new ArrayList<>();
private long spanId;
Expand Down Expand Up @@ -581,6 +667,11 @@ public Span.SpanBuilder name(String name) {
return this;
}

public Span.SpanBuilder traceIdHigh(long traceIdHigh) {
this.traceIdHigh = traceIdHigh;
return this;
}

public Span.SpanBuilder traceId(long traceId) {
this.traceId = traceId;
return this;
Expand Down Expand Up @@ -652,23 +743,12 @@ public Span.SpanBuilder savedSpan(Span savedSpan) {
}

public Span build() {
Span span = new Span(this.begin, this.end, this.name, this.traceId,
this.parents, this.spanId, this.remote, this.exportable,
this.processId, this.savedSpan);
span.logs.addAll(this.logs);
span.tags.putAll(this.tags);
span.baggage.putAll(this.baggage);
return span;
return new Span(this);
}

@Override
public String toString() {
return "SpanBuilder{" + "begin=" + this.begin + ", end=" + this.end
+ ", name=" + this.name + ", traceId=" + this.traceId + ", parents="
+ this.parents + ", spanId=" + this.spanId + ", remote=" + this.remote
+ ", exportable=" + this.exportable + ", processId='" + this.processId
+ '\'' + ", savedSpan=" + this.savedSpan + ", logs=" + this.logs
+ ", tags=" + this.tags + '}';
return new Span(this).toString();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ public Span joinTrace(SpanTextMap textMap) {
return extractSpanFromHeaders(carrier, Span.builder());
}

private Span extractSpanFromHeaders(Map<String, String> carrier, Span.SpanBuilder builder) {
Span.SpanBuilder spanBuilder = builder;
long traceId = Span
.hexToId(carrier.get(TraceMessageHeaders.TRACE_ID_NAME));
long spanId = Span.hexToId(carrier.get(TraceMessageHeaders.SPAN_ID_NAME));
spanBuilder = spanBuilder.traceId(traceId).spanId(spanId);
private Span extractSpanFromHeaders(Map<String, String> carrier, Span.SpanBuilder spanBuilder) {
String traceId = carrier.get(TraceMessageHeaders.TRACE_ID_NAME);
spanBuilder = spanBuilder
.traceIdHigh(traceId.length() == 32 ? Span.hexToId(traceId, 0) : 0)
.traceId(Span.hexToId(traceId))
.spanId(Span.hexToId(carrier.get(TraceMessageHeaders.SPAN_ID_NAME)));
spanBuilder.exportable(
Span.SPAN_SAMPLED.equals(carrier.get(TraceMessageHeaders.SAMPLED_NAME)));
String processId = carrier.get(TraceMessageHeaders.PROCESS_ID_NAME);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private boolean isSampled(Map<String, String> initialMessage, String sampledHead
}

private void addHeaders(Span span, SpanTextMap textMap) {
addHeader(textMap, TraceMessageHeaders.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
addHeader(textMap, TraceMessageHeaders.TRACE_ID_NAME, span.traceIdString());
addHeader(textMap, TraceMessageHeaders.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
if (span.isExportable()) {
addAnnotations(this.traceKeys, textMap, span);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,33 @@ public Span joinTrace(SpanTextMap textMap) {
String uri = carrier.get(URI_HEADER);
boolean skip = this.skipPattern.matcher(uri).matches()
|| Span.SPAN_NOT_SAMPLED.equals(carrier.get(Span.SAMPLED_NAME));
long traceId = Span
.hexToId(carrier.get(Span.TRACE_ID_NAME));
long spanId = spanId(carrier, traceId);
return buildParentSpan(carrier, uri, skip, traceId, spanId);
long spanId = spanId(carrier);
return buildParentSpan(carrier, uri, skip, spanId);
} catch (Exception e) {
log.error("Exception occurred while trying to extract span from carrier", e);
return null;
}
}

private long spanId(Map<String, String> carrier, long traceId) {
private long spanId(Map<String, String> carrier) {
String spanId = carrier.get(Span.SPAN_ID_NAME);
if (spanId == null) {
if (log.isDebugEnabled()) {
log.debug("Request is missing a span id but it has a trace id. We'll assume that this is "
+ "a root span with span id equal to trace id");
+ "a root span with span id equal to the lower 64-bits of the trace id");
}
return traceId;
return Span.hexToId(carrier.get(Span.TRACE_ID_NAME));
} else {
return Span.hexToId(spanId);
}
}

private Span buildParentSpan(Map<String, String> carrier, String uri, boolean skip,
long traceId, long spanId) {
Span.SpanBuilder span = Span.builder().traceId(traceId).spanId(spanId);
private Span buildParentSpan(Map<String, String> carrier, String uri, boolean skip, long spanId) {
String traceId = carrier.get(Span.TRACE_ID_NAME);
Span.SpanBuilder span = Span.builder()
.traceIdHigh(traceId.length() == 32 ? Span.hexToId(traceId, 0) : 0)
.traceId(Span.hexToId(traceId))
.spanId(spanId);
String processId = carrier.get(Span.PROCESS_ID_NAME);
String parentName = carrier.get(Span.SPAN_NAME_NAME);
if (StringUtils.hasText(parentName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ZipkinHttpSpanInjector implements HttpSpanInjector {

@Override
public void inject(Span span, SpanTextMap carrier) {
setIdHeader(carrier, Span.TRACE_ID_NAME, span.getTraceId());
setHeader(carrier, Span.TRACE_ID_NAME, span.traceIdString());
setIdHeader(carrier, Span.SPAN_ID_NAME, span.getSpanId());
setHeader(carrier, Span.SAMPLED_NAME, span.isExportable() ? Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
setHeader(carrier, Span.SPAN_NAME_NAME, span.getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void inject(Span span, SpanTextMap carrier) {
}
carrier.put(Span.SAMPLED_NAME, span.isExportable() ?
Span.SPAN_SAMPLED : Span.SPAN_NOT_SAMPLED);
carrier.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
carrier.put(Span.TRACE_ID_NAME, span.traceIdString());
carrier.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
carrier.put(Span.SPAN_NAME_NAME, span.getName());
if (getParentId(span) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public Slf4jSpanLogger(String nameSkipPattern) {
public void logStartedSpan(Span parent, Span span) {
MDC.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
MDC.put(Span.SPAN_EXPORT_NAME, String.valueOf(span.isExportable()));
MDC.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
MDC.put(Span.TRACE_ID_NAME, span.traceIdString());
log("Starting span: {}", span);
if (parent != null) {
log("With parent: {}", parent);
Expand All @@ -59,7 +59,7 @@ public void logStartedSpan(Span parent, Span span) {
@Override
public void logContinuedSpan(Span span) {
MDC.put(Span.SPAN_ID_NAME, Span.idToHex(span.getSpanId()));
MDC.put(Span.TRACE_ID_NAME, Span.idToHex(span.getTraceId()));
MDC.put(Span.TRACE_ID_NAME, span.traceIdString());
MDC.put(Span.SPAN_EXPORT_NAME, String.valueOf(span.isExportable()));
log("Continued span: {}", span);
}
Expand Down
Loading

0 comments on commit cbca519

Please sign in to comment.