Skip to content

Commit 5984a2d

Browse files
[SDK] Implements options for the ParentBasedSampler with default values (#3553)
1 parent c406864 commit 5984a2d

File tree

6 files changed

+123
-28
lines changed

6 files changed

+123
-28
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ Increment the:
2424
* [SDK] Update default exemplar reservoir size for exponential histograms
2525
[#3551](https://github.com/open-telemetry/opentelemetry-cpp/pull/3551)
2626

27+
* [SDK] Implements options for the ParentBasedSampler with default values
28+
[#3553](https://github.com/open-telemetry/opentelemetry-cpp/pull/3553)
29+
2730
## [1.22 2025-07-11]
2831

2932
* [DOC] Udpate link to membership document

sdk/include/opentelemetry/sdk/trace/samplers/parent.h

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include "opentelemetry/common/key_value_iterable.h"
1010
#include "opentelemetry/nostd/string_view.h"
1111
#include "opentelemetry/sdk/trace/sampler.h"
12+
#include "opentelemetry/sdk/trace/samplers/always_off.h"
13+
#include "opentelemetry/sdk/trace/samplers/always_on.h"
1214
#include "opentelemetry/trace/span_context.h"
1315
#include "opentelemetry/trace/span_metadata.h"
1416
#include "opentelemetry/trace/trace_id.h"
@@ -21,16 +23,36 @@ namespace trace
2123
{
2224

2325
/**
24-
* The ParentBased sampler is a composite sampler. ParentBased(delegateSampler) either respects
25-
* the parent span's sampling decision or delegates to delegateSampler for root spans.
26+
* The ParentBased sampler is a composite sampler that delegates sampling decisions based on the
27+
* parent span's context.
28+
*
29+
* The decision is delegated to one of five configurable samplers:
30+
* - No parent exists (root span): delegates to `root sampler`.
31+
* - A remote parent exists and was sampled: delegates to `remote_parent_sampled_sampler` (default
32+
* to AlwaysOnSampler).
33+
* - A remote parent exists and was not sampled: delegates to `remote_parent_nonsampled_sampler`
34+
* (default to AlwaysOffSampler).
35+
* - A local parent exists and was sampled: delegates to `local_parent_sampled_sampler` (default to
36+
* AlwaysOnSampler).
37+
* - A local parent exists and was not sampled: delegates to `local_parent_nonsampled_sampler`
38+
* (default to AlwaysOffSampler).
2639
*/
2740
class ParentBasedSampler : public Sampler
2841
{
2942
public:
30-
explicit ParentBasedSampler(const std::shared_ptr<Sampler> &delegate_sampler) noexcept;
31-
/** The decision either respects the parent span's sampling decision or delegates to
32-
* delegateSampler for root spans
33-
* @return Returns DROP always
43+
explicit ParentBasedSampler(const std::shared_ptr<Sampler> &root_sampler,
44+
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler =
45+
std::make_shared<AlwaysOnSampler>(),
46+
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler =
47+
std::make_shared<AlwaysOffSampler>(),
48+
const std::shared_ptr<Sampler> &local_parent_sampled_sampler =
49+
std::make_shared<AlwaysOnSampler>(),
50+
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler =
51+
std::make_shared<AlwaysOffSampler>()) noexcept;
52+
53+
/** Implements the decision logic by checking the parent context and delegating to the appropriate
54+
* configured sampler
55+
* @return The SamplingResult from the delegated sampler
3456
*/
3557
SamplingResult ShouldSample(
3658
const opentelemetry::trace::SpanContext &parent_context,
@@ -46,9 +68,14 @@ class ParentBasedSampler : public Sampler
4668
nostd::string_view GetDescription() const noexcept override;
4769

4870
private:
49-
const std::shared_ptr<Sampler> delegate_sampler_;
71+
const std::shared_ptr<Sampler> root_sampler_;
72+
const std::shared_ptr<Sampler> remote_parent_sampled_sampler_;
73+
const std::shared_ptr<Sampler> remote_parent_nonsampled_sampler_;
74+
const std::shared_ptr<Sampler> local_parent_sampled_sampler_;
75+
const std::shared_ptr<Sampler> local_parent_nonsampled_sampler_;
5076
const std::string description_;
5177
};
78+
5279
} // namespace trace
5380
} // namespace sdk
5481
OPENTELEMETRY_END_NAMESPACE

sdk/include/opentelemetry/sdk/trace/samplers/parent_factory.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,17 @@ class ParentBasedSamplerFactory
2323
/**
2424
* Create a ParentBasedSampler.
2525
*/
26-
static std::unique_ptr<Sampler> Create(const std::shared_ptr<Sampler> &delegate_sampler);
26+
static std::unique_ptr<Sampler> Create(const std::shared_ptr<Sampler> &root_sampler);
27+
28+
/**
29+
* Create a ParentBasedSampler.
30+
*/
31+
static std::unique_ptr<Sampler> Create(
32+
const std::shared_ptr<Sampler> &root_sampler,
33+
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler,
34+
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler,
35+
const std::shared_ptr<Sampler> &local_parent_sampled_sampler,
36+
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler);
2737
};
2838

2939
} // namespace trace

sdk/src/trace/samplers/parent.cc

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright The OpenTelemetry Authors
22
// SPDX-License-Identifier: Apache-2.0
33

4-
#include <map>
54
#include <memory>
65
#include <string>
76

@@ -21,9 +20,18 @@ namespace sdk
2120
{
2221
namespace trace
2322
{
24-
ParentBasedSampler::ParentBasedSampler(const std::shared_ptr<Sampler> &delegate_sampler) noexcept
25-
: delegate_sampler_(delegate_sampler),
26-
description_("ParentBased{" + std::string{delegate_sampler->GetDescription()} + "}")
23+
ParentBasedSampler::ParentBasedSampler(
24+
const std::shared_ptr<Sampler> &root_sampler,
25+
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler,
26+
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler,
27+
const std::shared_ptr<Sampler> &local_parent_sampled_sampler,
28+
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler) noexcept
29+
: root_sampler_(root_sampler),
30+
remote_parent_sampled_sampler_(remote_parent_sampled_sampler),
31+
remote_parent_nonsampled_sampler_(remote_parent_nonsampled_sampler),
32+
local_parent_sampled_sampler_(local_parent_sampled_sampler),
33+
local_parent_nonsampled_sampler_(local_parent_nonsampled_sampler),
34+
description_("ParentBased{" + std::string{root_sampler->GetDescription()} + "}")
2735
{}
2836

2937
SamplingResult ParentBasedSampler::ShouldSample(
@@ -36,24 +44,38 @@ SamplingResult ParentBasedSampler::ShouldSample(
3644
{
3745
if (!parent_context.IsValid())
3846
{
39-
// If no parent (root span) exists returns the result of the delegateSampler
40-
return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes,
41-
links);
47+
// If no parent (root span) exists returns the result of the root_sampler
48+
return root_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes,
49+
links);
4250
}
4351

4452
// If parent exists:
4553
if (parent_context.IsSampled())
4654
{
47-
return {Decision::RECORD_AND_SAMPLE, nullptr, parent_context.trace_state()};
55+
if (parent_context.IsRemote())
56+
{
57+
return remote_parent_sampled_sampler_->ShouldSample(parent_context, trace_id, name, span_kind,
58+
attributes, links);
59+
}
60+
return local_parent_sampled_sampler_->ShouldSample(parent_context, trace_id, name, span_kind,
61+
attributes, links);
4862
}
4963

50-
return {Decision::DROP, nullptr, parent_context.trace_state()};
64+
// Parent is not sampled
65+
if (parent_context.IsRemote())
66+
{
67+
return remote_parent_nonsampled_sampler_->ShouldSample(parent_context, trace_id, name,
68+
span_kind, attributes, links);
69+
}
70+
return local_parent_nonsampled_sampler_->ShouldSample(parent_context, trace_id, name, span_kind,
71+
attributes, links);
5172
}
5273

5374
nostd::string_view ParentBasedSampler::GetDescription() const noexcept
5475
{
5576
return description_;
5677
}
78+
5779
} // namespace trace
5880
} // namespace sdk
5981
OPENTELEMETRY_END_NAMESPACE

sdk/src/trace/samplers/parent_factory.cc

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <memory>
55

66
#include "opentelemetry/sdk/trace/sampler.h"
7+
#include "opentelemetry/sdk/trace/samplers/always_off.h"
8+
#include "opentelemetry/sdk/trace/samplers/always_on.h"
79
#include "opentelemetry/sdk/trace/samplers/parent.h"
810
#include "opentelemetry/sdk/trace/samplers/parent_factory.h"
911
#include "opentelemetry/version.h"
@@ -15,9 +17,24 @@ namespace trace
1517
{
1618

1719
std::unique_ptr<Sampler> ParentBasedSamplerFactory::Create(
18-
const std::shared_ptr<Sampler> &delegate_sampler)
20+
const std::shared_ptr<Sampler> &root_sampler)
1921
{
20-
std::unique_ptr<Sampler> sampler(new ParentBasedSampler(delegate_sampler));
22+
std::unique_ptr<Sampler> sampler = ParentBasedSamplerFactory::Create(
23+
root_sampler, std::make_shared<AlwaysOnSampler>(), std::make_shared<AlwaysOffSampler>(),
24+
std::make_shared<AlwaysOnSampler>(), std::make_shared<AlwaysOffSampler>());
25+
return sampler;
26+
}
27+
28+
std::unique_ptr<Sampler> ParentBasedSamplerFactory::Create(
29+
const std::shared_ptr<Sampler> &root_sampler,
30+
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler,
31+
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler,
32+
const std::shared_ptr<Sampler> &local_parent_sampled_sampler,
33+
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler)
34+
{
35+
std::unique_ptr<Sampler> sampler(new ParentBasedSampler(
36+
root_sampler, remote_parent_sampled_sampler, remote_parent_nonsampled_sampler,
37+
local_parent_sampled_sampler, local_parent_nonsampled_sampler));
2138
return sampler;
2239
}
2340

sdk/test/trace/parent_sampler_test.cc

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,14 @@ TEST(ParentBasedSampler, ShouldSample)
5151
opentelemetry::common::KeyValueIterableView<M> view{m1};
5252
trace_api::SpanContextKeyValueIterableView<L> links{l1};
5353
auto trace_state = trace_api::TraceState::FromHeader("congo=t61rcWkgMzE");
54-
trace_api::SpanContext parent_context_sampled(trace_id, span_id, trace_api::TraceFlags{1}, false,
55-
trace_state);
56-
trace_api::SpanContext parent_context_nonsampled(trace_id, span_id, trace_api::TraceFlags{0},
57-
false, trace_state);
54+
trace_api::SpanContext parent_context_sampled_local(trace_id, span_id, trace_api::TraceFlags{1},
55+
false, trace_state);
56+
trace_api::SpanContext parent_context_nonsampled_local(
57+
trace_id, span_id, trace_api::TraceFlags{0}, false, trace_state);
58+
trace_api::SpanContext parent_context_sampled_remote(trace_id, span_id, trace_api::TraceFlags{1},
59+
true, trace_state);
60+
trace_api::SpanContext parent_context_nonsampled_remote(
61+
trace_id, span_id, trace_api::TraceFlags{0}, true, trace_state);
5862

5963
// Case 1: Parent doesn't exist. Return result of delegateSampler()
6064
auto sampling_result = sampler_off.ShouldSample(trace_api::SpanContext::GetInvalid(), trace_id,
@@ -67,17 +71,29 @@ TEST(ParentBasedSampler, ShouldSample)
6771
ASSERT_EQ("", sampling_result.trace_state->ToHeader());
6872
ASSERT_EQ("", sampling_result2.trace_state->ToHeader());
6973

70-
// Case 2: Parent exists and SampledFlag is true
74+
// Case 2: Parent exists and SampledFlag is true and RemoteFlag is false
7175
auto sampling_result3 =
72-
sampler_off.ShouldSample(parent_context_sampled, trace_id, "", span_kind, view, links);
76+
sampler_off.ShouldSample(parent_context_sampled_local, trace_id, "", span_kind, view, links);
7377
ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result3.decision);
7478
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result3.trace_state->ToHeader());
7579

