Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS Propagation support #510

Merged
merged 1 commit into from
Oct 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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