Skip to content

Commit

Permalink
Supports 128-bit trace IDs, generating them on traceId128Bit(true) (#258
Browse files Browse the repository at this point in the history
)

Traditionally, Zipkin trace IDs were 64-bit. Starting with Zipkin 1.14,
128-bit trace identifiers are supported. This can be useful in sites that
have very large traffic volume, persist traces forever, or are re-using
externally generated 128-bit IDs.

If you want Brave to generate 128-bit trace identifiers when starting new
root spans, set `Brave.Builder.traceId128Bit(true)`

When 128-bit trace ids are propagated, they will be twice as long as
before. For example, the `X-B3-TraceId` header will hold a 32-character
value like `163ac35c9f6413ad48485a3953bb6124`.

Before doing this, ensure your Zipkin setup is up-to-date, and downstream
instrumented services can read the longer 128-bit trace IDs.

Note: this only affects the trace ID, not span IDs. For example, span ids
within a trace are always 64-bit.
  • Loading branch information
adriancole authored Oct 28, 2016
1 parent 0818902 commit 181a2f4
Show file tree
Hide file tree
Showing 37 changed files with 531 additions and 211 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.github.kristofa.brave.ClientRequestInterceptor;
import com.github.kristofa.brave.ClientResponseInterceptor;
import com.github.kristofa.brave.ClientTracer;
import com.github.kristofa.brave.IdConversion;
import com.github.kristofa.brave.SpanId;
import com.github.kristofa.brave.http.BraveHttpHeaders;
import com.github.kristofa.brave.http.DefaultSpanNameProvider;
Expand Down Expand Up @@ -43,6 +44,8 @@ public class ITBraveHttpRequestAndResponseInterceptor {
private static final Long SPAN_ID = 151864L;

private static final Long TRACE_ID = 8494864L;
private static final String TRACE_ID_STRING =
SpanId.builder().spanId(TRACE_ID).build().traceIdString();
private MockHttpServer mockServer;
private DefaultHttpResponseProvider responseProvider;
private ClientTracer clientTracer;
Expand All @@ -69,7 +72,7 @@ public void testTracingTrue() throws ClientProtocolException, IOException, Unsat

final HttpRequestImpl request = new HttpRequestImpl();
request.method(Method.GET).path(FULL_PATH)
.httpMessageHeader(BraveHttpHeaders.TraceId.getName(), Long.toString(TRACE_ID, 16))
.httpMessageHeader(BraveHttpHeaders.TraceId.getName(), TRACE_ID_STRING)
.httpMessageHeader(BraveHttpHeaders.SpanId.getName(), Long.toString(SPAN_ID, 16))
.httpMessageHeader(BraveHttpHeaders.Sampled.getName(), "1");
final HttpResponseImpl response = new HttpResponseImpl(200, null, null);
Expand Down Expand Up @@ -105,7 +108,7 @@ public void testTracingTrueHttpNoOk() throws ClientProtocolException, IOExceptio

final HttpRequestImpl request = new HttpRequestImpl();
request.method(Method.GET).path(FULL_PATH)
.httpMessageHeader(BraveHttpHeaders.TraceId.getName(), Long.toString(TRACE_ID, 16))
.httpMessageHeader(BraveHttpHeaders.TraceId.getName(), TRACE_ID_STRING)
.httpMessageHeader(BraveHttpHeaders.SpanId.getName(), Long.toString(SPAN_ID, 16))
.httpMessageHeader(BraveHttpHeaders.Sampled.getName(), "1");
final HttpResponseImpl response = new HttpResponseImpl(400, null, null);
Expand Down Expand Up @@ -173,7 +176,7 @@ public void testQueryParams() throws ClientProtocolException, IOException, Unsat

final HttpRequestImpl request = new HttpRequestImpl();
request.method(Method.GET).path(FULL_PATH).queryParameter("x", "1").queryParameter("y", "2")
.httpMessageHeader(BraveHttpHeaders.TraceId.getName(), Long.toString(TRACE_ID, 16))
.httpMessageHeader(BraveHttpHeaders.TraceId.getName(), TRACE_ID_STRING)
.httpMessageHeader(BraveHttpHeaders.SpanId.getName(), Long.toString(SPAN_ID, 16))
.httpMessageHeader(BraveHttpHeaders.Sampled.getName(), "1");
final HttpResponseImpl response = new HttpResponseImpl(200, null, null);
Expand Down
20 changes: 20 additions & 0 deletions brave-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,23 @@ will get proper trace/span state.

Instead of using `BraveExecutorService` or the `ServerSpanThreadBinder` directly you can also
use the `BraveCallable` and `BraveRunnable`. These are used internally by the BraveExecutorService.

## 128-bit trace IDs

Traditionally, Zipkin trace IDs were 64-bit. Starting with Zipkin 1.14,
128-bit trace identifiers are supported. This can be useful in sites that
have very large traffic volume, persist traces forever, or are re-using
externally generated 128-bit IDs.

If you want Brave to generate 128-bit trace identifiers when starting new
root spans, set `Brave.Builder.traceId128Bit(true)`

When 128-bit trace ids are propagated, they will be twice as long as
before. For example, the `X-B3-TraceId` header will hold a 32-character
value like `163ac35c9f6413ad48485a3953bb6124`.

Before doing this, ensure your Zipkin setup is up-to-date, and downstream
instrumented services can read the longer 128-bit trace IDs.

Note: this only affects the trace ID, not span IDs. For example, span ids
within a trace are always 64-bit.
2 changes: 1 addition & 1 deletion brave-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
<dependency>
<groupId>io.zipkin.reporter</groupId>
<artifactId>zipkin-reporter</artifactId>
<version>0.6.1</version>
<version>0.6.4</version>
</dependency>
<!-- for value types... don't worry. this dependency is compile only! -->
<dependency>
Expand Down
10 changes: 10 additions & 0 deletions brave-core/src/main/java/com/github/kristofa/brave/Brave.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public static class Builder {
private Sampler sampler = Sampler.create(1.0f);
private boolean allowNestedLocalSpans = false;
private AnnotationSubmitter.Clock clock = AnnotationSubmitter.DefaultClock.INSTANCE;
private boolean traceId128Bit = false;

/**
* Builder which initializes with serviceName = "unknown".
Expand Down Expand Up @@ -156,6 +157,12 @@ public Builder clock(AnnotationSubmitter.Clock clock) {
return this;
}

/** When true, new root spans will have 128-bit trace IDs. Defaults to false (64-bit) */
public Builder traceId128Bit(boolean traceId128Bit) {
this.traceId128Bit = traceId128Bit;
return this;
}

public Brave build() {
return new Brave(this);
}
Expand Down Expand Up @@ -260,6 +267,7 @@ private Brave(Builder builder) {
.state(builder.state)
.traceSampler(builder.sampler)
.clock(builder.clock)
.traceId128Bit(builder.traceId128Bit)
.build();

clientTracer = ClientTracer.builder()
Expand All @@ -268,6 +276,7 @@ private Brave(Builder builder) {
.state(builder.state)
.traceSampler(builder.sampler)
.clock(builder.clock)
.traceId128Bit(builder.traceId128Bit)
.build();

localTracer = LocalTracer.builder()
Expand All @@ -277,6 +286,7 @@ private Brave(Builder builder) {
.spanAndEndpoint(SpanAndEndpoint.LocalSpanAndEndpoint.create(builder.state))
.traceSampler(builder.sampler)
.clock(builder.clock)
.traceId128Bit(builder.traceId128Bit)
.build();

serverRequestInterceptor = new ServerRequestInterceptor(serverTracer);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.github.kristofa.brave;

import com.google.auto.value.AutoValue;

import com.github.kristofa.brave.SpanAndEndpoint.ClientSpanAndEndpoint;
import com.github.kristofa.brave.internal.Nullable;
import com.google.auto.value.AutoValue;
import com.twitter.zipkin.gen.Endpoint;
import com.twitter.zipkin.gen.Span;
import zipkin.Constants;

import java.util.Random;
import zipkin.Constants;
import zipkin.reporter.Reporter;

/**
Expand Down Expand Up @@ -40,6 +38,7 @@ public static Builder builder() {
abstract Sampler traceSampler();
@Override
abstract AnnotationSubmitter.Clock clock();
abstract boolean traceId128Bit();

@AutoValue.Builder
public abstract static class Builder {
Expand Down Expand Up @@ -67,6 +66,7 @@ public final Builder spanCollector(SpanCollector spanCollector) {

public abstract Builder traceSampler(Sampler sampler);
public abstract Builder clock(AnnotationSubmitter.Clock clock);
abstract Builder traceId128Bit(boolean traceId128Bit);

public abstract ClientTracer build();
}
Expand Down Expand Up @@ -156,8 +156,13 @@ private SpanId getNewSpanId() {

long newSpanId = randomGenerator().nextLong();
SpanId.Builder builder = SpanId.builder().spanId(newSpanId);
if (parentSpan == null) return builder.build(); // new trace
return builder.traceId(parentSpan.getTrace_id()).parentId(parentSpan.getId()).build();
if (parentSpan == null) { // new trace
if (traceId128Bit()) builder.traceIdHigh(randomGenerator().nextLong());
return builder.build();
}
return builder.traceIdHigh(parentSpan.getTrace_id_high())
.traceId(parentSpan.getTrace_id())
.parentId(parentSpan.getId()).build();
}

ClientTracer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,24 @@ public static String convertToString(final long id) {
* Parses a 1 to 32 character lower-hex string with no prefix into an unsigned long, tossing any
* bits higher than 64.
*/
public static long convertToLong(final String lowerHex) {
public static long convertToLong(String lowerHex) {
int length = lowerHex.length();
if (length < 1 || length > 32) throw isntLowerHexLong(lowerHex);

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

return convertToLong(lowerHex, beginIndex);
}

/**
* Parses a 16 character lower-hex string with no prefix into an unsigned long, starting at the
* specified index.
*/
public static long convertToLong(String lowerHex, int index) {
long result = 0;
for (; i < length; i++) {
char c = lowerHex.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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ static Builder builder(LocalTracer source) {
@Override
abstract AnnotationSubmitter.Clock clock();

abstract boolean traceId128Bit();

@AutoValue.Builder
abstract static class Builder {

Expand All @@ -75,6 +77,8 @@ abstract static class Builder {

abstract Builder clock(AnnotationSubmitter.Clock clock);

abstract Builder traceId128Bit(boolean traceId128Bit);

abstract LocalTracer build();

}
Expand All @@ -101,8 +105,13 @@ private SpanId getNewSpanId() {
Span parentSpan = getNewSpanParent();
long newSpanId = randomGenerator().nextLong();
SpanId.Builder builder = SpanId.builder().spanId(newSpanId);
if (parentSpan == null) return builder.build(); // new trace
return builder.traceId(parentSpan.getTrace_id()).parentId(parentSpan.getId()).build();
if (parentSpan == null) { // new trace
if (traceId128Bit()) builder.traceIdHigh(randomGenerator().nextLong());
return builder.traceId(newSpanId).build();
}
return builder.traceIdHigh(parentSpan.getTrace_id_high())
.traceId(parentSpan.getTrace_id())
.parentId(parentSpan.getId()).build();
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,7 @@ public void handle(ServerRequestAdapter adapter) {
boolean clientOriginatedTrace = traceData.getSpanId() != null;
if (clientOriginatedTrace) {
LOGGER.fine("Received span information as part of request.");
SpanId spanId = traceData.getSpanId();
serverTracer.setStateCurrentTrace(spanId.traceId, spanId.spanId,
spanId.nullableParentId(), adapter.getSpanName());
serverTracer.setStateCurrentTrace(traceData.getSpanId(), adapter.getSpanName());
} else {
LOGGER.fine("Received no span state.");
serverTracer.setStateUnknown(adapter.getSpanName());
Expand Down
38 changes: 6 additions & 32 deletions brave-core/src/main/java/com/github/kristofa/brave/ServerSpan.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import com.twitter.zipkin.gen.Span;

import static com.github.kristofa.brave.internal.Util.checkNotNull;

/**
* The ServerSpan is initialized by {@link ServerTracer} and keeps track of Trace/Span state of our service request.
*
Expand All @@ -13,8 +15,8 @@
@AutoValue
public abstract class ServerSpan {

public final static ServerSpan EMPTY = ServerSpan.create(null);
static final ServerSpan NOT_SAMPLED = ServerSpan.create(false);
public static final ServerSpan EMPTY = new AutoValue_ServerSpan(null, null);
static final ServerSpan NOT_SAMPLED = new AutoValue_ServerSpan(null, false);

/**
* Gets the Trace/Span context.
Expand All @@ -34,36 +36,8 @@ public abstract class ServerSpan {
@Nullable
public abstract Boolean getSample();

static ServerSpan create(Span span, Boolean sample) {
return new AutoValue_ServerSpan(span, sample);
}

/**
* Creates a new initializes instance. Using this constructor also indicates we need to sample this request.
*
* @param traceId Trace id.
* @param spanId Span id.
* @param parentSpanId Parent span id, can be <code>null</code>.
* @param name Span name. Should be lowercase and not <code>null</code> or empty.
*/
static ServerSpan create(long traceId, long spanId, @Nullable Long parentSpanId, String name) {
Span span = new Span();
span.setTrace_id(traceId);
span.setId(spanId);
if (parentSpanId != null) {
span.setParent_id(parentSpanId);
}
span.setName(name);
return create(span, true);
}

/**
* Creates a new empty instance with no Span but with sample indication.
*
* @param sample Indicates if we should sample this span.
*/
static ServerSpan create(final Boolean sample) {
return create(null, sample);
static ServerSpan create(Span span) {
return new AutoValue_ServerSpan(checkNotNull(span, "span"), true);
}

ServerSpan(){
Expand Down
Loading

0 comments on commit 181a2f4

Please sign in to comment.