-
Notifications
You must be signed in to change notification settings - Fork 714
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP TraceContext propagation handler
- Loading branch information
Adrian Cole
committed
Jun 14, 2018
1 parent
e13017a
commit 706e068
Showing
11 changed files
with
539 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# brave-propagation-trace-context | ||
|
||
TODO: this is very early impl and doesn't do things like compare if it | ||
should really resume a trace or not. It is a several hour spike and will | ||
continue |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<parent> | ||
<groupId>io.zipkin.brave</groupId> | ||
<artifactId>brave-propagation-parent</artifactId> | ||
<version>5.0.1-SNAPSHOT</version> | ||
</parent> | ||
<modelVersion>4.0.0</modelVersion> | ||
|
||
<artifactId>brave-propagation-trace-context</artifactId> | ||
<name>Brave Propagation: Trace-Context (traceparent, tracestate)</name> | ||
|
||
<properties> | ||
<main.basedir>${project.basedir}/../..</main.basedir> | ||
<main.java.version>1.6</main.java.version> | ||
<main.signature.artifact>java16</main.signature.artifact> | ||
</properties> | ||
|
||
<build> | ||
<plugins> | ||
<plugin> | ||
<artifactId>maven-jar-plugin</artifactId> | ||
<configuration> | ||
<archive> | ||
<manifestEntries> | ||
<Automatic-Module-Name>brave.propagation.trace-context</Automatic-Module-Name> | ||
</manifestEntries> | ||
</archive> | ||
</configuration> | ||
</plugin> | ||
</plugins> | ||
</build> | ||
</project> |
74 changes: 74 additions & 0 deletions
74
...ion/trace-context/src/main/java/brave/propagation/tracecontext/TraceContextExtractor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package brave.propagation.tracecontext; | ||
|
||
import brave.propagation.Propagation.Getter; | ||
import brave.propagation.SamplingFlags; | ||
import brave.propagation.TraceContext; | ||
import brave.propagation.TraceContext.Extractor; | ||
import brave.propagation.TraceContextOrSamplingFlags; | ||
import brave.propagation.tracecontext.TraceContextPropagation.Extra; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
import static brave.propagation.tracecontext.TraceparentFormat.FORMAT_LENGTH; | ||
import static brave.propagation.tracecontext.TraceparentFormat.maybeExtractParent; | ||
import static brave.propagation.tracecontext.TraceparentFormat.validateFormat; | ||
|
||
final class TraceContextExtractor<C, K> implements Extractor<C> { | ||
final Getter<C, K> getter; | ||
final K tracestateKey; | ||
final TracestateFormat tracestateFormat; | ||
|
||
TraceContextExtractor(TraceContextPropagation<K> propagation, Getter<C, K> getter) { | ||
this.getter = getter; | ||
this.tracestateKey = propagation.tracestateKey; | ||
this.tracestateFormat = new TracestateFormat(propagation.stateName); | ||
} | ||
|
||
@Override | ||
public TraceContextOrSamplingFlags extract(C carrier) { | ||
if (carrier == null) throw new NullPointerException("carrier == null"); | ||
String tracestateString = getter.get(carrier, tracestateKey); | ||
if (tracestateString == null) return EMPTY; | ||
|
||
TraceparentFormatHandler handler = new TraceparentFormatHandler(); | ||
CharSequence otherState = tracestateFormat.parseAndReturnOtherState(tracestateString, handler); | ||
|
||
List<Object> extra; | ||
if (otherState == null) { | ||
extra = DEFAULT_EXTRA; | ||
} else { | ||
Extra e = new Extra(); | ||
e.otherState = otherState; | ||
extra = Collections.singletonList(e); | ||
} | ||
|
||
if (handler.context == null) { | ||
if (extra == DEFAULT_EXTRA) return EMPTY; | ||
return TraceContextOrSamplingFlags.newBuilder() | ||
.extra(extra) | ||
.samplingFlags(SamplingFlags.EMPTY) | ||
.build(); | ||
} | ||
return TraceContextOrSamplingFlags.newBuilder().context(handler.context).extra(extra).build(); | ||
} | ||
|
||
static final class TraceparentFormatHandler implements TracestateFormat.Handler { | ||
TraceContext context; | ||
|
||
@Override | ||
public boolean onThisState(CharSequence tracestateString, int pos) { | ||
if (validateFormat(tracestateString, pos) < FORMAT_LENGTH) { | ||
return false; | ||
} | ||
context = maybeExtractParent(tracestateString, pos); | ||
return true; | ||
} | ||
} | ||
|
||
/** When present, this context was created with TracestatePropagation */ | ||
static final Extra MARKER = new Extra(); | ||
|
||
static final List<Object> DEFAULT_EXTRA = Collections.singletonList(MARKER); | ||
static final TraceContextOrSamplingFlags EMPTY = | ||
TraceContextOrSamplingFlags.EMPTY.toBuilder().extra(DEFAULT_EXTRA).build(); | ||
} |
39 changes: 39 additions & 0 deletions
39
...tion/trace-context/src/main/java/brave/propagation/tracecontext/TraceContextInjector.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package brave.propagation.tracecontext; | ||
|
||
import brave.propagation.Propagation.Setter; | ||
import brave.propagation.TraceContext; | ||
import brave.propagation.TraceContext.Injector; | ||
import brave.propagation.tracecontext.TraceContextPropagation.Extra; | ||
|
||
import static brave.propagation.tracecontext.TraceparentFormat.writeTraceparentFormat; | ||
|
||
final class TraceContextInjector<C, K> implements Injector<C> { | ||
final TracestateFormat tracestateFormat; | ||
final Setter<C, K> setter; | ||
final K traceparentKey, tracestateKey; | ||
|
||
TraceContextInjector(TraceContextPropagation<K> propagation, Setter<C, K> setter) { | ||
this.tracestateFormat = new TracestateFormat(propagation.stateName); | ||
this.traceparentKey = propagation.traceparentKey; | ||
this.tracestateKey = propagation.tracestateKey; | ||
this.setter = setter; | ||
} | ||
|
||
@Override | ||
public void inject(TraceContext traceContext, C carrier) { | ||
String thisState = writeTraceparentFormat(traceContext); | ||
setter.put(carrier, traceparentKey, thisState); | ||
|
||
CharSequence otherState = null; | ||
for (int i = 0, length = traceContext.extra().size(); i < length; i++) { | ||
Object next = traceContext.extra().get(i); | ||
if (next instanceof Extra) { | ||
otherState = ((Extra) next).otherState; | ||
break; | ||
} | ||
} | ||
|
||
String tracestate = tracestateFormat.write(thisState, otherState); | ||
setter.put(carrier, tracestateKey, tracestate); | ||
} | ||
} |
67 changes: 67 additions & 0 deletions
67
...n/trace-context/src/main/java/brave/propagation/tracecontext/TraceContextPropagation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package brave.propagation.tracecontext; | ||
|
||
import brave.propagation.Propagation; | ||
import brave.propagation.TraceContext.Extractor; | ||
import brave.propagation.TraceContext.Injector; | ||
import java.util.Arrays; | ||
import java.util.List; | ||
|
||
public final class TraceContextPropagation<K> implements Propagation<K> { | ||
|
||
public static final Propagation.Factory FACTORY = | ||
new Propagation.Factory() { | ||
@Override | ||
public <K> Propagation<K> create(KeyFactory<K> keyFactory) { | ||
return new TraceContextPropagation<>(keyFactory); | ||
} | ||
|
||
@Override | ||
public boolean requires128BitTraceId() { | ||
return true; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "TracestatePropagationFactory"; | ||
} | ||
}; | ||
|
||
final String stateName; | ||
final K traceparentKey, tracestateKey; | ||
final List<K> fields; | ||
|
||
TraceContextPropagation(KeyFactory<K> keyFactory) { | ||
this.stateName = "tc"; | ||
this.traceparentKey = keyFactory.create("traceparent"); | ||
this.tracestateKey = keyFactory.create("tracestate"); | ||
this.fields = Arrays.asList(traceparentKey, tracestateKey); | ||
} | ||
|
||
@Override | ||
public List<K> keys() { | ||
return fields; | ||
} | ||
|
||
@Override | ||
public <C> Injector<C> injector(Setter<C, K> setter) { | ||
if (setter == null) throw new NullPointerException("setter == null"); | ||
return new TraceContextInjector<>(this, setter); | ||
} | ||
|
||
@Override | ||
public <C> Extractor<C> extractor(Getter<C, K> getter) { | ||
if (getter == null) throw new NullPointerException("getter == null"); | ||
return new TraceContextExtractor<>(this, getter); | ||
} | ||
|
||
static final class Extra { // hidden intentionally | ||
CharSequence otherState; | ||
|
||
@Override | ||
public String toString() { | ||
return "TracestatePropagation{" | ||
+ (otherState != null ? ("fields=" + otherState.toString()) : "") | ||
+ "}"; | ||
} | ||
} | ||
} |
114 changes: 114 additions & 0 deletions
114
...agation/trace-context/src/main/java/brave/propagation/tracecontext/TraceparentFormat.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package brave.propagation.tracecontext; | ||
|
||
import brave.internal.Nullable; | ||
import brave.propagation.TraceContext; | ||
import java.util.logging.Logger; | ||
|
||
import static brave.internal.HexCodec.lenientLowerHexToUnsignedLong; | ||
import static brave.internal.HexCodec.writeHexLong; | ||
|
||
final class TraceparentFormat { | ||
static final Logger logger = Logger.getLogger(TraceparentFormat.class.getName()); | ||
static final int FORMAT_LENGTH = 55; | ||
|
||
static String writeTraceparentFormat(TraceContext context) { | ||
// 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01 | ||
char[] result = new char[FORMAT_LENGTH]; | ||
result[0] = '0'; // version | ||
result[1] = '0'; // version | ||
result[2] = '-'; // delimiter | ||
writeHexLong(result, 3, context.traceIdHigh()); | ||
writeHexLong(result, 19, context.traceId()); | ||
result[35] = '-'; // delimiter | ||
writeHexLong(result, 36, context.spanId()); | ||
result[52] = '-'; // delimiter | ||
result[53] = '0'; // options | ||
result[54] = context.sampled() != null && context.sampled() ? '1' : '0'; // options | ||
return new String(result); | ||
} | ||
|
||
/** returns the count of valid characters read from the input position */ | ||
static int validateFormat(CharSequence parent, int pos) { | ||
int length = Math.max(parent.length() - pos, FORMAT_LENGTH); | ||
if (length < FORMAT_LENGTH) { | ||
logger.fine("Bad length."); | ||
return 0; | ||
} | ||
|
||
for (int i = 0; i < length; i++) { | ||
char c = parent.charAt(i + pos); | ||
if (c == '-') { | ||
// There are delimiters separating the version, trace ID, span ID and options fields. | ||
if (i != 2 && i != 35 && i != 52) { | ||
logger.fine("Expected hyphen at " + (i + pos)); | ||
return i; | ||
} | ||
// Everything else is hex | ||
} else if ((c < '0' || c > '9') && (c < 'a' || c > 'f')) { | ||
logger.fine("Expected lower hex at " + (i + pos)); | ||
return i; | ||
} | ||
} | ||
return length; | ||
} | ||
|
||
static @Nullable TraceContext maybeExtractParent(CharSequence parent, int pos) { | ||
int version = parseUnsigned16BitLowerHex(parent, pos); | ||
if (version == -1) { | ||
logger.fine("Malformed version."); | ||
return null; | ||
} | ||
if (version != 0) { | ||
logger.fine("Unsupported version."); | ||
return null; | ||
} | ||
|
||
long traceIdHigh = lenientLowerHexToUnsignedLong(parent, pos + 3, pos + 19); | ||
long traceId = lenientLowerHexToUnsignedLong(parent, pos + 19, pos + 35); | ||
if (traceIdHigh == 0L && traceId == 0L) { | ||
logger.fine("Invalid input: expected non-zero trace ID"); | ||
return null; | ||
} | ||
|
||
long spanId = lenientLowerHexToUnsignedLong(parent, pos + 36, pos + 52); | ||
if (spanId == 0L) { | ||
logger.fine("Invalid input: expected non-zero span ID"); | ||
return null; | ||
} | ||
|
||
int traceOptions = parseUnsigned16BitLowerHex(parent, pos + 53); | ||
if (traceOptions == -1) { | ||
logger.fine("Malformed trace options."); | ||
return null; | ||
} | ||
|
||
// TODO: treat it as a bitset? | ||
// https://github.com/w3c/distributed-tracing/issues/8#issuecomment-382958021 | ||
// TODO handle deferred decision https://github.com/w3c/distributed-tracing/issues/8 | ||
boolean sampled = (traceOptions & 1) == 1; | ||
|
||
return TraceContext.newBuilder() | ||
.traceIdHigh(traceIdHigh) | ||
.traceId(traceId) | ||
.spanId(spanId) | ||
.sampled(sampled) | ||
.build(); | ||
} | ||
|
||
/** Returns -1 if it wasn't hex */ | ||
static int parseUnsigned16BitLowerHex(CharSequence lowerHex, int pos) { | ||
int result = 0; | ||
for (int i = 0; i < 2; i++) { | ||
char c = lowerHex.charAt(pos + i); | ||
result <<= 4; | ||
if (c >= '0' && c <= '9') { | ||
result |= c - '0'; | ||
} else if (c >= 'a' && c <= 'f') { | ||
result |= c - 'a' + 10; | ||
} else { | ||
return -1; | ||
} | ||
} | ||
return result; | ||
} | ||
} |
Oops, something went wrong.