Skip to content

feat(sampler-composite): add ComposableAnnotatingSampler and ComposableRuleBasedSampler#6305

Merged
trentm merged 6 commits intoopen-telemetry:mainfrom
trentm:trentm-moar-composite-samplers
Feb 6, 2026
Merged

feat(sampler-composite): add ComposableAnnotatingSampler and ComposableRuleBasedSampler#6305
trentm merged 6 commits intoopen-telemetry:mainfrom
trentm:trentm-moar-composite-samplers

Conversation

@trentm
Copy link
Copy Markdown
Contributor

@trentm trentm commented Jan 15, 2026

In #5839 the initial work adding composable samplers, specified by:

That PR added most of the described composable samplers, all but

This PR adds those final two built-in composable samplers defined by the spec.

The main motivating reason for adding these is: the rule-based composable sampler is the intended path towards supporting a way to have instrumentation skip some some routes, e.g. an HTTP /healthcheck route.
See https://opentelemetry.io/blog/2025/declarative-config/#health-check-exclusion

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 15, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.49%. Comparing base (856edbd) to head (ce2b062).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #6305      +/-   ##
==========================================
+ Coverage   95.48%   95.49%   +0.01%     
==========================================
  Files         363      365       +2     
  Lines       11563    11594      +31     
  Branches     2669     2673       +4     
==========================================
+ Hits        11041    11072      +31     
  Misses        522      522              
Files with missing lines Coverage Δ
...mental/packages/sampler-composite/src/alwaysoff.ts 100.00% <100.00%> (ø)
...imental/packages/sampler-composite/src/alwayson.ts 100.00% <100.00%> (ø)
...ental/packages/sampler-composite/src/annotating.ts 100.00% <100.00%> (ø)
...mental/packages/sampler-composite/src/rulebased.ts 100.00% <100.00%> (ø)
...tal/packages/sampler-composite/src/traceidratio.ts 95.45% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@trentm
Copy link
Copy Markdown
Contributor Author

trentm commented Jan 15, 2026

FWIW, the .toString() of a ComposableRuleBasedSampler looks something like this:

ComposableRuleBasedSampler([
    (isHealthCheck, ComposableAlwaysOffSampler),
    (isCheckout,
        ComposableAnnotatingSampler(ComposableAlwaysOnSampler, attributes)),
    ((anonymous),
        ComposableAnnotatingSampler(ComposableAlwaysOnSampler, attributes))
])

though, without the line-breaks and indentation.
This somewhat matches the equivalent in Java-land (https://github.com/open-telemetry/opentelemetry-java/blob/caadc26366089e7e4c14926f26e7cdcbd3c34989/sdk-extensions/incubator/src/main/java/io/opentelemetry/sdk/extension/incubator/trace/samplers/ComposableRuleBasedSampler.java#L24-L38) though with some JavaScript-isms: the .name of the predicate function is used to describe the predicate (using '(anonymous)' for anonymous functions as Node.js' util.inspect() does).

I have not added a representation of the attributes to the <ComposableAnnotatingSampler>.toString() output. I thought that could get out of hand if a number of attributes are set for a particular instance. I don't care strongly though, if it is felt listing the attributes would improve the string description.

private readonly rules: SamplingRule[];
private readonly description: string;

constructor(rules: SamplingRule[]) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if this is the good reference but I read ComposableSampler gets 2 params:

  • the rules
  • span kind (optional)

I the span kind is provided the getSamplingIntent logic changes a bit. From the docs

For calculating the SamplingIntent, if the Span kind matches the specified kind, 
or the specified kind is not given, the sampler goes through the list in the provided 
order and calls SpanMatches on Predicates passing the same arguments as 
received. If a call returns true, the result is as returned by GetSamplingIntent called 
on the corresponding Composable - no other Predicates are evaluated. If the 
SpanKind does not match, or none of the calls to SpanMatches yield true, the result 
is obtained by calling GetSamplingIntent on ConsistentAlwaysOffSampler.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed, the specifics from the OTEPs changed in some cases when making it to the spec. https://opentelemetry.io/docs/specs/otel/trace/sdk/#composablerulebased says:

Required parameters:

rules - A list of (Predicate, ComposableSampler) pairs, where Predicate is a function that evaluates whether a rule applies

Hence no separate span kind argument. Note that the predicate (type SamplingPredicate does get the spanKind, along with other arguments for its decision).

toString(): string;
}

export type SamplingPredicate = (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(same thing about the reference The docs define a SpanMatches method in the predicate. So its defined as an interface rather than a function. My guess is by having

interface SamplingPredicate {
  spanMatches: (...args: Parameters<Sampler['shouldSample']>) => boolean;
}

implementors are able to provide a predicate that holds state...

Writing that last sentence I realised this can be achieved using closures so you can ignore my comment.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same answer as above. The language that made it into the spec is where Predicate is a function that evaluates whether a rule applies.

@trentm trentm added this pull request to the merge queue Feb 6, 2026
Merged via the queue into open-telemetry:main with commit 0fb06b4 Feb 6, 2026
27 checks passed
@trentm trentm deleted the trentm-moar-composite-samplers branch February 6, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants