Skip to content
Open
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 @@ -123,6 +123,9 @@ Increment the:
to align with the OpenTelemetry specification
[#4161](https://github.com/open-telemetry/opentelemetry-cpp/issues/4161)

* [SDK] Implement the ProbabilitySampler
[#4135](https://github.com/open-telemetry/opentelemetry-cpp/pull/4135)

* [BUILD] Fix protobuf build failure
[#4154](https://github.com/open-telemetry/opentelemetry-cpp/pull/4154)

Expand Down
7 changes: 6 additions & 1 deletion docs/public/sdk/GettingStarted.rst
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ Sampler
^^^^^^^

Sampling is mechanism to control/reducing the number of samples of traces collected and sent to the backend.
OpenTelemetry C++ SDK offers four samplers out of the box:
OpenTelemetry C++ SDK offers five samplers out of the box:

- AlwaysOnSampler which samples every trace regardless of upstream sampling decisions.
- AlwaysOffSampler which doesn’t sample any trace, regardless of upstream sampling decisions.
- ParentBased which uses the parent span to make sampling decisions, if present.
- TraceIdRatioBased which samples a configurable percentage of traces.
- ProbabilitySampler which samples a configurable ratio of traces following the consistent probability sampling specification.

.. code:: cpp

Expand All @@ -134,6 +135,10 @@ OpenTelemetry C++ SDK offers four samplers out of the box:
auto always_off_sampler = std::unique_ptr<sdktrace::TraceIdRatioBasedSampler>
(new sdktrace::TraceIdRatioBasedSampler(ratio));

//ProbabilitySampler - Sample 50% generated spans using consistent probability sampling
auto probability_sampler = std::unique_ptr<sdktrace::ProbabilitySampler>
(new sdktrace::ProbabilitySampler(ratio));

TracerContext
^^^^^^^^^^^^^

Expand Down
59 changes: 59 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/samplers/probability.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <stdint.h>
#include <string>

#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/trace/span_metadata.h"
#include "opentelemetry/trace/trace_id.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{
/**
* The ProbabilitySampler computes and returns a decision based on the
* span's 56-bit randomness value and the configured ratio, following the
* consistent probability sampling specification.
*/
class ProbabilitySampler : public Sampler
{
public:
/**
* @param ratio a required value, 1.0 >= ratio >= 0.0. If the randomness
* value of the span is greater than or equal to the rejection threshold
* derived from the ratio, ShouldSample will return RECORD_AND_SAMPLE.
*/
explicit ProbabilitySampler(double ratio);

/**
* @return Returns either RECORD_AND_SAMPLE or DROP based on the comparison
* of the span's randomness value against the configured rejection
* threshold. The parent SampledFlag is ignored.
*/
SamplingResult ShouldSample(
const opentelemetry::trace::SpanContext &parent_context,
opentelemetry::trace::TraceId trace_id,
nostd::string_view /*name*/,
opentelemetry::trace::SpanKind /*span_kind*/,
const opentelemetry::common::KeyValueIterable & /*attributes*/,
const opentelemetry::trace::SpanContextKeyValueIterable & /*links*/) noexcept override;

/**
* @return Description MUST be ProbabilitySampler{0.000100}
*/
nostd::string_view GetDescription() const noexcept override;

private:
std::string description_;
const uint64_t threshold_;
};
} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#pragma once

#include <memory>

#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{

/**
* Factory class for ProbabilitySampler.
*/
class ProbabilitySamplerFactory
{
public:
/**
* Create a ProbabilitySampler.
*/
static std::unique_ptr<Sampler> Create(double ratio);
};

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
10 changes: 9 additions & 1 deletion sdk/src/configuration/sdk_builder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@
#include "opentelemetry/sdk/trace/samplers/always_off_factory.h"
#include "opentelemetry/sdk/trace/samplers/always_on_factory.h"
#include "opentelemetry/sdk/trace/samplers/parent_factory.h"
#include "opentelemetry/sdk/trace/samplers/probability_factory.h"
#include "opentelemetry/sdk/trace/samplers/trace_id_ratio_factory.h"
#include "opentelemetry/sdk/trace/simple_processor_factory.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
Expand Down Expand Up @@ -390,7 +391,14 @@ class SamplerBuilder : public opentelemetry::sdk::configuration::SamplerConfigur
const opentelemetry::sdk::configuration::ComposableProbabilitySamplerConfiguration *model)
override
{
sampler = opentelemetry::sdk::trace::TraceIdRatioBasedSamplerFactory::Create(model->ratio);
if (model->ratio > 0.0)
{
sampler = opentelemetry::sdk::trace::ProbabilitySamplerFactory::Create(model->ratio);
}
else
{
sampler = opentelemetry::sdk::trace::AlwaysOffSamplerFactory::Create();
}
}

void VisitComposableParentThreshold(
Expand Down
2 changes: 2 additions & 0 deletions sdk/src/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ add_library(
samplers/always_off_factory.cc
samplers/parent.cc
samplers/parent_factory.cc
samplers/probability.cc
samplers/probability_factory.cc
samplers/trace_id_ratio.cc
samplers/trace_id_ratio_factory.cc
samplers/ot_trace_state.cc
Expand Down
122 changes: 122 additions & 0 deletions sdk/src/trace/samplers/probability.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <atomic>
#include <cstdint>
#include <map>
#include <string>

#include "opentelemetry/nostd/shared_ptr.h"
#include "opentelemetry/nostd/string_view.h"
#include "opentelemetry/sdk/common/global_log_handler.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/samplers/probability.h"
#include "opentelemetry/trace/span_context.h"
#include "opentelemetry/trace/trace_flags.h"
#include "opentelemetry/trace/trace_id.h"
#include "opentelemetry/trace/trace_state.h"
#include "opentelemetry/version.h"

#include "ot_trace_state.h"

namespace trace_api = opentelemetry::trace;

namespace
{
// Clamps ratio to [0, 1]. NaN and negatives map to 0 (never-sample).
double ClampProbability(double ratio) noexcept
{
if (!(ratio > 0.0))
return 0.0;
if (ratio > 1.0)
return 1.0;
return ratio;
}
} // namespace

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{

ProbabilitySampler::ProbabilitySampler(double ratio)
: description_("ProbabilitySampler{" + std::to_string(ClampProbability(ratio)) + "}"),
threshold_(CalculateThreshold(ClampProbability(ratio)))
{}

SamplingResult ProbabilitySampler::ShouldSample(
const trace_api::SpanContext &parent_context,
trace_api::TraceId trace_id,
nostd::string_view /*name*/,
trace_api::SpanKind /*span_kind*/,
const opentelemetry::common::KeyValueIterable & /*attributes*/,
const trace_api::SpanContextKeyValueIterable & /*links*/) noexcept
{
auto parent_trace_state = parent_context.trace_state();
if (!parent_trace_state)
{
parent_trace_state = trace_api::TraceState::GetDefault();
}

std::string ot_value;
parent_trace_state->Get(kOtTraceStateKey, ot_value);
OtelTraceState ot_state = OtelTraceState::Parse(ot_value);

bool drop = threshold_ == kMaxThreshold;
if (!drop)
{
uint64_t randomness = 0;
if (ot_state.has_random_value)
{
// An explicit "rv" from an upstream Level 2 participant wins over the
// trace id, keeping the sampling decision consistent across the trace.
randomness = ot_state.random_value;
}
else
{
if (parent_context.IsValid() && !parent_context.trace_flags().IsRandom())
{
static std::atomic<bool> warned{false};

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.

Non-blocking: probably sample on the warning instead of just showing the first warning.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Kept it once-per-process to stay off the per-span hot path.

if (!warned.exchange(true))
{
OTEL_INTERNAL_LOG_WARN(
"ProbabilitySampler presumes TraceID randomness, but the W3C random trace flag is "
"not set. Upgrade the caller to W3C Trace Context Level 2.");
}
}
randomness = GetRandomnessFromTraceId(trace_id);
}
drop = randomness < threshold_;
}

// Record the effective threshold when sampling; a dropped span carries no
// probability, so its inherited (now stale) "th" must be erased. The "rv"
// sub-key and any other "ot" sub-keys are preserved by OtelTraceState.
if (drop)
{
ot_state.has_threshold = false;
}
else
{
ot_state.has_threshold = true;
ot_state.threshold = threshold_;
}

std::string new_ot_value = ot_state.Serialize();
auto trace_state = parent_trace_state->Delete(kOtTraceStateKey);
if (!new_ot_value.empty())
{
trace_state = trace_state->Set(kOtTraceStateKey, new_ot_value);
}

return {drop ? Decision::DROP : Decision::RECORD_AND_SAMPLE, nullptr, trace_state};
}

nostd::string_view ProbabilitySampler::GetDescription() const noexcept
{
return description_;
}
} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
22 changes: 22 additions & 0 deletions sdk/src/trace/samplers/probability_factory.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include "opentelemetry/sdk/trace/samplers/probability_factory.h"
#include "opentelemetry/sdk/trace/samplers/probability.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{

std::unique_ptr<Sampler> ProbabilitySamplerFactory::Create(double ratio)
{
std::unique_ptr<Sampler> sampler(new ProbabilitySampler(ratio));
return sampler;
}

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
16 changes: 16 additions & 0 deletions sdk/test/trace/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,22 @@ cc_test(
],
)

cc_test(
name = "probability_sampler_test",
srcs = [
"probability_sampler_test.cc",
],
tags = [
"test",
"trace",
],
deps = [
"//sdk/src/common:random",
"//sdk/src/trace",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "trace_id_ratio_sampler_test",
srcs = [
Expand Down
1 change: 1 addition & 0 deletions sdk/test/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ foreach(
always_off_sampler_test
always_on_sampler_test
parent_sampler_test
probability_sampler_test
trace_id_ratio_sampler_test
composable_sampler_test
batch_span_processor_test
Expand Down
Loading
Loading