From ebde177d0930edd463ece150981f926263985ea2 Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Tue, 3 Oct 2017 20:39:34 +0800 Subject: [PATCH] Makes traceIdHigh convertable to AWS X-Ray This customizes the trace ID generator to make the high bits convertable to Amazon X-Ray trace ID format v1. See https://github.com/openzipkin/zipkin/issues/1754 --- brave/src/main/java/brave/Tracer.java | 2 +- .../main/java/brave/internal/Platform.java | 23 ++++ .../java/brave/internal/PlatformTest.java | 16 +++ .../brave/internal/PlatformBenchmarks.java | 111 ++++++++++++++++++ 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 instrumentation/benchmarks/src/main/java/brave/internal/PlatformBenchmarks.java diff --git a/brave/src/main/java/brave/Tracer.java b/brave/src/main/java/brave/Tracer.java index 8ce83c1517..decd08e812 100644 --- a/brave/src/main/java/brave/Tracer.java +++ b/brave/src/main/java/brave/Tracer.java @@ -240,7 +240,7 @@ TraceContext nextContext(@Nullable TraceContext parent, SamplingFlags samplingFl return TraceContext.newBuilder() .sampled(sampled) .debug(samplingFlags.debug()) - .traceIdHigh(traceId128Bit ? Platform.get().randomLong() : 0L).traceId(nextId) + .traceIdHigh(traceId128Bit ? Platform.get().nextTraceIdHigh() : 0L).traceId(nextId) .spanId(nextId).build(); } diff --git a/brave/src/main/java/brave/internal/Platform.java b/brave/src/main/java/brave/internal/Platform.java index fe9039cdf3..eaebd8cd41 100644 --- a/brave/src/main/java/brave/internal/Platform.java +++ b/brave/src/main/java/brave/internal/Platform.java @@ -2,6 +2,7 @@ import brave.Clock; import brave.Tracer; +import brave.Tracing; import com.google.auto.value.AutoValue; import java.net.InetAddress; import java.net.NetworkInterface; @@ -112,6 +113,15 @@ static Platform findPlatform() { */ public abstract long randomLong(); + /** + * Returns the high 8-bytes for use in {@link Tracing.Builder#traceId128Bit 128-bit trace IDs}. + * + *

