Skip to content

Commit

Permalink
WIP TraceContext propagation handler
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrian Cole committed Mar 12, 2020
1 parent 0dc3752 commit 1bf1465
Show file tree
Hide file tree
Showing 12 changed files with 697 additions and 0 deletions.
1 change: 1 addition & 0 deletions brave/src/main/java/brave/propagation/Propagation.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ interface KeyFactory<K> {

/** Replaces a propagated key with the given value */
interface Setter<C, K> {
// BRAVE6: make this a charsequence as there's no need to allocate a string
void put(C carrier, K key, String value);
}

Expand Down
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
<module>brave-bom</module>
<module>brave-tests</module>
<module>context</module>
<module>propagation</module>
<module>instrumentation</module>
<module>spring-beans</module>
</modules>
Expand Down
49 changes: 49 additions & 0 deletions propagation/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2013-2020 The OpenZipkin Authors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
-->
<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">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-parent</artifactId>
<version>5.10.2-SNAPSHOT</version>
</parent>

<artifactId>brave-propagation-parent</artifactId>
<name>Brave: Trace Propagation Formats</name>
<packaging>pom</packaging>

<properties>
<main.basedir>${project.basedir}/..</main.basedir>
</properties>

<modules>
<module>w3c</module>
</modules>

<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-tests</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
14 changes: 14 additions & 0 deletions propagation/w3c/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# brave-propagation-w3c

This project includes propagation handlers for W3C defined headers.

## Trace Context
The [Trace Context][https://w3c.github.io/trace-context/] specification defines two headers:

* `traceparent` - almost the same as our [B3-single format](https://github.com/openzipkin/b3-propagation#single-header)
* `tracestate` - vendor-specific (or format-specific): may impact how to interpret `traceparent`

This implementation can survive mixed systems who follow the specification and forward the
`tracestate` header. When writing the `traceparent` header, this also overwrites the `tracestate`
entry named 'b3' (in B3 single format). When reading headers, this entry is favored over the
`traceparent`, allowing the the next span to re-attach to the last known 'b3' header.
48 changes: 48 additions & 0 deletions propagation/w3c/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright 2013-2020 The OpenZipkin Authors
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied. See the License for the specific language governing permissions and limitations under
the License.
-->
<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.10.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>brave-propagation-w3c</artifactId>
<name>Brave Propagation: W3C Tracing headers (traceparent, tracestate, etc.)</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.w3c</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2013-2020 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package brave.propagation.w3c;

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.w3c.TraceContextPropagation.Extra;
import java.util.Collections;
import java.util.List;

import static brave.propagation.w3c.TraceparentFormat.FORMAT_LENGTH;
import static brave.propagation.w3c.TraceparentFormat.maybeExtractParent;
import static brave.propagation.w3c.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();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2013-2020 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package brave.propagation.w3c;

import brave.propagation.Propagation.Setter;
import brave.propagation.TraceContext;
import brave.propagation.TraceContext.Injector;
import brave.propagation.w3c.TraceContextPropagation.Extra;

import static brave.propagation.w3c.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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2013-2020 The OpenZipkin Authors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package brave.propagation.w3c;

import brave.propagation.Propagation;
import brave.propagation.TraceContext;
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> {
// TODO: not sure if we will want a constant here, or something like a builder. For example, we
// probably will need a primary state handler (ex b3) to catch data no longer in the traceparent
// format. At any rate, we will need to know what state is primarily ours, so that probably means
// not having a constant, unless that constant uses b3 single impl for the tracestate entry.
public static final Propagation.Factory FACTORY =
new Propagation.Factory() {
@Override public <K> Propagation<K> create(KeyFactory<K> keyFactory) {
return new TraceContextPropagation<>(keyFactory);
}

/**
* Traceparent doesn't support sharing the same span ID, though it may be possible to support
* this by extending it with a b3 state entry.
*/
@Override public boolean supportsJoin() {
return false;
}

@Override public TraceContext decorate(TraceContext context) {
// TODO: almost certain we will need to decorate as not all contexts will start with an
// incoming request (ex schedule or client-originated traces)
return super.decorate(context);
}

@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 = "b3";
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);
}

// TODO: this probably needs to carry more state than the upstream at some point, eventhough we
// can start with pass-through integration.
static final class Extra { // hidden intentionally
CharSequence otherState;

@Override public String toString() {
return "TracestatePropagation{"
+ (otherState != null ? ("fields=" + otherState.toString()) : "")
+ "}";
}
}
}
Loading

0 comments on commit 1bf1465

Please sign in to comment.