Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 12 additions & 0 deletions examples/tracer_configurator/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 "$<TARGET_FILE:example_tracer_configurator>")
endif()
146 changes: 146 additions & 0 deletions examples/tracer_configurator/main.cc
Original file line number Diff line number Diff line change
@@ -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 <initializer_list>
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#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<opentelemetry::trace::Tracer> &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<scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>> EnableAll()
{
return std::make_unique<scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>>(
scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>::Builder(
trace_sdk::TracerConfig::Default())
.Build());
}

// Builds a ScopeConfigurator that enables only the named tracers; all others are disabled.
std::unique_ptr<scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>> EnableOnly(
std::initializer_list<nostd::string_view> names)
{
scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>::Builder builder(
trace_sdk::TracerConfig::Disabled());
for (nostd::string_view name : names)
{
builder.AddConditionNameEquals(name, trace_sdk::TracerConfig::Default());
}
return std::make_unique<scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>>(builder.Build());
}

// Builds a ScopeConfigurator that disables all tracers.
std::unique_ptr<scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>> DisableAll()
{
return std::make_unique<scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>>(
scope_cfg::ScopeConfigurator<trace_sdk::TracerConfig>::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<trace_sdk::TracerProvider>(
std::move(processor), opentelemetry::sdk::resource::Resource::Create({}),
std::make_unique<trace_sdk::AlwaysOnSampler>(),
std::make_unique<trace_sdk::RandomIdGenerator>(), 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;
}
14 changes: 13 additions & 1 deletion sdk/include/opentelemetry/sdk/trace/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<InstrumentationScope> instrumentation_scope_;
std::shared_ptr<TracerContext> context_;
TracerConfig tracer_config_;
opentelemetry::sdk::common::AtomicSharedPtr<const TracerConfig> tracer_config_;
static const std::shared_ptr<opentelemetry::trace::NoopTracer> kNoopTracer;
};
} // namespace trace
Expand Down
9 changes: 9 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/tracer_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<instrumentationscope::ScopeConfigurator<TracerConfig>>
tracer_configurator) noexcept;

/**
* Force all active SpanProcessors to flush any buffered spans
* within the given timeout.
Expand Down
10 changes: 10 additions & 0 deletions sdk/include/opentelemetry/sdk/trace/tracer_provider.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,16 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T
*/
void AddProcessor(std::unique_ptr<SpanProcessor> 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<instrumentationscope::ScopeConfigurator<TracerConfig>>
tracer_configurator) noexcept;

/**
* Obtain the resource associated with this tracer provider.
* @return The resource for this tracer provider.
Expand Down
21 changes: 18 additions & 3 deletions sdk/src/trace/tracer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,11 @@ Tracer::Tracer(std::shared_ptr<TracerContext> context,
std::unique_ptr<InstrumentationScope> 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<const TracerConfig>(
context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_)))
{
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
UpdateEnabled(tracer_config_.IsEnabled());
UpdateEnabled(tracer_config_.load()->IsEnabled());
#endif
}

Expand All @@ -58,7 +59,12 @@ nostd::shared_ptr<opentelemetry::trace::Span> 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())
Comment thread
dbarker marked this conversation as resolved.
Outdated
#endif
{
return kNoopTracer->StartSpan(name, attributes, links, options);
}
Expand Down Expand Up @@ -199,6 +205,15 @@ void Tracer::CloseWithMicroseconds(uint64_t timeout) noexcept
std::chrono::microseconds{static_cast<std::chrono::microseconds::rep>(timeout)});
}
}

void Tracer::UpdateTracerConfig(const TracerConfig &config) noexcept
{
tracer_config_.store(std::make_shared<const TracerConfig>(config));
#if OPENTELEMETRY_ABI_VERSION_NO >= 2
UpdateEnabled(config.IsEnabled());
#endif
}

} // namespace trace
} // namespace sdk
OPENTELEMETRY_END_NAMESPACE
7 changes: 7 additions & 0 deletions sdk/src/trace/tracer_context.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ void TracerContext::AddProcessor(std::unique_ptr<SpanProcessor> processor) noexc
multi_processor->AddProcessor(std::move(processor));
}

void TracerContext::SetTracerConfigurator(
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>>
tracer_configurator) noexcept
{
tracer_configurator_ = std::move(tracer_configurator);
}

SpanProcessor &TracerContext::GetProcessor() const noexcept
{
return *processor_;
Expand Down
20 changes: 20 additions & 0 deletions sdk/src/trace/tracer_provider.cc
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,26 @@ void TracerProvider::AddProcessor(std::unique_ptr<SpanProcessor> processor) noex
context_->AddProcessor(std::move(processor));
}

void TracerProvider::UpdateTracerConfigurator(
std::unique_ptr<instrumentationscope::ScopeConfigurator<TracerConfig>>
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<std::mutex> guard(lock_);
context_->SetTracerConfigurator(std::move(tracer_configurator));
Comment thread
dbarker marked this conversation as resolved.
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();
Expand Down
1 change: 1 addition & 0 deletions sdk/test/trace/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading
Loading