Skip to content

Commit 65fadc5

Browse files
committed
Cleanups
1 parent 8720246 commit 65fadc5

File tree

4 files changed

+135
-13
lines changed

4 files changed

+135
-13
lines changed

sdk-extensions/incubator/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies {
4040
testImplementation(project(":exporters:zipkin"))
4141
testImplementation(project(":sdk-extensions:jaeger-remote-sampler"))
4242
testImplementation(project(":extensions:trace-propagators"))
43+
testImplementation("edu.berkeley.cs.jqf:jqf-fuzz")
4344
testImplementation("io.opentelemetry.contrib:opentelemetry-aws-xray-propagator")
4445
testImplementation("com.linecorp.armeria:armeria-junit5")
4546

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/trace/samplers/CompositeSampler.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.opentelemetry.api.trace.SpanKind;
1717
import io.opentelemetry.api.trace.TraceState;
1818
import io.opentelemetry.context.Context;
19+
import io.opentelemetry.sdk.internal.RandomSupplier;
1920
import io.opentelemetry.sdk.trace.data.LinkData;
2021
import io.opentelemetry.sdk.trace.samplers.Sampler;
2122
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
@@ -49,7 +50,7 @@ public SamplingResult shouldSample(
4950
Attributes attributes,
5051
List<LinkData> parentLinks) {
5152
TraceState traceState = Span.fromContext(parentContext).getSpanContext().getTraceState();
52-
OtelTraceState otTraceState = OtelTraceState.parse(traceState);
53+
OtelTraceState otelTraceState = OtelTraceState.parse(traceState);
5354

5455
SamplingIntent intent =
5556
delegate.getSamplingIntent(parentContext, traceId, name, spanKind, attributes, parentLinks);
@@ -59,28 +60,32 @@ public SamplingResult shouldSample(
5960
if (isValidThreshold(intent.getThreshold())) {
6061
thresholdReliable = intent.isThresholdReliable();
6162
long randomValue;
62-
if (isValidRandomValue(otTraceState.getRandomValue())) {
63-
randomValue = otTraceState.getRandomValue();
63+
if (thresholdReliable) {
64+
if (isValidRandomValue(otelTraceState.getRandomValue())) {
65+
randomValue = otelTraceState.getRandomValue();
66+
} else {
67+
// Use last 56 bits of trace ID as random value.
68+
randomValue = OtelEncodingUtils.longFromBase16String(traceId, 16) & 0x00FFFFFFFFFFFFFFL;
69+
}
6470
} else {
65-
// Use last 56 bits of trace ID as random value.
66-
randomValue = OtelEncodingUtils.longFromBase16String(traceId, 16) & 0x00FFFFFFFFFFFFFFL;
71+
randomValue = RandomSupplier.platformDefault().get().nextLong() & 0x00FFFFFFFFFFFFFFL;
6772
}
6873
sampled = intent.getThreshold() <= randomValue;
6974
}
7075

7176
SamplingDecision decision =
7277
sampled ? SamplingDecision.RECORD_AND_SAMPLE : SamplingDecision.DROP;
7378
if (sampled && thresholdReliable) {
74-
otTraceState =
79+
otelTraceState =
7580
new OtelTraceState(
76-
otTraceState.getRandomValue(), intent.getThreshold(), otTraceState.getRest());
81+
otelTraceState.getRandomValue(), intent.getThreshold(), otelTraceState.getRest());
7782
} else {
78-
otTraceState =
83+
otelTraceState =
7984
new OtelTraceState(
80-
otTraceState.getRandomValue(), INVALID_THRESHOLD, otTraceState.getRest());
85+
otelTraceState.getRandomValue(), INVALID_THRESHOLD, otelTraceState.getRest());
8186
}
8287

83-
String ot = otTraceState.serialize();
88+
String serializedState = otelTraceState.serialize();
8489
return new SamplingResult() {
8590
@Override
8691
public SamplingDecision getDecision() {
@@ -95,8 +100,9 @@ public Attributes getAttributes() {
95100
@Override
96101
public TraceState getUpdatedTraceState(TraceState parentTraceState) {
97102
TraceState newTraceState = intent.getTraceStateUpdater().apply(traceState);
98-
if (!ot.isEmpty()) {
99-
newTraceState = newTraceState.toBuilder().put(OTEL_TRACE_STATE_KEY, ot).build();
103+
if (!serializedState.isEmpty()) {
104+
newTraceState =
105+
newTraceState.toBuilder().put(OTEL_TRACE_STATE_KEY, serializedState).build();
100106
}
101107
return newTraceState;
102108
}

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/trace/samplers/OtelTraceState.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ final class OtelTraceState {
2525
new OtelTraceState(INVALID_RANDOM_VALUE, INVALID_THRESHOLD, Collections.emptyList());
2626

2727
private static final int MAX_OTEL_TRACE_STATE_LENGTH = 256;
28-
private static final int MAX_VALUE_LENGTH = 14; // 56 bits, 4 bits per hex digit
28+
// visible for testing
29+
static final int MAX_VALUE_LENGTH = 14; // 56 bits, 4 bits per hex digit
2930

3031
static final String OTEL_TRACE_STATE_KEY = "ot";
3132

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.extension.incubator.trace.samplers;
7+
8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import com.pholser.junit.quickcheck.generator.InRange;
11+
import edu.berkeley.cs.jqf.fuzz.Fuzz;
12+
import edu.berkeley.cs.jqf.fuzz.JQF;
13+
import edu.berkeley.cs.jqf.fuzz.junit.GuidedFuzzing;
14+
import edu.berkeley.cs.jqf.fuzz.random.NoGuidance;
15+
import io.opentelemetry.api.trace.TraceState;
16+
import java.util.Collections;
17+
import org.junit.jupiter.api.Test;
18+
import org.junit.runner.Result;
19+
import org.junit.runner.RunWith;
20+
21+
@SuppressWarnings("SystemOut")
22+
class OtelTraceStateFuzzTest {
23+
24+
@RunWith(JQF.class)
25+
public static class TestCases {
26+
@Fuzz
27+
public void roundTripRandomValues(long rv, long th) {
28+
OtelTraceState input = new OtelTraceState(rv, th, Collections.emptyList());
29+
OtelTraceState output =
30+
OtelTraceState.parse(TraceState.builder().put("ot", input.serialize()).build());
31+
assertState(output, rv, th);
32+
}
33+
34+
@Fuzz
35+
public void roundTripValidValues(
36+
@InRange(minLong = 0, maxLong = ImmutableSamplingIntent.MAX_RANDOM_VALUE) long rv,
37+
@InRange(minLong = 0, maxLong = ImmutableSamplingIntent.MAX_THRESHOLD) long th) {
38+
OtelTraceState input = new OtelTraceState(rv, th, Collections.emptyList());
39+
OtelTraceState output =
40+
OtelTraceState.parse(TraceState.builder().put("ot", input.serialize()).build());
41+
assertState(output, rv, th);
42+
}
43+
44+
private static void assertState(OtelTraceState state, long rv, long th) {
45+
boolean hasRv = false;
46+
boolean hasTh = false;
47+
if (rv >= 0 && rv <= ImmutableSamplingIntent.MAX_RANDOM_VALUE) {
48+
assertThat(state.getRandomValue()).isEqualTo(rv);
49+
hasRv = true;
50+
} else {
51+
assertThat(state.getRandomValue()).isEqualTo(ImmutableSamplingIntent.INVALID_RANDOM_VALUE);
52+
}
53+
if (th >= 0 && th <= ImmutableSamplingIntent.MAX_THRESHOLD) {
54+
assertThat(state.getThreshold()).isEqualTo(th);
55+
hasTh = state.getThreshold() != ImmutableSamplingIntent.MAX_THRESHOLD;
56+
} else {
57+
assertThat(state.getThreshold()).isEqualTo(ImmutableSamplingIntent.INVALID_THRESHOLD);
58+
}
59+
String[] parts = state.serialize().split(";");
60+
String thStr = null;
61+
String rvStr = null;
62+
if (hasRv && hasTh) {
63+
assertThat(parts).hasSize(2);
64+
thStr = parts[0];
65+
rvStr = parts[1];
66+
} else if (hasRv) {
67+
assertThat(parts).hasSize(1);
68+
rvStr = parts[0];
69+
} else if (hasTh) {
70+
assertThat(parts).hasSize(1);
71+
thStr = parts[0];
72+
}
73+
74+
if (hasRv) {
75+
assertThat(rvStr).startsWith("rv:");
76+
rvStr = rvStr.substring("rv:".length());
77+
assertThat(rvStr).hasSize(OtelTraceState.MAX_VALUE_LENGTH);
78+
assertThat(rvStr).isHexadecimal();
79+
}
80+
81+
if (hasTh) {
82+
assertThat(thStr).startsWith("th:");
83+
thStr = thStr.substring("th:".length());
84+
assertThat(thStr).hasSizeBetween(1, OtelTraceState.MAX_VALUE_LENGTH);
85+
assertThat(thStr).isHexadecimal();
86+
if (th == 0) {
87+
assertThat(thStr).isEqualTo("0");
88+
} else {
89+
assertThat(thStr).doesNotMatch("[^0]+0");
90+
}
91+
}
92+
}
93+
}
94+
95+
// driver methods to avoid having to use the vintage junit engine, and to enable increasing the
96+
// number of iterations:
97+
98+
@Test
99+
void roundTripFuzzing() {
100+
Result result = runTestCase("roundTripRandomValues");
101+
assertThat(result.wasSuccessful()).isTrue();
102+
}
103+
104+
@Test
105+
void roundTripValidValues() {
106+
Result result = runTestCase("roundTripValidValues");
107+
assertThat(result.wasSuccessful()).isTrue();
108+
}
109+
110+
private static Result runTestCase(String testCaseName) {
111+
return GuidedFuzzing.run(
112+
TestCases.class, testCaseName, new NoGuidance(10000, System.out), System.out);
113+
}
114+
}

0 commit comments

Comments
 (0)