Skip to content

Commit

Permalink
WIP: tracecontext
Browse files Browse the repository at this point in the history
  • Loading branch information
Adrian Cole committed Mar 16, 2020
1 parent e55da25 commit c45f5f3
Show file tree
Hide file tree
Showing 17 changed files with 1,462 additions and 1 deletion.
5 changes: 5 additions & 0 deletions brave-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@
<artifactId>brave-spring-beans</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-propagation-w3c</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
6 changes: 6 additions & 0 deletions instrumentation/benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@
<artifactId>grpc-core</artifactId>
<version>${grpc.version}</version>
</dependency>

<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>brave-propagation-w3c</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2019 The OpenZipkin Authors
* 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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* 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.internal.HexCodec;
import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContext.Extractor;
import brave.propagation.TraceContext.Injector;
import brave.propagation.TraceContextOrSamplingFlags;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@Measurement(iterations = 5, time = 1)
@Warmup(iterations = 10, time = 1)
@Fork(3)
@BenchmarkMode(Mode.SampleTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class TraceContextPropagationBenchmarks {
static final Propagation<String> tc =
TraceContextPropagation.FACTORY.create(Propagation.KeyFactory.STRING);
static final Injector<Map<String, String>> tcInjector = tc.injector(Map::put);
static final Extractor<Map<String, String>> tcExtractor = tc.extractor(Map::get);

static final TraceContext context = TraceContext.newBuilder()
.traceIdHigh(HexCodec.lowerHexToUnsignedLong("67891233abcdef01"))
.traceId(HexCodec.lowerHexToUnsignedLong("2345678912345678"))
.spanId(HexCodec.lowerHexToUnsignedLong("463ac35c9f6413ad"))
.sampled(true)
.build();

// TODO: add tracestate examples which prefer the b3 entry
static final Map<String, String> incoming128 = new LinkedHashMap<String, String>() {
{
put("traceparent", TraceparentFormat.writeTraceparentFormat(context));
}
};

static final Map<String, String> incomingPadded = new LinkedHashMap<String, String>() {
{
put("traceparent",
TraceparentFormat.writeTraceparentFormat(context.toBuilder().traceIdHigh(0).build()));
}
};

static final Map<String, String> incomingMalformed = new LinkedHashMap<String, String>() {
{
put("traceparent", "b970dafd-0d95-40aa-95d8-1d8725aebe40"); // not ok
}
};

static final Map<String, String> nothingIncoming = Collections.emptyMap();

@Benchmark public void inject() {
Map<String, String> carrier = new LinkedHashMap<>();
tcInjector.inject(context, carrier);
}

@Benchmark public TraceContextOrSamplingFlags extract_128() {
return tcExtractor.extract(incoming128);
}

@Benchmark public TraceContextOrSamplingFlags extract_padded() {
return tcExtractor.extract(incomingPadded);
}

@Benchmark public TraceContextOrSamplingFlags extract_nothing() {
return tcExtractor.extract(nothingIncoming);
}

@Benchmark public TraceContextOrSamplingFlags extract_malformed() {
return tcExtractor.extract(incomingMalformed);
}

// Convenience main entry-point
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.addProfiler("gc")
.include(".*" + TraceContextPropagationBenchmarks.class.getSimpleName())
.build();

new Runner(opt).run();
}
}
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>
62 changes: 62 additions & 0 deletions propagation/w3c/RATIONALE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# brave-propagation-w3c rationale

## Trace Context Specification

### Why do we write a tracestate entry?
We write both "traceparent" and a "tracestate" entry for two reasons. The first is due to
incompatibility between the "traceparent" format and our trace context. This is described in another
section.

The other reason is durability across hops. When your direct upstream is not the same tracing
system, its span ID (which they call parentId in section 3.2.2.4) will not be valid: using it will
break the trace. Instead, we look at our "tracestate" entry, which represents the last known place
in the same system.

### What is supported by Brave, but not by traceparent format?

#### `SamplingFlags` can precede a trace
B3 has always had the ability to influence the start of a trace with a sampling decision. For
example, you can propagate `b3=0` to force the next hop to not trace the request. This is used for
things like not sampling health checks. Conversely, you can force sampling by propagating `b3=d`
(the debug flag). `traceparent` requires a trace ID and a span ID, so cannot propagate this.

#### `TraceIdContext` (trace ID only)
Amazon trace format can propagate a trace ID prior to starting a trace, for correlation purposes.
`traceparent` requires a trace ID and a span ID, so cannot a trace ID standalone.

#### Not yet sampled/ deferred decision
B3 and Amazon formats support assigning a span ID prior to a sampling decision. `traceparent` has no
way to tell the difference between an explicit no and lack of a decision, as it only has one bit
flag.

#### Debug flag
B3 has always had a debug flag, which is a way to force a trace even if normal sampling says no.
`traceparent` cannot distinguish between this and a normal decision, as it only has one bit flag.

#### Trace-scoped sampling decision
`traceparent` does not distinguish between a hop-level or a trace scoped decision in the format.
This means that traces can be broken as it is valid to change the decision at every step (which
breaks the hierarchy). This is the main reason why we need a separate `tracestate` entry.

### Why serialize the trace context in two formats?

The "traceparent" header is only portable to get the `TraceContext.traceId()` and
`TraceContext.spanId()`. Section 3.2.2.5.1, the sampled flag, is incompatible with B3 sampling. The
format also lacks fields for `TraceContext.parentId()` and `TraceContext.debug()`. This requires us
to re-serialize the same context in two formats: one for compliance ("traceparent") and one that
actually stores the context (B3 single format).

The impact on users will be higher overhead and confusion when comparing the sampled value of
"traceparent" which may be different than "b3".

### Why is traceparent incompatible with B3?

It may seem like incompatibility between "traceparent" and B3 were accidental, but that was not the
case. The Zipkin community held the first meetings leading to this specification, and were directly
involved in the initial design. Use cases of B3 were well known by working group members. Choices to
become incompatible with B3 (and Amazon X-Ray format) sampling were conscious, as were decisions to
omit other fields we use. These decisions were made in spite of protest from Zipkin community
members and others. There is a rationale document for the specification, but the working group has
so far not explained these decisions, or even mention B3 at all.

https://github.com/w3c/trace-context/blob/d2ed8084780efcedab7e60c48b06ca3c00bea55c/http_header_format_rationale.md
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.
64 changes: 64 additions & 0 deletions propagation/w3c/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?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>

<dependencies>
<!-- to mock Platform calls -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

<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>
Loading

0 comments on commit c45f5f3

Please sign in to comment.