-
Notifications
You must be signed in to change notification settings - Fork 178
[dynamic-control] Add trace sampling-rate implementer #2634
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
39ec45e
Add trace sampling-rate implementer
jackshirazi 4c5a54d
copilot feedback
jackshirazi 7c1821e
Update dynamic-control/src/main/java/io/opentelemetry/contrib/dynamic…
jackshirazi 4265fca
Update dynamic-control/src/test/java/io/opentelemetry/contrib/dynamic…
jackshirazi ea18f97
copilot feedback
jackshirazi 83104db
spotless
jackshirazi c44044b
static import singletonList per jaydeluca feedback
jackshirazi 5ba568d
spotless
jackshirazi 89306d1
bump for CI crashed link check
jackshirazi 6955b81
bump again for CI crashed link check
jackshirazi File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
dynamic-control/src/main/java/io/opentelemetry/contrib/dynamic/policy/PolicyImplementer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.contrib.dynamic.policy; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Applies validated telemetry policies to runtime components. | ||
| * | ||
| * <p>Implementers are notified when policies change and are responsible for translating those | ||
| * policies into concrete runtime behavior (for example, updating a sampler). Implementers also | ||
| * declare the {@link PolicyValidator}s they support so that only relevant policies are delivered to | ||
| * them. | ||
| */ | ||
| public interface PolicyImplementer { | ||
|
|
||
| /** | ||
| * Called when the relevant policies have changed. | ||
| * | ||
| * <p>Implementers should treat the provided list as authoritative for their policy types and | ||
| * update runtime state accordingly. | ||
| * | ||
| * <p>The upstream policy pipeline is assumed to have already merged policies for prioritization | ||
| * and conflict resolution. The provided list is expected to be consistent and not contain | ||
| * conflicting policies for the same type. | ||
| * | ||
| * @param policies the set of policies that apply to this implementer | ||
| */ | ||
| void onPoliciesChanged(List<TelemetryPolicy> policies); | ||
|
|
||
| /** | ||
| * Returns the validators that this implementer supports. | ||
| * | ||
| * <p>These validators define which policy types and aliases the implementer can accept and | ||
| * process. | ||
| * | ||
| * @return the list of supported validators | ||
| */ | ||
| List<PolicyValidator> getValidators(); | ||
| } |
76 changes: 76 additions & 0 deletions
76
...main/java/io/opentelemetry/contrib/dynamic/policy/TraceSamplingRatePolicyImplementer.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.contrib.dynamic.policy; | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import io.opentelemetry.contrib.dynamic.sampler.DelegatingSampler; | ||
| import io.opentelemetry.sdk.trace.samplers.Sampler; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * Implements the {@code trace-sampling} policy by updating a {@link DelegatingSampler}. | ||
| * | ||
| * <p>This implementer listens for validated {@link TelemetryPolicy} updates of type {@code | ||
| * "trace-sampling"} and applies the {@code probability} field to the delegate sampler using {@link | ||
| * Sampler#traceIdRatioBased(double)} wrapped by {@link Sampler#parentBased(Sampler)}. | ||
| * | ||
| * <p>If the policy spec is {@code null} (policy removal), the delegate falls back to {@link | ||
| * Sampler#alwaysOn()}. | ||
| * | ||
| * <p>Validation is performed by {@link TraceSamplingValidator}; this implementer only consumes | ||
| * policies produced by that validator. | ||
| * | ||
| * <p>Policies with a non-null spec that omit {@code probability} are ignored, which should not | ||
| * occur if validation is functioning correctly. | ||
| * | ||
| * <p>This class is thread-safe. Calls to {@link #onPoliciesChanged(List)} can occur concurrently | ||
| * with sampling operations on the associated {@link DelegatingSampler}. | ||
| */ | ||
| public final class TraceSamplingRatePolicyImplementer implements PolicyImplementer { | ||
|
|
||
| private static final String TRACE_SAMPLING_TYPE = "trace-sampling"; | ||
| private static final String PROBABILITY_FIELD = "probability"; | ||
| private static final List<PolicyValidator> VALIDATORS = | ||
| Collections.<PolicyValidator>singletonList(new TraceSamplingValidator()); | ||
|
|
||
| private final DelegatingSampler delegatingSampler; | ||
|
|
||
| /** | ||
| * Creates a new implementer that updates the provided {@link DelegatingSampler}. | ||
| * | ||
| * @param delegatingSampler the sampler to update when policies change | ||
| */ | ||
| public TraceSamplingRatePolicyImplementer(DelegatingSampler delegatingSampler) { | ||
| Objects.requireNonNull(delegatingSampler, "delegatingSampler cannot be null"); | ||
| this.delegatingSampler = delegatingSampler; | ||
| } | ||
jackshirazi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @Override | ||
| public List<PolicyValidator> getValidators() { | ||
| return VALIDATORS; | ||
| } | ||
jackshirazi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| @Override | ||
| public void onPoliciesChanged(List<TelemetryPolicy> policies) { | ||
| for (TelemetryPolicy policy : policies) { | ||
| if (!TRACE_SAMPLING_TYPE.equals(policy.getType())) { | ||
| continue; | ||
| } | ||
| JsonNode spec = policy.getSpec(); | ||
| if (spec == null) { | ||
| delegatingSampler.setDelegate(Sampler.alwaysOn()); | ||
| continue; | ||
| } | ||
| if (spec.has(PROBABILITY_FIELD)) { | ||
| double ratio = spec.get(PROBABILITY_FIELD).asDouble(1.0); | ||
| Sampler sampler = Sampler.parentBased(Sampler.traceIdRatioBased(ratio)); | ||
| delegatingSampler.setDelegate(sampler); | ||
| } | ||
jackshirazi marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
| } | ||
107 changes: 107 additions & 0 deletions
107
.../java/io/opentelemetry/contrib/dynamic/policy/TraceSamplingRatePolicyImplementerTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| /* | ||
| * Copyright The OpenTelemetry Authors | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| package io.opentelemetry.contrib.dynamic.policy; | ||
|
|
||
| import static java.util.Collections.singletonList; | ||
| import static org.assertj.core.api.Assertions.assertThat; | ||
|
|
||
| import com.fasterxml.jackson.databind.JsonNode; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import io.opentelemetry.api.common.Attributes; | ||
| import io.opentelemetry.api.trace.SpanKind; | ||
| import io.opentelemetry.context.Context; | ||
| import io.opentelemetry.contrib.dynamic.sampler.DelegatingSampler; | ||
| import io.opentelemetry.sdk.trace.samplers.Sampler; | ||
| import io.opentelemetry.sdk.trace.samplers.SamplingDecision; | ||
| import io.opentelemetry.sdk.trace.samplers.SamplingResult; | ||
| import java.util.Arrays; | ||
| import java.util.Collections; | ||
| import java.util.List; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| class TraceSamplingRatePolicyImplementerTest { | ||
|
|
||
| private static final ObjectMapper MAPPER = new ObjectMapper(); | ||
|
|
||
| @Test | ||
| void nullSpecFallsBackToAlwaysOn() { | ||
| DelegatingSampler delegatingSampler = new DelegatingSampler(Sampler.alwaysOff()); | ||
| TraceSamplingRatePolicyImplementer implementer = | ||
| new TraceSamplingRatePolicyImplementer(delegatingSampler); | ||
|
|
||
| implementer.onPoliciesChanged(singletonList(new TelemetryPolicy("trace-sampling", null))); | ||
|
|
||
| assertThat(decisionFor(delegatingSampler)).isEqualTo(SamplingDecision.RECORD_AND_SAMPLE); | ||
| } | ||
|
|
||
| @Test | ||
| void appliesProbabilityToDelegate() { | ||
| DelegatingSampler delegatingSampler = new DelegatingSampler(Sampler.alwaysOff()); | ||
| TraceSamplingRatePolicyImplementer implementer = | ||
| new TraceSamplingRatePolicyImplementer(delegatingSampler); | ||
|
|
||
| implementer.onPoliciesChanged( | ||
| singletonList(new TelemetryPolicy("trace-sampling", spec("probability", 1.0)))); | ||
|
|
||
| assertThat(decisionFor(delegatingSampler)).isEqualTo(SamplingDecision.RECORD_AND_SAMPLE); | ||
| } | ||
|
|
||
| @Test | ||
| void ignoresUnrelatedPolicyTypes() { | ||
| DelegatingSampler delegatingSampler = new DelegatingSampler(Sampler.alwaysOff()); | ||
| TraceSamplingRatePolicyImplementer implementer = | ||
| new TraceSamplingRatePolicyImplementer(delegatingSampler); | ||
|
|
||
| implementer.onPoliciesChanged( | ||
| singletonList(new TelemetryPolicy("other-policy", spec("value", 1.0)))); | ||
|
|
||
| assertThat(decisionFor(delegatingSampler)).isEqualTo(SamplingDecision.DROP); | ||
| } | ||
|
|
||
| @Test | ||
| void ignoresTraceSamplingPolicyWithoutProbability() { | ||
| DelegatingSampler delegatingSampler = new DelegatingSampler(Sampler.alwaysOff()); | ||
| TraceSamplingRatePolicyImplementer implementer = | ||
| new TraceSamplingRatePolicyImplementer(delegatingSampler); | ||
|
|
||
| implementer.onPoliciesChanged( | ||
| singletonList(new TelemetryPolicy("trace-sampling", spec("other-field", 1.0)))); | ||
|
|
||
| assertThat(decisionFor(delegatingSampler)).isEqualTo(SamplingDecision.DROP); | ||
| } | ||
|
|
||
| @Test | ||
| void lastTraceSamplingPolicyWins() { | ||
| DelegatingSampler delegatingSampler = new DelegatingSampler(Sampler.alwaysOff()); | ||
| TraceSamplingRatePolicyImplementer implementer = | ||
| new TraceSamplingRatePolicyImplementer(delegatingSampler); | ||
|
|
||
| List<TelemetryPolicy> policies = | ||
| Arrays.asList( | ||
| new TelemetryPolicy("trace-sampling", spec("probability", 0.0)), | ||
| new TelemetryPolicy("trace-sampling", spec("probability", 1.0))); | ||
|
|
||
| implementer.onPoliciesChanged(policies); | ||
|
|
||
| assertThat(decisionFor(delegatingSampler)).isEqualTo(SamplingDecision.RECORD_AND_SAMPLE); | ||
| } | ||
|
|
||
| private static SamplingDecision decisionFor(DelegatingSampler sampler) { | ||
| SamplingResult result = | ||
| sampler.shouldSample( | ||
| Context.root(), | ||
| "00000000000000000000000000000001", | ||
| "test-span", | ||
| SpanKind.INTERNAL, | ||
| Attributes.empty(), | ||
| Collections.emptyList()); | ||
| return result.getDecision(); | ||
| } | ||
|
|
||
| private static JsonNode spec(String field, double value) { | ||
| return MAPPER.createObjectNode().put(field, value); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.