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
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,16 @@ Increment the:

## [Unreleased]

* [SDK] Support `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG` environment variables
[#4709](https://github.com/open-telemetry/opentelemetry-cpp/issues/4709)
Users can now configure the sampler via environment variables, supporting:
- `always_on`, `always_off`, `traceidratio`, `parentbased_traceidratio`,
`parentbased_always_on`, `parentbased_always_off`
- For ratio-based samplers, `OTEL_TRACES_SAMPLER_ARG` specifies the ratio (default 1.0)
- Environment configuration applies to TracerProvider, TracerContext, and factory methods
- Explicitly supplied samplers always take precedence over environment variables
- Invalid sampler names or ratios fall back to the default AlwaysOnSampler with a warning

## [1.30.0] 2026-10-22

This release contains:
Expand Down
45 changes: 45 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/sampler_env.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// 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
{

namespace sampler_env
{

/**
* @brief Returns a sampler built from OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG
* environment variables, or a default AlwaysOnSampler if the variables are not set.
*
* Supported sampler values:
* - always_on: Always sample the trace
* - always_off: Never sample the trace
* - traceidratio: Sample a fraction of traces based on trace ID, requires OTEL_TRACES_SAMPLER_ARG
* - parentbased_traceidratio: Sample based on parent and trace ID, requires OTEL_TRACES_SAMPLER_ARG
* - parentbased_always_on: Use parent sample decision, default to AlwaysOn
* - parentbased_always_off: Use parent sample decision, default to AlwaysOff
*
* For traceidratio and parentbased_traceidratio, OTEL_TRACES_SAMPLER_ARG specifies the ratio
* (0.0 to 1.0). If unset, defaults to 1.0.
*
* If an unknown sampler name or invalid ratio is provided, logs a warning and returns
* AlwaysOnSampler.
*/
OPENTELEMETRY_EXPORT std::unique_ptr<Sampler> GetSamplerFromEnv();

} // namespace sampler_env

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE

3 changes: 2 additions & 1 deletion sdk/include/opentelemetry/sdk/trace/tracer_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/random_id_generator.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"
#include "opentelemetry/sdk/trace/span_limits.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
Expand Down Expand Up @@ -44,7 +45,7 @@ class TracerContext
std::vector<std::unique_ptr<SpanProcessor>> &&processor,
const opentelemetry::sdk::resource::Resource &resource =
opentelemetry::sdk::resource::Resource::Create({}),
std::unique_ptr<Sampler> sampler = std::unique_ptr<AlwaysOnSampler>(new AlwaysOnSampler),
std::unique_ptr<Sampler> sampler = nullptr,
std::unique_ptr<IdGenerator> id_generator =
std::unique_ptr<IdGenerator>(new RandomIdGenerator()),
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator =
Expand Down
10 changes: 6 additions & 4 deletions sdk/include/opentelemetry/sdk/trace/tracer_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/random_id_generator.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#include "opentelemetry/sdk/trace/samplers/always_on.h"
#include "opentelemetry/sdk/trace/span_limits.h"
#include "opentelemetry/sdk/trace/tracer.h"
Expand All @@ -36,8 +37,9 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T
* @param processor The span processor for this tracer provider. This must
* not be a nullptr.
* @param resource The resources for this tracer provider.
* @param sampler The sampler for this tracer provider. This must
* not be a nullptr.
* @param sampler The sampler for this tracer provider. If null, a sampler is built from
* OTEL_TRACES_SAMPLER and OTEL_TRACES_SAMPLER_ARG environment variables, defaulting to
* AlwaysOnSampler if unset.
* @param id_generator The custom id generator for this tracer provider. This must
* not be a nullptr
* @param tracer_configurator Provides access to a function that computes the TracerConfig for
Expand All @@ -48,7 +50,7 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T
std::unique_ptr<SpanProcessor> processor,
const opentelemetry::sdk::resource::Resource &resource =
opentelemetry::sdk::resource::Resource::Create({}),
std::unique_ptr<Sampler> sampler = std::unique_ptr<AlwaysOnSampler>(new AlwaysOnSampler),
std::unique_ptr<Sampler> sampler = nullptr,
std::unique_ptr<IdGenerator> id_generator =
std::unique_ptr<IdGenerator>(new RandomIdGenerator()),
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator =
Expand All @@ -62,7 +64,7 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T
std::vector<std::unique_ptr<SpanProcessor>> &&processors,
const opentelemetry::sdk::resource::Resource &resource =
opentelemetry::sdk::resource::Resource::Create({}),
std::unique_ptr<Sampler> sampler = std::unique_ptr<AlwaysOnSampler>(new AlwaysOnSampler),
std::unique_ptr<Sampler> sampler = nullptr,
std::unique_ptr<IdGenerator> id_generator =
std::unique_ptr<IdGenerator>(new RandomIdGenerator()),
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator =
Expand Down
1 change: 1 addition & 0 deletions sdk/src/trace/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_library(
simple_processor_factory.cc
span_data.cc
span_limits.cc
sampler_env.cc
samplers/always_on_factory.cc
samplers/always_off_factory.cc
samplers/parent.cc
Expand Down
141 changes: 141 additions & 0 deletions sdk/src/trace/sampler_env.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

#include <cmath>
#include <memory>
#include <string>

#include "opentelemetry/sdk/common/env_variables.h"
#include "opentelemetry/sdk/common/global_log_handler.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#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/trace_id_ratio_factory.h"
#include "opentelemetry/version.h"

OPENTELEMETRY_BEGIN_NAMESPACE
namespace sdk
{
namespace trace
{

namespace sampler_env
{

namespace
{

// Case-insensitive comparison of two ASCII strings
static bool CaseInsensitiveAsciiEquals(const std::string &lhs, const std::string &rhs) noexcept
{
if (lhs.size() != rhs.size())
{
return false;
}
for (size_t i = 0; i < lhs.size(); ++i)
{
char a = lhs[i];
char b = rhs[i];
if (a >= 'A' && a <= 'Z')
a += 32;
if (b >= 'A' && b <= 'Z')
b += 32;
if (a != b)
{
return false;
}
}
return true;
}

} // namespace

static constexpr const char *kTracesSamplerEnv = "OTEL_TRACES_SAMPLER";
static constexpr const char *kTracesSamplerArgEnv = "OTEL_TRACES_SAMPLER_ARG";
static constexpr double kDefaultTraceIdRatio = 1.0;

std::unique_ptr<Sampler> GetSamplerFromEnv()
{
std::string sampler_name;
bool has_sampler_env =
opentelemetry::sdk::common::GetStringEnvironmentVariable(kTracesSamplerEnv, sampler_name);

if (!has_sampler_env || sampler_name.empty())
{
// When unset, preserve current default behavior (AlwaysOnSampler)
return AlwaysOnSamplerFactory::Create();
}

float ratio_arg = kDefaultTraceIdRatio;
bool has_ratio_env =
opentelemetry::sdk::common::GetFloatEnvironmentVariable(kTracesSamplerArgEnv, ratio_arg);

// For traceidratio and parentbased_traceidratio, default to 1.0 if arg is unset
if (!has_ratio_env)
{
ratio_arg = static_cast<float>(kDefaultTraceIdRatio);
}

// Build sampler based on environment variable value
if (CaseInsensitiveAsciiEquals(sampler_name, "always_on"))
{
return AlwaysOnSamplerFactory::Create();
}
else if (CaseInsensitiveAsciiEquals(sampler_name, "always_off"))
{
return AlwaysOffSamplerFactory::Create();
}
else if (CaseInsensitiveAsciiEquals(sampler_name, "traceidratio"))
{
// Validate ratio
if (std::isnan(ratio_arg) || std::isinf(ratio_arg) || ratio_arg < 0.0f || ratio_arg > 1.0f)
{
OTEL_INTERNAL_LOG_WARN("Environment variable <" << kTracesSamplerArgEnv
<< "> has invalid value <" << ratio_arg
<< "> for traceidratio sampler, "
<< "falling back to AlwaysOnSampler");
return AlwaysOnSamplerFactory::Create();
}
return TraceIdRatioBasedSamplerFactory::Create(ratio_arg);
}
else if (CaseInsensitiveAsciiEquals(sampler_name, "parentbased_traceidratio"))
{
// Validate ratio - if invalid, use default ratio of 1.0
if (std::isnan(ratio_arg) || std::isinf(ratio_arg) || ratio_arg < 0.0f || ratio_arg > 1.0f)
{
OTEL_INTERNAL_LOG_WARN("Environment variable <" << kTracesSamplerArgEnv
<< "> has invalid value <" << ratio_arg
<< "> for parentbased_traceidratio sampler, "
<< "using default ratio of 1.0");
ratio_arg = static_cast<float>(kDefaultTraceIdRatio);
}
auto root_sampler = TraceIdRatioBasedSamplerFactory::Create(ratio_arg);
return ParentBasedSamplerFactory::Create(std::shared_ptr<Sampler>(root_sampler.release()));
}
else if (CaseInsensitiveAsciiEquals(sampler_name, "parentbased_always_on"))
{
auto root_sampler = AlwaysOnSamplerFactory::Create();
return ParentBasedSamplerFactory::Create(std::shared_ptr<Sampler>(root_sampler.release()));
}
else if (CaseInsensitiveAsciiEquals(sampler_name, "parentbased_always_off"))
{
auto root_sampler = AlwaysOffSamplerFactory::Create();
return ParentBasedSamplerFactory::Create(std::shared_ptr<Sampler>(root_sampler.release()));
}
else
{
// Unknown sampler name
OTEL_INTERNAL_LOG_WARN("Environment variable <" << kTracesSamplerEnv << "> has unknown value <"
<< sampler_name
<< ">, falling back to AlwaysOnSampler");
return AlwaysOnSamplerFactory::Create();
}
}

} // namespace sampler_env

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
8 changes: 7 additions & 1 deletion sdk/src/trace/tracer_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "opentelemetry/sdk/trace/multi_span_processor.h"
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#include "opentelemetry/sdk/trace/span_limits.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
#include "opentelemetry/sdk/trace/tracer_context.h"
Expand All @@ -38,7 +39,12 @@ TracerContext::TracerContext(
processor_(std::unique_ptr<SpanProcessor>(new MultiSpanProcessor(std::move(processors)))),
tracer_configurator_(std::move(tracer_configurator)),
span_limits_(span_limits)
{}
{
if (!sampler_)
{
sampler_ = sampler_env::GetSamplerFromEnv();
}
}

Sampler &TracerContext::GetSampler() const noexcept
{
Expand Down
3 changes: 2 additions & 1 deletion sdk/src/trace/tracer_context_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/random_id_generator_factory.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#include "opentelemetry/sdk/trace/samplers/always_on_factory.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
#include "opentelemetry/sdk/trace/tracer_context.h"
Expand All @@ -34,7 +35,7 @@ std::unique_ptr<TracerContext> TracerContextFactory::Create(
std::vector<std::unique_ptr<SpanProcessor>> &&processors,
const opentelemetry::sdk::resource::Resource &resource)
{
auto sampler = AlwaysOnSamplerFactory::Create();
auto sampler = sampler_env::GetSamplerFromEnv();
return Create(std::move(processors), resource, std::move(sampler));
}

Expand Down
20 changes: 15 additions & 5 deletions sdk/src/trace/tracer_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "opentelemetry/sdk/trace/id_generator.h"
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#include "opentelemetry/sdk/trace/span_limits.h"
#include "opentelemetry/sdk/trace/tracer.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
Expand Down Expand Up @@ -82,6 +83,10 @@ TracerProvider::TracerProvider(
SpanLimits span_limits)
: noop_tracer_(CreateNoopTracerFallback())
{
if (!sampler)
{
sampler = sampler_env::GetSamplerFromEnv();
}
std::vector<std::unique_ptr<SpanProcessor>> processors;
processors.push_back(std::move(processor));
context_ = std::make_shared<TracerContext>(std::move(processors), resource, std::move(sampler),
Expand All @@ -98,15 +103,20 @@ TracerProvider::TracerProvider(
std::unique_ptr<IdGenerator> id_generator,
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>> tracer_configurator,
SpanLimits span_limits)
: context_(std::make_shared<TracerContext>(std::move(processors),
: noop_tracer_(CreateNoopTracerFallback())
{
if (!sampler)
{
sampler = sampler_env::GetSamplerFromEnv();
}
context_ = std::make_shared<TracerContext>(std::move(processors),
resource,
std::move(sampler),
std::move(id_generator),
std::move(tracer_configurator),
span_limits)),
noop_tracer_(CreateNoopTracerFallback()),
initialized_(true)
{}
span_limits);
initialized_ = true;
}

TracerProvider::~TracerProvider()
{
Expand Down
5 changes: 3 additions & 2 deletions sdk/src/trace/tracer_provider_factory.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "opentelemetry/sdk/trace/processor.h"
#include "opentelemetry/sdk/trace/random_id_generator_factory.h"
#include "opentelemetry/sdk/trace/sampler.h"
#include "opentelemetry/sdk/trace/sampler_env.h"
#include "opentelemetry/sdk/trace/samplers/always_on_factory.h"
#include "opentelemetry/sdk/trace/span_limits.h"
#include "opentelemetry/sdk/trace/tracer_config.h"
Expand All @@ -36,7 +37,7 @@ std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> TracerProviderFactory
std::unique_ptr<SpanProcessor> processor,
const opentelemetry::sdk::resource::Resource &resource)
{
auto sampler = AlwaysOnSamplerFactory::Create();
auto sampler = sampler_env::GetSamplerFromEnv();
return Create(std::move(processor), resource, std::move(sampler));
}

Expand Down Expand Up @@ -100,7 +101,7 @@ std::unique_ptr<opentelemetry::sdk::trace::TracerProvider> TracerProviderFactory
std::vector<std::unique_ptr<SpanProcessor>> &&processors,
const opentelemetry::sdk::resource::Resource &resource)
{
auto sampler = AlwaysOnSamplerFactory::Create();
auto sampler = sampler_env::GetSamplerFromEnv();
return Create(std::move(processors), resource, std::move(sampler));
}

Expand Down
Loading
Loading