Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ Increment the:
* [SDK] Update default exemplar reservoir size for exponential histograms
[#3551](https://github.com/open-telemetry/opentelemetry-cpp/pull/3551)

* [SDK] Implements options for the ParentBasedSampler with default values
[#3553](https://github.com/open-telemetry/opentelemetry-cpp/pull/3553)

## [1.22 2025-07-11]

* [DOC] Udpate link to membership document
Expand Down
41 changes: 34 additions & 7 deletions sdk/include/opentelemetry/sdk/trace/samplers/parent.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#include "opentelemetry/common/key_value_iterable.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/samplers/always_off.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"
#include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/span_metadata.h"
#include "opentelemetry/trace/trace_id.h"
Expand All @@ -21,16 +23,36 @@ namespace trace
{

/**
* The ParentBased sampler is a composite sampler. ParentBased(delegateSampler) either respects
* the parent span's sampling decision or delegates to delegateSampler for root spans.
* The ParentBased sampler is a composite sampler that delegates sampling decisions based on the
* parent span's context.
*
* The decision is delegated to one of five configurable samplers:
* - No parent exists (root span): delegates to `root sampler`.
* - A remote parent exists and was sampled: delegates to `remote_parent_sampled_sampler` (default
* to AlwaysOnSampler).
* - A remote parent exists and was not sampled: delegates to `remote_parent_nonsampled_sampler`
* (default to AlwaysOffSampler).
* - A local parent exists and was sampled: delegates to `local_parent_sampled_sampler` (default to
* AlwaysOnSampler).
* - A local parent exists and was not sampled: delegates to `local_parent_nonsampled_sampler`
* (default to AlwaysOffSampler).
*/
class ParentBasedSampler : public Sampler
{
public:
explicit ParentBasedSampler(const std::shared_ptr<Sampler> &delegate_sampler) noexcept;
/** The decision either respects the parent span's sampling decision or delegates to
* delegateSampler for root spans
* @return Returns DROP always
explicit ParentBasedSampler(const std::shared_ptr<Sampler> &root_sampler,
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler =
std::make_shared<AlwaysOnSampler>(),
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler =
std::make_shared<AlwaysOffSampler>(),
const std::shared_ptr<Sampler> &local_parent_sampled_sampler =
std::make_shared<AlwaysOnSampler>(),
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler =
std::make_shared<AlwaysOffSampler>()) noexcept;

/** Implements the decision logic by checking the parent context and delegating to the appropriate
* configured sampler
* @return The SamplingResult from the delegated sampler
*/
SamplingResult ShouldSample(
const opentelemetry::trace::SpanContext &parent_context,
Expand All @@ -46,9 +68,14 @@ class ParentBasedSampler : public Sampler
nostd::string_view GetDescription() const noexcept override;

private:
const std::shared_ptr<Sampler> delegate_sampler_;
const std::shared_ptr<Sampler> root_sampler_;
const std::shared_ptr<Sampler> remote_parent_sampled_sampler_;
const std::shared_ptr<Sampler> remote_parent_nonsampled_sampler_;
const std::shared_ptr<Sampler> local_parent_sampled_sampler_;
const std::shared_ptr<Sampler> local_parent_nonsampled_sampler_;
const std::string description_;
};

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
12 changes: 11 additions & 1 deletion sdk/include/opentelemetry/sdk/trace/samplers/parent_factory.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,17 @@ class ParentBasedSamplerFactory
/**
* Create a ParentBasedSampler.
*/
static std::unique_ptr<Sampler> Create(const std::shared_ptr<Sampler> &delegate_sampler);
static std::unique_ptr<Sampler> Create(const std::shared_ptr<Sampler> &root_sampler);

/**
* Create a ParentBasedSampler.
*/
static std::unique_ptr<Sampler> Create(
const std::shared_ptr<Sampler> &root_sampler,
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler,
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler,
const std::shared_ptr<Sampler> &local_parent_sampled_sampler,
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler);
};

} // namespace trace
Expand Down
40 changes: 31 additions & 9 deletions sdk/src/trace/samplers/parent.cc
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <map>
#include <memory>
#include <string>

Expand All @@ -21,9 +20,18 @@ namespace sdk
{
namespace trace
{
ParentBasedSampler::ParentBasedSampler(const std::shared_ptr<Sampler> &delegate_sampler) noexcept
: delegate_sampler_(delegate_sampler),
description_("ParentBased{" + std::string{delegate_sampler->GetDescription()} + "}")
ParentBasedSampler::ParentBasedSampler(
const std::shared_ptr<Sampler> &root_sampler,
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler,
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler,
const std::shared_ptr<Sampler> &local_parent_sampled_sampler,
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler) noexcept
: root_sampler_(root_sampler),
remote_parent_sampled_sampler_(remote_parent_sampled_sampler),
remote_parent_nonsampled_sampler_(remote_parent_nonsampled_sampler),
local_parent_sampled_sampler_(local_parent_sampled_sampler),
local_parent_nonsampled_sampler_(local_parent_nonsampled_sampler),
description_("ParentBased{" + std::string{root_sampler->GetDescription()} + "}")
{}

SamplingResult ParentBasedSampler::ShouldSample(
Expand All @@ -36,24 +44,38 @@ SamplingResult ParentBasedSampler::ShouldSample(
{
if (!parent_context.IsValid())
{
// If no parent (root span) exists returns the result of the delegateSampler
return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes,
links);
// If no parent (root span) exists returns the result of the root_sampler
return root_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes,
links);
}

// If parent exists:
if (parent_context.IsSampled())
{
return {Decision::RECORD_AND_SAMPLE, nullptr, parent_context.trace_state()};
if (parent_context.IsRemote())
{
return remote_parent_sampled_sampler_->ShouldSample(parent_context, trace_id, name, span_kind,
attributes, links);
}
return local_parent_sampled_sampler_->ShouldSample(parent_context, trace_id, name, span_kind,
attributes, links);
}

return {Decision::DROP, nullptr, parent_context.trace_state()};
// Parent is not sampled
if (parent_context.IsRemote())
{
return remote_parent_nonsampled_sampler_->ShouldSample(parent_context, trace_id, name,
span_kind, attributes, links);
}
return local_parent_nonsampled_sampler_->ShouldSample(parent_context, trace_id, name, span_kind,
attributes, links);
}

nostd::string_view ParentBasedSampler::GetDescription() const noexcept
{
return description_;
}

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
21 changes: 19 additions & 2 deletions sdk/src/trace/samplers/parent_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <memory>

#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/samplers/always_off.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"
#include "opentelemetry/sdk/trace/samplers/parent.h"
#include "opentelemetry/sdk/trace/samplers/parent_factory.h"
#include "opentelemetry/version.h"
Expand All @@ -15,9 +17,24 @@ namespace trace
{

std::unique_ptr<Sampler> ParentBasedSamplerFactory::Create(
const std::shared_ptr<Sampler> &delegate_sampler)
const std::shared_ptr<Sampler> &root_sampler)
{
std::unique_ptr<Sampler> sampler(new ParentBasedSampler(delegate_sampler));
std::unique_ptr<Sampler> sampler = ParentBasedSamplerFactory::Create(
root_sampler, std::make_shared<AlwaysOnSampler>(), std::make_shared<AlwaysOffSampler>(),
std::make_shared<AlwaysOnSampler>(), std::make_shared<AlwaysOffSampler>());
return sampler;
}

std::unique_ptr<Sampler> ParentBasedSamplerFactory::Create(
const std::shared_ptr<Sampler> &root_sampler,
const std::shared_ptr<Sampler> &remote_parent_sampled_sampler,
const std::shared_ptr<Sampler> &remote_parent_nonsampled_sampler,
const std::shared_ptr<Sampler> &local_parent_sampled_sampler,
const std::shared_ptr<Sampler> &local_parent_nonsampled_sampler)
{
std::unique_ptr<Sampler> sampler(new ParentBasedSampler(
root_sampler, remote_parent_sampled_sampler, remote_parent_nonsampled_sampler,
local_parent_sampled_sampler, local_parent_nonsampled_sampler));
return sampler;
}

Expand Down
34 changes: 25 additions & 9 deletions sdk/test/trace/parent_sampler_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,14 @@ TEST(ParentBasedSampler, ShouldSample)
opentelemetry::common::KeyValueIterableView<M> view{m1};
trace_api::SpanContextKeyValueIterableView<L> links{l1};
auto trace_state = trace_api::TraceState::FromHeader("congo=t61rcWkgMzE");
trace_api::SpanContext parent_context_sampled(trace_id, span_id, trace_api::TraceFlags{1}, false,
trace_state);
trace_api::SpanContext parent_context_nonsampled(trace_id, span_id, trace_api::TraceFlags{0},
false, trace_state);
trace_api::SpanContext parent_context_sampled_local(trace_id, span_id, trace_api::TraceFlags{1},
false, trace_state);
trace_api::SpanContext parent_context_nonsampled_local(
trace_id, span_id, trace_api::TraceFlags{0}, false, trace_state);
trace_api::SpanContext parent_context_sampled_remote(trace_id, span_id, trace_api::TraceFlags{1},
true, trace_state);
trace_api::SpanContext parent_context_nonsampled_remote(
trace_id, span_id, trace_api::TraceFlags{0}, true, trace_state);

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

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

// Case 3: Parent exists and SampledFlag is false
auto sampling_result4 =
sampler_on.ShouldSample(parent_context_nonsampled, trace_id, "", span_kind, view, links);
// Case 3: Parent exists and SampledFlag is false and RemoteFlag is false
auto sampling_result4 = sampler_on.ShouldSample(parent_context_nonsampled_local, trace_id, "",
span_kind, view, links);
ASSERT_EQ(Decision::DROP, sampling_result4.decision);
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result4.trace_state->ToHeader());

// Case 4: Parent exists, SampledFlag is true and RemoteFlag is true
auto sampling_result5 =
sampler_off.ShouldSample(parent_context_sampled_remote, trace_id, "", span_kind, view, links);
ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result5.decision);
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result5.trace_state->ToHeader());

// Case 5: Parent exists, SampledFlag is false and RemoteFlag is true
auto sampling_result6 = sampler_on.ShouldSample(parent_context_nonsampled_remote, trace_id, "",
span_kind, view, links);
ASSERT_EQ(Decision::DROP, sampling_result6.decision);
ASSERT_EQ("congo=t61rcWkgMzE", sampling_result6.trace_state->ToHeader());
}

TEST(ParentBasedSampler, GetDescription)
Expand Down
Loading