Change TraceFlags to be a class, expose all helpers of the class itself. (#2709)

Signed-off-by: Bogdan Drutu <bogdandrutu@gmail.com>
This commit is contained in:
Bogdan Drutu 2021-02-05 09:06:19 -08:00 committed by GitHub
parent 0bad3066fb
commit 6c30f411f3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 195 additions and 81 deletions

View File

@ -26,7 +26,11 @@ abstract class ImmutableSpanContext implements SpanContext {
}
static SpanContext create(
String traceIdHex, String spanIdHex, byte traceFlags, TraceState traceState, boolean remote) {
String traceIdHex,
String spanIdHex,
TraceFlags traceFlags,
TraceState traceState,
boolean remote) {
return new AutoValue_ImmutableSpanContext(
traceIdHex, spanIdHex, traceFlags, traceState, remote);
}

View File

@ -18,8 +18,8 @@ import javax.annotation.concurrent.Immutable;
* equals/hashCode implementations. If an implementation does not strictly conform to these
* requirements, behavior of the OpenTelemetry APIs and default SDK cannot be guaranteed. It is
* strongly suggested that you use the implementation that is provided here via {@link
* #create(String, String, byte, TraceState)} or {@link #createFromRemoteParent(String, String,
* byte, TraceState)}.
* #create(String, String, TraceFlags, TraceState)} or {@link #createFromRemoteParent(String,
* String, TraceFlags, TraceState)}.
*/
@Immutable
public interface SpanContext {
@ -36,14 +36,14 @@ public interface SpanContext {
/**
* Creates a new {@code SpanContext} with the given identifiers and options.
*
* @param traceIdHex the trace identifier of the span context.
* @param spanIdHex the span identifier of the span context.
* @param traceFlags the byte representation of the {@link TraceFlags}
* @param traceState the trace state for the span context.
* @param traceIdHex the trace identifier of the {@code SpanContext}.
* @param spanIdHex the span identifier of the {@code SpanContext}.
* @param traceFlags the trace flags of the {@code SpanContext}.
* @param traceState the trace state for the {@code SpanContext}.
* @return a new {@code SpanContext} with the given identifiers and options.
*/
static SpanContext create(
String traceIdHex, String spanIdHex, byte traceFlags, TraceState traceState) {
String traceIdHex, String spanIdHex, TraceFlags traceFlags, TraceState traceState) {
return ImmutableSpanContext.create(
traceIdHex, spanIdHex, traceFlags, traceState, /* remote=*/ false);
}
@ -52,14 +52,14 @@ public interface SpanContext {
* Creates a new {@code SpanContext} that was propagated from a remote parent, with the given
* identifiers and options.
*
* @param traceIdHex the trace identifier of the span context.
* @param spanIdHex the span identifier of the span context.
* @param traceFlags the byte representation of the {@link TraceFlags}
* @param traceState the trace state for the span context.
* @param traceIdHex the trace identifier of the {@code SpanContext}.
* @param spanIdHex the span identifier of the {@code SpanContext}.
* @param traceFlags the trace flags of the {@code SpanContext}.
* @param traceState the trace state for the {@code SpanContext}.
* @return a new {@code SpanContext} with the given identifiers and options.
*/
static SpanContext createFromRemoteParent(
String traceIdHex, String spanIdHex, byte traceFlags, TraceState traceState) {
String traceIdHex, String spanIdHex, TraceFlags traceFlags, TraceState traceState) {
return ImmutableSpanContext.create(
traceIdHex, spanIdHex, traceFlags, traceState, /* remote=*/ true);
}
@ -101,15 +101,15 @@ public interface SpanContext {
/** Whether the span in this context is sampled. */
default boolean isSampled() {
return (getTraceFlags() & 1) == 1;
return getTraceFlags().isSampled();
}
/** The byte-representation of {@link TraceFlags}. */
byte getTraceFlags();
default void copyTraceFlagsHexTo(char[] dest, int destOffset) {
BigendianEncoding.byteToBase16(getTraceFlags(), dest, destOffset);
}
/**
* Returns the trace flags associated with this {@link SpanContext}.
*
* @return the trace flags associated with this {@link SpanContext}.
*/
TraceFlags getTraceFlags();
/**
* Returns the {@code TraceState} associated with this {@code SpanContext}.

View File

@ -5,50 +5,137 @@
package io.opentelemetry.api.trace;
import java.util.Objects;
import javax.annotation.concurrent.Immutable;
/**
* Helper methods for dealing with trace flags options. These options are propagated to all child
* {@link Span spans}. These determine features such as whether a {@code Span} should be traced. It
* is implemented as a bitmask.
* Helper methods for dealing with trace flags options. A valid trace flags is a 2 character
* lowercase hex (base16) String.
*
* <p>These options are propagated to all child {@link Span spans}. These determine features such as
* whether a {@code Span} should be traced.
*/
@Immutable
public final class TraceFlags {
private TraceFlags() {}
private static final TraceFlags[] INSTANCES = buildInstances();
// Bit to represent whether trace is sampled or not.
private static final byte IS_SAMPLED = 0x1;
// the default flags are a 0 byte.
private static final byte DEFAULT = 0x0;
private static final byte SAMPLED_BIT = 0x01;
private static final int SIZE = 1;
private static final int HEX_SIZE = 2 * SIZE;
private static final TraceFlags DEFAULT = INSTANCES[byteToUnsignedInt((byte) 0x00)];
private static final TraceFlags SAMPLED = INSTANCES[byteToUnsignedInt(SAMPLED_BIT)];
/** Returns the size in Hex of trace flags. */
public static int getHexLength() {
return HEX_SIZE;
private static final int HEX_LENGTH = 2;
private final String hexRep;
private final byte byteRep;
/**
* Returns the length of the lowercase hex (base16) representation of the {@link TraceFlags}.
*
* @return the length of the lowercase hex (base16) representation of the {@link TraceFlags}.
*/
public static int getLength() {
return HEX_LENGTH;
}
/**
* Returns the default byte representation of the flags.
* Returns the default (with all flag bits off) byte representation of the {@link TraceFlags}.
*
* @return the default byte representation of the flags.
* @return the default (with all flag bits off) byte representation of the {@link TraceFlags}.
*/
public static byte getDefault() {
public static TraceFlags getDefault() {
return DEFAULT;
}
/**
* Returns the byte representation of the flags with the sampling bit set to {@code 1}.
* Returns the lowercase hex (base16) representation of the {@link TraceFlags} with the sampling
* flag bit on.
*
* @return the byte representation of the flags with the sampling bit set to {@code 1}.
* @return the lowercase hex (base16) representation of the {@link TraceFlags} with the sampling
* flag bit on.
*/
public static byte getSampled() {
return IS_SAMPLED;
public static TraceFlags getSampled() {
return SAMPLED;
}
/** Extract the byte representation of the flags from a hex-representation. */
public static byte byteFromHex(CharSequence src, int srcOffset) {
return BigendianEncoding.byteFromBase16(src.charAt(srcOffset), src.charAt(srcOffset + 1));
/**
* Returns the {@link TraceFlags} converted from the given lowercase hex (base16) representation.
*
* @param src the buffer where the hex (base16) representation of the {@link TraceFlags} is.
* @param srcOffset the offset int buffer.
* @return the {@link TraceFlags} converted from the given lowercase hex (base16) representation.
* @throws NullPointerException if {@code src} is null.
* @throws IndexOutOfBoundsException if {@code src} is too short.
* @throws IllegalArgumentException if invalid characters in the {@code src}.
*/
public static TraceFlags fromHex(CharSequence src, int srcOffset) {
Objects.requireNonNull(src, "src");
return INSTANCES[
byteToUnsignedInt(
BigendianEncoding.byteFromBase16(src.charAt(srcOffset), src.charAt(srcOffset + 1)))];
}
/**
* Returns the {@link TraceFlags} converted from the given byte representation.
*
* @param traceFlagsByte the byte representation of the {@link TraceFlags}.
* @return the {@link TraceFlags} converted from the given byte representation.
*/
public static TraceFlags fromByte(byte traceFlagsByte) {
return INSTANCES[byteToUnsignedInt(traceFlagsByte)];
}
private static TraceFlags[] buildInstances() {
TraceFlags[] instances = new TraceFlags[256];
for (int i = 0; i < 256; i++) {
instances[i] = new TraceFlags((byte) i);
}
return instances;
}
private static int byteToUnsignedInt(byte x) {
// Equivalent with Byte.toUnsignedInt(), but cannot use it because of Android.
return x & 255;
}
private TraceFlags(byte byteRep) {
char[] result = new char[2];
BigendianEncoding.byteToBase16(byteRep, result, 0);
this.hexRep = new String(result);
this.byteRep = byteRep;
}
/**
* Returns {@code true} if the sampling bit is on for this {@link TraceFlags}, otherwise {@code
* false}.
*
* @return {@code true} if the sampling bit is on for this {@link TraceFlags}, otherwise {@code *
* false}.
*/
public boolean isSampled() {
return (this.byteRep & SAMPLED_BIT) != 0;
}
/**
* Returns the lowercase hex (base16) representation of this {@link TraceFlags}.
*
* @return the byte representation of the {@link TraceFlags}.
*/
public String asHex() {
return this.hexRep;
}
/**
* Returns the byte representation of this {@link TraceFlags}.
*
* @return the byte representation of the {@link TraceFlags}.
*/
public byte asByte() {
return this.byteRep;
}
@Override
public String toString() {
return asHex();
}
}

View File

@ -50,7 +50,7 @@ public final class W3CTraceContextPropagator implements TextMapPropagator {
private static final int TRACEPARENT_DELIMITER_SIZE = 1;
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_OPTION_HEX_SIZE = TraceFlags.getLength();
private static final int TRACE_ID_OFFSET = VERSION_SIZE + TRACEPARENT_DELIMITER_SIZE;
private static final int SPAN_ID_OFFSET =
TRACE_ID_OFFSET + TRACE_ID_HEX_SIZE + TRACEPARENT_DELIMITER_SIZE;
@ -124,7 +124,9 @@ public final class W3CTraceContextPropagator implements TextMapPropagator {
}
chars[TRACE_OPTION_OFFSET - 1] = TRACEPARENT_DELIMITER;
spanContext.copyTraceFlagsHexTo(chars, TRACE_OPTION_OFFSET);
String traceFlagsHex = spanContext.getTraceFlags().asHex();
chars[TRACE_OPTION_OFFSET] = traceFlagsHex.charAt(0);
chars[TRACE_OPTION_OFFSET + 1] = traceFlagsHex.charAt(1);
setter.set(carrier, TRACE_PARENT, new String(chars, 0, TRACEPARENT_HEADER_SIZE));
TraceState traceState = spanContext.getTraceState();
if (traceState.isEmpty()) {
@ -213,7 +215,7 @@ public final class W3CTraceContextPropagator implements TextMapPropagator {
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);
TraceFlags traceFlags = TraceFlags.fromHex(traceparent, TRACE_OPTION_OFFSET);
return SpanContext.createFromRemoteParent(
traceId, spanId, traceFlags, TraceState.getDefault());
}

View File

@ -13,15 +13,34 @@ import org.junit.jupiter.api.Test;
class TraceFlagsTest {
@Test
void isDefaultSampled() {
assertThat(TraceFlags.getDefault()).isEqualTo((byte) 0x0);
void defaultInstances() {
assertThat(TraceFlags.getDefault().asHex()).isEqualTo("00");
assertThat(TraceFlags.getSampled().asHex()).isEqualTo("01");
}
@Test
void toByteFromBase16() {
assertThat(TraceFlags.byteFromHex("ff", 0)).isEqualTo((byte) 0xff);
assertThat(TraceFlags.byteFromHex("01", 0)).isEqualTo((byte) 0x1);
assertThat(TraceFlags.byteFromHex("05", 0)).isEqualTo((byte) 0x5);
assertThat(TraceFlags.byteFromHex("00", 0)).isEqualTo((byte) 0x0);
void isSampled() {
assertThat(TraceFlags.fromByte((byte) 0xff).isSampled()).isTrue();
assertThat(TraceFlags.fromByte((byte) 0x01).isSampled()).isTrue();
assertThat(TraceFlags.fromByte((byte) 0x05).isSampled()).isTrue();
assertThat(TraceFlags.fromByte((byte) 0x00).isSampled()).isFalse();
}
@Test
void toFromHex() {
for (int i = 0; i < 256; i++) {
String hex = Integer.toHexString(i);
if (hex.length() == 1) {
hex = "0" + hex;
}
assertThat(TraceFlags.fromHex(hex, 0).asHex()).isEqualTo(hex);
}
}
@Test
void toFromByte() {
for (int i = 0; i < 256; i++) {
assertThat(TraceFlags.fromByte((byte) i).asByte()).isEqualTo((byte) i);
}
}
}

View File

@ -32,7 +32,6 @@ class W3CTraceContextPropagatorTest {
TraceState.builder().set("foo", "bar").set("bar", "baz").build();
private static final String TRACE_ID_BASE16 = "ff000000000000000000000000000041";
private static final String SPAN_ID_BASE16 = "ff00000000000041";
private static final byte SAMPLED_TRACE_OPTIONS = TraceFlags.getSampled();
private static final String TRACEPARENT_HEADER_SAMPLED =
"00-" + TRACE_ID_BASE16 + "-" + SPAN_ID_BASE16 + "-01";
private static final String TRACEPARENT_HEADER_NOT_SAMPLED =
@ -79,7 +78,7 @@ class W3CTraceContextPropagatorTest {
Context context =
withSpanContext(
SpanContext.create(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()),
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()),
Context.current());
w3cTraceContextPropagator.inject(
context,
@ -97,7 +96,7 @@ class W3CTraceContextPropagatorTest {
SpanContext.create(
TraceId.getInvalid(),
SpanId.getInvalid(),
SAMPLED_TRACE_OPTIONS,
TraceFlags.getSampled(),
TraceState.builder().set("foo", "bar").build()),
Context.current()),
carrier,
@ -111,7 +110,7 @@ class W3CTraceContextPropagatorTest {
Context context =
withSpanContext(
SpanContext.create(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()),
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()),
Context.current());
w3cTraceContextPropagator.inject(context, carrier, setter);
assertThat(carrier)
@ -137,7 +136,8 @@ class W3CTraceContextPropagatorTest {
Map<String, String> carrier = new LinkedHashMap<>();
Context context =
withSpanContext(
SpanContext.create(TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TRACE_STATE),
SpanContext.create(
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TRACE_STATE),
Context.current());
w3cTraceContextPropagator.inject(context, carrier, setter);
assertThat(carrier)
@ -176,7 +176,7 @@ class W3CTraceContextPropagatorTest {
getSpanContext(w3cTraceContextPropagator.extract(Context.current(), carrier, getter)))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()));
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()));
}
@Test
@ -187,7 +187,7 @@ class W3CTraceContextPropagatorTest {
getSpanContext(w3cTraceContextPropagator.extract(Context.current(), carrier, getter)))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()));
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()));
}
@Test
@ -221,7 +221,7 @@ class W3CTraceContextPropagatorTest {
getSpanContext(w3cTraceContextPropagator.extract(Context.current(), carrier, getter)))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TRACE_STATE));
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TRACE_STATE));
}
@Test
@ -399,7 +399,7 @@ class W3CTraceContextPropagatorTest {
w3cTraceContextPropagator.extract(Context.current(), invalidHeaders, getter)))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()));
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()));
}
@Test
@ -414,7 +414,7 @@ class W3CTraceContextPropagatorTest {
w3cTraceContextPropagator.extract(Context.current(), invalidHeaders, getter)))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()));
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()));
}
@Test
@ -429,7 +429,7 @@ class W3CTraceContextPropagatorTest {
w3cTraceContextPropagator.extract(Context.current(), invalidHeaders, getter)))
.isEqualTo(
SpanContext.createFromRemoteParent(
TRACE_ID_BASE16, SPAN_ID_BASE16, SAMPLED_TRACE_OPTIONS, TraceState.getDefault()));
TRACE_ID_BASE16, SPAN_ID_BASE16, TraceFlags.getSampled(), TraceState.getDefault()));
}
@Test

