From c943e4cd50a0e0107a5362a738d22713d8860a6b Mon Sep 17 00:00:00 2001 From: Adrian Cole Date: Wed, 16 Nov 2016 14:55:28 +0800 Subject: [PATCH] Switches ID encoding to fixed-length (#450) Before, we were using variable encoding for trace and span identifiers. This complicates search for those who are copy/pasting fixed-length IDs provisioned upstream. This moves to standard formatting, while maintaining tolerant reads. The code added will also be used to support 128-bit (32 char) trace IDs. Fixes #449 --- .../springframework/cloud/sleuth/Span.java | 26 +++++++++++++++++-- .../cloud/sleuth/SpanTests.java | 4 +-- ...ttpClientRibbonRequestCustomizerTests.java | 6 ++--- ...ttpClientRibbonRequestCustomizerTests.java | 6 ++--- ...estClientRibbonRequestCustomizerTests.java | 6 ++--- 5 files changed, 35 insertions(+), 13 deletions(-) diff --git a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java index 25fdbd1e5..6ab7d8043 100644 --- a/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java +++ b/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/Span.java @@ -453,12 +453,34 @@ public class Span implements SpanContext { } /** - * Represents given long id as hex string + * Represents given long id as 16-character lower-hex string */ public static String idToHex(long id) { - return Long.toHexString(id); + char[] data = new char[16]; + writeHexLong(data, 0, id); + return new String(data); } + /** Inspired by {@code okio.Buffer.writeLong} */ + static void writeHexLong(char[] data, int pos, long v) { + writeHexByte(data, pos + 0, (byte) ((v >>> 56L) & 0xff)); + writeHexByte(data, pos + 2, (byte) ((v >>> 48L) & 0xff)); + writeHexByte(data, pos + 4, (byte) ((v >>> 40L) & 0xff)); + writeHexByte(data, pos + 6, (byte) ((v >>> 32L) & 0xff)); + writeHexByte(data, pos + 8, (byte) ((v >>> 24L) & 0xff)); + writeHexByte(data, pos + 10, (byte) ((v >>> 16L) & 0xff)); + writeHexByte(data, pos + 12, (byte) ((v >>> 8L) & 0xff)); + writeHexByte(data, pos + 14, (byte) (v & 0xff)); + } + + static void writeHexByte(char[] data, int pos, byte b) { + data[pos + 0] = HEX_DIGITS[(b >> 4) & 0xf]; + data[pos + 1] = HEX_DIGITS[b & 0xf]; + } + + static final char[] HEX_DIGITS = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + /** * Parses a 1 to 32 character lower-hex string with no prefix into an unsigned long, tossing any * bits higher than 64. diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java index fc61600ef..907b9d6dc 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/SpanTests.java @@ -36,12 +36,12 @@ import static org.assertj.core.api.BDDAssertions.then; public class SpanTests { @Test - public void should_convert_long_to_hex_string() throws Exception { + public void should_convert_long_to_16_character_hex_string() throws Exception { long someLong = 123123L; String hexString = Span.idToHex(someLong); - then(hexString).isEqualTo("1e0f3"); + then(hexString).isEqualTo("000000000001e0f3"); } @Test diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java index 28c97ead3..f7d1ff935 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/ApacheHttpClientRibbonRequestCustomizerTests.java @@ -65,9 +65,9 @@ public class ApacheHttpClientRibbonRequestCustomizerTests { this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder)); HttpUriRequest request = requestBuilder.build(); - thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1"); - thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "2"); - thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "3"); + thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "0000000000000001"); + thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "0000000000000002"); + thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "0000000000000003"); thenThereIsAHeaderWithNameAndValue(request, Span.PROCESS_ID_NAME, "processId"); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java index 08e6cdb55..0b5e868fe 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/OkHttpClientRibbonRequestCustomizerTests.java @@ -62,9 +62,9 @@ public class OkHttpClientRibbonRequestCustomizerTests { this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder)); Request request = requestBuilder.build(); - thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1"); - thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "2"); - thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "3"); + thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "0000000000000001"); + thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "0000000000000002"); + thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "0000000000000003"); thenThereIsAHeaderWithNameAndValue(request, Span.PROCESS_ID_NAME, "processId"); } diff --git a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java index 12b37bbc5..286bc2a4c 100644 --- a/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java +++ b/spring-cloud-sleuth-core/src/test/java/org/springframework/cloud/sleuth/instrument/zuul/RestClientRibbonRequestCustomizerTests.java @@ -62,9 +62,9 @@ public class RestClientRibbonRequestCustomizerTests { this.customizer.inject(this.span, this.customizer.toSpanTextMap(requestBuilder)); HttpRequest request = requestBuilder.build(); - thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "1"); - thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "2"); - thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "3"); + thenThereIsAHeaderWithNameAndValue(request, Span.SPAN_ID_NAME, "0000000000000001"); + thenThereIsAHeaderWithNameAndValue(request, Span.TRACE_ID_NAME, "0000000000000002"); + thenThereIsAHeaderWithNameAndValue(request, Span.PARENT_ID_NAME, "0000000000000003"); thenThereIsAHeaderWithNameAndValue(request, Span.PROCESS_ID_NAME, "processId"); }