Skip to content

Commit

Permalink
AWS propagation support (openzipkin#510)
Browse files Browse the repository at this point in the history
  • Loading branch information
adriancole committed Oct 15, 2017
1 parent a6bedf5 commit 40a43cd
Show file tree
Hide file tree
Showing 11 changed files with 708 additions and 5 deletions.
10 changes: 5 additions & 5 deletions brave/src/main/java/brave/internal/HexCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public final class HexCodec {
* 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 lowerHexToUnsignedLong(String lowerHex) {
public static long lowerHexToUnsignedLong(CharSequence lowerHex) {
int length = lowerHex.length();
if (length < 1 || length > 32) throw isntLowerHexLong(lowerHex);

Expand All @@ -19,9 +19,9 @@ public static long lowerHexToUnsignedLong(String lowerHex) {

/**
* Parses a 16 character lower-hex string with no prefix into an unsigned long, starting at the
* spe index.
* specified index.
*/
public static long lowerHexToUnsignedLong(String lowerHex, int index) {
public static long lowerHexToUnsignedLong(CharSequence lowerHex, int index) {
long result = 0;
for (int endIndex = Math.min(index + 16, lowerHex.length()); index < endIndex; index++) {
char c = lowerHex.charAt(index);
Expand All @@ -37,7 +37,7 @@ public static long lowerHexToUnsignedLong(String lowerHex, int index) {
return result;
}

static NumberFormatException isntLowerHexLong(String lowerHex) {
static NumberFormatException isntLowerHexLong(CharSequence lowerHex) {
throw new NumberFormatException(
lowerHex + " should be a 1 to 32 character lower-hex string with no prefix");
}
Expand Down Expand Up @@ -76,7 +76,7 @@ public static void writeHexLong(char[] data, int pos, long v) {
static final char[] HEX_DIGITS =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

static void writeHexByte(char[] data, int pos, byte b) {
public static void writeHexByte(char[] data, int pos, byte b) {
data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf];
data[pos + 1] = HEX_DIGITS[b & 0xf];
}
Expand Down
5 changes: 5 additions & 0 deletions instrumentation/benchmarks/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@
<artifactId>resteasy-undertow</artifactId>
<version>${resteasy.version}</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave-propagation-aws</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import brave.http.HttpServerBenchmarks;
import brave.okhttp3.TracingCallFactory;
import brave.propagation.aws.AWSPropagation;
import brave.sampler.Sampler;
import brave.servlet.TracingFilter;
import io.undertow.servlet.Servlets;
Expand Down Expand Up @@ -78,6 +79,15 @@ public Traced128() {
}
}

public static class TracedAWS extends ForwardingTracingFilter {
public TracedAWS() {
super(Tracing.newBuilder()
.propagationFactory(new AWSPropagation.Factory())
.spanReporter(Reporter.NOOP)
.build());
}
}

@Override protected void init(DeploymentInfo servletBuilder) {
servletBuilder.addFilter(new FilterInfo("Unsampled", Unsampled.class))
.addFilterUrlMapping("Unsampled", "/unsampled", REQUEST)
Expand All @@ -88,6 +98,9 @@ public Traced128() {
.addFilter(new FilterInfo("Traced128", Traced128.class))
.addFilterUrlMapping("Traced128", "/traced128", REQUEST)
.addFilterUrlMapping("Traced128", "/traced128/api", REQUEST)
.addFilter(new FilterInfo("TracedAWS", TracedAWS.class))
.addFilterUrlMapping("TracedAWS", "/tracedaws", REQUEST)
.addFilterUrlMapping("TracedAWS", "/tracedaws/api", REQUEST)
.addServlets(Servlets.servlet("HelloServlet", HelloServlet.class).addMapping("/*"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,18 @@ protected int initServer() throws ServletException {
.execute().body().close();
}

@Benchmark public void tracedawsServer_get() throws Exception {
get("/tracedaws");
}

@Benchmark public void tracedawsServer_get_resumeTrace() throws Exception {
client.newCall(new Request.Builder().url(baseUrl() + "/traced128")
.header("X-Amzn-Trace-Id",
"Root=1-67891233-abcdef012345678912345678;Parent=463ac35c9f6413ad;Sampled=1")
.build())
.execute().body().close();
}

void get(String path) throws IOException {
client.newCall(new Request.Builder().url(baseUrl() + path).build()).execute().body().close();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* Copyright 2015-2016 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.internal;

import brave.propagation.Propagation;
import brave.propagation.TraceContext;
import brave.propagation.TraceContext.Extractor;
import brave.propagation.TraceContext.Injector;
import brave.propagation.TraceContextOrSamplingFlags;
import brave.propagation.aws.AWSPropagation;
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.Scope;
import org.openjdk.jmh.annotations.State;
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.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Thread)
public class PropagationBenchmarks {

static final Injector<Map<String, String>> b3Injector =
Propagation.B3_STRING.injector(Map::put);
static final Injector<Map<String, String>> awsInjector =
new AWSPropagation.Factory().create(Propagation.KeyFactory.STRING).injector(Map::put);
static final Extractor<Map<String, String>> b3Extractor =
Propagation.B3_STRING.extractor(Map::get);
static final Extractor<Map<String, String>> awsExtractor =
new AWSPropagation.Factory().create(Propagation.KeyFactory.STRING).extractor(Map::get);

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

static final Map<String, String> incoming = new LinkedHashMap<String, String>() {
{
b3Injector.inject(context, this);
awsInjector.inject(context, this);
}
};

Map<String, String> carrier = new LinkedHashMap<>();

@Benchmark public void inject_b3() {
b3Injector.inject(context, carrier);
}

@Benchmark public void inject_aws() {
awsInjector.inject(context, carrier);
}

@Benchmark public TraceContextOrSamplingFlags extract_b3() {
return b3Extractor.extract(incoming);
}

@Benchmark public TraceContextOrSamplingFlags extract_aws() {
return awsExtractor.extract(incoming);
}

// Convenience main entry-point
public static void main(String[] args) throws RunnerException {
Options opt = new OptionsBuilder()
.include(".*" + PropagationBenchmarks.class.getSimpleName() + ".extract_aws")
.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 @@ -100,6 +100,7 @@
<module>brave-tests</module>
<module>context</module>
<module>instrumentation</module>
<module>propagation</module>
<module>spring-beans</module>
<module>archive/brave-core</module>
<module>archive/brave-http</module>
Expand Down
45 changes: 45 additions & 0 deletions propagation/aws/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# brave-propagation-aws
This changes brave to use "x-amzn-trace-id" as opposed to "x-b3" prefixed headers to propagate trace
context across processes.

To enable this, configure `brave.Tracing` with `AWSPropagation.Factory` like so:

```java
tracing = Tracing.newBuilder()
.propagationFactory(new AWSPropagation.Factory())
...
.build();
```

## Notes
* This does not send spans to Amazon. If you want to do that, use [io.zipkin.aws:reporter-xray-udp](https://github.com/openzipkin/zipkin-aws).
* Unless you send spans to amazon, the impact is only which headers are used by Brave.
* This neither depends on, nor coordinates with [com.amazonaws:aws-xray-recorder-sdk-core](http://docs.aws.amazon.com/xray/latest/devguide/xray-sdk-java.html).
* If you are using Amazon X-Ray SDK in the same app, Brave will not "see" those traces and visa versa.
* If you would like X-Ray SDK integration (such that traces are mutually visible), please raise an issue.
* This implicitly switches Brave to use 128-bit trace IDS
* Internally, Amazon's root timestamp is encoded in the first 32-bits of the 128-bit trace ID.

## Utilities
There are a couple added utilities for parsing and generating an AWS trace ID string:

* `AWSPropagation.traceIdString` - used to generate a formatted trace ID for correlation purposes.
* `AWSPropagation.extract` - extracts a trace context from a string such as an environment variable.

Ex to extract the trace ID from the built-in AWS Lambda variable
```java
extracted = AWSPropagation.extract(System.getenv("_X_AMZN_TRACE_ID"));
```

## Extra fields
Amazon's trace ID format allows propagation of "extra" fields. For example, someone can add
diagnostic variables by appending them to the trace header as discussed in the [ALB blog](https://aws.amazon.com/blogs/aws/application-performance-percentiles-and-request-tracing-for-aws-application-load-balancer/).

Ex: the below header includes a custom field `CalledFrom=Foo`, which is non-standard, but will be
propagated throughout the trace.
```
X-Amzn-Trace-Id: Root=1-58211399-36d228ad5d99923122bbe354;CalledFrom=Foo
```

Internally, this field is stored in `TraceContext.extra()`, which allows it to be carried from the
point a trace context is extracted until where it is injected into an outgoing request or message.
18 changes: 18 additions & 0 deletions propagation/aws/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?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>4.8.2-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>brave-propagation-aws</artifactId>
<name>Brave Propagation: Amazon Web Services (AWS)</name>

<properties>
<main.basedir>${project.basedir}/../..</main.basedir>
<main.java.version>1.6</main.java.version>
<main.signature.artifact>java16</main.signature.artifact>
</properties>
</project>
Loading

0 comments on commit 40a43cd

Please sign in to comment.