View File

@ -201,8 +201,11 @@ public final class AwsXrayPropagator implements TextMapPropagator {
return SpanContext.getInvalid();
}
byte traceFlags = isSampled ? TraceFlags.getSampled() : TraceFlags.getDefault();
return SpanContext.createFromRemoteParent(traceId, spanId, traceFlags, TraceState.getDefault());
return SpanContext.createFromRemoteParent(
traceId,
spanId,
isSampled ? TraceFlags.getSampled() : TraceFlags.getDefault(),
TraceState.getDefault());
}
private static String parseTraceId(String xrayTraceId) {

View File

@ -31,7 +31,7 @@ final class Common {
static SpanContext buildSpanContext(String traceId, String spanId, String sampled) {
try {
byte traceFlags =
TraceFlags traceFlags =
TRUE_INT.equals(sampled) || Boolean.parseBoolean(sampled) // accept either "1" or "true"
? TraceFlags.getSampled()
: TraceFlags.getDefault();

View File

@ -238,16 +238,17 @@ public final class JaegerPropagator implements TextMapPropagator {
private static SpanContext buildSpanContext(String traceId, String spanId, String flags) {
try {
int flagsInt = Integer.parseInt(flags);
byte traceFlags = ((flagsInt & 1) == 1) ? TraceFlags.getSampled() : TraceFlags.getDefault();
String otelTraceId = StringUtils.padLeft(traceId, MAX_TRACE_ID_LENGTH);
String otelSpanId = StringUtils.padLeft(spanId, MAX_SPAN_ID_LENGTH);
if (!TraceId.isValid(otelTraceId) || !SpanId.isValid(otelSpanId)) {
return SpanContext.getInvalid();
}
int flagsInt = Integer.parseInt(flags);
return SpanContext.createFromRemoteParent(
otelTraceId, otelSpanId, traceFlags, TraceState.getDefault());
otelTraceId,
otelSpanId,
((flagsInt & 1) == 1) ? TraceFlags.getSampled() : TraceFlags.getDefault(),
TraceState.getDefault());
} catch (RuntimeException e) {
logger.log(
Level.FINE,

View File

@ -30,7 +30,7 @@ class LogSinkSdkProviderTest {
.setUnixTimeMillis(System.currentTimeMillis())
.setTraceId(TraceId.getInvalid())
.setSpanId(SpanId.getInvalid())
.setFlags(TraceFlags.getDefault())
.setFlags(TraceFlags.getDefault().asByte())
.setSeverity(severity)
.setSeverityText("really severe")
.setName("log1")

View File

@ -192,7 +192,11 @@ final class SdkSpanBuilder implements SpanBuilder {
TraceState samplingResultTraceState =
samplingResult.getUpdatedTraceState(parentSpanContext.getTraceState());
SpanContext spanContext =
createSpanContext(traceId, spanId, samplingResultTraceState, isSampled(samplingDecision));
SpanContext.create(
traceId,
spanId,
isSampled(samplingDecision) ? TraceFlags.getSampled() : TraceFlags.getDefault(),
samplingResultTraceState);
if (!isRecording(samplingDecision)) {
return Span.wrap(spanContext);
@ -227,12 +231,6 @@ final class SdkSpanBuilder implements SpanBuilder {
startEpochNanos);
}
private static SpanContext createSpanContext(
String traceId, String spanId, TraceState traceState, boolean isSampled) {
byte traceFlags = isSampled ? TraceFlags.getSampled() : TraceFlags.getDefault();
return SpanContext.create(traceId, spanId, traceFlags, traceState);
}
private static Clock getClock(Span parent, Clock clock) {
if (parent instanceof RecordEventsReadableSpan) {
RecordEventsReadableSpan parentRecordEventsSpan = (RecordEventsReadableSpan) parent;

View File

@ -905,12 +905,12 @@ class SdkSpanBuilderTest {
"SpanData\\{spanContext=ImmutableSpanContext\\{"
+ "traceId=[0-9a-f]{32}, "
+ "spanId=[0-9a-f]{16}, "
+ "traceFlags=1, "
+ "traceFlags=01, "
+ "traceState=ArrayBasedTraceState\\{entries=\\[]}, remote=false}, "
+ "parentSpanContext=ImmutableSpanContext\\{"
+ "traceId=00000000000000000000000000000000, "
+ "spanId=0000000000000000, "
+ "traceFlags=0, "
+ "traceFlags=00, "
+ "traceState=ArrayBasedTraceState\\{entries=\\[]}, remote=false}, "
+ "resource=Resource\\{attributes=\\{service.name=\"unknown_service:java\", "
+ "telemetry.sdk.language=\"java\", telemetry.sdk.name=\"opentelemetry\", "