Skip to content

Commit 74bc3bd

Browse files
authored
Add incubator implementation of composite sampling spec (#7626)
1 parent 14d604e commit 74bc3bd

18 files changed

+1520
-0
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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.trace.SpanKind;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.sdk.trace.data.LinkData;
12+
import java.util.List;
13+
import java.util.function.Function;
14+
15+
enum ComposableAlwaysOffSampler implements ComposableSampler {
16+
INSTANCE;
17+
18+
private static final SamplingIntent INTENT =
19+
SamplingIntent.create(
20+
ImmutableSamplingIntent.INVALID_THRESHOLD,
21+
/* thresholdReliable= */ false,
22+
Attributes.empty(),
23+
Function.identity());
24+
25+
@Override
26+
public SamplingIntent getSamplingIntent(
27+
Context parentContext,
28+
String traceId,
29+
String name,
30+
SpanKind spanKind,
31+
Attributes attributes,
32+
List<LinkData> parentLinks) {
33+
return INTENT;
34+
}
35+
36+
@Override
37+
public String getDescription() {
38+
return "ComposableAlwaysOffSampler";
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return getDescription();
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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 io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.trace.SpanKind;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.sdk.trace.data.LinkData;
12+
import java.util.List;
13+
import java.util.function.Function;
14+
15+
enum ComposableAlwaysOnSampler implements ComposableSampler {
16+
INSTANCE;
17+
18+
private static final SamplingIntent INTENT =
19+
SamplingIntent.create(
20+
ImmutableSamplingIntent.MIN_THRESHOLD,
21+
/* thresholdReliable= */ true,
22+
Attributes.empty(),
23+
Function.identity());
24+
25+
@Override
26+
public SamplingIntent getSamplingIntent(
27+
Context parentContext,
28+
String traceId,
29+
String name,
30+
SpanKind spanKind,
31+
Attributes attributes,
32+
List<LinkData> parentLinks) {
33+
return INTENT;
34+
}
35+
36+
@Override
37+
public String getDescription() {
38+
return "ComposableAlwaysOnSampler";
39+
}
40+
41+
@Override
42+
public String toString() {
43+
return getDescription();
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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 io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.INVALID_THRESHOLD;
9+
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.MIN_THRESHOLD;
10+
import static io.opentelemetry.sdk.extension.incubator.trace.samplers.ImmutableSamplingIntent.isValidThreshold;
11+
12+
import io.opentelemetry.api.common.Attributes;
13+
import io.opentelemetry.api.trace.Span;
14+
import io.opentelemetry.api.trace.SpanContext;
15+
import io.opentelemetry.api.trace.SpanKind;
16+
import io.opentelemetry.context.Context;
17+
import io.opentelemetry.sdk.trace.data.LinkData;
18+
import java.util.List;
19+
import java.util.function.Function;
20+
21+
final class ComposableParentThresholdSampler implements ComposableSampler {
22+
23+
private final ComposableSampler rootSampler;
24+
private final String description;
25+
26+
ComposableParentThresholdSampler(ComposableSampler rootSampler) {
27+
this.rootSampler = rootSampler;
28+
this.description = "ComposableParentThresholdSampler{rootSampler=" + rootSampler + "}";
29+
}
30+
31+
@Override
32+
public SamplingIntent getSamplingIntent(
33+
Context parentContext,
34+
String traceId,
35+
String name,
36+
SpanKind spanKind,
37+
Attributes attributes,
38+
List<LinkData> parentLinks) {
39+
SpanContext parentSpanContext = Span.fromContext(parentContext).getSpanContext();
40+
if (!parentSpanContext.isValid()) {
41+
return rootSampler.getSamplingIntent(
42+
parentContext, traceId, name, spanKind, attributes, parentLinks);
43+
}
44+
45+
OtelTraceState otTraceState = OtelTraceState.parse(parentSpanContext.getTraceState());
46+
if (isValidThreshold(otTraceState.getThreshold())) {
47+
return ImmutableSamplingIntent.create(
48+
otTraceState.getThreshold(),
49+
/* thresholdReliable= */ true,
50+
Attributes.empty(),
51+
Function.identity());
52+
}
53+
54+
long threshold =
55+
parentSpanContext.getTraceFlags().isSampled() ? MIN_THRESHOLD : INVALID_THRESHOLD;
56+
return ImmutableSamplingIntent.create(
57+
threshold, /* thresholdReliable= */ false, Attributes.empty(), Function.identity());
58+
}
59+
60+
@Override
61+
public String getDescription() {
62+
return description;
63+
}
64+
65+
@Override
66+
public String toString() {
67+
return this.getDescription();
68+
}
69+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
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 io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.trace.SpanKind;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.sdk.trace.data.LinkData;
12+
import java.util.List;
13+
14+
/** A sampler that can be composed to make a final sampling decision. */
15+
public interface ComposableSampler {
16+
/** Returns a {@link ComposableSampler} that does not sample any span. */
17+
static ComposableSampler alwaysOff() {
18+
return ComposableAlwaysOffSampler.INSTANCE;
19+
}
20+
21+
/** Returns a {@link ComposableSampler} that samples all spans. */
22+
static ComposableSampler alwaysOn() {
23+
return ComposableAlwaysOnSampler.INSTANCE;
24+
}
25+
26+
/** Returns a {@link ComposableSampler} that samples each span with a fixed ratio. */
27+
static ComposableSampler traceIdRatioBased(double ratio) {
28+
return new ComposableTraceIdRatioBasedSampler(ratio);
29+
}
30+
31+
/**
32+
* Returns a {@link ComposableSampler} that respects the sampling decision of the parent span or
33+
* falls back to the given sampler if it is a root span.
34+
*/
35+
static ComposableSampler parentThreshold(ComposableSampler rootSampler) {
36+
return new ComposableParentThresholdSampler(rootSampler);
37+
}
38+
39+
/** Returns the {@link SamplingIntent} to use to make a sampling decision. */
40+
SamplingIntent getSamplingIntent(
41+
Context parentContext,
42+
String traceId,
43+
String name,
44+
SpanKind spanKind,
45+
Attributes attributes,
46+
List<LinkData> parentLinks);
47+
48+
/** Returns a description of the sampler implementation. */
49+
String getDescription();
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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 io.opentelemetry.api.common.Attributes;
9+
import io.opentelemetry.api.trace.SpanKind;
10+
import io.opentelemetry.context.Context;
11+
import io.opentelemetry.sdk.trace.data.LinkData;
12+
import java.util.List;
13+
import java.util.function.Function;
14+
15+
final class ComposableTraceIdRatioBasedSampler implements ComposableSampler {
16+
private static long calculateThreshold(double ratio) {
17+
return ImmutableSamplingIntent.MAX_THRESHOLD
18+
- Math.round(ratio * (double) ImmutableSamplingIntent.MAX_THRESHOLD);
19+
}
20+
21+
private final SamplingIntent intent;
22+
private final String description;
23+
24+
ComposableTraceIdRatioBasedSampler(double ratio) {
25+
long threshold = calculateThreshold(ratio);
26+
String thresholdStr;
27+
if (threshold == ImmutableSamplingIntent.MAX_THRESHOLD) {
28+
thresholdStr = "max";
29+
30+
// Same as ComposableAlwaysOffSampler, notably the threshold is not considered reliable.
31+
// The spec mentions returning an instance of ComposableAlwaysOffSampler in this case but
32+
// it seems clearer if the description of the sampler matches the user's request.
33+
this.intent =
34+
SamplingIntent.create(
35+
ImmutableSamplingIntent.INVALID_THRESHOLD,
36+
/* thresholdReliable= */ false,
37+
Attributes.empty(),
38+
Function.identity());
39+
} else {
40+
StringBuilder sb = new StringBuilder();
41+
OtelTraceState.serializeTh(threshold, sb);
42+
thresholdStr = sb.toString();
43+
44+
this.intent =
45+
SamplingIntent.create(
46+
threshold, /* thresholdReliable= */ true, Attributes.empty(), Function.identity());
47+
}
48+
this.description =
49+
"ComposableTraceIdRatioBasedSampler{threshold=" + thresholdStr + ", ratio=" + ratio + "}";
50+
}
51+
52+
@Override
53+
public SamplingIntent getSamplingIntent(
54+
Context parentContext,
55+
String traceId,
56+
String name,
57+
SpanKind spanKind,
58+
Attributes attributes,
59+
List<LinkData> parentLinks) {
60+
return intent;
61+
}
62+
63+
@Override
64+
public String getDescription() {
65+
return description;
66+
}
67+
68+
@Override
69+
public String toString() {
70+
return this.getDescription();
71+
}
72+
}

0 commit comments

Comments
 (0)