Skip to content

Add AlwaysRecordSampler#4823

Open
majanjua-amzn wants to merge 1 commit intoopen-telemetry:mainfrom
majanjua-amzn:always-record-sampler
Open

Add AlwaysRecordSampler#4823
majanjua-amzn wants to merge 1 commit intoopen-telemetry:mainfrom
majanjua-amzn:always-record-sampler

Conversation

@majanjua-amzn
Copy link
Copy Markdown
Contributor

@majanjua-amzn majanjua-amzn commented Nov 28, 2025

Description

Adds a new AlwaysRecordSampler as per:

This sampler behaves the same as it's root sampler, with the exception that it will replace any DROP decisions with an equivalent RECORD one, while maintaining the attributes and trace state from the decision created by the root sampler.

Also renamed the folder holding the tests for experimental sampling features from composite_sampler to sampling_experimental, as more than just the composite sampler is being tested in this folder, respecting the organization of the files in the src folder.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

Unit tests

Does This PR Require a Contrib Repo Change?

  • Yes. - Link to PR:
  • No.

Checklist:

  • Followed the style guidelines of this project
  • Changelogs have been updated
  • Unit tests have been added
  • Documentation has been updated

@xrmx
Copy link
Copy Markdown
Contributor

xrmx commented Nov 28, 2025

We need to wait for the specs to be approved and released before merging this

@xrmx xrmx moved this to Ready for review in Python PR digest Nov 28, 2025
@majanjua-amzn
Copy link
Copy Markdown
Contributor Author

@xrmx spec is merged, ready for review!

@majanjua-amzn
Copy link
Copy Markdown
Contributor Author

Hey folks, any update here? @xrmx perhaps?

Also, seems the PR build is failing with something unrelated to my changes, perhaps needs to be rerun

Copy link
Copy Markdown
Contributor

@42Questions 42Questions left a comment

Choose a reason for hiding this comment

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

Had some questions regarding the use of passed attributes vs. result.attributes, and some of the testing.

_root_sampler: Sampler

def __init__(self, root_sampler: Sampler):
if not root_sampler:
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.

Suggested change
if not root_sampler:
if root_sampler is None:

This sampler will return the sampling result of the provided `_root_sampler`, unless the
sampling result contains the sampling decision `Decision.DROP`, in which case, a
new sampling result will be returned that is functionally equivalent to the original, except that
it contains the sampling decision `SamplingDecision.RECORD_ONLY`. This ensures that all
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.

Suggested change
it contains the sampling decision `SamplingDecision.RECORD_ONLY`. This ensures that all
it contains the sampling decision `Decision.RECORD_ONLY`. This ensures that all

Comment on lines +74 to +81
def _wrap_result_with_record_only_result(
result: SamplingResult, attributes: Attributes
) -> SamplingResult:
return SamplingResult(
Decision.RECORD_ONLY,
attributes,
result.trace_state,
)
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.

It might make sense to in-line this since it's only called once, also is it intentional that the result.attributes are not being used?

Suggested change
def _wrap_result_with_record_only_result(
result: SamplingResult, attributes: Attributes
) -> SamplingResult:
return SamplingResult(
Decision.RECORD_ONLY,
attributes,
result.trace_state,
)
def _wrap_result_with_record_only_result(
result: SamplingResult
) -> SamplingResult:
return SamplingResult(
Decision.RECORD_ONLY,
result.attributes,
result.trace_state,
)

self.assertNotEqual(actual_result, root_result)
self.assertEqual(actual_result.decision, expected_decision)

self.assertEqual(actual_result.attributes, root_result.attributes)
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.

What happens if the root sampler adds or changes attributes? This also ties into how _wrap_result_with_record_only_result doesn't use result.attributes, is this intentional?

Comment on lines +70 to +71
sampling_trace_state: TraceState = TraceState()
sampling_trace_state.add("key", sampling_decision.name)
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.

add returns a new TraceState,
def add(self, key: str, value: str) -> "TraceState":
...
Returns:
A new TraceState with the modifications applied.
...
"""

so could this be:

Suggested change
sampling_trace_state: TraceState = TraceState()
sampling_trace_state.add("key", sampling_decision.name)
sampling_trace_state = TraceState().add("key", sampling_decision.name)

([#4862](https://github.com/open-telemetry/opentelemetry-python/pull/4862))
- `opentelemetry-exporter-otlp-proto-http`: fix retry logic and error handling for connection failures in trace, metric, and log exporters
([#4709](https://github.com/open-telemetry/opentelemetry-python/pull/4709))
- feat: Add AlwaysRecordSampler
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.

Might want to align the changelog style

Suggested change
- feat: Add AlwaysRecordSampler
- Add AlwaysRecordSampler

Comment on lines +68 to +71
def get_description(self):
return (
"AlwaysRecordSampler{" + self._root_sampler.get_description() + "}"
)
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.

Suggested change
def get_description(self):
return (
"AlwaysRecordSampler{" + self._root_sampler.get_description() + "}"
)
def get_description(self) -> str:
return f"AlwaysRecordSampler{{{self._root_sampler.get_description()}}}"

@tammy-baylis-swi tammy-baylis-swi moved this from Ready for review to Reviewed PRs that need fixes in Python PR digest Feb 26, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 3, 2026

This PR has been automatically marked as stale because it has not had any activity for 14 days. It will be closed if no further activity occurs within 14 days of this comment.
If you're still working on this, please add a comment or push new commits.

@github-actions github-actions bot added the Stale label Mar 3, 2026
@tammy-baylis-swi
Copy link
Copy Markdown
Contributor

Hi @majanjua-amzn , any thoughts on the last round of suggestions?

github-merge-queue bot pushed a commit to open-telemetry/opentelemetry-specification that referenced this pull request Mar 13, 2026
The `AlwaysRecord` sampler has been in our development since the
December 2025 release, and it has prototypes in the following languages:

* Java: open-telemetry/opentelemetry-java#7877
* Go: open-telemetry/opentelemetry-go#7714
* PHP: open-telemetry/opentelemetry-php#1834
* Javascript:
open-telemetry/opentelemetry-js#6168
* Python:
open-telemetry/opentelemetry-python#4823
* DotNet:
open-telemetry/opentelemetry-dotnet#6732 (got
closed but hopefully we bring it back)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Reviewed PRs that need fixes

Development

Successfully merging this pull request may close these issues.

4 participants