Skip to content

Commit

Permalink
Tolerate reads of 128 bit X-B3-TraceId
Browse files Browse the repository at this point in the history
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.

See openzipkin/b3-propagation#6
  • Loading branch information
Adrian Cole committed Sep 15, 2016
1 parent f00585a commit 5b3c206
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -421,15 +420,35 @@ public static String idToHex(long id) {
}

/**
* 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 isntLowerHexLong(hexString);

// 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 isntLowerHexLong(hexString);
}
}
return result;
}

static NumberFormatException isntLowerHexLong(String lowerHex) {
throw new NumberFormatException(
lowerHex + " should be a 1 to 32 character lower-hex string with no prefix");
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ public void should_convert_hex_string_to_long() throws Exception {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,20 @@ public void shouldNotTraceIgnoredChannel() {
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,19 @@ public void should_not_throw_exception_if_parent_id_is_invalid() {
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));
}
}

0 comments on commit 5b3c206

Please sign in to comment.