From 75137db51acc2acccffbda6ca6671a2a5c4ac1f2 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Thu, 7 May 2026 15:04:16 -0400 Subject: [PATCH 1/7] [SDK] Support dynamic TracerConfigurator updates --- examples/CMakeLists.txt | 1 + examples/tracer_configurator/CMakeLists.txt | 12 ++ examples/tracer_configurator/main.cc | 146 +++++++++++++++ sdk/include/opentelemetry/sdk/trace/tracer.h | 14 +- .../opentelemetry/sdk/trace/tracer_context.h | 9 + .../opentelemetry/sdk/trace/tracer_provider.h | 10 + sdk/src/trace/tracer.cc | 21 ++- sdk/src/trace/tracer_context.cc | 7 + sdk/src/trace/tracer_provider.cc | 20 ++ sdk/test/trace/BUILD | 1 + sdk/test/trace/tracer_provider_test.cc | 175 ++++++++++++++++++ 11 files changed, 412 insertions(+), 4 deletions(-) create mode 100644 examples/tracer_configurator/CMakeLists.txt create mode 100644 examples/tracer_configurator/main.cc diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 3ce9d6859b..21171f12cd 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -28,6 +28,7 @@ add_subdirectory(metrics_simple) add_subdirectory(multithreaded) add_subdirectory(multi_processor) add_subdirectory(environment_carrier) +add_subdirectory(tracer_configurator) if(WITH_EXAMPLES_HTTP) add_subdirectory(http) diff --git a/examples/tracer_configurator/CMakeLists.txt b/examples/tracer_configurator/CMakeLists.txt new file mode 100644 index 0000000000..831fea9a08 --- /dev/null +++ b/examples/tracer_configurator/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright The OpenTelemetry Authors +# SPDX-License-Identifier: Apache-2.0 + +add_executable(example_tracer_configurator main.cc) +target_link_libraries( + example_tracer_configurator PRIVATE opentelemetry-cpp::trace + opentelemetry-cpp::ostream_span_exporter) + +if(BUILD_TESTING) + add_test(NAME examples.tracer_configurator + COMMAND "$") +endif() diff --git a/examples/tracer_configurator/main.cc b/examples/tracer_configurator/main.cc new file mode 100644 index 0000000000..11982c0427 --- /dev/null +++ b/examples/tracer_configurator/main.cc @@ -0,0 +1,146 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +// This example shows how to use TracerProvider::UpdateTracerConfigurator to control which +// tracers are enabled at runtime. Updating the TracerConfigurator affects all existing and future +// tracers provided by the TracerProvider. It is thread safe and can be called concurrently with +// tracer and span creation. +// +// Two instrumentation scopes are shown: +// 1. "my_library" (example instrumented user code), +// 2. "external_library_foo" (example instrumented third-party dependency). +// +// The example simulates a debugging workflow where only the tracer configuration is changed at +// runtime through the provider: +// +// Stage 1 – Start with all tracing disabled (low-overhead production default). +// Stage 2 – An issue is reported. Enable only the user library traces to get an initial signal. +// Stage 3 – The issue involves external libraries too. Enable all traces for full visibility. +// Stage 4 – Investigation complete. Disable all tracing again. + +#include +#include +#include +#include +#include +#include + +#include "opentelemetry/exporters/ostream/span_exporter_factory.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/random_id_generator.h" +#include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/tracer_config.h" +#include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/version.h" + +namespace trace_sdk = opentelemetry::sdk::trace; +namespace trace_exporter = opentelemetry::exporter::trace; +namespace scope_cfg = opentelemetry::sdk::instrumentationscope; +namespace nostd = opentelemetry::nostd; + +namespace +{ +void DoWork(opentelemetry::nostd::shared_ptr &tracer, + nostd::string_view tracer_name, + nostd::string_view operation) +{ + auto span = tracer->StartSpan(operation); + std::cout << (span->IsRecording() ? "[active] " : "[off] ") << tracer_name << " / " + << operation << "\n"; + span->End(); +} + +// Builds a ScopeConfigurator that enables all tracers (the default). +std::unique_ptr> EnableAll() +{ + return std::make_unique>( + scope_cfg::ScopeConfigurator::Builder( + trace_sdk::TracerConfig::Default()) + .Build()); +} + +// Builds a ScopeConfigurator that enables only the named tracers; all others are disabled. +std::unique_ptr> EnableOnly( + std::initializer_list names) +{ + scope_cfg::ScopeConfigurator::Builder builder( + trace_sdk::TracerConfig::Disabled()); + for (nostd::string_view name : names) + { + builder.AddConditionNameEquals(name, trace_sdk::TracerConfig::Default()); + } + return std::make_unique>(builder.Build()); +} + +// Builds a ScopeConfigurator that disables all tracers. +std::unique_ptr> DisableAll() +{ + return std::make_unique>( + scope_cfg::ScopeConfigurator::Builder( + trace_sdk::TracerConfig::Disabled()) + .Build()); +} +} // namespace + +int main() +{ + auto exporter = trace_exporter::OStreamSpanExporterFactory::Create(); + auto processor = trace_sdk::SimpleSpanProcessorFactory::Create(std::move(exporter)); + + // Start with all tracing disabled + auto provider = std::make_shared( + std::move(processor), opentelemetry::sdk::resource::Resource::Create({}), + std::make_unique(), + std::make_unique(), DisableAll()); + + auto my_library_tracer = provider->GetTracer("my_library"); + auto external_library_foo_tracer = provider->GetTracer("external_library_foo"); + + // ------------------------------------------------------------------------- + // Stage 1: all tracing disabled + // ------------------------------------------------------------------------- + std::cout << "=== Stage 1: disable all (production default) ===\n"; + + DoWork(my_library_tracer, "my_library", "DoWork"); // disabled + DoWork(external_library_foo_tracer, "external_library_foo", "FooOperation"); // disabled + + // ------------------------------------------------------------------------- + // Stage 2: enable only user library tracing + // + // EnableOnly() sets Disabled() as the default and adds explicit per-name + // overrides. Existing tracer handles reflect the change immediately + // ------------------------------------------------------------------------- + std::cout << "\n=== Stage 2: enable only 'my_library' ===\n"; + + provider->UpdateTracerConfigurator(EnableOnly({"my_library"})); + + DoWork(my_library_tracer, "my_library", "DoWork"); // enabled + DoWork(external_library_foo_tracer, "external_library_foo", "FooOperation"); // disabled + + // ------------------------------------------------------------------------- + // Stage 3: enable tracing in all libraries + // ------------------------------------------------------------------------- + std::cout << "\n=== Stage 3: enable all ===\n"; + + provider->UpdateTracerConfigurator(EnableAll()); + + DoWork(my_library_tracer, "my_library", "DoWork"); // enabled + DoWork(external_library_foo_tracer, "external_library_foo", "FooOperation"); // enabled + + // ------------------------------------------------------------------------- + // Stage 4: disable all tracing again + // ------------------------------------------------------------------------- + std::cout << "\n=== Stage 4: disable all ===\n"; + + provider->UpdateTracerConfigurator(DisableAll()); + + DoWork(my_library_tracer, "my_library", "DoWork"); // disabled + DoWork(external_library_foo_tracer, "external_library_foo", "FooOperation"); // disabled + + provider->ForceFlush(); + provider->Shutdown(); + return 0; +} diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index 26693e1896..02c794b12f 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -8,6 +8,7 @@ #include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/common/atomic_shared_ptr.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" @@ -103,11 +104,22 @@ class Tracer final : public opentelemetry::trace::Tracer, Sampler &GetSampler() { return context_->GetSampler(); } private: + // TracerProvider needs access to UpdateTracerConfig to propagate configuration updates to + // existing tracers. + friend class TracerProvider; + + /** + * Update this tracer's TracerConfig. Called only by + * TracerProvider::UpdateTracerConfigurator when the provider-level + * TracerConfigurator is replaced at runtime. + */ + void UpdateTracerConfig(const TracerConfig &config) noexcept; + // order of declaration is important here - instrumentation scope should destroy after // tracer-context. std::shared_ptr instrumentation_scope_; std::shared_ptr context_; - TracerConfig tracer_config_; + opentelemetry::sdk::common::AtomicSharedPtr tracer_config_; static const std::shared_ptr kNoopTracer; }; } // namespace trace diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_context.h b/sdk/include/opentelemetry/sdk/trace/tracer_context.h index 81edbb5322..518c1cbdf6 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_context.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_context.h @@ -102,6 +102,15 @@ class TracerContext */ opentelemetry::sdk::trace::IdGenerator &GetIdGenerator() const noexcept; + /** + * Replace the TracerConfigurator for this context. + * + * Note: This method is not thread safe. + * @param tracer_configurator The new configurator. + */ + void SetTracerConfigurator(std::unique_ptr> + tracer_configurator) noexcept; + /** * Force all active SpanProcessors to flush any buffered spans * within the given timeout. diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index 096f64bec6..6e4be011df 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -109,6 +109,16 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T */ void AddProcessor(std::unique_ptr processor) noexcept; + /** + * Update the TracerConfigurator for this provider, recreate and propagate the resulting + * TracerConfig to all existing Tracers while new Tracers will use the updated configuration. + * + * @param tracer_configurator The new configurator. + */ + void UpdateTracerConfigurator( + std::unique_ptr> + tracer_configurator) noexcept; + /** * Obtain the resource associated with this tracer provider. * @return The resource for this tracer provider. diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index b3d5477318..a99c606e0b 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -45,10 +45,11 @@ Tracer::Tracer(std::shared_ptr context, std::unique_ptr instrumentation_scope) noexcept : instrumentation_scope_{std::move(instrumentation_scope)}, context_{std::move(context)}, - tracer_config_(context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_)) + tracer_config_(std::make_shared( + context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_))) { #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - UpdateEnabled(tracer_config_.IsEnabled()); + UpdateEnabled(tracer_config_.load()->IsEnabled()); #endif } @@ -58,7 +59,12 @@ nostd::shared_ptr Tracer::StartSpan( const opentelemetry::trace::SpanContextKeyValueIterable &links, const opentelemetry::trace::StartSpanOptions &options) noexcept { - if (!tracer_config_.IsEnabled()) + // Check if the tracer is enabled using the API Tracer::Enabled() accessor if available. +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + if (!Enabled()) +#else + if (!tracer_config_.load()->IsEnabled()) +#endif { return kNoopTracer->StartSpan(name, attributes, links, options); } @@ -199,6 +205,15 @@ void Tracer::CloseWithMicroseconds(uint64_t timeout) noexcept std::chrono::microseconds{static_cast(timeout)}); } } + +void Tracer::UpdateTracerConfig(const TracerConfig &config) noexcept +{ + tracer_config_.store(std::make_shared(config)); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + UpdateEnabled(config.IsEnabled()); +#endif +} + } // namespace trace } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/tracer_context.cc b/sdk/src/trace/tracer_context.cc index c3cbfb9d76..9f87b96c87 100644 --- a/sdk/src/trace/tracer_context.cc +++ b/sdk/src/trace/tracer_context.cc @@ -64,6 +64,13 @@ void TracerContext::AddProcessor(std::unique_ptr processor) noexc multi_processor->AddProcessor(std::move(processor)); } +void TracerContext::SetTracerConfigurator( + std::unique_ptr> + tracer_configurator) noexcept +{ + tracer_configurator_ = std::move(tracer_configurator); +} + SpanProcessor &TracerContext::GetProcessor() const noexcept { return *processor_; diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index e6837985c2..90b6a7c92e 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -129,6 +129,26 @@ void TracerProvider::AddProcessor(std::unique_ptr processor) noex context_->AddProcessor(std::move(processor)); } +void TracerProvider::UpdateTracerConfigurator( + std::unique_ptr> + tracer_configurator) noexcept +{ + // The only way to set the TracerConfig of a tracer is on Tracer construction in + // TracerProvider::GetTracer or through Tracer::UpdateTracerConfig (which is private and only + // accessed by TracerProvider). + // + // Lock the provider mutex while updating the TracerConfiguartor in the context and setting the + // new TracerConfig of all existing tracers. + const std::lock_guard guard(lock_); + context_->SetTracerConfigurator(std::move(tracer_configurator)); + for (auto &tracer : tracers_) + { + auto new_config = + context_->GetTracerConfigurator().ComputeConfig(tracer->GetInstrumentationScope()); + tracer->UpdateTracerConfig(new_config); + } +} + const resource::Resource &TracerProvider::GetResource() const noexcept { return context_->GetResource(); diff --git a/sdk/test/trace/BUILD b/sdk/test/trace/BUILD index 06ca595858..2de6d3c733 100644 --- a/sdk/test/trace/BUILD +++ b/sdk/test/trace/BUILD @@ -14,6 +14,7 @@ cc_test( "trace", ], deps = [ + "//exporters/memory:in_memory_span_exporter", "//sdk/src/resource", "//sdk/src/trace", "@com_google_googletest//:gtest_main", diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index a3434afdd1..a63ea92cf7 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -6,10 +6,16 @@ #include #include +#include +#include +#include + #include "opentelemetry/common/macros.h" +#include "opentelemetry/exporters/memory/in_memory_span_exporter.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/exporter.h" #include "opentelemetry/sdk/trace/id_generator.h" @@ -17,9 +23,11 @@ #include "opentelemetry/sdk/trace/random_id_generator.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/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/simple_processor_factory.h" #include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/sdk/trace/tracer_provider.h" #include "opentelemetry/sdk/trace/tracer_provider_factory.h" @@ -37,6 +45,7 @@ using namespace opentelemetry::sdk::trace; using namespace opentelemetry::sdk::resource; +using opentelemetry::sdk::instrumentationscope::ScopeConfigurator; TEST(TracerProvider, GetTracer) { @@ -341,3 +350,169 @@ TEST(TracerProvider, ForceFlush) EXPECT_TRUE(tp1.ForceFlush()); } + +TEST(TracerProvider, UpdateTracerConfiguratorDisableByName) +{ + auto processor = SimpleSpanProcessorFactory::Create( + std::make_unique()); + + // Start with all tracers enabled (the default configurator enables everything). + TracerProvider provider(std::move(processor)); + + auto tracer_disabled_by_update = provider.GetTracer("scope.disabled"); + auto tracer_unaffected = provider.GetTracer("scope.unaffected"); + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + ASSERT_TRUE(tracer_disabled_by_update->Enabled()); + ASSERT_TRUE(tracer_unaffected->Enabled()); +#endif + ASSERT_TRUE(tracer_disabled_by_update->StartSpan("op")->IsRecording()); + ASSERT_TRUE(tracer_unaffected->StartSpan("op")->IsRecording()); + + // Disable "scope.disabled" by name; "scope.unaffected" must remain enabled. + auto configurator_with_one_scope_disabled = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()) + .AddConditionNameEquals("scope.disabled", TracerConfig::Disabled()) + .Build()); + + provider.UpdateTracerConfigurator(std::move(configurator_with_one_scope_disabled)); + +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + EXPECT_FALSE(tracer_disabled_by_update->Enabled()); + EXPECT_TRUE(tracer_unaffected->Enabled()); +#endif + EXPECT_FALSE(tracer_disabled_by_update->StartSpan("op")->IsRecording()); + EXPECT_TRUE(tracer_unaffected->StartSpan("op")->IsRecording()); +} + +TEST(TracerProvider, UpdateTracerConfiguratorReEnable) +{ + auto processor = SimpleSpanProcessorFactory::Create( + std::make_unique()); + + // Start with all tracers disabled via the initial configurator. + auto all_disabled_configurator = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Disabled()).Build()); + TracerProvider provider( + std::move(processor), Resource::Create({}), std::make_unique(), + std::make_unique(), std::move(all_disabled_configurator)); + + auto existing_tracer = provider.GetTracer("scope.existing"); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + ASSERT_FALSE(existing_tracer->Enabled()); +#endif + ASSERT_FALSE(existing_tracer->StartSpan("op")->IsRecording()); + + // Re-enable all tracers by installing a default (all-enabled) configurator. + auto all_enabled_configurator = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()).Build()); + provider.UpdateTracerConfigurator(std::move(all_enabled_configurator)); + + // The existing tracer handle must immediately reflect the updated config. +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + EXPECT_TRUE(existing_tracer->Enabled()); +#endif + EXPECT_TRUE(existing_tracer->StartSpan("op")->IsRecording()); + + // Tracers obtained after the update also reflect the new configurator. + auto tracer_obtained_after_update = provider.GetTracer("scope.new"); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + EXPECT_TRUE(tracer_obtained_after_update->Enabled()); +#endif + EXPECT_TRUE(tracer_obtained_after_update->StartSpan("op")->IsRecording()); +} + +TEST(TracerProvider, UpdateTracerConfiguratorNewTracerUsesUpdatedConfig) +{ + auto processor = SimpleSpanProcessorFactory::Create( + std::make_unique()); + TracerProvider provider(std::move(processor)); + + // Install a configurator that disables "scope.disabled" before any tracer + // for that scope has been obtained. + auto configurator_with_one_scope_disabled = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()) + .AddConditionNameEquals("scope.disabled", TracerConfig::Disabled()) + .Build()); + provider.UpdateTracerConfigurator(std::move(configurator_with_one_scope_disabled)); + + // A tracer obtained after the update must already reflect the new config. + auto tracer_for_disabled_scope = provider.GetTracer("scope.disabled"); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + EXPECT_FALSE(tracer_for_disabled_scope->Enabled()); +#endif + EXPECT_FALSE(tracer_for_disabled_scope->StartSpan("op")->IsRecording()); + + // Scopes not named in the disable by name configurator remain enabled. + auto tracer_for_unaffected_scope = provider.GetTracer("scope.unaffected"); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + EXPECT_TRUE(tracer_for_unaffected_scope->Enabled()); +#endif + EXPECT_TRUE(tracer_for_unaffected_scope->StartSpan("op")->IsRecording()); +} + +TEST(TracerProvider, UpdateTracerConfiguratorConcurrentStartSpan) +{ + auto processor = SimpleSpanProcessorFactory::Create( + std::make_unique()); + TracerProvider provider(std::move(processor)); + + auto tracer = provider.GetTracer("scope.concurrent"); + + std::atomic stop{false}; + std::atomic worker_saw_disabled{false}; + std::atomic worker_saw_enabled{false}; + + std::promise worker_ready; + std::future worker_ready_future = worker_ready.get_future(); + + // Worker: call StartSpan in a tight loop and flag each observed state. + std::thread worker([&] { + worker_ready.set_value(); + while (!stop.load(std::memory_order_relaxed)) + { + auto span = tracer->StartSpan("op"); + if (span->IsRecording()) + { + worker_saw_enabled.store(true, std::memory_order_relaxed); + } + else + { + worker_saw_disabled.store(true, std::memory_order_relaxed); + } + span->End(); + } + }); + + worker_ready_future.wait(); + + // Disable all, then wait for the worker to actually observe a disabled span. + // This synchronisation guarantees the worker ran while the tracer was disabled. + auto disable_all = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Disabled()).Build()); + provider.UpdateTracerConfigurator(std::move(disable_all)); + while (!worker_saw_disabled.load(std::memory_order_relaxed)) + { + std::this_thread::yield(); + } + + // Re-enable all, then wait for the worker to observe an enabled span. + auto enable_all = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()).Build()); + provider.UpdateTracerConfigurator(std::move(enable_all)); + while (!worker_saw_enabled.load(std::memory_order_relaxed)) + { + std::this_thread::yield(); + } + + stop.store(true, std::memory_order_relaxed); + worker.join(); + + EXPECT_TRUE(worker_saw_disabled.load()); + EXPECT_TRUE(worker_saw_enabled.load()); + + EXPECT_TRUE(tracer->StartSpan("op")->IsRecording()); +#if OPENTELEMETRY_ABI_VERSION_NO >= 2 + EXPECT_TRUE(tracer->Enabled()); +#endif +} From 6ee3f6902b316cc89c6f5f94193f4cb4d1e99b91 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Tue, 12 May 2026 22:39:26 -0400 Subject: [PATCH 2/7] address review feedback --- sdk/include/opentelemetry/sdk/trace/tracer.h | 4 ++++ sdk/src/trace/tracer.cc | 9 ++++++++- sdk/src/trace/tracer_context.cc | 7 +++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index 02c794b12f..b3d4bc5ace 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -5,6 +5,7 @@ #include +#include #include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" @@ -120,6 +121,9 @@ class Tracer final : public opentelemetry::trace::Tracer, std::shared_ptr instrumentation_scope_; std::shared_ptr context_; opentelemetry::sdk::common::AtomicSharedPtr tracer_config_; +#if OPENTELEMETRY_ABI_VERSION_NO < 2 + std::atomic is_enabled_; +#endif static const std::shared_ptr kNoopTracer; }; } // namespace trace diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index a99c606e0b..d173e34f8b 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -47,6 +47,10 @@ Tracer::Tracer(std::shared_ptr context, context_{std::move(context)}, tracer_config_(std::make_shared( context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_))) +#if OPENTELEMETRY_ABI_VERSION_NO < 2 + , + is_enabled_(tracer_config_.load()->IsEnabled()) +#endif { #if OPENTELEMETRY_ABI_VERSION_NO >= 2 UpdateEnabled(tracer_config_.load()->IsEnabled()); @@ -63,7 +67,7 @@ nostd::shared_ptr Tracer::StartSpan( #if OPENTELEMETRY_ABI_VERSION_NO >= 2 if (!Enabled()) #else - if (!tracer_config_.load()->IsEnabled()) + if (!is_enabled_.load(std::memory_order_relaxed)) #endif { return kNoopTracer->StartSpan(name, attributes, links, options); @@ -209,8 +213,11 @@ void Tracer::CloseWithMicroseconds(uint64_t timeout) noexcept void Tracer::UpdateTracerConfig(const TracerConfig &config) noexcept { tracer_config_.store(std::make_shared(config)); + #if OPENTELEMETRY_ABI_VERSION_NO >= 2 UpdateEnabled(config.IsEnabled()); +#else + is_enabled_.store(config.IsEnabled(), std::memory_order_relaxed); #endif } diff --git a/sdk/src/trace/tracer_context.cc b/sdk/src/trace/tracer_context.cc index 9f87b96c87..adb3cfe538 100644 --- a/sdk/src/trace/tracer_context.cc +++ b/sdk/src/trace/tracer_context.cc @@ -6,6 +6,7 @@ #include #include +#include "opentelemetry/sdk/common/global_log_handler.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" @@ -68,6 +69,12 @@ void TracerContext::SetTracerConfigurator( std::unique_ptr> tracer_configurator) noexcept { + if (!tracer_configurator) + { + OTEL_INTERNAL_LOG_ERROR( + "[TracerContext::SetTracerConfigurator] tracer_configurator must not be null, ignoring."); + return; + } tracer_configurator_ = std::move(tracer_configurator); } From 5e79961526d9c995e6a2f5f891a292415b7ee06a Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Tue, 12 May 2026 22:45:49 -0400 Subject: [PATCH 3/7] add changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f5dd627e9..6915aa0aeb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [SDK] Add `TracerProvider::UpdateTracerConfigurator()` and example + [#4065](https://github.com/open-telemetry/opentelemetry-cpp/issues/4065) + * [API] Fix `Logger::Enabled()` [#2667](https://github.com/open-telemetry/opentelemetry-cpp/issues/2667) From ca61658d376fb9c3db90bfecff0817400ddf96cc Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Tue, 26 May 2026 22:41:50 -0400 Subject: [PATCH 4/7] address review feedback to reduce tracer provider lock scope. use mutex and value for tracer config objects --- sdk/include/opentelemetry/sdk/trace/tracer.h | 7 ++++--- sdk/src/trace/tracer.cc | 20 ++++++++++++-------- sdk/src/trace/tracer_provider.cc | 20 ++++++++++++-------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index b3d4bc5ace..eca85cd904 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -6,10 +6,10 @@ #include #include +#include #include "opentelemetry/common/key_value_iterable.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" -#include "opentelemetry/sdk/common/atomic_shared_ptr.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" @@ -114,13 +114,14 @@ class Tracer final : public opentelemetry::trace::Tracer, * TracerProvider::UpdateTracerConfigurator when the provider-level * TracerConfigurator is replaced at runtime. */ - void UpdateTracerConfig(const TracerConfig &config) noexcept; + void UpdateTracerConfig(TracerConfig config) noexcept; // order of declaration is important here - instrumentation scope should destroy after // tracer-context. std::shared_ptr instrumentation_scope_; std::shared_ptr context_; - opentelemetry::sdk::common::AtomicSharedPtr tracer_config_; + mutable std::mutex tracer_config_mutex_; + TracerConfig tracer_config_; #if OPENTELEMETRY_ABI_VERSION_NO < 2 std::atomic is_enabled_; #endif diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 0482d25a3f..df37d19c30 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -43,15 +44,14 @@ Tracer::Tracer(std::shared_ptr context, std::unique_ptr instrumentation_scope) noexcept : instrumentation_scope_{std::move(instrumentation_scope)}, context_{std::move(context)}, - tracer_config_(std::make_shared( - context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_))) + tracer_config_(context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_)) #if OPENTELEMETRY_ABI_VERSION_NO < 2 , - is_enabled_(tracer_config_.load()->IsEnabled()) + is_enabled_(tracer_config_.IsEnabled()) #endif { #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - UpdateEnabled(tracer_config_.load()->IsEnabled()); + UpdateEnabled(tracer_config_.IsEnabled()); #endif } @@ -208,14 +208,18 @@ void Tracer::CloseWithMicroseconds(uint64_t timeout) noexcept } } -void Tracer::UpdateTracerConfig(const TracerConfig &config) noexcept +void Tracer::UpdateTracerConfig(TracerConfig config) noexcept { - tracer_config_.store(std::make_shared(config)); + const bool enabled = config.IsEnabled(); + { + std::lock_guard lock(tracer_config_mutex_); + tracer_config_ = std::move(config); + } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - UpdateEnabled(config.IsEnabled()); + UpdateEnabled(enabled); #else - is_enabled_.store(config.IsEnabled(), std::memory_order_relaxed); + is_enabled_.store(enabled, std::memory_order_relaxed); #endif } diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index ebd666925d..3086d5e0d5 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -132,19 +132,23 @@ void TracerProvider::UpdateTracerConfigurator( std::unique_ptr> tracer_configurator) noexcept { + // Lock the provider mutex while updating the TracerConfiguartor in the context and capture the + // current tracers + std::vector> current_tracers; + { + const std::lock_guard guard(lock_); + context_->SetTracerConfigurator(std::move(tracer_configurator)); + current_tracers = tracers_; + } + // The only way to set the TracerConfig of a tracer is on Tracer construction in // TracerProvider::GetTracer or through Tracer::UpdateTracerConfig (which is private and only // accessed by TracerProvider). - // - // Lock the provider mutex while updating the TracerConfiguartor in the context and setting the - // new TracerConfig of all existing tracers. - const std::lock_guard guard(lock_); - context_->SetTracerConfigurator(std::move(tracer_configurator)); - for (auto &tracer : tracers_) + for (auto &tracer : current_tracers) { - auto new_config = + TracerConfig new_config = context_->GetTracerConfigurator().ComputeConfig(tracer->GetInstrumentationScope()); - tracer->UpdateTracerConfig(new_config); + tracer->UpdateTracerConfig(std::move(new_config)); } } From 219efc622242a4fed7aba058e5fd400ebc233807 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Tue, 26 May 2026 23:20:20 -0400 Subject: [PATCH 5/7] fix iwyu and clang-tidy warnings --- examples/tracer_configurator/main.cc | 7 +++++-- sdk/src/trace/tracer.cc | 2 +- sdk/src/trace/tracer_provider.cc | 2 +- sdk/test/trace/tracer_provider_test.cc | 1 + 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/tracer_configurator/main.cc b/examples/tracer_configurator/main.cc index 11982c0427..859fefad81 100644 --- a/examples/tracer_configurator/main.cc +++ b/examples/tracer_configurator/main.cc @@ -20,21 +20,24 @@ #include #include -#include #include #include #include #include "opentelemetry/exporters/ostream/span_exporter_factory.h" +#include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" +#include "opentelemetry/sdk/trace/exporter.h" +#include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/random_id_generator.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" #include "opentelemetry/sdk/trace/simple_processor_factory.h" #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_provider.h" -#include "opentelemetry/version.h" +#include "opentelemetry/trace/span.h" +#include "opentelemetry/trace/tracer.h" namespace trace_sdk = opentelemetry::sdk::trace; namespace trace_exporter = opentelemetry::exporter::trace; diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index df37d19c30..bea2397c10 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -213,7 +213,7 @@ void Tracer::UpdateTracerConfig(TracerConfig config) noexcept const bool enabled = config.IsEnabled(); { std::lock_guard lock(tracer_config_mutex_); - tracer_config_ = std::move(config); + tracer_config_ = config; } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 3086d5e0d5..dead7bb99d 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -148,7 +148,7 @@ void TracerProvider::UpdateTracerConfigurator( { TracerConfig new_config = context_->GetTracerConfigurator().ComputeConfig(tracer->GetInstrumentationScope()); - tracer->UpdateTracerConfig(std::move(new_config)); + tracer->UpdateTracerConfig(new_config); } } diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index e95257ea51..687f63d07f 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -31,6 +31,7 @@ #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/sdk/trace/tracer_provider.h" #include "opentelemetry/sdk/trace/tracer_provider_factory.h" +#include "opentelemetry/trace/span.h" #include "opentelemetry/trace/tracer.h" #if OPENTELEMETRY_ABI_VERSION_NO >= 2 From b22d0b24ea2e4263e1445b987d77bb4fa039322e Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Wed, 27 May 2026 21:07:46 +0000 Subject: [PATCH 6/7] fix iwyu warning --- sdk/src/trace/tracer.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index bea2397c10..da69315d77 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include #include #include From 9b3d78b579292fe281f8e0de5e93987824f39220 Mon Sep 17 00:00:00 2001 From: Douglas Barker Date: Thu, 28 May 2026 19:37:46 +0000 Subject: [PATCH 7/7] fix iwyu warning --- sdk/src/trace/tracer.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index da69315d77..a7636b037c 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #include -#include +#if OPENTELEMETRY_ABI_VERSION_NO < 2 +# include +#endif #include #include #include