Skip to content

Commit

Permalink
Add is_remote_parent span flags to OTLP exported Spans and SpanLinks
Browse files Browse the repository at this point in the history
Signed-off-by: Alexander Wert <alexander.wert@elastic.co>
  • Loading branch information
AlexanderWert committed Apr 18, 2024
1 parent 8937a10 commit b9d8496
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ final class ImmutableTraceFlags implements TraceFlags {
private static final ImmutableTraceFlags[] INSTANCES = buildInstances();
// Bit to represent whether trace is sampled or not.
private static final byte SAMPLED_BIT = 0x01;
static final int CONTEXT_HAS_IS_REMOTE_BIT = 0x00000100;
static final int CONTEXT_IS_REMOTE_BIT = 0x00000200;
static final int CONTEXT_IS_REMOTE_MASK = CONTEXT_HAS_IS_REMOTE_BIT | CONTEXT_IS_REMOTE_BIT;

static final ImmutableTraceFlags DEFAULT = fromByte((byte) 0x00);
static final ImmutableTraceFlags SAMPLED = fromByte(SAMPLED_BIT);
Expand Down Expand Up @@ -65,6 +68,15 @@ public byte asByte() {
return this.byteRep;
}

@Override
public int withParentIsRemoteFlags(boolean isParentRemote) {
if (isParentRemote) {
return (this.byteRep & 0xff) | CONTEXT_IS_REMOTE_MASK;
} else {
return (this.byteRep & 0xff) | CONTEXT_HAS_IS_REMOTE_BIT;
}
}

@Override
public String toString() {
return asHex();
Expand Down
32 changes: 32 additions & 0 deletions api/all/src/main/java/io/opentelemetry/api/trace/TraceFlags.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,28 @@ static TraceFlags getSampled() {
return ImmutableTraceFlags.SAMPLED;
}

/**
* Returns the int (fixed32) representation of the 4 bytes flags with the
* has_parent_context_is_remote flag bit on.
*
* @return the int (fixed32) representation of the 4 bytes flags with the *
* has_parent_context_is_remote flag bit on.
*/
static int getHasParentIsRemote() {
return ImmutableTraceFlags.CONTEXT_HAS_IS_REMOTE_BIT;
}

/**
* Returns the int (fixed32) representation of the 4 bytes flags with the
* has_parent_context_is_remote and parent_context_is_remote flag bits on.
*
* @return the int (fixed32) representation of the 4 bytes flags with the
* has_parent_context_is_remote and parent_context_is_remote flag bits on.
*/
static int getParentIsRemote() {
return ImmutableTraceFlags.CONTEXT_IS_REMOTE_MASK;
}

/**
* Returns the {@link TraceFlags} converted from the given lowercase hex (base16) representation.
*
Expand Down Expand Up @@ -89,4 +111,14 @@ static TraceFlags fromByte(byte traceFlagsByte) {
* @return the byte representation of the {@link TraceFlags}.
*/
byte asByte();

/**
* Returns the int (fixed32) representation of this {@link TraceFlags} enriched with the flags
* indicating a remote parent.
*
* @param isParentRemote indicates whether the parent context is remote
* @return the int (fixed32) representation of this {@link TraceFlags} enriched with the flags
* indicating a remote parent.
*/
int withParentIsRemoteFlags(boolean isParentRemote);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,17 @@ void toFromByte() {
assertThat(TraceFlags.fromByte((byte) i).asByte()).isEqualTo((byte) i);
}
}

@Test
void withParentIsRemoteFlags() {
assertThat(TraceFlags.fromByte((byte) 0xff).withParentIsRemoteFlags(false)).isEqualTo(0x1ff);
assertThat(TraceFlags.fromByte((byte) 0x01).withParentIsRemoteFlags(false)).isEqualTo(0x101);
assertThat(TraceFlags.fromByte((byte) 0x05).withParentIsRemoteFlags(false)).isEqualTo(0x105);
assertThat(TraceFlags.fromByte((byte) 0x00).withParentIsRemoteFlags(false)).isEqualTo(0x100);

assertThat(TraceFlags.fromByte((byte) 0xff).withParentIsRemoteFlags(true)).isEqualTo(0x3ff);
assertThat(TraceFlags.fromByte((byte) 0x01).withParentIsRemoteFlags(true)).isEqualTo(0x301);
assertThat(TraceFlags.fromByte((byte) 0x05).withParentIsRemoteFlags(true)).isEqualTo(0x305);
assertThat(TraceFlags.fromByte((byte) 0x00).withParentIsRemoteFlags(true)).isEqualTo(0x300);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ final class SpanLinkMarshaler extends MarshalerWithSize {
private final KeyValueMarshaler[] attributeMarshalers;
private final int droppedAttributesCount;
private final TraceFlags traceFlags;
private final boolean isLinkContextRemote;

static SpanLinkMarshaler[] createRepeated(List<LinkData> links) {
if (links.isEmpty()) {
Expand All @@ -50,13 +51,15 @@ static SpanLinkMarshaler create(LinkData link) {
traceState.isEmpty()
? EMPTY_BYTES
: encodeTraceState(traceState).getBytes(StandardCharsets.UTF_8);

return new SpanLinkMarshaler(
link.getSpanContext().getTraceId(),
link.getSpanContext().getSpanId(),
link.getSpanContext().getTraceFlags(),
traceStateUtf8,
KeyValueMarshaler.createForAttributes(link.getAttributes()),
link.getTotalAttributeCount() - link.getAttributes().size());
link.getTotalAttributeCount() - link.getAttributes().size(),
link.getSpanContext().isRemote());
}

private SpanLinkMarshaler(
Expand All @@ -65,21 +68,24 @@ private SpanLinkMarshaler(
TraceFlags traceFlags,
byte[] traceStateUtf8,
KeyValueMarshaler[] attributeMarshalers,
int droppedAttributesCount) {
int droppedAttributesCount,
boolean isLinkContextRemote) {
super(
calculateSize(
traceId,
spanId,
traceFlags,
traceStateUtf8,
attributeMarshalers,
droppedAttributesCount));
droppedAttributesCount,
isLinkContextRemote));
this.traceId = traceId;
this.spanId = spanId;
this.traceFlags = traceFlags;
this.traceStateUtf8 = traceStateUtf8;
this.attributeMarshalers = attributeMarshalers;
this.droppedAttributesCount = droppedAttributesCount;
this.isLinkContextRemote = isLinkContextRemote;
}

@Override
Expand All @@ -89,7 +95,8 @@ public void writeTo(Serializer output) throws IOException {
output.serializeString(Span.Link.TRACE_STATE, traceStateUtf8);
output.serializeRepeatedMessage(Span.Link.ATTRIBUTES, attributeMarshalers);
output.serializeUInt32(Span.Link.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount);
output.serializeByteAsFixed32(Span.Link.FLAGS, traceFlags.asByte());
output.serializeFixed32(
Span.Link.FLAGS, traceFlags.withParentIsRemoteFlags(isLinkContextRemote));
}

private static int calculateSize(
Expand All @@ -98,14 +105,17 @@ private static int calculateSize(
TraceFlags flags,
byte[] traceStateUtf8,
KeyValueMarshaler[] attributeMarshalers,
int droppedAttributesCount) {
int droppedAttributesCount,
boolean isLinkContextRemote) {
int size = 0;
size += MarshalerUtil.sizeTraceId(Span.Link.TRACE_ID, traceId);
size += MarshalerUtil.sizeSpanId(Span.Link.SPAN_ID, spanId);
size += MarshalerUtil.sizeBytes(Span.Link.TRACE_STATE, traceStateUtf8);
size += MarshalerUtil.sizeRepeatedMessage(Span.Link.ATTRIBUTES, attributeMarshalers);
size += MarshalerUtil.sizeUInt32(Span.Link.DROPPED_ATTRIBUTES_COUNT, droppedAttributesCount);
size += MarshalerUtil.sizeByteAsFixed32(Span.Link.FLAGS, flags.asByte());
size +=
MarshalerUtil.sizeFixed32(
Span.Link.FLAGS, flags.withParentIsRemoteFlags(isLinkContextRemote));
return size;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ final class SpanMarshaler extends MarshalerWithSize {
private final int droppedLinksCount;
private final SpanStatusMarshaler spanStatusMarshaler;
private final TraceFlags flags;
private final boolean isParentContextRemote;

// Because SpanMarshaler is always part of a repeated field, it cannot return "null".
static SpanMarshaler create(SpanData spanData) {
Expand Down Expand Up @@ -75,7 +76,8 @@ static SpanMarshaler create(SpanData spanData) {
spanLinkMarshalers,
spanData.getTotalRecordedLinks() - spanData.getLinks().size(),
SpanStatusMarshaler.create(spanData.getStatus()),
spanData.getSpanContext().getTraceFlags());
spanData.getSpanContext().getTraceFlags(),
spanData.getParentSpanContext().isRemote());
}

private SpanMarshaler(
Expand All @@ -94,7 +96,8 @@ private SpanMarshaler(
SpanLinkMarshaler[] spanLinkMarshalers,
int droppedLinksCount,
SpanStatusMarshaler spanStatusMarshaler,
TraceFlags flags) {
TraceFlags flags,
boolean isParentContextRemote) {
super(
calculateSize(
traceId,
Expand All @@ -112,7 +115,8 @@ private SpanMarshaler(
spanLinkMarshalers,
droppedLinksCount,
spanStatusMarshaler,
flags));
flags,
isParentContextRemote));
this.traceId = traceId;
this.spanId = spanId;
this.traceStateUtf8 = traceStateUtf8;
Expand All @@ -129,6 +133,7 @@ private SpanMarshaler(
this.droppedLinksCount = droppedLinksCount;
this.spanStatusMarshaler = spanStatusMarshaler;
this.flags = flags;
this.isParentContextRemote = isParentContextRemote;
}

@Override
Expand All @@ -154,7 +159,7 @@ public void writeTo(Serializer output) throws IOException {
output.serializeUInt32(Span.DROPPED_LINKS_COUNT, droppedLinksCount);

output.serializeMessage(Span.STATUS, spanStatusMarshaler);
output.serializeByteAsFixed32(Span.FLAGS, flags.asByte());
output.serializeFixed32(Span.FLAGS, flags.withParentIsRemoteFlags(isParentContextRemote));
}

private static int calculateSize(
Expand All @@ -173,7 +178,8 @@ private static int calculateSize(
SpanLinkMarshaler[] spanLinkMarshalers,
int droppedLinksCount,
SpanStatusMarshaler spanStatusMarshaler,
TraceFlags flags) {
TraceFlags flags,
boolean isParentContextRemote) {
int size = 0;
size += MarshalerUtil.sizeTraceId(Span.TRACE_ID, traceId);
size += MarshalerUtil.sizeSpanId(Span.SPAN_ID, spanId);
Expand All @@ -196,7 +202,8 @@ private static int calculateSize(
size += MarshalerUtil.sizeUInt32(Span.DROPPED_LINKS_COUNT, droppedLinksCount);

size += MarshalerUtil.sizeMessage(Span.STATUS, spanStatusMarshaler);
size += MarshalerUtil.sizeByteAsFixed32(Span.FLAGS, flags.asByte());
size +=
MarshalerUtil.sizeFixed32(Span.FLAGS, flags.withParentIsRemoteFlags(isParentContextRemote));
return size;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ class TraceRequestMarshalerTest {
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.fromBytes(TRACE_ID_BYTES);
private static final byte[] SPAN_ID_BYTES = new byte[] {0, 0, 0, 0, 4, 3, 2, 1};
private static final byte[] PARENT_SPAN_ID_BYTES = new byte[] {0, 0, 0, 0, 5, 6, 7, 8};
private static final String SPAN_ID = SpanId.fromBytes(SPAN_ID_BYTES);
private static final String PARENT_SPAN_ID = SpanId.fromBytes(PARENT_SPAN_ID_BYTES);
private static final String TRACE_STATE_VALUE = "baz=qux,foo=bar";
private static final SpanContext SPAN_CONTEXT =
SpanContext.create(
Expand All @@ -65,6 +67,10 @@ class TraceRequestMarshalerTest {
TraceFlags.getSampled(),
TraceState.builder().put("foo", "bar").put("baz", "qux").build());

private static final SpanContext PARENT_SPAN_CONTEXT =
SpanContext.createFromRemoteParent(
TRACE_ID, PARENT_SPAN_ID, TraceFlags.getSampled(), TraceState.builder().build());

@Test
void toProtoResourceSpans() {
ResourceSpansMarshaler[] resourceSpansMarshalers =
Expand Down Expand Up @@ -148,7 +154,8 @@ void toProtoSpan() {
assertThat(protoSpan.getTraceId().toByteArray()).isEqualTo(TRACE_ID_BYTES);
assertThat(protoSpan.getSpanId().toByteArray()).isEqualTo(SPAN_ID_BYTES);
assertThat(protoSpan.getFlags())
.isEqualTo(((int) SPAN_CONTEXT.getTraceFlags().asByte()) & 0x00ff);
.isEqualTo(
(SPAN_CONTEXT.getTraceFlags().asByte() & 0xff) | TraceFlags.getHasParentIsRemote());
assertThat(protoSpan.getTraceState()).isEqualTo(TRACE_STATE_VALUE);
assertThat(protoSpan.getParentSpanId().toByteArray()).isEqualTo(new byte[] {});
assertThat(protoSpan.getName()).isEqualTo("GET /api/endpoint");
Expand Down Expand Up @@ -227,14 +234,47 @@ void toProtoSpan() {
Span.Link.newBuilder()
.setTraceId(ByteString.copyFrom(TRACE_ID_BYTES))
.setSpanId(ByteString.copyFrom(SPAN_ID_BYTES))
.setFlags(SPAN_CONTEXT.getTraceFlags().asByte())
.setFlags(
(SPAN_CONTEXT.getTraceFlags().asByte() & 0xff)
| TraceFlags.getHasParentIsRemote())
.setTraceState(encodeTraceState(SPAN_CONTEXT.getTraceState()))
.build());
assertThat(protoSpan.getDroppedLinksCount()).isEqualTo(1); // 2 - 1
assertThat(protoSpan.getStatus())
.isEqualTo(Status.newBuilder().setCode(STATUS_CODE_OK).build());
}

@Test
void toProtoSpan_withRemoteParent() {
Span protoSpan =
parse(
Span.getDefaultInstance(),
SpanMarshaler.create(
TestSpanData.builder()
.setHasEnded(true)
.setSpanContext(SPAN_CONTEXT)
.setParentSpanContext(PARENT_SPAN_CONTEXT)
.setName("GET /api/endpoint")
.setKind(SpanKind.SERVER)
.setStartEpochNanos(12345)
.setEndEpochNanos(12349)
.setStatus(StatusData.ok())
.build()));

assertThat(protoSpan.getTraceId().toByteArray()).isEqualTo(TRACE_ID_BYTES);
assertThat(protoSpan.getSpanId().toByteArray()).isEqualTo(SPAN_ID_BYTES);
assertThat(protoSpan.getFlags())
.isEqualTo((SPAN_CONTEXT.getTraceFlags().asByte() & 0xff) | TraceFlags.getParentIsRemote());
assertThat(protoSpan.getTraceState()).isEqualTo(TRACE_STATE_VALUE);
assertThat(protoSpan.getParentSpanId().toByteArray()).isEqualTo(PARENT_SPAN_ID_BYTES);
assertThat(protoSpan.getName()).isEqualTo("GET /api/endpoint");
assertThat(protoSpan.getKind()).isEqualTo(SPAN_KIND_SERVER);
assertThat(protoSpan.getStartTimeUnixNano()).isEqualTo(12345);
assertThat(protoSpan.getEndTimeUnixNano()).isEqualTo(12349);
assertThat(protoSpan.getStatus())
.isEqualTo(Status.newBuilder().setCode(STATUS_CODE_OK).build());
}

@Test
void toProtoSpanKind() {
assertThat(SpanMarshaler.toProtoSpanKind(SpanKind.INTERNAL))
Expand Down Expand Up @@ -318,11 +358,28 @@ void toProtoSpanLink_WithoutAttributes() {
Span.Link.newBuilder()
.setTraceId(ByteString.copyFrom(TRACE_ID_BYTES))
.setSpanId(ByteString.copyFrom(SPAN_ID_BYTES))
.setFlags(SPAN_CONTEXT.getTraceFlags().asByte())
.setFlags(
(SPAN_CONTEXT.getTraceFlags().asByte() & 0xff)
| TraceFlags.getHasParentIsRemote())
.setTraceState(TRACE_STATE_VALUE)
.build());
}

@Test
void toProtoSpanLink_WithRemoteContext() {
assertThat(
parse(
Span.Link.getDefaultInstance(),
SpanLinkMarshaler.create(LinkData.create(PARENT_SPAN_CONTEXT))))
.isEqualTo(
Span.Link.newBuilder()
.setTraceId(ByteString.copyFrom(TRACE_ID_BYTES))
.setSpanId(ByteString.copyFrom(PARENT_SPAN_ID_BYTES))
.setFlags(
(SPAN_CONTEXT.getTraceFlags().asByte() & 0xff) | TraceFlags.getParentIsRemote())
.build());
}

@Test
void toProtoSpanLink_WithAttributes() {
assertThat(
Expand All @@ -335,7 +392,9 @@ void toProtoSpanLink_WithAttributes() {
Span.Link.newBuilder()
.setTraceId(ByteString.copyFrom(TRACE_ID_BYTES))
.setSpanId(ByteString.copyFrom(SPAN_ID_BYTES))
.setFlags(SPAN_CONTEXT.getTraceFlags().asByte())
.setFlags(
(SPAN_CONTEXT.getTraceFlags().asByte() & 0xff)
| TraceFlags.getHasParentIsRemote())
.setTraceState(TRACE_STATE_VALUE)
.addAttributes(
KeyValue.newBuilder()
Expand Down

0 comments on commit b9d8496

Please sign in to comment.