Skip to content

Commit 40a2591

Browse files
jack-bergrobsunday
authored andcommitted
Add declarative config support for RuleBasedRoutingSampler (open-telemetry#1440)
1 parent 6d39e93 commit 40a2591

File tree

6 files changed

+393
-1
lines changed

6 files changed

+393
-1
lines changed

.github/component_owners.yml

+1
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ components:
6565
- jeanbisutti
6666
samplers:
6767
- trask
68+
- jack-berg
6869
static-instrumenter:
6970
- anosek-an
7071
kafka-exporter:

samplers/README.md

+60
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,67 @@
11
# Samplers
22

3+
## Declarative configuration
4+
5+
The following samplers support [declarative configuration](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/configuration#declarative-configuration):
6+
7+
* `RuleBasedRoutingSampler`
8+
9+
To use:
10+
11+
* Add a dependency on `io.opentelemetry:opentelemetry-sdk-extension-incubator:<version>`
12+
* Follow the [instructions](https://github.com/open-telemetry/opentelemetry-java/blob/main/sdk-extensions/incubator/README.md#file-configuration) to configure OpenTelemetry with declarative configuration.
13+
* Configure the `.tracer_provider.sampler` to include the `rule_based_routing` sampler.
14+
15+
NOTE: Not yet available for use with the OTEL java agent, but should be in the near future. Please check back for updates.
16+
17+
Schema for `rule_based_routing` sampler:
18+
19+
```yaml
20+
# The fallback sampler to the use if the criteria is not met.
21+
fallback_sampler:
22+
always_on:
23+
# Filter to spans of this span_kind. Must be one of: SERVER, CLIENT, INTERNAL, CONSUMER, PRODUCER.
24+
span_kind: SERVER # only apply to server spans
25+
# List of rules describing spans to drop. Spans are dropped if they match one of the rules.
26+
rules:
27+
# The action to take when the rule is matches. Must be of: DROP, RECORD_AND_SAMPLE.
28+
- action: DROP
29+
# The span attribute to match against.
30+
attribute: url.path
31+
# The pattern to compare the span attribute to.
32+
pattern: /actuator.*
33+
```
34+
35+
`rule_based_routing` sampler can be used anywhere a sampler is used in the configuration model. For example, the following YAML demonstrates a typical configuration, setting `rule_based_routing` sampler as the `root` sampler of `parent_based` sampler. In this configuration:
36+
37+
* The `parent_based` sampler samples based on the sampling status of the parent.
38+
* Or, if there is no parent, delegates to the `rule_based_routing` sampler.
39+
* The `rule_based_routing` sampler drops spans where `kind=SERVER` and `url.full matches /actuator.*`, else it samples and records.
40+
41+
```yaml
42+
// ... the rest of the configuration file is omitted for brevity
43+
// For more examples see: https://github.com/open-telemetry/opentelemetry-configuration/blob/main/README.md#starter-templates
44+
tracer_provider:
45+
sampler:
46+
parent_based:
47+
# Configure the parent_based sampler's root sampler to be rule_based_routing sampler.
48+
root:
49+
rule_based_routing:
50+
# Fallback to the always_on sampler if the criteria is not met.
51+
fallback_sampler:
52+
always_on:
53+
# Only apply to SERVER spans.
54+
span_kind: SERVER
55+
rules:
56+
# Drop spans where url.path matches the regex /actuator.* (i.e. spring boot actuator endpoints).
57+
- action: DROP
58+
attribute: url.path
59+
pattern: /actuator.*
60+
```
61+
362
## Component owners
463

64+
- [Jack Berg](https://github.com/jack-berg), New Relic
565
- [Trask Stalnaker](https://github.com/trask), Microsoft
666

767
Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).

samplers/build.gradle.kts

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ otelJava.moduleName.set("io.opentelemetry.contrib.sampler")
88

99
dependencies {
1010
api("io.opentelemetry:opentelemetry-sdk")
11+
12+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
13+
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")
14+
1115
implementation("io.opentelemetry.semconv:opentelemetry-semconv")
16+
1217
testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
1318
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
14-
api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
19+
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
1520
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.sampler.internal;
7+
8+
import io.opentelemetry.api.common.AttributeKey;
9+
import io.opentelemetry.api.trace.SpanKind;
10+
import io.opentelemetry.contrib.sampler.RuleBasedRoutingSampler;
11+
import io.opentelemetry.contrib.sampler.RuleBasedRoutingSamplerBuilder;
12+
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
13+
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
14+
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
15+
import io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfiguration;
16+
import io.opentelemetry.sdk.trace.samplers.Sampler;
17+
import java.util.List;
18+
19+
/**
20+
* Declarative configuration SPI implementation for {@link RuleBasedRoutingSampler}.
21+
*
22+
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
23+
* at any time.
24+
*/
25+
public class RuleBasedRoutingSamplerComponentProvider implements ComponentProvider<Sampler> {
26+
27+
private static final String ACTION_RECORD_AND_SAMPLE = "RECORD_AND_SAMPLE";
28+
private static final String ACTION_DROP = "DROP";
29+
30+
@Override
31+
public Class<Sampler> getType() {
32+
return Sampler.class;
33+
}
34+
35+
@Override
36+
public String getName() {
37+
return "rule_based_routing";
38+
}
39+
40+
@Override
41+
public Sampler create(StructuredConfigProperties config) {
42+
StructuredConfigProperties fallbackModel = config.getStructured("fallback_sampler");
43+
if (fallbackModel == null) {
44+
throw new ConfigurationException(
45+
"rule_based_routing sampler .fallback is required but is null");
46+
}
47+
Sampler fallbackSampler;
48+
try {
49+
fallbackSampler = FileConfiguration.createSampler(fallbackModel);
50+
} catch (ConfigurationException e) {
51+
throw new ConfigurationException(
52+
"rule_Based_routing sampler failed to create .fallback sampler", e);
53+
}
54+
55+
String spanKindString = config.getString("span_kind", "SERVER");
56+
SpanKind spanKind;
57+
try {
58+
spanKind = SpanKind.valueOf(spanKindString);
59+
} catch (IllegalArgumentException e) {
60+
throw new ConfigurationException(
61+
"rule_based_routing sampler .span_kind is invalid: " + spanKindString, e);
62+
}
63+
64+
RuleBasedRoutingSamplerBuilder builder =
65+
RuleBasedRoutingSampler.builder(spanKind, fallbackSampler);
66+
67+
List<StructuredConfigProperties> rules = config.getStructuredList("rules");
68+
if (rules == null || rules.isEmpty()) {
69+
throw new ConfigurationException("rule_based_routing sampler .rules is required");
70+
}
71+
72+
for (StructuredConfigProperties rule : rules) {
73+
String attribute = rule.getString("attribute");
74+
if (attribute == null) {
75+
throw new ConfigurationException(
76+
"rule_based_routing sampler .rules[].attribute is required");
77+
}
78+
AttributeKey<String> attributeKey = AttributeKey.stringKey(attribute);
79+
String pattern = rule.getString("pattern");
80+
if (pattern == null) {
81+
throw new ConfigurationException("rule_based_routing sampler .rules[].pattern is required");
82+
}
83+
String action = rule.getString("action");
84+
if (action == null) {
85+
throw new ConfigurationException("rule_based_routing sampler .rules[].action is required");
86+
}
87+
if (action.equals(ACTION_RECORD_AND_SAMPLE)) {
88+
builder.recordAndSample(attributeKey, pattern);
89+
} else if (action.equals(ACTION_DROP)) {
90+
builder.drop(attributeKey, pattern);
91+
} else {
92+
throw new ConfigurationException(
93+
"rule_based_routing sampler .rules[].action is must be "
94+
+ ACTION_RECORD_AND_SAMPLE
95+
+ " or "
96+
+ ACTION_DROP);
97+
}
98+
}
99+
100+
return builder.build();
101+
}
102+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
io.opentelemetry.contrib.sampler.internal.RuleBasedRoutingSamplerComponentProvider

0 commit comments

Comments
 (0)