Skip to content

Commit 10b06bd

Browse files
committed
Add ExperimentalComposableRuleBasedSamplerModel implementation
1 parent 20d944a commit 10b06bd

File tree

4 files changed

+560
-2
lines changed

4 files changed

+560
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.sdk.extension.incubator.fileconfig;
7+
8+
import static io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfigUtil.requireNonNull;
9+
import static java.util.stream.Collectors.toSet;
10+
11+
import io.opentelemetry.api.common.Attributes;
12+
import io.opentelemetry.api.trace.Span;
13+
import io.opentelemetry.api.trace.SpanContext;
14+
import io.opentelemetry.api.trace.SpanKind;
15+
import io.opentelemetry.context.Context;
16+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalComposableRuleBasedSamplerModel;
17+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalComposableRuleBasedSamplerRuleAttributePatternsModel;
18+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalComposableRuleBasedSamplerRuleAttributeValuesModel;
19+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalComposableRuleBasedSamplerRuleModel;
20+
import io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.ExperimentalSpanParent;
21+
import io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableRuleBasedSamplerBuilder;
22+
import io.opentelemetry.sdk.extension.incubator.trace.samplers.ComposableSampler;
23+
import io.opentelemetry.sdk.extension.incubator.trace.samplers.SamplingPredicate;
24+
import io.opentelemetry.sdk.internal.IncludeExcludePredicate;
25+
import io.opentelemetry.sdk.trace.data.LinkData;
26+
import java.util.HashSet;
27+
import java.util.List;
28+
import java.util.Set;
29+
import java.util.StringJoiner;
30+
import java.util.function.Predicate;
31+
import javax.annotation.Nullable;
32+
33+
final class ComposableRuleBasedSamplerFactory
34+
implements Factory<ExperimentalComposableRuleBasedSamplerModel, ComposableSampler> {
35+
36+
private static final ComposableRuleBasedSamplerFactory INSTANCE =
37+
new ComposableRuleBasedSamplerFactory();
38+
39+
private ComposableRuleBasedSamplerFactory() {}
40+
41+
static ComposableRuleBasedSamplerFactory getInstance() {
42+
return INSTANCE;
43+
}
44+
45+
@Override
46+
public ComposableSampler create(
47+
ExperimentalComposableRuleBasedSamplerModel model, DeclarativeConfigContext context) {
48+
ComposableRuleBasedSamplerBuilder builder = ComposableSampler.ruleBasedBuilder();
49+
50+
List<ExperimentalComposableRuleBasedSamplerRuleModel> rules = model.getRules();
51+
if (rules != null) {
52+
rules.forEach(
53+
rule -> {
54+
AttributeMatcher valueMatcher = attributeValuesMatcher(rule.getAttributeValues());
55+
AttributeMatcher patternMatcher = attributePatternsMatcher(rule.getAttributePatterns());
56+
// TODO: should be null when omitted but is empty
57+
Set<ExperimentalSpanParent> matchingParents =
58+
rule.getParent() != null && !rule.getParent().isEmpty()
59+
? new HashSet<>(rule.getParent())
60+
: null;
61+
// TODO: should be null when omitted but is empty
62+
Set<SpanKind> matchingSpanKinds =
63+
rule.getSpanKinds() != null && !rule.getSpanKinds().isEmpty()
64+
? rule.getSpanKinds().stream()
65+
.map(DeclarativeConfigSamplingPredicate::toSpanKind)
66+
.collect(toSet())
67+
: null;
68+
69+
SamplingPredicate predicate =
70+
new DeclarativeConfigSamplingPredicate(
71+
valueMatcher, patternMatcher, matchingParents, matchingSpanKinds);
72+
ComposableSampler sampler =
73+
ComposableSamplerFactory.getInstance()
74+
.create(requireNonNull(rule.getSampler(), "rule sampler"), context);
75+
builder.add(predicate, sampler);
76+
});
77+
}
78+
79+
return builder.build();
80+
}
81+
82+
@Nullable
83+
private static AttributeMatcher attributeValuesMatcher(
84+
@Nullable
85+
ExperimentalComposableRuleBasedSamplerRuleAttributeValuesModel attributeValuesModel) {
86+
if (attributeValuesModel == null) {
87+
return null;
88+
}
89+
return new AttributeMatcher(
90+
requireNonNull(attributeValuesModel.getKey(), "attribute_values key"),
91+
IncludeExcludePredicate.createExactMatching(attributeValuesModel.getValues(), null));
92+
}
93+
94+
@Nullable
95+
private static AttributeMatcher attributePatternsMatcher(
96+
@Nullable
97+
ExperimentalComposableRuleBasedSamplerRuleAttributePatternsModel attributePatternsModel) {
98+
if (attributePatternsModel == null) {
99+
return null;
100+
}
101+
return new AttributeMatcher(
102+
requireNonNull(attributePatternsModel.getKey(), "attribute_patterns key"),
103+
IncludeExcludePredicate.createPatternMatching(
104+
attributePatternsModel.getIncluded(), attributePatternsModel.getExcluded()));
105+
}
106+
107+
// Visible for testing
108+
static final class DeclarativeConfigSamplingPredicate implements SamplingPredicate {
109+
@Nullable private final AttributeMatcher attributeValuesMatcher;
110+
@Nullable private final AttributeMatcher attributePatternsMatcher;
111+
@Nullable private final Set<ExperimentalSpanParent> matchingParents;
112+
@Nullable private final Set<SpanKind> matchingSpanKinds;
113+
114+
DeclarativeConfigSamplingPredicate(
115+
@Nullable AttributeMatcher attributeValuesMatcher,
116+
@Nullable AttributeMatcher attributePatternsMatcher,
117+
@Nullable Set<ExperimentalSpanParent> matchingParents,
118+
@Nullable Set<SpanKind> matchingSpanKinds) {
119+
this.attributeValuesMatcher = attributeValuesMatcher;
120+
this.attributePatternsMatcher = attributePatternsMatcher;
121+
this.matchingParents = matchingParents;
122+
this.matchingSpanKinds = matchingSpanKinds;
123+
}
124+
125+
@Override
126+
public boolean matches(
127+
Context parentContext,
128+
String traceId,
129+
String name,
130+
SpanKind spanKind,
131+
Attributes attributes,
132+
List<LinkData> parentLinks) {
133+
// all conditions must match
134+
135+
// check attribute value condition
136+
if (attributeValuesMatcher != null && !attributesMatch(attributeValuesMatcher, attributes)) {
137+
return false;
138+
}
139+
140+
// check attribute pattern condition
141+
if (attributePatternsMatcher != null
142+
&& !attributesMatch(attributePatternsMatcher, attributes)) {
143+
return false;
144+
}
145+
146+
// check parent condition
147+
if (matchingParents != null
148+
&& !matchingParents.contains(
149+
toSpanParent(Span.fromContext(parentContext).getSpanContext()))) {
150+
return false;
151+
}
152+
153+
// check span kind conditions
154+
if (matchingSpanKinds != null && !matchingSpanKinds.contains(spanKind)) {
155+
return false;
156+
}
157+
158+
// If no conditions are specified, match all spans
159+
return true;
160+
}
161+
162+
private static boolean attributesMatch(AttributeMatcher matcher, Attributes attributes) {
163+
boolean[] match = new boolean[] {false};
164+
attributes.forEach(
165+
(key, value) -> {
166+
if (matcher.matchesKey(key.getKey()) && matcher.matchesValue(String.valueOf(value))) {
167+
match[0] = true;
168+
}
169+
});
170+
return match[0];
171+
}
172+
173+
@Override
174+
public String toString() {
175+
StringJoiner joiner = new StringJoiner(", ", "DeclarativeConfigSamplingPredicate{", "}");
176+
joiner.add("attributeValuesMatcher=" + attributeValuesMatcher);
177+
joiner.add("attributePatterns=" + attributePatternsMatcher);
178+
joiner.add("matchingParents=" + matchingParents);
179+
joiner.add("matchingSpanKinds=" + matchingSpanKinds);
180+
return joiner.toString();
181+
}
182+
183+
private static SpanKind toSpanKind(
184+
io.opentelemetry.sdk.extension.incubator.fileconfig.internal.model.SpanKind spanKind) {
185+
switch (spanKind) {
186+
case INTERNAL:
187+
return SpanKind.INTERNAL;
188+
case SERVER:
189+
return SpanKind.SERVER;
190+
case CLIENT:
191+
return SpanKind.CLIENT;
192+
case PRODUCER:
193+
return SpanKind.PRODUCER;
194+
case CONSUMER:
195+
return SpanKind.CONSUMER;
196+
}
197+
throw new IllegalArgumentException("Unrecognized span kind: " + spanKind);
198+
}
199+
200+
// Visible for testing
201+
static ExperimentalSpanParent toSpanParent(SpanContext parentSpanContext) {
202+
if (!parentSpanContext.isValid()) {
203+
return ExperimentalSpanParent.NONE;
204+
}
205+
if (parentSpanContext.isRemote()) {
206+
return ExperimentalSpanParent.REMOTE;
207+
}
208+
return ExperimentalSpanParent.LOCAL;
209+
}
210+
}
211+
212+
// Visible for testing
213+
static class AttributeMatcher {
214+
private final Predicate<String> attributeKeyMatcher;
215+
private final Predicate<String> attributeValueMatcher;
216+
217+
AttributeMatcher(String attributeKey, Predicate<String> attributeValueMatcher) {
218+
this(new EqualsPredicate(attributeKey), attributeValueMatcher);
219+
}
220+
221+
AttributeMatcher(
222+
Predicate<String> attributeKeyMatcher, Predicate<String> attributeValueMatcher) {
223+
this.attributeKeyMatcher = attributeKeyMatcher;
224+
this.attributeValueMatcher = attributeValueMatcher;
225+
}
226+
227+
boolean matchesKey(String attributeKey) {
228+
return attributeKeyMatcher.test(attributeKey);
229+
}
230+
231+
boolean matchesValue(String attributeValue) {
232+
return attributeValueMatcher.test(attributeValue);
233+
}
234+
235+
@Override
236+
public String toString() {
237+
return "AttributeMatcher{keyMatcher="
238+
+ attributeKeyMatcher
239+
+ ", valueMatcher="
240+
+ attributeValueMatcher
241+
+ '}';
242+
}
243+
}
244+
245+
private static class EqualsPredicate implements Predicate<String> {
246+
private final String value;
247+
248+
private EqualsPredicate(String value) {
249+
this.value = value;
250+
}
251+
252+
@Override
253+
public boolean test(String s) {
254+
return value.equals(s);
255+
}
256+
257+
@Override
258+
public String toString() {
259+
return "EqualsPredicate{" + value + '}';
260+
}
261+
}
262+
}

sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/fileconfig/ComposableSamplerFactory.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ public ComposableSampler create(
4141
}
4242
ExperimentalComposableRuleBasedSamplerModel ruleBased = model.getRuleBased();
4343
if (ruleBased != null) {
44-
// TODO: interpret model
45-
return ComposableSampler.ruleBasedBuilder().build();
44+
return ComposableRuleBasedSamplerFactory.getInstance().create(ruleBased, context);
4645
}
4746
Map.Entry<String, ?> keyValue =
4847
FileConfigUtil.getSingletonMapEntry(model.getAdditionalProperties(), "composable sampler");

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,9 @@ public SamplingIntent getSamplingIntent(
6060
public String getDescription() {
6161
return description;
6262
}
63+
64+
@Override
65+
public String toString() {
66+
return this.getDescription();
67+
}
6368
}

0 commit comments

Comments
 (0)