Tolerate reads of 128 bit X-B3-TraceId (#408)

The first step of transitioning to 128bit `X-B3-TraceId` is tolerantly reading 32 character long ids by throwing away the high bits (any characters left of 16 characters). This allows the tracing system to more flexibly introduce 128bit trace id support in the future.

Ex. when `X-B3-TraceId: 463ac35c9f6413ad48485a3953bb6124` is received, parse the lower 64 bits (right most 16 characters ex48485a3953bb6124) as the trace id.
This commit is contained in:
Adrian Cole
2016-09-15 21:12:33 +08:00
committed by Adrian Cole
parent 00fe8e0f5a
commit 02473f7c18
4 changed files with 60 additions and 7 deletions

View File

@@ -16,7 +16,6 @@
package org.springframework.cloud.sleuth;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -421,15 +420,30 @@ public class Span {
}
/**
* Represents hex string as long
* 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 hexToId(String hexString) {
Assert.hasText(hexString, "Can't convert empty hex string to long");
try {
return new BigInteger(hexString, 16).longValue();
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Malformed id [" + hexString + "]", e);
int length = hexString.length();
if (length < 1 || length > 32) throw new IllegalArgumentException("Malformed id");
// trim off any high bits
int i = length > 16 ? length - 16 : 0;
long result = 0;
for (; i < length; i++) {
char c = hexString.charAt(i);
result <<= 4;
if (c >= '0' && c <= '9') {
result |= c - '0';
} else if (c >= 'a' && c <= 'f') {
result |= c - 'a' + 10;
} else {
throw new IllegalArgumentException("Malformed id");
}
}
return result;
}
@Override

View File

@@ -52,6 +52,16 @@ public class SpanTests {
then(someLong).isEqualTo(123123L);
}
@Test
public void should_convert_lower_64bits_of_hex_string_to_long() throws Exception {
String hex128Bits = "463ac35c9f6413ad48485a3953bb6124";
String lower64Bits = "48485a3953bb6124";
long someLong = Span.hexToId(hex128Bits);
then(someLong).isEqualTo(Span.hexToId(lower64Bits));
}
@Test(expected = IllegalArgumentException.class)
public void should_throw_exception_when_null_string_is_to_be_converted_to_long() throws Exception {
Span.hexToId(null);

View File

@@ -275,6 +275,20 @@ public class TraceChannelInterceptorTests implements MessageHandler {
then(TestSpanContextHolder.getCurrentSpan()).isNull();
}
@Test
public void downgrades128bitIdsByDroppingHighBits() {
String hex128Bits = "463ac35c9f6413ad48485a3953bb6124";
String lower64Bits = "48485a3953bb6124";
this.tracedChannel.send(MessageBuilder.withPayload("hi")
.setHeader(Span.TRACE_ID_NAME, hex128Bits)
.setHeader(Span.SPAN_ID_NAME, Span.idToHex(20L)).build());
then(this.message).isNotNull();
long traceId = Span.hexToId(this.message.getHeaders()
.get(TraceMessageHeaders.TRACE_ID_NAME, String.class));
then(traceId).isEqualTo(Span.hexToId(lower64Bits));
}
@Configuration
@EnableAutoConfiguration
static class App {

View File

@@ -93,4 +93,19 @@ public class HttpServletRequestExtractorTests {
then(e).hasMessageContaining("Malformed id");
}
}
}
@Test
public void should_downgrade_128bit_trace_id_by_dropping_high_bits() {
String hex128Bits = "463ac35c9f6413ad48485a3953bb6124";
String lower64Bits = "48485a3953bb6124";
BDDMockito.given(this.request.getHeader(Span.TRACE_ID_NAME))
.willReturn(hex128Bits);
BDDMockito.given(this.request.getHeader(Span.SPAN_ID_NAME))
.willReturn(lower64Bits);
Span span = this.extractor.joinTrace(this.request);
then(span.getTraceId()).isEqualTo(Span.hexToId(lower64Bits));
}
}