diff --git a/CHANGELOG.md b/CHANGELOG.md index e2d3cb6820..9a3a040202 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) + ## [1.27.0] 2026-05-13 * [RELEASE] Bump main branch to 1.27.0-dev diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6052d2f04d..9006a8002d 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) add_subdirectory(explicit_parent) if(WITH_EXAMPLES_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..b3d4bc5ace 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -5,9 +5,11 @@ #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" @@ -103,11 +105,25 @@ 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_; +#if OPENTELEMETRY_ABI_VERSION_NO < 2 + std::atomic is_enabled_; +#endif 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..d173e34f8b 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -45,10 +45,15 @@ 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 + , + is_enabled_(tracer_config_.load()->IsEnabled()) +#endif { #if OPENTELEMETRY_ABI_VERSION_NO >= 2 - UpdateEnabled(tracer_config_.IsEnabled()); + UpdateEnabled(tracer_config_.load()->IsEnabled()); #endif } @@ -58,7 +63,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 (!is_enabled_.load(std::memory_order_relaxed)) +#endif { return kNoopTracer->StartSpan(name, attributes, links, options); } @@ -199,6 +209,18 @@ 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()); +#else + is_enabled_.store(config.IsEnabled(), std::memory_order_relaxed); +#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..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" @@ -64,6 +65,19 @@ void TracerContext::AddProcessor(std::unique_ptr processor) noexc multi_processor->AddProcessor(std::move(processor)); } +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); +} + 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 +}