From 82210685819562726bea58c9ae926b91d082da6b Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 5 Oct 2017 21:29:53 +0800 Subject: [PATCH 1/3] Encodes epoch seconds into first 32bits of a 128-bit trace ID (#724) Amazon will throw out trace IDs that aren't associated with a recent timestamp. This encodes the current epoch seconds into the first 32 of a 128-bit trace ID to support conversion to an Amazon Root ID. --- .../cloud/sleuth/trace/DefaultTracer.java | 19 +++++++++++++++++-- .../sleuth/trace/DefaultTracerTests.java | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java index 2fd4849cb..6f7c00287 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/trace/DefaultTracer.java @@ -112,7 +112,7 @@ public class DefaultTracer implements Tracer { else { long id = createId(); span = Span.builder().name(shortenedName) - .traceIdHigh(this.traceId128 ? createId() : 0L) + .traceIdHigh(this.traceId128 ? createTraceIdHigh() : 0L) .traceId(id) .spanId(id).build(); if (sampler == null) { @@ -185,7 +185,7 @@ public class DefaultTracer implements Tracer { long id = createId(); if (parent == null) { Span span = Span.builder().name(shortenedName) - .traceIdHigh(this.traceId128 ? createId() : 0L) + .traceIdHigh(this.traceId128 ? createTraceIdHigh() : 0L) .traceId(id) .spanId(id).build(); span = sampledSpan(span, this.defaultSampler); @@ -222,6 +222,21 @@ public class DefaultTracer implements Tracer { return span; } + /** + * Encodes a timestamp into the upper 32-bits, so that it can be converted to an Amazon trace ID. + * + *

For example, an Amazon trace ID is composed of the following: {@code |-- 32 bits for epoch + * seconds -- | -- 96 bits for random data -- |} + * + *

To support this, {@link Span#getTraceIdHigh() traceIdHigh} holds the epoch seconds and first + * 32 random bits: and {@link Span#getTraceId()} traceId} holds the remaining 64 random bits. + */ + private long createTraceIdHigh() { + long epochSeconds = System.currentTimeMillis() / 1000; + int random = this.random.nextInt(); + return (epochSeconds & 0xffffffffL) << 32 | (random & 0xffffffffL); + } + private long createId() { return this.random.nextLong(); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java index 98b666289..742268027 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/trace/DefaultTracerTests.java @@ -19,6 +19,7 @@ package org.springframework.cloud.sleuth.trace; import java.util.ArrayList; import java.util.List; import java.util.Random; +import java.util.Date; import org.junit.After; import org.junit.Before; @@ -231,6 +232,20 @@ public class DefaultTracerTests { tracer.close(span); } + /** + * To support conversion to Amazon trace IDs, the first 32 bits of the trace ID are epoch seconds. + */ + @Test + public void creates128bitTraceIdWithEncodedTimestamp() { + DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), + this.spanNamer, this.spanLogger, this.spanReporter, true, new TraceKeys()); + Span span = tracer.createSpan(bigName()); + String traceId = span.traceIdString(); + long epochSeconds = Long.parseLong(traceId.substring(0, 8), 16); + then(new Date(epochSeconds * 1000)).isToday(); + tracer.close(span); + } + @Test public void shouldCreateChildOfSpanWithShortenedName() { DefaultTracer tracer = new DefaultTracer(new AlwaysSampler(), new Random(), From 05a6c35a2cb8d7dfdb500aea15820846bc965f59 Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 19 Oct 2017 13:45:47 +0200 Subject: [PATCH 2/3] Adjusting the adjusted span; fixes #750 --- .../cloud/sleuth/zipkin/ZipkinSpanListener.java | 14 +++++++------- .../sleuth/zipkin/ZipkinSpanListenerTests.java | 9 ++++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java index 35e1a25ed..7edcf46cc 100644 --- a/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java +++ b/spring-cloud-sleuth-zipkin/src/main/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListener.java @@ -16,6 +16,12 @@ package org.springframework.cloud.sleuth.zipkin; +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; + import org.springframework.cloud.commons.util.IdUtils; import org.springframework.cloud.sleuth.Log; import org.springframework.cloud.sleuth.NoOpSpanAdjuster; @@ -29,12 +35,6 @@ import zipkin.BinaryAnnotation; import zipkin.Constants; import zipkin.Endpoint; -import java.nio.charset.Charset; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; - /** * Listener of Sleuth events. Reports to Zipkin via {@link ZipkinSpanReporter}. * @@ -100,7 +100,7 @@ public class ZipkinSpanListener implements SpanReporter { //TODO: Consider adding support for the debug flag (related to #496) Span convertedSpan = span; for (SpanAdjuster adjuster : this.spanAdjusters) { - convertedSpan = adjuster.adjust(span); + convertedSpan = adjuster.adjust(convertedSpan); } zipkin.Span.Builder zipkinSpan = zipkin.Span.builder(); Endpoint endpoint = this.endpointLocator.local(); diff --git a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListenerTests.java b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListenerTests.java index b9c200a72..8ef2bea38 100644 --- a/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListenerTests.java +++ b/spring-cloud-sleuth-zipkin/src/test/java/org/springframework/cloud/sleuth/zipkin/ZipkinSpanListenerTests.java @@ -20,6 +20,7 @@ import org.assertj.core.api.Condition; import zipkin.Constants; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.annotation.PostConstruct; @@ -307,12 +308,14 @@ public class ZipkinSpanListenerTests { public void should_adjust_span_before_reporting_it() { this.parent.logEvent(Span.CLIENT_RECV); ZipkinSpanListener spanListener = new ZipkinSpanListener(this.spanReporter, - this.endpointLocator, null, Collections.singletonList( - span -> Span.builder().from(span).name("foo").build())); + this.endpointLocator, null, Arrays.asList( + (SpanAdjuster) span -> Span.builder().from(span).name("foo").build(), + (SpanAdjuster) span -> Span.builder().from(span).name(span.getName() + "bar").build() + )); zipkin.Span result = spanListener.convert(this.parent); - assertThat(result.name).isEqualTo("foo"); + assertThat(result.name).isEqualTo("foobar"); } @Test From 2404afc639e0753c18a639f497482cc75e35630d Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Thu, 19 Oct 2017 16:24:42 +0200 Subject: [PATCH 3/3] Added docs about parallelStream; fixes #744 --- docs/src/main/asciidoc/spring-cloud-sleuth.adoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc index b0372bf87..992166fdd 100644 --- a/docs/src/main/asciidoc/spring-cloud-sleuth.adoc +++ b/docs/src/main/asciidoc/spring-cloud-sleuth.adoc @@ -795,6 +795,10 @@ Here you can see an example of how to pass tracing information with `TraceableEx include::../../../../spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/async/TraceableExecutorServiceTests.java[tags=completablefuture,indent=0] ---- +IMPORTANT: Sleuth doesn't work with `parallelStream()` out of the box. If you want +to have the tracing information propagated through the stream you have to use the +approach with `supplyAsync(...)` as presented above. + ===== Customization of Executors Sometimes you need to set up a custom instance of the `AsyncExecutor`. In the following snippet you