The upper 4-bytes are epoch seconds and the lower 4-bytes are random. This makes it + * convertible to Amazon + * X-Ray trace ID format v1. + */ + public abstract long nextTraceIdHigh(); + /** gets a timestamp based on duration since the create tick. */ @Override public long currentTimeMicroseconds() { @@ -136,6 +146,15 @@ static Jre7 buildIfSupported(boolean zipkinV1Present) { @Override public long randomLong() { return java.util.concurrent.ThreadLocalRandom.current().nextLong(); } + + @IgnoreJRERequirement + @Override public long nextTraceIdHigh() { + return nextTraceIdHigh(java.util.concurrent.ThreadLocalRandom.current()); + } + } + + static long nextTraceIdHigh(Random prng) { + return ((System.currentTimeMillis() / 1000) << 32) + prng.nextInt(); } @AutoValue @@ -149,5 +168,9 @@ static Jre6 build(boolean zipkinV1Present) { @Override public long randomLong() { return prng().nextLong(); } + + @Override public long nextTraceIdHigh() { + return nextTraceIdHigh(prng()); + } } } diff --git a/brave/src/test/java/brave/internal/PlatformTest.java b/brave/src/test/java/brave/internal/PlatformTest.java index 13dbcfd0ed..35ce6ddf5e 100644 --- a/brave/src/test/java/brave/internal/PlatformTest.java +++ b/brave/src/test/java/brave/internal/PlatformTest.java @@ -5,8 +5,10 @@ import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; +import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Random; import java.util.Set; import java.util.Vector; import java.util.concurrent.Callable; @@ -47,6 +49,10 @@ public class PlatformTest { @Override public long randomLong() { return 1L; } + + @Override public long nextTraceIdHigh() { + return 1L; + } }; when(System.nanoTime()).thenReturn(1000L); // 1 microsecond @@ -54,6 +60,16 @@ public class PlatformTest { assertThat(platform.currentTimeMicroseconds()).isEqualTo(1); } + // example from X-Amzn-Trace-Id: Root=1-5759e988-bd862e3fe1be46a994272793;Sampled=1 + @Test public void randomLong_epochSecondsPlusRandom() { + mockStatic(System.class); + when(System.currentTimeMillis()).thenReturn(1465510280_000L); // Thursday, June 9, 2016 10:11:20 PM + + long traceIdHigh = platform.nextTraceIdHigh(); + + assertThat(HexCodec.toLowerHex(traceIdHigh)).startsWith("5759e988"); + } + @Test public void zipkinV1Absent() throws ClassNotFoundException { mockStatic(Class.class); when(Class.forName(zipkin.Endpoint.class.getName())) diff --git a/instrumentation/benchmarks/src/main/java/brave/internal/PlatformBenchmarks.java b/instrumentation/benchmarks/src/main/java/brave/internal/PlatformBenchmarks.java new file mode 100644 index 0000000000..eccb61804b --- /dev/null +++ b/instrumentation/benchmarks/src/main/java/brave/internal/PlatformBenchmarks.java @@ -0,0 +1,111 @@ +/** + * 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 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.Group; +import org.openjdk.jmh.annotations.GroupThreads; +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.Group) +public class PlatformBenchmarks { + Platform jre6 = Platform.Jre6.build(false); + Platform jre7 = Platform.Jre7.buildIfSupported(false); + + @Benchmark @Group("no_contention") @GroupThreads(1) + public long no_contention_nextTraceIdHigh_jre6() { + return jre6.nextTraceIdHigh(); + } + + @Benchmark @Group("mild_contention") @GroupThreads(2) + public long mild_contention_nextTraceIdHigh_jre6() { + return jre6.nextTraceIdHigh(); + } + + @Benchmark @Group("high_contention") @GroupThreads(8) + public long high_contention_nextTraceIdHigh_jre6() { + return jre6.nextTraceIdHigh(); + } + + @Benchmark @Group("no_contention") @GroupThreads(1) + public long no_contention_randomLong_jre6() { + return jre6.randomLong(); + } + + @Benchmark @Group("mild_contention") @GroupThreads(2) + public long mild_contention_randomLong_jre6() { + return jre6.randomLong(); + } + + @Benchmark @Group("high_contention") @GroupThreads(8) + public long high_contention_randomLong_jre6() { + return jre6.randomLong(); + } + + @Benchmark @Group("no_contention") @GroupThreads(1) + public long no_contention_nextTraceIdHigh_jre7() { + return jre7.nextTraceIdHigh(); + } + + @Benchmark @Group("mild_contention") @GroupThreads(2) + public long mild_contention_nextTraceIdHigh_jre7() { + return jre7.nextTraceIdHigh(); + } + + @Benchmark @Group("high_contention") @GroupThreads(8) + public long high_contention_nextTraceIdHigh_jre7() { + return jre7.nextTraceIdHigh(); + } + + @Benchmark @Group("no_contention") @GroupThreads(1) + public long no_contention_randomLong_jre7() { + return jre7.randomLong(); + } + + @Benchmark @Group("mild_contention") @GroupThreads(2) + public long mild_contention_randomLong_jre7() { + return jre7.randomLong(); + } + + @Benchmark @Group("high_contention") @GroupThreads(8) + public long high_contention_randomLong_jre7() { + return jre7.randomLong(); + } + + // Convenience main entry-point + public static void main(String[] args) throws RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + PlatformBenchmarks.class.getSimpleName() + ".*") + .build(); + + new Runner(opt).run(); + } +}