[BREAKING CHANGE] Cleanup TraceId/SpanId classes. Ensure consistency. (#2696)

Summary of changes:
* getHexLength renamed to getLength
* bytesFromHex/bytesToHex renamed to asBytes/fromBytes.
* bytesFromHex/bytesToHex renamed to asLongHigh/asLongLow
* Update javadoc.

Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com>
This commit is contained in:
Bogdan Drutu 2021-02-04 07:38:25 -08:00 committed by GitHub
parent 0b250c1c91
commit 77d993d653
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 262 additions and 179 deletions

View File

@ -116,6 +116,20 @@ final class BigendianEncoding {
byteToBase16((byte) (value & 0xFFL), dest, destOffset + 7 * BYTE_BASE16);
}
static byte[] bytesFromBase16(CharSequence value, int length) {
byte[] result = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
result[i / 2] = byteFromBase16(value, i);
}
return result;
}
static void bytesToBase16(byte[] bytes, char[] dest) {
for (int i = 0; i < bytes.length; i++) {
byteToBase16(bytes[i], dest, i * 2);
}
}
/**
* Encodes the specified byte, and returns the encoded {@code String}.
*
@ -123,16 +137,10 @@ final class BigendianEncoding {
* @param dest the destination char array.
* @param destOffset the starting offset in the destination char array.
*/
static void byteToBase16String(byte value, char[] dest, int destOffset) {
byteToBase16(value, dest, destOffset);
}
static byte[] bytesFromBase16(CharSequence value, int offset, int length) {
byte[] result = new byte[length / 2];
for (int i = 0; i < length; i += 2) {
result[i / 2] = byteFromBase16String(value, offset + i);
}
return result;
static void byteToBase16(byte value, char[] dest, int destOffset) {
int b = value & 0xFF;
dest[destOffset] = ENCODING[b];
dest[destOffset + 1] = ENCODING[b | 0x100];
}
/**
@ -144,7 +152,7 @@ final class BigendianEncoding {
* @throws IllegalArgumentException if the input is not a valid encoded string according to this
* encoding.
*/
static byte byteFromBase16String(CharSequence chars, int offset) {
static byte byteFromBase16(CharSequence chars, int offset) {
Utils.checkArgument(chars.length() >= offset + 2, "chars too small");
return decodeByte(chars.charAt(offset), chars.charAt(offset + 1));
}
@ -156,12 +164,6 @@ final class BigendianEncoding {
return (byte) decoded;
}
private static void byteToBase16(byte value, char[] dest, int destOffset) {
int b = value & 0xFF;
dest[destOffset] = ENCODING[b];
dest[destOffset + 1] = ENCODING[b | 0x100];
}
static boolean isValidBase16String(CharSequence value) {
for (int i = 0; i < value.length(); i++) {
char b = value.charAt(i);
@ -182,12 +184,4 @@ final class BigendianEncoding {
}
private BigendianEncoding() {}
static String toLowerBase16(byte[] bytes) {
char[] chars = new char[bytes.length * 2];
for (int i = 0; i < bytes.length; i++) {
byteToBase16(bytes[i], chars, i * 2);
}
return new String(chars);
}
}

View File

@ -76,7 +76,7 @@ public interface SpanContext {
* SpanContext}.
*/
default byte[] getTraceIdBytes() {
return TraceId.bytesFromHex(getTraceId());
return TraceId.asBytes(getTraceId());
}
/**
@ -91,7 +91,7 @@ public interface SpanContext {
* SpanContext}.
*/
default byte[] getSpanIdBytes() {
return SpanId.bytesFromHex(getSpanId());
return SpanId.asBytes(getSpanId());
}
/** Whether the span in this context is sampled. */
@ -103,7 +103,7 @@ public interface SpanContext {
byte getTraceFlags();
default void copyTraceFlagsHexTo(char[] dest, int destOffset) {
BigendianEncoding.byteToBase16String(getTraceFlags(), dest, destOffset);
BigendianEncoding.byteToBase16(getTraceFlags(), dest, destOffset);
}
/**

View File

@ -5,30 +5,41 @@
package io.opentelemetry.api.trace;
import java.util.Objects;
import javax.annotation.concurrent.Immutable;
/**
* Helper methods for dealing with a span identifier. A valid span identifier is an 8-byte array
* with at least one non-zero byte. In lowercase hex (base16) representation, a 16 character hex
* String, where at least one of the characters is not a '0'.
* Helper methods for dealing with a span identifier. A valid span identifier is a 16 character
* lowercase hex (base16) String, where at least one of the characters is not a "0".
*
* <p>There are two more other representation that this class helps with:
*
* <ul>
* <li>Bytes: a 8-byte array, where valid means that at least one of the bytes is not `\0`.
* <li>Long: a {@code long} value, where valid means that the value is non-zero.
* </ul>
*/
@Immutable
public final class SpanId {
private static final ThreadLocal<char[]> charBuffer = new ThreadLocal<>();
private static final int HEX_SIZE = 16;
private static final int HEX_LENGTH = 16;
private static final String INVALID = "0000000000000000";
private SpanId() {}
/** Returns the length of the lowercase hex (base16) representation of the {@code SpanId}. */
public static int getHexLength() {
return HEX_SIZE;
/**
* Returns the length of the lowercase hex (base16) representation of the {@code SpanId}.
*
* @return the length of the lowercase hex (base16) representation of the {@code SpanId}.
*/
public static int getLength() {
return HEX_LENGTH;
}
/**
* Returns the invalid {@code SpanId} in lowercase hex (base16) representation. All characters are
* '\0'.
* "0".
*
* @return the invalid {@code SpanId} lowercase in hex (base16) representation.
*/
@ -43,44 +54,81 @@ public final class SpanId {
* @return {@code true} if the span identifier is valid.
*/
public static boolean isValid(CharSequence spanId) {
return (spanId.length() == HEX_SIZE)
return (spanId.length() == HEX_LENGTH)
&& !INVALID.contentEquals(spanId)
&& BigendianEncoding.isValidBase16String(spanId);
}
/**
* Returns a {@code SpanId} built from a lowercase hex (base16) representation.
* Returns the lowercase hex (base16) representation of the {@code SpanId} converted from the
* given bytes representation.
*
* @param src the lowercase hex (base16) representation.
* @return a {@code SpanId} built from a lowercase hex (base16) representation.
* @throws NullPointerException if {@code src} is null.
* @throws IllegalArgumentException if not enough characters in the {@code src}.
* @param spanIdBytes the bytes (8-byte array) representation of the {@code SpanId}.
* @return the lowercase hex (base16) representation of the {@code SpanId}.
* @throws NullPointerException if {@code spanIdBytes} is null.
* @throws IllegalArgumentException if not enough characters in the {@code spanIdBytes}.
*/
public static byte[] bytesFromHex(CharSequence src) {
return BigendianEncoding.bytesFromBase16(src, 0, HEX_SIZE);
public static String fromBytes(byte[] spanIdBytes) {
Objects.requireNonNull(spanIdBytes, "spanIdBytes");
char[] result = getTemporaryBuffer();
BigendianEncoding.bytesToBase16(spanIdBytes, result);
return new String(result);
}
/** Encode the bytes as hex (base16), padded with '0's on the left. */
public static String bytesToHex(byte[] spanId) {
return BigendianEncoding.toLowerBase16(spanId);
/**
* Returns the bytes (8-byte array) representation of the {@code SpanId} converted from the given
* lowercase hex (base16) representation.
*
* @param spanId the lowercase hex (base16) representation of the {@code SpanId}.
* @return the bytes (8-byte array) representation of the {@code SpanId}.
* @throws NullPointerException if {@code spanId} is null.
* @throws IllegalArgumentException if not enough characters in the {@code spanId}.
*/
public static byte[] asBytes(CharSequence spanId) {
Objects.requireNonNull(spanId, "spanId");
return BigendianEncoding.bytesFromBase16(spanId, HEX_LENGTH);
}
/** Generate a valid {@link SpanId} from the given long value. */
/**
* Returns the lowercase hex (base16) representation of the {@code SpanId} converted from the
* given {@code long} value representation.
*
* <p>There is no restriction on the specified values, other than the already established validity
* rules applying to {@code SpanId}. Specifying 0 for the long value will effectively return
* {@link #getInvalid()}.
*
* <p>This is equivalent to calling {@link #fromBytes(byte[])} with the specified value stored as
* big-endian.
*
* @param id the higher part of the {@code TraceId}.
* @return the lowercase hex (base16) representation of the {@code SpanId}.
*/
public static String fromLong(long id) {
if (id == 0) {
return getInvalid();
}
char[] result = getTemporaryBuffer();
BigendianEncoding.longToBase16String(id, result, 0);
return new String(result);
}
/** Convert the the given hex spanId into a long representation. */
public static long asLong(CharSequence src) {
return BigendianEncoding.longFromBase16String(src, 0);
/**
* Returns the {@code long} value representation of the {@code SpanId} converted from the given
* lowercase hex (base16) representation.
*
* @param spanId the lowercase hex (base16) representation of the {@code SpanId}.
* @return the {@code long} value representation of the {@code SpanId}.
* @throws NullPointerException if {@code spanId} is null.
*/
public static long asLong(CharSequence spanId) {
Objects.requireNonNull(spanId, "spanId");
return BigendianEncoding.longFromBase16String(spanId, 0);
}
private static char[] getTemporaryBuffer() {
char[] chars = charBuffer.get();
if (chars == null) {
chars = new char[HEX_SIZE];
chars = new char[HEX_LENGTH];
charBuffer.set(chars);
}
return chars;

View File

@ -49,6 +49,6 @@ public final class TraceFlags {
/** Extract the byte representation of the flags from a hex-representation. */
public static byte byteFromHex(CharSequence src, int srcOffset) {
return BigendianEncoding.byteFromBase16String(src, srcOffset);
return BigendianEncoding.byteFromBase16(src, srcOffset);
}
}

View File

@ -9,27 +9,41 @@ import java.util.Objects;
import javax.annotation.concurrent.Immutable;
/**
* Helper methods for dealing with a trace identifier. A valid trace identifier is a 16-byte array
* with at least one non-zero byte. In lowercase hex (base16) representation, a 32 character hex
* String, where at least one of the characters is not a '0'.
* Helper methods for dealing with a trace identifier. A valid trace identifier is a 32 character
* lowercase hex (base16) String, where at least one of the characters is not a "0".
*
* <p>There are two more other representation that this class helps with:
*
* <ul>
* <li>Bytes: a 16-byte array, where valid means that at least one of the bytes is not `\0`.
* <li>Long: two {@code long} values, where valid means that at least one of values is non-zero.
* To avoid allocating new objects this representation uses two parts, "high part"
* representing the left most part of the {@code TraceId} and "low part" representing the
* right most part of the {@code TraceId}. This is equivalent with the values being stored as
* big-endian.
* </ul>
*/
@Immutable
public final class TraceId {
private static final ThreadLocal<char[]> charBuffer = new ThreadLocal<>();
private static final int HEX_SIZE = 32;
private static final int HEX_LENGTH = 32;
private static final String INVALID = "00000000000000000000000000000000";
private TraceId() {}
/** Returns the length of the lowercase hex (base16) representation of the {@code TraceId}. */
public static int getHexLength() {
return HEX_SIZE;
/**
* Returns the length of the lowercase hex (base16) representation of the {@code TraceId}.
*
* @return the length of the lowercase hex (base16) representation of the {@code TraceId}.
*/
public static int getLength() {
return HEX_LENGTH;
}
/**
* Returns the invalid {@code TraceId} in lowercase hex (base16) representation. All characters
* are '\0'.
* are "0".
*
* @return the invalid {@code TraceId} in lowercase hex (base16) representation.
*/
@ -44,84 +58,108 @@ public final class TraceId {
* @return {@code true} if the {@code TraceId} is valid.
*/
public static boolean isValid(CharSequence traceId) {
return (traceId.length() == HEX_SIZE)
return (traceId.length() == HEX_LENGTH)
&& !INVALID.contentEquals(traceId)
&& BigendianEncoding.isValidBase16String(traceId);
}
/**
* Returns a {@code TraceId} built from a lowercase hex (base16) representation.
* Returns the lowercase hex (base16) representation of the {@code TraceId} converted from the
* given bytes representation.
*
* @param src the lowercase hex (base16) representation.
* @return a {@code TraceId} built from a lowercase hex (base16) representation.
* @throws NullPointerException if {@code src} is null.
* @throws IllegalArgumentException if not enough characters in the {@code src}.
* @param traceIdBytes the bytes (16-byte array) representation of the {@code TraceId}.
* @return the lowercase hex (base16) representation of the {@code TraceId}.
* @throws NullPointerException if {@code traceIdBytes} is null.
* @throws IllegalArgumentException if not enough characters in the {@code traceIdBytes}.
*/
public static byte[] bytesFromHex(CharSequence src) {
Objects.requireNonNull(src, "src");
return BigendianEncoding.bytesFromBase16(src, 0, HEX_SIZE);
public static String fromBytes(byte[] traceIdBytes) {
Objects.requireNonNull(traceIdBytes, "traceIdBytes");
char[] result = getTemporaryBuffer();
BigendianEncoding.bytesToBase16(traceIdBytes, result);
return new String(result);
}
/**
* Returns the lowercase hex (base16) encoding of this {@code TraceId}.
* Returns the bytes (16-byte array) representation of the {@code TraceId} converted from the
* given lowercase hex (base16) representation.
*
* @return the lowercase hex (base16) encoding of this {@code TraceId}.
* @param traceId the lowercase hex (base16) representation of the {@code TraceId}.
* @return the bytes (16-byte array) representation of the {@code TraceId}.
* @throws NullPointerException if {@code traceId} is null.
* @throws IllegalArgumentException if not enough characters in the {@code traceId}.
*/
public static String bytesToHex(byte[] traceId) {
char[] chars = new char[HEX_SIZE];
BigendianEncoding.longToBase16String(BigendianEncoding.longFromByteArray(traceId, 0), chars, 0);
BigendianEncoding.longToBase16String(
BigendianEncoding.longFromByteArray(traceId, 8), chars, 16);
return new String(chars);
public static byte[] asBytes(CharSequence traceId) {
Objects.requireNonNull(traceId, "traceId");
return BigendianEncoding.bytesFromBase16(traceId, HEX_LENGTH);
}
/**
* Constructs a {@code TraceId} whose representation is specified by two long values representing
* the lower and higher parts.
* Returns the bytes (16-byte array) representation of the {@code TraceId} converted from the
* given two {@code long} values representing the lower and higher parts.
*
* <p>There is no restriction on the specified values, other than the already established validity
* rules applying to {@code TraceId}. Specifying 0 for both values will effectively make the new
* {@code TraceId} invalid.
* rules applying to {@code TraceId}. Specifying 0 for both values will effectively return {@link
* #getInvalid()}.
*
* <p>This is equivalent to calling {@link #bytesToHex(byte[])} with the specified values stored
* as big-endian.
* <p>This is equivalent to calling {@link #fromBytes(byte[])} with the specified values stored as
* big-endian.
*
* @param idHi the higher part of the {@code TraceId}.
* @param idLo the lower part of the {@code TraceId}.
* @param traceIdLongHighPart the higher part of the long values representation of the {@code
* TraceId}.
* @param traceIdLongLowPart the lower part of the long values representation of the {@code
* TraceId}.
* @return the lowercase hex (base16) representation of the {@code TraceId}.
*/
public static String fromLongs(long idHi, long idLo) {
public static String fromLongs(long traceIdLongHighPart, long traceIdLongLowPart) {
if (traceIdLongHighPart == 0 && traceIdLongLowPart == 0) {
return getInvalid();
}
char[] chars = getTemporaryBuffer();
BigendianEncoding.longToBase16String(idHi, chars, 0);
BigendianEncoding.longToBase16String(idLo, chars, 16);
BigendianEncoding.longToBase16String(traceIdLongHighPart, chars, 0);
BigendianEncoding.longToBase16String(traceIdLongLowPart, chars, 16);
return new String(chars);
}
/**
* Returns the rightmost 8 bytes of the trace-id as a long value. This is used in
* ProbabilitySampler.
* Returns the rightmost 8 bytes of the trace-id as a long value. This is used in {@code
* TraceIdRatioBasedSampler}.
*
* <p>This method is marked as internal and subject to change.
*
* @return the rightmost 8 bytes of the trace-id as a long value.
*/
public static long getTraceIdRandomPart(CharSequence traceId) {
return traceIdLowBytesAsLong(traceId);
return lowPartAsLong(traceId);
}
/** Convert the "high bytes" of the given hex traceId into a long representation. */
public static long traceIdHighBytesAsLong(CharSequence traceId) {
/**
* Returns the "high part" of the {@code long} values representation of the {@code TraceId}
* converted from the given lowercase hex (base16) representation.
*
* @param traceId the lowercase hex (base16) representation of the {@code TraceId}.
* @return the {@code long} value representation of the {@code TraceId}.
* @throws NullPointerException if {@code traceId} is null.
*/
public static long highPartAsLong(CharSequence traceId) {
return BigendianEncoding.longFromBase16String(traceId, 0);
}
/** Convert the "low bytes" of the given hex traceId into a long representation. */
public static long traceIdLowBytesAsLong(CharSequence traceId) {
/**
* Returns the "low part" of the {@code long} values representation of the {@code TraceId}
* converted from the given lowercase hex (base16) representation.
*
* @param traceId the lowercase hex (base16) representation of the {@code TraceId}.
* @return the {@code long} value representation of the {@code TraceId}.
* @throws NullPointerException if {@code traceId} is null.
*/
public static long lowPartAsLong(CharSequence traceId) {
return BigendianEncoding.longFromBase16String(traceId, BigendianEncoding.LONG_BASE16);
}
private static char[] getTemporaryBuffer() {
char[] chars = charBuffer.get();
if (chars == null) {
chars = new char[HEX_SIZE];
chars = new char[HEX_LENGTH];
charBuffer.set(chars);
}
return chars;

View File

@ -48,8 +48,8 @@ public final class W3CTraceContextPropagator implements TextMapPropagator {
private static final int VERSION_SIZE = 2;
private static final char TRACEPARENT_DELIMITER = '-';
private static final int TRACEPARENT_DELIMITER_SIZE = 1;
private static final int TRACE_ID_HEX_SIZE = TraceId.getHexLength();
private static final int SPAN_ID_HEX_SIZE = SpanId.getHexLength();
private static final int TRACE_ID_HEX_SIZE = TraceId.getLength();
private static final int SPAN_ID_HEX_SIZE = SpanId.getLength();
private static final int TRACE_OPTION_HEX_SIZE = TraceFlags.getHexLength();
private static final int TRACE_ID_OFFSET = VERSION_SIZE + TRACEPARENT_DELIMITER_SIZE;
private static final int SPAN_ID_OFFSET =
@ -210,8 +210,8 @@ public final class W3CTraceContextPropagator implements TextMapPropagator {
}
String traceId =
traceparent.substring(TRACE_ID_OFFSET, TRACE_ID_OFFSET + TraceId.getHexLength());
String spanId = traceparent.substring(SPAN_ID_OFFSET, SPAN_ID_OFFSET + SpanId.getHexLength());
traceparent.substring(TRACE_ID_OFFSET, TRACE_ID_OFFSET + TraceId.getLength());
String spanId = traceparent.substring(SPAN_ID_OFFSET, SPAN_ID_OFFSET + SpanId.getLength());
if (TraceId.isValid(traceId) && SpanId.isValid(spanId)) {
byte traceFlags = TraceFlags.byteFromHex(traceparent, TRACE_OPTION_OFFSET);
return SpanContext.createFromRemoteParent(

View File

@ -179,7 +179,7 @@ class BigendianEncodingTest {
@Test
void invalidOffset() {
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16String("fff", 2))
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16("fff", 2))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("chars too small");
}
@ -187,16 +187,16 @@ class BigendianEncodingTest {
@Test
@SuppressWarnings("checkstyle:AvoidEscapedUnicodeCharacters")
void invalidBytes() {
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16String("gf", 0))
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16("gf", 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("invalid character g");
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16String("\u0129f", 0))
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16("\u0129f", 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("invalid character \u0129");
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16String("fg", 0))
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16("fg", 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("invalid character g");
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16String("f\u0129", 0))
assertThatThrownBy(() -> BigendianEncoding.byteFromBase16("f\u0129", 0))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("invalid character \u0129");
}

View File

@ -15,38 +15,37 @@ class SpanIdTest {
private static final byte[] firstBytes = new byte[] {0, 0, 0, 0, 0, 0, 0, 'a'};
private static final byte[] secondBytes = new byte[] {(byte) 0xFF, 0, 0, 0, 0, 0, 0, 'A'};
@Test
void invalid() {
assertThat(SpanId.getInvalid()).isEqualTo("0000000000000000");
assertThat(SpanId.asBytes(SpanId.getInvalid())).isEqualTo(new byte[] {0, 0, 0, 0, 0, 0, 0, 0});
assertThat(SpanId.asLong(SpanId.getInvalid())).isEqualTo(0);
}
@Test
void isValid() {
assertThat(SpanId.isValid(SpanId.getInvalid())).isFalse();
assertThat(SpanId.isValid(SpanId.bytesToHex(firstBytes))).isTrue();
assertThat(SpanId.isValid(SpanId.bytesToHex(secondBytes))).isTrue();
assertThat(SpanId.isValid(SpanId.fromBytes(firstBytes))).isTrue();
assertThat(SpanId.isValid(SpanId.fromBytes(secondBytes))).isTrue();
assertThat(SpanId.isValid("000000000000z000")).isFalse();
}
@Test
void fromLowerHex() {
assertThat(SpanId.bytesToHex(SpanId.bytesFromHex("0000000000000000")))
.isEqualTo(SpanId.getInvalid());
assertThat(SpanId.bytesFromHex("0000000000000061")).isEqualTo(firstBytes);
assertThat(SpanId.bytesFromHex("ff00000000000041")).isEqualTo(secondBytes);
assertThat(SpanId.fromBytes(SpanId.asBytes("0000000000000000"))).isEqualTo(SpanId.getInvalid());
assertThat(SpanId.asBytes("0000000000000061")).isEqualTo(firstBytes);
assertThat(SpanId.asBytes("ff00000000000041")).isEqualTo(secondBytes);
}
@Test
public void toLowerHex() {
assertThat(SpanId.getInvalid()).isEqualTo("0000000000000000");
assertThat(SpanId.bytesToHex(firstBytes)).isEqualTo("0000000000000061");
assertThat(SpanId.bytesToHex(secondBytes)).isEqualTo("ff00000000000041");
assertThat(SpanId.fromBytes(firstBytes)).isEqualTo("0000000000000061");
assertThat(SpanId.fromBytes(secondBytes)).isEqualTo("ff00000000000041");
}
@Test
void spanId_ToString() {
assertThat(SpanId.getInvalid()).contains("0000000000000000");
assertThat(SpanId.bytesToHex(firstBytes)).contains("0000000000000061");
assertThat(SpanId.bytesToHex(secondBytes)).contains("ff00000000000041");
}
@Test
void toAndFromLong() {
void toFromLong() {
Random random = new Random();
for (int i = 0; i < 1000; i++) {
long id = random.nextLong();

View File

@ -17,15 +17,19 @@ class TraceIdTest {
new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'a'};
private static final byte[] secondBytes =
new byte[] {(byte) 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'A'};
private static final String first = TraceId.bytesToHex(firstBytes);
private static final String first = TraceId.fromBytes(firstBytes);
private static final String second =
TraceId.fromLongs(
ByteBuffer.wrap(secondBytes).getLong(), ByteBuffer.wrap(secondBytes, 8, 8).getLong());
@Test
void invalidTraceId() {
assertThat(TraceId.getTraceIdRandomPart(TraceId.getInvalid())).isEqualTo(0);
void invalid() {
assertThat(TraceId.getInvalid()).isEqualTo("00000000000000000000000000000000");
assertThat(TraceId.asBytes(TraceId.getInvalid()))
.isEqualTo(new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0});
assertThat(TraceId.highPartAsLong(TraceId.getInvalid())).isEqualTo(0);
assertThat(TraceId.lowPartAsLong(TraceId.getInvalid())).isEqualTo(0);
}
@Test
@ -43,21 +47,21 @@ class TraceIdTest {
byte[] id = {
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00
};
String traceId = TraceId.bytesToHex(id);
String traceId = TraceId.fromBytes(id);
assertThat(TraceId.getTraceIdRandomPart(traceId)).isEqualTo(0x090A0B0C0D0E0F00L);
}
@Test
void testGetRandomTracePart_NegativeLongRepresentation() {
byte[] id = {
(byte) 0xFF, // force a negative value
0x01,
0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
0x08,
0x00,
(byte) 0xFF, // force a negative value
0x0A,
0x0B,
@ -67,34 +71,35 @@ class TraceIdTest {
0x0F,
0x00
};
String traceId = TraceId.bytesToHex(id);
assertThat(TraceId.getTraceIdRandomPart(traceId)).isEqualTo(0xFF0A0B0C0D0E0F00L);
String traceId = TraceId.fromBytes(id);
assertThat(TraceId.highPartAsLong(traceId)).isEqualTo(0xFF01020304050600L);
assertThat(TraceId.lowPartAsLong(traceId)).isEqualTo(0xFF0A0B0C0D0E0F00L);
}
@Test
void fromLowerHex() {
assertThat(TraceId.bytesToHex(TraceId.bytesFromHex("00000000000000000000000000000000")))
assertThat(TraceId.fromBytes(TraceId.asBytes("00000000000000000000000000000000")))
.isEqualTo(TraceId.getInvalid());
assertThat(TraceId.bytesFromHex("00000000000000000000000000000061")).isEqualTo(firstBytes);
assertThat(TraceId.bytesFromHex("ff000000000000000000000000000041")).isEqualTo(secondBytes);
assertThat(TraceId.asBytes("00000000000000000000000000000061")).isEqualTo(firstBytes);
assertThat(TraceId.asBytes("ff000000000000000000000000000041")).isEqualTo(secondBytes);
}
@Test
void toLowerHex() {
assertThat(TraceId.getInvalid()).isEqualTo("00000000000000000000000000000000");
assertThat(TraceId.bytesToHex(firstBytes)).isEqualTo("00000000000000000000000000000061");
assertThat(TraceId.bytesToHex(secondBytes)).isEqualTo("ff000000000000000000000000000041");
assertThat(TraceId.fromBytes(firstBytes)).isEqualTo("00000000000000000000000000000061");
assertThat(TraceId.fromBytes(secondBytes)).isEqualTo("ff000000000000000000000000000041");
}
@Test
void toAndFromLongs() {
void toFromLongs() {
Random random = new Random();
for (int i = 0; i < 10000; i++) {
long idHi = random.nextLong();
long idLo = random.nextLong();
String traceId = TraceId.fromLongs(idHi, idLo);
assertThat(TraceId.traceIdHighBytesAsLong(traceId)).isEqualTo(idHi);
assertThat(TraceId.traceIdLowBytesAsLong(traceId)).isEqualTo(idLo);
assertThat(TraceId.highPartAsLong(traceId)).isEqualTo(idHi);
assertThat(TraceId.lowPartAsLong(traceId)).isEqualTo(idLo);
}
}
}

View File

@ -67,8 +67,8 @@ final class Adapter {
static Span toJaeger(SpanData span) {
Span target = new Span();
long traceIdHigh = TraceId.traceIdHighBytesAsLong(span.getTraceId());
long traceIdLow = TraceId.traceIdLowBytesAsLong(span.getTraceId());
long traceIdHigh = TraceId.highPartAsLong(span.getTraceId());
long traceIdLow = TraceId.lowPartAsLong(span.getTraceId());
long spanIdAsLong = SpanId.asLong(span.getSpanId());
target.setTraceIdHigh(traceIdHigh);
@ -232,8 +232,8 @@ final class Adapter {
// https://github.com/open-telemetry/opentelemetry-java/pull/481/files#r312577862
return new SpanRef(
SpanRefType.FOLLOWS_FROM,
TraceId.traceIdLowBytesAsLong(link.getSpanContext().getTraceId()),
TraceId.traceIdHighBytesAsLong(link.getSpanContext().getTraceId()),
TraceId.lowPartAsLong(link.getSpanContext().getTraceId()),
TraceId.highPartAsLong(link.getSpanContext().getTraceId()),
SpanId.asLong(link.getSpanContext().getSpanId()));
}
}

View File

@ -107,8 +107,8 @@ class JaegerThriftSpanExporterTest {
Span expectedSpan =
new Span()
.setTraceIdHigh(TraceId.traceIdHighBytesAsLong(TRACE_ID))
.setTraceIdLow(TraceId.traceIdLowBytesAsLong(TRACE_ID))
.setTraceIdHigh(TraceId.highPartAsLong(TRACE_ID))
.setTraceIdLow(TraceId.lowPartAsLong(TRACE_ID))
.setSpanId(SpanId.asLong(SPAN_ID))
.setOperationName("GET /api/endpoint")
.setReferences(Collections.emptyList())
@ -206,8 +206,8 @@ class JaegerThriftSpanExporterTest {
Span expectedSpan1 =
new Span()
.setTraceIdHigh(TraceId.traceIdHighBytesAsLong(TRACE_ID))
.setTraceIdLow(TraceId.traceIdLowBytesAsLong(TRACE_ID))
.setTraceIdHigh(TraceId.highPartAsLong(TRACE_ID))
.setTraceIdLow(TraceId.lowPartAsLong(TRACE_ID))
.setSpanId(SpanId.asLong(SPAN_ID))
.setOperationName("GET /api/endpoint/1")
.setReferences(Collections.emptyList())
@ -222,8 +222,8 @@ class JaegerThriftSpanExporterTest {
Span expectedSpan2 =
new Span()
.setTraceIdHigh(TraceId.traceIdHighBytesAsLong(TRACE_ID))
.setTraceIdLow(TraceId.traceIdLowBytesAsLong(TRACE_ID))
.setTraceIdHigh(TraceId.highPartAsLong(TRACE_ID))
.setTraceIdLow(TraceId.lowPartAsLong(TRACE_ID))
.setSpanId(SpanId.asLong(SPAN_ID_2))
.setOperationName("GET /api/endpoint/2")
.setReferences(Collections.emptyList())

View File

@ -73,9 +73,9 @@ class AdapterTest {
// test
Model.Span jaegerSpan = Adapter.toJaeger(span);
assertThat(TraceId.bytesToHex(jaegerSpan.getTraceId().toByteArray()))
assertThat(TraceId.fromBytes(jaegerSpan.getTraceId().toByteArray()))
.isEqualTo(span.getTraceId());
assertThat(SpanId.bytesToHex(jaegerSpan.getSpanId().toByteArray())).isEqualTo(span.getSpanId());
assertThat(SpanId.fromBytes(jaegerSpan.getSpanId().toByteArray())).isEqualTo(span.getSpanId());
assertThat(jaegerSpan.getOperationName()).isEqualTo("GET /api/endpoint");
assertThat(jaegerSpan.getStartTime()).isEqualTo(Timestamps.fromMillis(startMs));
assertThat(Durations.toMillis(jaegerSpan.getDuration())).isEqualTo(duration);
@ -216,8 +216,8 @@ class AdapterTest {
Model.SpanRef spanRef = Adapter.toSpanRef(link);
// verify
assertThat(SpanId.bytesToHex(spanRef.getSpanId().toByteArray())).isEqualTo(SPAN_ID);
assertThat(TraceId.bytesToHex(spanRef.getTraceId().toByteArray())).isEqualTo(TRACE_ID);
assertThat(SpanId.fromBytes(spanRef.getSpanId().toByteArray())).isEqualTo(SPAN_ID);
assertThat(TraceId.fromBytes(spanRef.getTraceId().toByteArray())).isEqualTo(TRACE_ID);
assertThat(spanRef.getRefType()).isEqualTo(Model.SpanRefType.FOLLOWS_FROM);
}
@ -330,8 +330,8 @@ class AdapterTest {
boolean found = false;
for (Model.SpanRef spanRef : jaegerSpan.getReferencesList()) {
if (Model.SpanRefType.FOLLOWS_FROM.equals(spanRef.getRefType())) {
assertThat(TraceId.bytesToHex(spanRef.getTraceId().toByteArray())).isEqualTo(LINK_TRACE_ID);
assertThat(SpanId.bytesToHex(spanRef.getSpanId().toByteArray())).isEqualTo(LINK_SPAN_ID);
assertThat(TraceId.fromBytes(spanRef.getTraceId().toByteArray())).isEqualTo(LINK_TRACE_ID);
assertThat(SpanId.fromBytes(spanRef.getSpanId().toByteArray())).isEqualTo(LINK_SPAN_ID);
found = true;
}
}
@ -342,8 +342,8 @@ class AdapterTest {
boolean found = false;
for (Model.SpanRef spanRef : jaegerSpan.getReferencesList()) {
if (Model.SpanRefType.CHILD_OF.equals(spanRef.getRefType())) {
assertThat(TraceId.bytesToHex(spanRef.getTraceId().toByteArray())).isEqualTo(TRACE_ID);
assertThat(SpanId.bytesToHex(spanRef.getSpanId().toByteArray())).isEqualTo(PARENT_SPAN_ID);
assertThat(TraceId.fromBytes(spanRef.getTraceId().toByteArray())).isEqualTo(TRACE_ID);
assertThat(SpanId.fromBytes(spanRef.getSpanId().toByteArray())).isEqualTo(PARENT_SPAN_ID);
found = true;
}
}

View File

@ -125,7 +125,7 @@ class JaegerGrpcSpanExporterTest {
Model.Batch batch = requestCaptor.getValue().getBatch();
assertThat(batch.getSpans(0).getOperationName()).isEqualTo("GET /api/endpoint");
assertThat(SpanId.bytesToHex(batch.getSpans(0).getSpanId().toByteArray())).isEqualTo(SPAN_ID);
assertThat(SpanId.fromBytes(batch.getSpans(0).getSpanId().toByteArray())).isEqualTo(SPAN_ID);
assertThat(
getTagValue(batch.getProcess().getTagsList(), "resource-attr-key")
@ -214,13 +214,13 @@ class JaegerGrpcSpanExporterTest {
if (processTag.isPresent()) {
assertThat(processTag2.isPresent()).isFalse();
assertThat(batch.getSpans(0).getOperationName()).isEqualTo("GET /api/endpoint/1");
assertThat(SpanId.bytesToHex(batch.getSpans(0).getSpanId().toByteArray()))
assertThat(SpanId.fromBytes(batch.getSpans(0).getSpanId().toByteArray()))
.isEqualTo(SPAN_ID);
assertThat(processTag.get().getVStr()).isEqualTo("resource-attr-value-1");
assertThat(batch.getProcess().getServiceName()).isEqualTo("myServiceName1");
} else if (processTag2.isPresent()) {
assertThat(batch.getSpans(0).getOperationName()).isEqualTo("GET /api/endpoint/2");
assertThat(SpanId.bytesToHex(batch.getSpans(0).getSpanId().toByteArray()))
assertThat(SpanId.fromBytes(batch.getSpans(0).getSpanId().toByteArray()))
.isEqualTo(SPAN_ID_2);
assertThat(processTag2.get().getVStr()).isEqualTo("resource-attr-value-2");
assertThat(batch.getProcess().getServiceName()).isEqualTo("myServiceName2");
@ -232,8 +232,7 @@ class JaegerGrpcSpanExporterTest {
private static void verifyBatch(Model.Batch batch) throws Exception {
assertThat(batch.getSpansCount()).isEqualTo(1);
assertThat(TraceId.bytesToHex(batch.getSpans(0).getTraceId().toByteArray()))
.isEqualTo(TRACE_ID);
assertThat(TraceId.fromBytes(batch.getSpans(0).getTraceId().toByteArray())).isEqualTo(TRACE_ID);
assertThat(batch.getProcess().getTagsCount()).isEqualTo(5);
assertThat(

View File

@ -49,10 +49,10 @@ public class RequestMarshalState {
InstrumentationLibraryInfo.create("name", null);
private static final byte[] TRACE_ID_BYTES =
new byte[] {123, 46, 23, 78, 12, 5, (byte) 180, (byte) 223, 45, 89, 71, 61, 62, 29, 34, 54};
private static final String TRACE_ID = TraceId.bytesToHex(TRACE_ID_BYTES);
private static final String TRACE_ID = TraceId.fromBytes(TRACE_ID_BYTES);
private static final byte[] SPAN_ID_BYTES =
new byte[] {(byte) 198, (byte) 245, (byte) 213, (byte) 156, 46, 31, 29, 101};
private static final String SPAN_ID = SpanId.bytesToHex(SPAN_ID_BYTES);
private static final String SPAN_ID = SpanId.fromBytes(SPAN_ID_BYTES);
private static final SpanContext SPAN_CONTEXT =
SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getSampled(), TraceState.getDefault());

View File

@ -54,9 +54,9 @@ class TraceMarshalerTest {
InstrumentationLibraryInfo.create("name", null);
private static final byte[] TRACE_ID_BYTES =
new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4};
private static final String TRACE_ID = TraceId.bytesToHex(TRACE_ID_BYTES);
private static final String TRACE_ID = TraceId.fromBytes(TRACE_ID_BYTES);
private static final byte[] SPAN_ID_BYTES = new byte[] {0, 0, 0, 0, 4, 3, 2, 1};
private static final String SPAN_ID = SpanId.bytesToHex(SPAN_ID_BYTES);
private static final String SPAN_ID = SpanId.fromBytes(SPAN_ID_BYTES);
private static final SpanContext SPAN_CONTEXT =
SpanContext.create(

View File

@ -17,8 +17,8 @@ import javax.annotation.concurrent.Immutable;
@Immutable
final class B3PropagatorInjectorSingleHeader implements B3PropagatorInjector {
private static final int SAMPLED_FLAG_SIZE = 1;
private static final int TRACE_ID_HEX_SIZE = TraceId.getHexLength();
private static final int SPAN_ID_HEX_SIZE = SpanId.getHexLength();
private static final int TRACE_ID_HEX_SIZE = TraceId.getLength();
private static final int SPAN_ID_HEX_SIZE = SpanId.getLength();
private static final int COMBINED_HEADER_DELIMITER_SIZE = 1;
private static final int SPAN_ID_OFFSET = TRACE_ID_HEX_SIZE + COMBINED_HEADER_DELIMITER_SIZE;
private static final int SAMPLED_FLAG_OFFSET =
@ -41,7 +41,7 @@ final class B3PropagatorInjectorSingleHeader implements B3PropagatorInjector {
chars[SPAN_ID_OFFSET - 1] = B3Propagator.COMBINED_HEADER_DELIMITER_CHAR;
String spanId = spanContext.getSpanId();
System.arraycopy(spanId.toCharArray(), 0, chars, SPAN_ID_OFFSET, SpanId.getHexLength());
System.arraycopy(spanId.toCharArray(), 0, chars, SPAN_ID_OFFSET, SpanId.getLength());
chars[SAMPLED_FLAG_OFFSET - 1] = B3Propagator.COMBINED_HEADER_DELIMITER_CHAR;
if (Boolean.TRUE.equals(context.get(B3Propagator.DEBUG_CONTEXT_KEY))) {

View File

@ -24,7 +24,7 @@ final class Common {
static final String TRUE_INT = "1";
static final String FALSE_INT = "0";
static final int MAX_TRACE_ID_LENGTH = TraceId.getHexLength();
static final int MAX_TRACE_ID_LENGTH = TraceId.getLength();
static final int MIN_TRACE_ID_LENGTH = MAX_TRACE_ID_LENGTH / 2;
private Common() {}
@ -50,7 +50,7 @@ final class Common {
static boolean isTraceIdValid(String value) {
return !(StringUtils.isNullOrEmpty(value)
|| (value.length() != MIN_TRACE_ID_LENGTH && value.length() != MAX_TRACE_ID_LENGTH)
|| !TraceId.isValid(StringUtils.padLeft(value, TraceId.getHexLength())));
|| !TraceId.isValid(StringUtils.padLeft(value, TraceId.getLength())));
}
static boolean isSpanIdValid(String value) {

View File

@ -42,16 +42,16 @@ public final class JaegerPropagator implements TextMapPropagator {
static final char DEPRECATED_PARENT_SPAN = '0';
static final char PROPAGATION_HEADER_DELIMITER = ':';
private static final int MAX_TRACE_ID_LENGTH = TraceId.getHexLength();
private static final int MAX_SPAN_ID_LENGTH = SpanId.getHexLength();
private static final int MAX_TRACE_ID_LENGTH = TraceId.getLength();
private static final int MAX_SPAN_ID_LENGTH = SpanId.getLength();
private static final int MAX_FLAGS_LENGTH = 2;
private static final char IS_SAMPLED_CHAR = '1';
private static final char NOT_SAMPLED_CHAR = '0';
private static final int PROPAGATION_HEADER_DELIMITER_SIZE = 1;
private static final int TRACE_ID_HEX_SIZE = TraceId.getHexLength();
private static final int SPAN_ID_HEX_SIZE = SpanId.getHexLength();
private static final int TRACE_ID_HEX_SIZE = TraceId.getLength();
private static final int SPAN_ID_HEX_SIZE = SpanId.getLength();
private static final int PARENT_SPAN_ID_SIZE = 1;
private static final int SAMPLED_FLAG_SIZE = 1;

View File

@ -64,7 +64,7 @@ public final class OtTracerPropagator implements TextMapPropagator {
// Lightstep trace id MUST be 64-bits therefore OpenTelemetry trace id is truncated to 64-bits
// by retaining least significant (right-most) bits.
setter.set(
carrier, TRACE_ID_HEADER, spanContext.getTraceId().substring(TraceId.getHexLength() / 2));
carrier, TRACE_ID_HEADER, spanContext.getTraceId().substring(TraceId.getLength() / 2));
setter.set(carrier, SPAN_ID_HEADER, spanContext.getSpanId());
setter.set(carrier, SAMPLED_HEADER, String.valueOf(spanContext.isSampled()));

View File

@ -42,9 +42,9 @@ import org.junit.jupiter.api.Test;
class SpanAdapterTest {
private static final byte[] TRACE_ID_BYTES =
new byte[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4};
private static final String TRACE_ID = TraceId.bytesToHex(TRACE_ID_BYTES);
private static final String TRACE_ID = TraceId.fromBytes(TRACE_ID_BYTES);
private static final byte[] SPAN_ID_BYTES = new byte[] {0, 0, 0, 0, 4, 3, 2, 1};
private static final String SPAN_ID = SpanId.bytesToHex(SPAN_ID_BYTES);
private static final String SPAN_ID = SpanId.fromBytes(SPAN_ID_BYTES);
private static final SpanContext SPAN_CONTEXT =
SpanContext.create(TRACE_ID, SPAN_ID, TraceFlags.getSampled(), TraceState.getDefault());

View File

@ -149,7 +149,7 @@ class TraceIdRatioBasedSamplerTest {
// This traceId will not be sampled by the Probability Sampler because the last 8 bytes as long
// is not less than probability * Long.MAX_VALUE;
String notSampledTraceId =
TraceId.bytesToHex(
TraceId.fromBytes(
new byte[] {
0,
0,
@ -180,7 +180,7 @@ class TraceIdRatioBasedSamplerTest {
// This traceId will be sampled by the Probability Sampler because the last 8 bytes as long
// is less than probability * Long.MAX_VALUE;
String sampledTraceId =
TraceId.bytesToHex(
TraceId.fromBytes(
new byte[] {
(byte) 0x00,
(byte) 0x00,