76-
// Case 3: Parent exists and SampledFlag is false
77-
auto sampling_result4 =
78-
sampler_on.ShouldSample(parent_context_nonsampled, trace_id, "", span_kind, view, links);
80+
// Case 3: Parent exists and SampledFlag is false and RemoteFlag is false
81+
auto sampling_result4 = sampler_on.ShouldSample(parent_context_nonsampled_local, trace_id, "",
82+
span_kind, view, links);
7983
ASSERT_EQ(Decision::DROP, sampling_result4.decision);
8084
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result4.trace_state->ToHeader());
85+
86+
// Case 4: Parent exists, SampledFlag is true and RemoteFlag is true
87+
auto sampling_result5 =
88+
sampler_off.ShouldSample(parent_context_sampled_remote, trace_id, "", span_kind, view, links);
89+
ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result5.decision);
90+
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result5.trace_state->ToHeader());
91+
92+
// Case 5: Parent exists, SampledFlag is false and RemoteFlag is true
93+
auto sampling_result6 = sampler_on.ShouldSample(parent_context_nonsampled_remote, trace_id, "",
94+
span_kind, view, links);
95+
ASSERT_EQ(Decision::DROP, sampling_result6.decision);
96+
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result6.trace_state->ToHeader());
8197
}
8298

8399
TEST(ParentBasedSampler, GetDescription)

0 commit comments

Comments
 (0)