From 981fbd18f1e2e2056fea6f69bae51158b916eb0f Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Thu, 5 Oct 2017 21:29:53 +0800 Subject: [PATCH] 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 76546d703..c96e96331 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 @@ -98,7 +98,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) { @@ -171,7 +171,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); @@ -208,6 +208,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 c6a051aaf..16968e5ce 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 @@ -16,6 +16,7 @@ package org.springframework.cloud.sleuth.trace; +import java.util.Date; import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -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(),