Skip to content

Commit

Permalink
Add declarative config support for RuleBasedRoutingSampler (open-tele…
Browse files Browse the repository at this point in the history
  • Loading branch information
jack-berg authored and robsunday committed Sep 17, 2024
1 parent 6d39e93 commit 40a2591
Show file tree
Hide file tree
Showing 6 changed files with 393 additions and 1 deletion.
1 change: 1 addition & 0 deletions .github/component_owners.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ components:
- jeanbisutti
samplers:
- trask
- jack-berg
static-instrumenter:
- anosek-an
kafka-exporter:
Expand Down
60 changes: 60 additions & 0 deletions samplers/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,67 @@
# Samplers

## Declarative configuration

The following samplers support [declarative configuration](https://github.com/open-telemetry/opentelemetry-specification/tree/main/specification/configuration#declarative-configuration):

* `RuleBasedRoutingSampler`

To use:

* Add a dependency on `io.opentelemetry:opentelemetry-sdk-extension-incubator:<version>`
* 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.
* Configure the `.tracer_provider.sampler` to include the `rule_based_routing` sampler.

NOTE: Not yet available for use with the OTEL java agent, but should be in the near future. Please check back for updates.

Schema for `rule_based_routing` sampler:

```yaml
# The fallback sampler to the use if the criteria is not met.
fallback_sampler:
always_on:
# Filter to spans of this span_kind. Must be one of: SERVER, CLIENT, INTERNAL, CONSUMER, PRODUCER.
span_kind: SERVER # only apply to server spans
# List of rules describing spans to drop. Spans are dropped if they match one of the rules.
rules:
# The action to take when the rule is matches. Must be of: DROP, RECORD_AND_SAMPLE.
- action: DROP
# The span attribute to match against.
attribute: url.path
# The pattern to compare the span attribute to.
pattern: /actuator.*
```
`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:

* The `parent_based` sampler samples based on the sampling status of the parent.
* Or, if there is no parent, delegates to the `rule_based_routing` sampler.
* The `rule_based_routing` sampler drops spans where `kind=SERVER` and `url.full matches /actuator.*`, else it samples and records.

```yaml
// ... the rest of the configuration file is omitted for brevity
// For more examples see: https://github.com/open-telemetry/opentelemetry-configuration/blob/main/README.md#starter-templates
tracer_provider:
sampler:
parent_based:
# Configure the parent_based sampler's root sampler to be rule_based_routing sampler.
root:
rule_based_routing:
# Fallback to the always_on sampler if the criteria is not met.
fallback_sampler:
always_on:
# Only apply to SERVER spans.
span_kind: SERVER
rules:
# Drop spans where url.path matches the regex /actuator.* (i.e. spring boot actuator endpoints).
- action: DROP
attribute: url.path
pattern: /actuator.*
```

## Component owners

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

Learn more about component owners in [component_owners.yml](../.github/component_owners.yml).
7 changes: 6 additions & 1 deletion samplers/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,13 @@ otelJava.moduleName.set("io.opentelemetry.contrib.sampler")

dependencies {
api("io.opentelemetry:opentelemetry-sdk")

compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
compileOnly("io.opentelemetry:opentelemetry-sdk-extension-incubator")

implementation("io.opentelemetry.semconv:opentelemetry-semconv")

testImplementation("io.opentelemetry.semconv:opentelemetry-semconv-incubating")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure")
api("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
testImplementation("io.opentelemetry:opentelemetry-sdk-extension-incubator")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.contrib.sampler.internal;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.contrib.sampler.RuleBasedRoutingSampler;
import io.opentelemetry.contrib.sampler.RuleBasedRoutingSamplerBuilder;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigurationException;
import io.opentelemetry.sdk.autoconfigure.spi.internal.ComponentProvider;
import io.opentelemetry.sdk.autoconfigure.spi.internal.StructuredConfigProperties;
import io.opentelemetry.sdk.extension.incubator.fileconfig.FileConfiguration;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import java.util.List;

/**
* Declarative configuration SPI implementation for {@link RuleBasedRoutingSampler}.
*
* <p>This class is internal and is hence not for public use. Its APIs are unstable and can change
* at any time.
*/
public class RuleBasedRoutingSamplerComponentProvider implements ComponentProvider<Sampler> {

private static final String ACTION_RECORD_AND_SAMPLE = "RECORD_AND_SAMPLE";
private static final String ACTION_DROP = "DROP";

@Override
public Class<Sampler> getType() {
return Sampler.class;
}

@Override
public String getName() {
return "rule_based_routing";
}

@Override
public Sampler create(StructuredConfigProperties config) {
StructuredConfigProperties fallbackModel = config.getStructured("fallback_sampler");
if (fallbackModel == null) {
throw new ConfigurationException(
"rule_based_routing sampler .fallback is required but is null");
}
Sampler fallbackSampler;
try {
fallbackSampler = FileConfiguration.createSampler(fallbackModel);
} catch (ConfigurationException e) {
throw new ConfigurationException(
"rule_Based_routing sampler failed to create .fallback sampler", e);
}

String spanKindString = config.getString("span_kind", "SERVER");
SpanKind spanKind;
try {
spanKind = SpanKind.valueOf(spanKindString);
} catch (IllegalArgumentException e) {
throw new ConfigurationException(
"rule_based_routing sampler .span_kind is invalid: " + spanKindString, e);
}

RuleBasedRoutingSamplerBuilder builder =
RuleBasedRoutingSampler.builder(spanKind, fallbackSampler);

List<StructuredConfigProperties> rules = config.getStructuredList("rules");
if (rules == null || rules.isEmpty()) {
throw new ConfigurationException("rule_based_routing sampler .rules is required");
}

for (StructuredConfigProperties rule : rules) {
String attribute = rule.getString("attribute");
if (attribute == null) {
throw new ConfigurationException(
"rule_based_routing sampler .rules[].attribute is required");
}
AttributeKey<String> attributeKey = AttributeKey.stringKey(attribute);
String pattern = rule.getString("pattern");
if (pattern == null) {
throw new ConfigurationException("rule_based_routing sampler .rules[].pattern is required");
}
String action = rule.getString("action");
if (action == null) {
throw new ConfigurationException("rule_based_routing sampler .rules[].action is required");
}
if (action.equals(ACTION_RECORD_AND_SAMPLE)) {
builder.recordAndSample(attributeKey, pattern);
} else if (action.equals(ACTION_DROP)) {
builder.drop(attributeKey, pattern);
} else {
throw new ConfigurationException(
"rule_based_routing sampler .rules[].action is must be "
+ ACTION_RECORD_AND_SAMPLE
+ " or "
+ ACTION_DROP);
}
}

return builder.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
io.opentelemetry.contrib.sampler.internal.RuleBasedRoutingSamplerComponentProvider
Loading

0 comments on commit 40a2591

Please sign in to comment.