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
57 changes: 57 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/samplers/parent_or_else.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#pragma once

#include "opentelemetry/sdk/common/atomic_shared_ptr.h"
#include "opentelemetry/sdk/trace/sampler.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{
namespace trace_api = opentelemetry::trace;

/**
* A placeholder class that stores the states of a span.
* Will be replaced by the real SpanContext class once it is implemented.
*/
class Sampler::SpanContext
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.

{
public:
inline explicit SpanContext(bool is_recording, bool sampled_flag)
: is_recording(is_recording), sampled_flag(sampled_flag)
{}

bool is_recording;
bool sampled_flag;
};

/**
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: probably add an empty line here.

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.

Done.

* The parent or else sampler is a composite sampler. ParentOrElse(delegateSampler) either respects
* the parent span's sampling decision or delegates to delegateSampler for root spans.
*/
class ParentOrElseSampler : public Sampler
{
public:
explicit ParentOrElseSampler(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 NOT_RECORD always
*/
SamplingResult ShouldSample(const SpanContext * parent_context,
trace_api::TraceId trace_id,
nostd::string_view name,
trace_api::SpanKind span_kind,
const trace_api::KeyValueIterable & attributes) noexcept override;

/**
* @return Description MUST be ParentOrElse{delegate_sampler_.getDescription()}
*/
std::string GetDescription() const noexcept override;

private:
const std::shared_ptr<Sampler> delegate_sampler_;
const std::string description_;
};
} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
2 changes: 1 addition & 1 deletion sdk/src/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
add_library(opentelemetry_trace tracer_provider.cc tracer.cc span.cc)
add_library(opentelemetry_trace tracer_provider.cc tracer.cc span.cc samplers/parent_or_else.cc)
41 changes: 41 additions & 0 deletions sdk/src/trace/samplers/parent_or_else.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include "opentelemetry/sdk/trace/samplers/parent_or_else.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{
ParentOrElseSampler::ParentOrElseSampler(std::shared_ptr<Sampler> delegate_sampler) noexcept
: delegate_sampler_(delegate_sampler),
description_("ParentOrElse{" + delegate_sampler->GetDescription() + "}")
{}

SamplingResult ParentOrElseSampler::ShouldSample(
const SpanContext *parent_context,
trace_api::TraceId trace_id,
nostd::string_view name,
trace_api::SpanKind span_kind,
const trace_api::KeyValueIterable &attributes) noexcept
{
if (parent_context == nullptr)
{
// If no parent (root span) exists returns the result of the delegateSampler
return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes);
}

// If parent exists:
if (parent_context->sampled_flag)
{
return {Decision::RECORD_AND_SAMPLE, nullptr};
}
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.

We could flatten this out, to increase readability:

if (parent_context == nullptr)
{
  return delegate_sampler_->ShouldSample(parent_context, trace_id, name, span_kind, attributes);
}

if (parent_context->sampled_flag)
{
  return {Decision::RECORD_AND_SAMPLE, nullptr};
}

return {Decision::NOT_RECORD, nullptr};


return {Decision::NOT_RECORD, nullptr};
}

std::string ParentOrElseSampler::GetDescription() const noexcept
{
return description_;
}
} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
15 changes: 12 additions & 3 deletions sdk/test/trace/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,6 @@ cc_test(
],
)



cc_test(
name = "always_off_sampler_test",
srcs = [
Expand All @@ -64,4 +62,15 @@ cc_test(
"//sdk/src/trace",
"@com_google_googletest//:gtest_main",
],
)
)

cc_test(
name = "parent_or_else_sampler_test",
srcs = [
"parent_or_else_sampler_test.cc",
],
deps = [
"//sdk/src/trace",
"@com_google_googletest//:gtest_main",
],
)
3 changes: 2 additions & 1 deletion sdk/test/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
foreach(testname tracer_provider_test span_data_test simple_processor_test
tracer_test always_off_sampler_test always_on_sampler_test)
tracer_test always_off_sampler_test always_on_sampler_test
parent_or_else_sampler_test)
add_executable(${testname} "${testname}.cc")
target_link_libraries(${testname} ${GTEST_BOTH_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT} opentelemetry_trace)
Expand Down
50 changes: 50 additions & 0 deletions sdk/test/trace/parent_or_else_sampler_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include <gtest/gtest.h>
#include <memory>
#include "opentelemetry/sdk/trace/samplers/always_off.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"
#include "opentelemetry/sdk/trace/samplers/parent_or_else.h"

using opentelemetry::sdk::trace::AlwaysOffSampler;
using opentelemetry::sdk::trace::AlwaysOnSampler;
using opentelemetry::sdk::trace::Decision;
using opentelemetry::sdk::trace::ParentOrElseSampler;

TEST(ParentOrElseSampler, ShouldSample)
{
ParentOrElseSampler sampler_off(std::make_shared<AlwaysOffSampler>());
ParentOrElseSampler sampler_on(std::make_shared<AlwaysOnSampler>());

// Set up parameters
opentelemetry::trace::TraceId trace_id;
opentelemetry::trace::SpanKind span_kind = opentelemetry::trace::SpanKind::kInternal;
using M = std::map<std::string, int>;
M m1 = {{}};
opentelemetry::trace::KeyValueIterableView<M> view{m1};
opentelemetry::sdk::trace::Sampler::SpanContext parent_context_sampled(true, true);
opentelemetry::sdk::trace::Sampler::SpanContext parent_context_nonsampled(true, false);

// Case 1: Parent doesn't exist. Return result of delegateSampler()
Comment thread
ziqizh marked this conversation as resolved.
auto sampling_result = sampler_off.ShouldSample(nullptr, trace_id, "", span_kind, view);
auto sampling_result2 = sampler_on.ShouldSample(nullptr, trace_id, "", span_kind, view);

ASSERT_EQ(Decision::NOT_RECORD, sampling_result.decision);
ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result2.decision);

// Case 2: Parent exists and SampledFlag is true
auto sampling_result3 =
sampler_off.ShouldSample(&parent_context_sampled, trace_id, "", span_kind, view);
ASSERT_EQ(Decision::RECORD_AND_SAMPLE, sampling_result3.decision);

// Case 3: Parent exists and SampledFlag is false
auto sampling_result4 =
sampler_on.ShouldSample(&parent_context_nonsampled, trace_id, "", span_kind, view);
ASSERT_EQ(Decision::NOT_RECORD, sampling_result4.decision);
}

TEST(ParentOrElseSampler, GetDescription)
{
ParentOrElseSampler sampler(std::make_shared<AlwaysOffSampler>());
ASSERT_EQ("ParentOrElse{AlwaysOffSampler}", sampler.GetDescription());
ParentOrElseSampler sampler2(std::make_shared<AlwaysOnSampler>());
ASSERT_EQ("ParentOrElse{AlwaysOnSampler}", sampler2.GetDescription());
}