Skip to content

Commit 3afaf35

Browse files
committed
Implement random trace flag from W3C TC v2
1 parent cfb959b commit 3afaf35

File tree

4 files changed

+102
-3
lines changed

4 files changed

+102
-3
lines changed

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/RandomIdGenerator.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
import io.opentelemetry.api.trace.SpanId;
99
import io.opentelemetry.api.trace.TraceId;
1010
import io.opentelemetry.sdk.internal.RandomSupplier;
11+
import io.opentelemetry.sdk.trace.internal.ExtendedIdGenerator;
1112
import java.util.Random;
1213
import java.util.function.Supplier;
1314

14-
enum RandomIdGenerator implements IdGenerator {
15+
enum RandomIdGenerator implements IdGenerator, ExtendedIdGenerator {
1516
INSTANCE;
1617

1718
private static final long INVALID_ID = 0;
@@ -42,4 +43,9 @@ public String generateTraceId() {
4243
public String toString() {
4344
return "RandomIdGenerator{}";
4445
}
46+
47+
@Override
48+
public boolean randomTraceId() {
49+
return true;
50+
}
4551
}

sdk/trace/src/main/java/io/opentelemetry/sdk/trace/SdkSpanBuilder.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.opentelemetry.sdk.internal.AttributeUtil;
2525
import io.opentelemetry.sdk.internal.AttributesMap;
2626
import io.opentelemetry.sdk.trace.data.LinkData;
27+
import io.opentelemetry.sdk.trace.internal.ExtendedIdGenerator;
2728
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
2829
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
2930
import java.util.ArrayList;
@@ -35,6 +36,9 @@
3536
/** {@link SdkSpanBuilder} is SDK implementation of {@link SpanBuilder}. */
3637
class SdkSpanBuilder implements SpanBuilder {
3738

39+
// TODO: Move to ImmutableTraceFlags when W3C Trace Context Level 2 is finalized.
40+
private static final byte TRACE_FLAGS_RANDOM_BIT = 0x10;
41+
3842
private final String spanName;
3943
private final InstrumentationScopeInfo instrumentationScopeInfo;
4044
private final TracerSharedState tracerSharedState;
@@ -193,13 +197,22 @@ public Span startSpan() {
193197
parentContext, traceId, spanName, spanKind, immutableAttributes, immutableLinks);
194198
SamplingDecision samplingDecision = samplingResult.getDecision();
195199

200+
TraceFlags traceFlags =
201+
isSampled(samplingDecision) ? TraceFlags.getSampled() : TraceFlags.getDefault();
202+
if (idGenerator instanceof ExtendedIdGenerator) {
203+
boolean randomTraceId = ((ExtendedIdGenerator) idGenerator).randomTraceId();
204+
if (randomTraceId) {
205+
traceFlags = TraceFlags.fromByte((byte) (traceFlags.asByte() | TRACE_FLAGS_RANDOM_BIT));
206+
}
207+
}
208+
196209
TraceState samplingResultTraceState =
197210
samplingResult.getUpdatedTraceState(parentSpanContext.getTraceState());
198211
SpanContext spanContext =
199212
ImmutableSpanContext.create(
200213
traceId,
201214
spanId,
202-
isSampled(samplingDecision) ? TraceFlags.getSampled() : TraceFlags.getDefault(),
215+
traceFlags,
203216
samplingResultTraceState,
204217
/* remote= */ false,
205218
tracerSharedState.isIdGeneratorSafeToSkipIdValidation());
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.trace.internal;
7+
8+
import io.opentelemetry.sdk.trace.IdGenerator;
9+
10+
/**
11+
* An extension to {@link IdGenerator} to allow opting in to the random flag in the draft <a
12+
* href="https://www.w3.org/TR/trace-context-2/#trace-flags">W3C Trace Context Level 2</a>
13+
*
14+
* <p>This class is internal and experimental. Its APIs are unstable and can change at any time. Its
15+
* APIs (or a version of them) may be promoted to the public stable API in the future, but no
16+
* guarantees are made.
17+
*/
18+
public interface ExtendedIdGenerator extends IdGenerator {
19+
/**
20+
* Returns {@code true} if the {@link IdGenerator} returns trace IDs with the right-most 7 bytes
21+
* being random.
22+
*/
23+
boolean randomTraceId();
24+
}

sdk/trace/src/test/java/io/opentelemetry/sdk/trace/SdkSpanBuilderTest.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.opentelemetry.context.Scope;
3434
import io.opentelemetry.sdk.trace.data.LinkData;
3535
import io.opentelemetry.sdk.trace.data.SpanData;
36+
import io.opentelemetry.sdk.trace.internal.ExtendedIdGenerator;
3637
import io.opentelemetry.sdk.trace.samplers.Sampler;
3738
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
3839
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
@@ -965,6 +966,61 @@ void isRecording() {
965966
assertThat(SdkSpanBuilder.isRecording(SamplingDecision.RECORD_AND_SAMPLE)).isTrue();
966967
}
967968

969+
@Test
970+
void traceFlags() {
971+
// Default ID generator and sampler populate both bits.
972+
assertThat(
973+
sdkTracer.spanBuilder(SPAN_NAME).startSpan().getSpanContext().getTraceFlags().asByte())
974+
.isEqualTo((byte) 0x11);
975+
976+
SdkTracerProvider tracerSdkFactory =
977+
SdkTracerProvider.builder()
978+
.setIdGenerator(
979+
new IdGenerator() {
980+
@Override
981+
public String generateSpanId() {
982+
return "111";
983+
}
984+
985+
@Override
986+
public String generateTraceId() {
987+
return "222";
988+
}
989+
})
990+
.addSpanProcessor(mockedSpanProcessor)
991+
.build();
992+
SdkTracer sdkTracer = (SdkTracer) tracerSdkFactory.get("SpanBuilderSdkTest");
993+
assertThat(
994+
sdkTracer.spanBuilder(SPAN_NAME).startSpan().getSpanContext().getTraceFlags().asByte())
995+
.isEqualTo((byte) 0x01);
996+
997+
tracerSdkFactory =
998+
SdkTracerProvider.builder()
999+
.setIdGenerator(
1000+
new ExtendedIdGenerator() {
1001+
@Override
1002+
public boolean randomTraceId() {
1003+
return false;
1004+
}
1005+
1006+
@Override
1007+
public String generateSpanId() {
1008+
return "111";
1009+
}
1010+
1011+
@Override
1012+
public String generateTraceId() {
1013+
return "222";
1014+
}
1015+
})
1016+
.addSpanProcessor(mockedSpanProcessor)
1017+
.build();
1018+
sdkTracer = (SdkTracer) tracerSdkFactory.get("SpanBuilderSdkTest");
1019+
assertThat(
1020+
sdkTracer.spanBuilder(SPAN_NAME).startSpan().getSpanContext().getTraceFlags().asByte())
1021+
.isEqualTo((byte) 0x01);
1022+
}
1023+
9681024
// SpanData is very commonly used in unit tests, we want the toString to make sure it's relatively
9691025
// easy to understand failure messages.
9701026
// TODO(anuraaga): Currently it isn't - we even return the same (or maybe incorrect?) stuff twice.
@@ -983,7 +1039,7 @@ void spanDataToString() {
9831039
"SpanData\\{spanContext=ImmutableSpanContext\\{"
9841040
+ "traceId=[0-9a-f]{32}, "
9851041
+ "spanId=[0-9a-f]{16}, "
986-
+ "traceFlags=01, "
1042+
+ "traceFlags=11, "
9871043
+ "traceState=ArrayBasedTraceState\\{entries=\\[]}, remote=false, valid=true}, "
9881044
+ "parentSpanContext=ImmutableSpanContext\\{"
9891045
+ "traceId=00000000000000000000000000000000, "

0 commit comments

Comments
 (0)