diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff1b44ed2..3a742f353c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,9 @@ Increment the: ## [Unreleased] +* [SDK] Add tracer scope configurator + [#3137](https://github.com/open-telemetry/opentelemetry-cpp/pull/3137) + ## [1.19 2025-01-22] * [PROMETHEUS_EXPORTER] Fix default for emitting otel_scope attributes diff --git a/sdk/include/opentelemetry/sdk/instrumentationscope/scope_configurator.h b/sdk/include/opentelemetry/sdk/instrumentationscope/scope_configurator.h new file mode 100644 index 0000000000..f2d046f53a --- /dev/null +++ b/sdk/include/opentelemetry/sdk/instrumentationscope/scope_configurator.h @@ -0,0 +1,145 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once +#include + +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace instrumentationscope +{ +/** + * A scope configurator is a function that returns the scope config for a given instrumentation + * scope. + */ +template +class ScopeConfigurator +{ +public: + /** + * A builder class for the ScopeConfigurator that facilitates the creation of ScopeConfigurators. + */ + class Builder + { + public: + /** + * Constructor for a builder object that cam be used to create a scope configurator. A minimally + * configured builder would build a ScopeConfigurator that applies the default_scope_config to + * every instrumentation scope. + * @param default_scope_config The default scope config that the built configurator should fall + * back on. + */ + explicit Builder(T default_scope_config) noexcept : default_scope_config_(default_scope_config) + {} + + /** + * Allows the user to pass a generic function that evaluates an instrumentation scope through a + * boolean check. If the check passes, the provided config is applied. Conditions are evaluated + * in order. + * @param scope_matcher a function that returns true if the scope being evaluated matches the + * criteria defined by the function. + * @param scope_config the scope configuration to return for the matched scope. + * @return this + */ + Builder &AddCondition(std::function scope_matcher, + T scope_config) + { + conditions_.emplace_back(scope_matcher, scope_config); + return *this; + } + + /** + * A convenience condition that specifically matches the scope name of the scope being + * evaluated. If the scope name matches to the provided string, then the provided scope + * configuration is applied to the scope. + * @param scope_name The scope name to which the config needs to be applied. + * @param scope_config The scope config for the matching scopes. + * @return this + */ + Builder &AddConditionNameEquals(nostd::string_view scope_name, T scope_config) + { + std::function name_equals_matcher = + [scope_name = std::string(scope_name)](const InstrumentationScope &scope_info) { + return scope_info.GetName() == scope_name; + }; + conditions_.emplace_back(name_equals_matcher, scope_config); + return *this; + } + + /** + * Constructs the scope configurator object that can be used to retrieve scope config depending + * on the instrumentation scope. + * @return a configured scope configurator. + */ + ScopeConfigurator Build() const + { + if (conditions_.size() == 0) + { + return ScopeConfigurator( + [default_scope_config_ = this->default_scope_config_](const InstrumentationScope &) { + return default_scope_config_; + }); + } + + // Return a configurator that processes all the conditions + return ScopeConfigurator( + [conditions_ = this->conditions_, default_scope_config_ = this->default_scope_config_]( + const InstrumentationScope &scope_info) { + for (Condition condition : conditions_) + { + if (condition.scope_matcher(scope_info)) + { + return condition.scope_config; + } + } + return default_scope_config_; + }); + } + + private: + /** + * An internal struct to encapsulate 'conditions' that can be applied to a + * ScopeConfiguratorBuilder. The applied conditions influence the behavior of the generated + * ScopeConfigurator. + */ + struct Condition + { + std::function scope_matcher; + T scope_config; + + Condition(const std::function &matcher, const T &config) + : scope_matcher(matcher), scope_config(config) + {} + }; + + T default_scope_config_; + std::vector conditions_; + }; + + // Public methods for ScopeConfigurator + + /** + * Invokes the underlying configurator function to get a valid scope configuration. + * @param scope_info The InstrumentationScope containing scope information for which configuration + * needs to be retrieved. + */ + T ComputeConfig(const InstrumentationScope &scope_info) const + { + return this->configurator_(scope_info); + } + +private: + // Prevent direct initialization of ScopeConfigurator objects. + explicit ScopeConfigurator(std::function configurator) + : configurator_(configurator) + {} + + std::function configurator_; +}; +} // namespace instrumentationscope +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index f738203b4f..26693e1896 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -13,7 +13,9 @@ #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" +#include "opentelemetry/trace/noop.h" #include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span_context_kv_iterable.h" #include "opentelemetry/trace/span_startoptions.h" @@ -105,6 +107,8 @@ class Tracer final : public opentelemetry::trace::Tracer, // tracer-context. std::shared_ptr instrumentation_scope_; std::shared_ptr context_; + TracerConfig tracer_config_; + static const std::shared_ptr kNoopTracer; }; } // namespace trace } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_config.h b/sdk/include/opentelemetry/sdk/trace/tracer_config.h new file mode 100644 index 0000000000..708d05b5e8 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/trace/tracer_config.h @@ -0,0 +1,59 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ +/** + * TracerConfig defines various configurable aspects of a Tracer's behavior. + * This class should not be used directly to configure a Tracer's behavior, instead a + * ScopeConfigurator should be used to compute the desired TracerConfig which can then be used to + * configure a Tracer. + */ +class TracerConfig +{ +public: + bool operator==(const TracerConfig &other) const noexcept; + + /** + * Returns if the Tracer is enabled or disabled. Tracers are enabled by default. + * @return a boolean indicating if the Tracer is enabled. Defaults to true. + */ + bool IsEnabled() const noexcept; + + /** + * Returns a TracerConfig that represents a disabled Tracer. A disabled tracer behaves like a + * no-op tracer. + * @return a static constant TracerConfig that represents a disabled tracer. + */ + static TracerConfig Disabled(); + + /** + * Returns a TracerConfig that represents an enabled Tracer. + * @return a static constant TracerConfig that represents an enabled tracer. + */ + static TracerConfig Enabled(); + + /** + * Returns a TracerConfig that represents a Tracer configured with the default behavior. + * The default behavior is guided by the OpenTelemetry specification. + * @return a static constant TracerConfig that represents a tracer configured with default + * behavior. + */ + static TracerConfig Default(); + +private: + explicit TracerConfig(const bool disabled = false) : disabled_(disabled) {} + bool disabled_; + static const TracerConfig kDefaultConfig; + static const TracerConfig kDisabledConfig; +}; +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_context.h b/sdk/include/opentelemetry/sdk/trace/tracer_context.h index e1b2cb25d2..90beb9a2ca 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_context.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_context.h @@ -7,12 +7,14 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/random_id_generator.h" #include "opentelemetry/sdk/trace/sampler.h" #include "opentelemetry/sdk/trace/samplers/always_on.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -43,7 +45,12 @@ class TracerContext opentelemetry::sdk::resource::Resource::Create({}), std::unique_ptr sampler = std::unique_ptr(new AlwaysOnSampler), std::unique_ptr id_generator = - std::unique_ptr(new RandomIdGenerator())) noexcept; + std::unique_ptr(new RandomIdGenerator()), + std::unique_ptr> tracer_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder( + TracerConfig::Default()) + .Build())) noexcept; virtual ~TracerContext() = default; @@ -77,6 +84,13 @@ class TracerContext */ const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + /** + * Obtain the ScopeConfigurator with this tracer context. + * @return The ScopeConfigurator for this tracer context. + */ + const instrumentationscope::ScopeConfigurator &GetTracerConfigurator() + const noexcept; + /** * Obtain the Id Generator associated with this tracer context. * @return The ID Generator for this tracer context. @@ -100,6 +114,7 @@ class TracerContext std::unique_ptr sampler_; std::unique_ptr id_generator_; std::unique_ptr processor_; + std::unique_ptr> tracer_configurator_; }; } // namespace trace diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_context_factory.h b/sdk/include/opentelemetry/sdk/trace/tracer_context_factory.h index 4954fd7c3f..0279bee93a 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_context_factory.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_context_factory.h @@ -54,6 +54,16 @@ class OPENTELEMETRY_EXPORT TracerContextFactory const opentelemetry::sdk::resource::Resource &resource, std::unique_ptr sampler, std::unique_ptr id_generator); + + /** + * Create a TracerContext. + */ + static std::unique_ptr Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator); }; } // namespace trace diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index 250a948a96..97b7ab7d30 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -39,6 +39,8 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T * not be a nullptr. * @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 + * Tracers provided by this TracerProvider. */ explicit TracerProvider( std::unique_ptr processor, @@ -46,7 +48,12 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T opentelemetry::sdk::resource::Resource::Create({}), std::unique_ptr sampler = std::unique_ptr(new AlwaysOnSampler), std::unique_ptr id_generator = - std::unique_ptr(new RandomIdGenerator())) noexcept; + std::unique_ptr(new RandomIdGenerator()), + std::unique_ptr> tracer_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder( + TracerConfig::Default()) + .Build())) noexcept; explicit TracerProvider( std::vector> &&processors, @@ -54,7 +61,12 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T opentelemetry::sdk::resource::Resource::Create({}), std::unique_ptr sampler = std::unique_ptr(new AlwaysOnSampler), std::unique_ptr id_generator = - std::unique_ptr(new RandomIdGenerator())) noexcept; + std::unique_ptr(new RandomIdGenerator()), + std::unique_ptr> tracer_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder( + TracerConfig::Default()) + .Build())) noexcept; /** * Initialize a new tracer provider with a specified context diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h index ed451a8c99..071a0551ca 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider_factory.h @@ -28,7 +28,7 @@ namespace trace class OPENTELEMETRY_EXPORT TracerProviderFactory { public: - /* Serie of builders with a single processor. */ + /* Series of creator methods with a single processor. */ static std::unique_ptr Create( std::unique_ptr processor); @@ -48,7 +48,14 @@ class OPENTELEMETRY_EXPORT TracerProviderFactory std::unique_ptr sampler, std::unique_ptr id_generator); - /* Serie of builders with a vector of processor. */ + static std::unique_ptr Create( + std::unique_ptr processor, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator); + + /* Series of creator methods with a vector of processors. */ static std::unique_ptr Create( std::vector> &&processors); @@ -68,6 +75,13 @@ class OPENTELEMETRY_EXPORT TracerProviderFactory std::unique_ptr sampler, std::unique_ptr id_generator); + static std::unique_ptr Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator); + /* Create with a tracer context. */ static std::unique_ptr Create( diff --git a/sdk/src/trace/CMakeLists.txt b/sdk/src/trace/CMakeLists.txt index 939a6ed2bc..b99c2ca42c 100644 --- a/sdk/src/trace/CMakeLists.txt +++ b/sdk/src/trace/CMakeLists.txt @@ -20,7 +20,8 @@ add_library( samplers/trace_id_ratio.cc samplers/trace_id_ratio_factory.cc random_id_generator.cc - random_id_generator_factory.cc) + random_id_generator_factory.cc + tracer_config.cc) set_target_properties(opentelemetry_trace PROPERTIES EXPORT_NAME trace) set_target_version(opentelemetry_trace) diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 73fc08d5dc..c166885600 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -13,9 +13,11 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/variant.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/sampler.h" #include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/trace/context.h" #include "opentelemetry/trace/noop.h" @@ -36,10 +38,14 @@ namespace sdk { namespace trace { +const std::shared_ptr Tracer::kNoopTracer = + std::make_shared(); Tracer::Tracer(std::shared_ptr context, std::unique_ptr instrumentation_scope) noexcept - : instrumentation_scope_{std::move(instrumentation_scope)}, context_{std::move(context)} + : instrumentation_scope_{std::move(instrumentation_scope)}, + context_{std::move(context)}, + tracer_config_(context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_)) {} nostd::shared_ptr Tracer::StartSpan( @@ -48,6 +54,10 @@ nostd::shared_ptr Tracer::StartSpan( const opentelemetry::trace::SpanContextKeyValueIterable &links, const opentelemetry::trace::StartSpanOptions &options) noexcept { + if (!tracer_config_.IsEnabled()) + { + return kNoopTracer->StartSpan(name, attributes, links, options); + } opentelemetry::trace::SpanContext parent_context = GetCurrentSpan()->GetContext(); if (nostd::holds_alternative(options.parent)) { @@ -165,7 +175,7 @@ void Tracer::ForceFlushWithMicroseconds(uint64_t timeout) noexcept void Tracer::CloseWithMicroseconds(uint64_t timeout) noexcept { // Trace context is shared by many tracers.So we just call ForceFlush to flush all pending spans - // and do not shutdown it. + // and do not shutdown it. if (context_) { context_->ForceFlush( diff --git a/sdk/src/trace/tracer_config.cc b/sdk/src/trace/tracer_config.cc new file mode 100644 index 0000000000..8c9dd93652 --- /dev/null +++ b/sdk/src/trace/tracer_config.cc @@ -0,0 +1,42 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer_config.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace trace +{ + +const TracerConfig TracerConfig::kDefaultConfig = TracerConfig(); +const TracerConfig TracerConfig::kDisabledConfig = TracerConfig(true); + +TracerConfig TracerConfig::Disabled() +{ + return kDisabledConfig; +} + +TracerConfig TracerConfig::Enabled() +{ + return kDefaultConfig; +} + +TracerConfig TracerConfig::Default() +{ + return kDefaultConfig; +} + +bool TracerConfig::IsEnabled() const noexcept +{ + return !disabled_; +} + +bool TracerConfig::operator==(const TracerConfig &other) const noexcept +{ + return disabled_ == other.disabled_; +} + +} // namespace trace +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/tracer_context.cc b/sdk/src/trace/tracer_context.cc index 65300d6f22..c3cbfb9d76 100644 --- a/sdk/src/trace/tracer_context.cc +++ b/sdk/src/trace/tracer_context.cc @@ -6,11 +6,13 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" #include "opentelemetry/sdk/trace/multi_span_processor.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/version.h" @@ -24,11 +26,14 @@ namespace resource = opentelemetry::sdk::resource; TracerContext::TracerContext(std::vector> &&processors, const resource::Resource &resource, std::unique_ptr sampler, - std::unique_ptr id_generator) noexcept + std::unique_ptr id_generator, + std::unique_ptr> + tracer_configurator) noexcept : resource_(resource), sampler_(std::move(sampler)), id_generator_(std::move(id_generator)), - processor_(std::unique_ptr(new MultiSpanProcessor(std::move(processors)))) + processor_(std::unique_ptr(new MultiSpanProcessor(std::move(processors)))), + tracer_configurator_(std::move(tracer_configurator)) {} Sampler &TracerContext::GetSampler() const noexcept @@ -41,6 +46,12 @@ const resource::Resource &TracerContext::GetResource() const noexcept return resource_; } +const instrumentationscope::ScopeConfigurator &TracerContext::GetTracerConfigurator() + const noexcept +{ + return *tracer_configurator_; +} + opentelemetry::sdk::trace::IdGenerator &TracerContext::GetIdGenerator() const noexcept { return *id_generator_; diff --git a/sdk/src/trace/tracer_context_factory.cc b/sdk/src/trace/tracer_context_factory.cc index 68ea39349e..9472dc041f 100644 --- a/sdk/src/trace/tracer_context_factory.cc +++ b/sdk/src/trace/tracer_context_factory.cc @@ -5,12 +5,14 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" #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/samplers/always_on_factory.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/sdk/trace/tracer_context_factory.h" #include "opentelemetry/version.h" @@ -51,8 +53,24 @@ std::unique_ptr TracerContextFactory::Create( std::unique_ptr sampler, std::unique_ptr id_generator) { - std::unique_ptr context(new TracerContext( - std::move(processors), resource, std::move(sampler), std::move(id_generator))); + auto tracer_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(TracerConfig::Default()) + .Build()); + return Create(std::move(processors), resource, std::move(sampler), std::move(id_generator), + std::move(tracer_configurator)); +} + +std::unique_ptr TracerContextFactory::Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator) +{ + std::unique_ptr context( + new TracerContext(std::move(processors), resource, std::move(sampler), + std::move(id_generator), std::move(tracer_configurator))); return context; } diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index ccf277f12f..5d9540662c 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -12,11 +12,13 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/global_log_handler.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/id_generator.h" #include "opentelemetry/sdk/trace/processor.h" #include "opentelemetry/sdk/trace/sampler.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/trace/tracer.h" @@ -36,31 +38,39 @@ TracerProvider::TracerProvider(std::unique_ptr context) noexcept OTEL_INTERNAL_LOG_DEBUG("[TracerProvider] TracerProvider created."); } -TracerProvider::TracerProvider(std::unique_ptr processor, - const resource::Resource &resource, - std::unique_ptr sampler, - std::unique_ptr id_generator) noexcept +TracerProvider::TracerProvider( + std::unique_ptr processor, + const resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> + tracer_configurator) noexcept { std::vector> processors; processors.push_back(std::move(processor)); - context_ = std::make_shared(std::move(processors), resource, std::move(sampler), - std::move(id_generator)); + context_ = + std::make_shared(std::move(processors), resource, std::move(sampler), + std::move(id_generator), std::move(tracer_configurator)); } -TracerProvider::TracerProvider(std::vector> &&processors, - const resource::Resource &resource, - std::unique_ptr sampler, - std::unique_ptr id_generator) noexcept +TracerProvider::TracerProvider( + std::vector> &&processors, + const resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> + tracer_configurator) noexcept : context_(std::make_shared(std::move(processors), resource, std::move(sampler), - std::move(id_generator))) + std::move(id_generator), + std::move(tracer_configurator))) {} TracerProvider::~TracerProvider() { // Tracer hold the shared pointer to the context. So we can not use destructor of TracerContext to - // Shutdown and flush all pending recordables when we have more than one tracers.These recordables + // Shutdown and flush all pending recordables when we have more than one tracer.These recordables // may use the raw pointer of instrumentation_scope_ in Tracer if (context_) { @@ -134,7 +144,6 @@ bool TracerProvider::ForceFlush(std::chrono::microseconds timeout) noexcept { return context_->ForceFlush(timeout); } - } // namespace trace } // namespace sdk OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/trace/tracer_provider_factory.cc b/sdk/src/trace/tracer_provider_factory.cc index eaeb669b91..49d2aac12d 100644 --- a/sdk/src/trace/tracer_provider_factory.cc +++ b/sdk/src/trace/tracer_provider_factory.cc @@ -5,12 +5,14 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/sdk/trace/id_generator.h" #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/samplers/always_on_factory.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" @@ -51,10 +53,26 @@ std::unique_ptr TracerProviderFactory const opentelemetry::sdk::resource::Resource &resource, std::unique_ptr sampler, std::unique_ptr id_generator) +{ + auto tracer_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(TracerConfig::Default()) + .Build()); + return Create(std::move(processor), resource, std::move(sampler), std::move(id_generator), + std::move(tracer_configurator)); +} + +std::unique_ptr TracerProviderFactory::Create( + std::unique_ptr processor, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator) { std::unique_ptr provider( new opentelemetry::sdk::trace::TracerProvider(std::move(processor), resource, - std::move(sampler), std::move(id_generator))); + std::move(sampler), std::move(id_generator), + std::move(tracer_configurator))); return provider; } @@ -87,10 +105,26 @@ std::unique_ptr TracerProviderFactory const opentelemetry::sdk::resource::Resource &resource, std::unique_ptr sampler, std::unique_ptr id_generator) +{ + auto tracer_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(TracerConfig::Default()) + .Build()); + return Create(std::move(processors), resource, std::move(sampler), std::move(id_generator), + std::move(tracer_configurator)); +} + +std::unique_ptr TracerProviderFactory::Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr sampler, + std::unique_ptr id_generator, + std::unique_ptr> tracer_configurator) { std::unique_ptr provider( new opentelemetry::sdk::trace::TracerProvider(std::move(processors), resource, - std::move(sampler), std::move(id_generator))); + std::move(sampler), std::move(id_generator), + std::move(tracer_configurator))); return provider; } diff --git a/sdk/test/trace/BUILD b/sdk/test/trace/BUILD index 9aa8a95cdf..455b24964d 100644 --- a/sdk/test/trace/BUILD +++ b/sdk/test/trace/BUILD @@ -143,6 +143,21 @@ cc_test( ], ) +cc_test( + name = "tracer_config_test", + srcs = [ + "tracer_config_test.cc", + ], + tags = [ + "test", + "trace", + ], + deps = [ + "//sdk/src/trace", + "@com_google_googletest//:gtest_main", + ], +) + otel_cc_benchmark( name = "sampler_benchmark", srcs = ["sampler_benchmark.cc"], diff --git a/sdk/test/trace/CMakeLists.txt b/sdk/test/trace/CMakeLists.txt index 385c8eaacd..7c1b75400c 100644 --- a/sdk/test/trace/CMakeLists.txt +++ b/sdk/test/trace/CMakeLists.txt @@ -11,7 +11,8 @@ foreach( always_on_sampler_test parent_sampler_test trace_id_ratio_sampler_test - batch_span_processor_test) + batch_span_processor_test + tracer_config_test) add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} diff --git a/sdk/test/trace/tracer_config_test.cc b/sdk/test/trace/tracer_config_test.cc new file mode 100644 index 0000000000..27f845d548 --- /dev/null +++ b/sdk/test/trace/tracer_config_test.cc @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/trace/tracer_config.h" +#include +#include +#include +#include +#include +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/nostd/unique_ptr.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" + +namespace trace_sdk = opentelemetry::sdk::trace; +namespace instrumentation_scope = opentelemetry::sdk::instrumentationscope; + +/** Tests to verify the basic behavior of trace_sdk::TracerConfig */ + +TEST(TracerConfig, CheckDisabledWorksAsExpected) +{ + trace_sdk::TracerConfig disabled_config = trace_sdk::TracerConfig::Disabled(); + ASSERT_FALSE(disabled_config.IsEnabled()); +} + +TEST(TracerConfig, CheckEnabledWorksAsExpected) +{ + trace_sdk::TracerConfig enabled_config = trace_sdk::TracerConfig::Enabled(); + ASSERT_TRUE(enabled_config.IsEnabled()); +} + +TEST(TracerConfig, CheckDefaultConfigWorksAccToSpec) +{ + trace_sdk::TracerConfig default_config = trace_sdk::TracerConfig::Default(); + ASSERT_TRUE(default_config.IsEnabled()); +} + +/** Tests to verify the behavior of trace_sdk::TracerConfig::DefaultConfigurator */ + +static std::pair attr1 = { + "accept_single_attr", true}; +static std::pair attr2 = { + "accept_second_attr", "some other attr"}; +static std::pair attr3 = { + "accept_third_attr", 3}; + +static instrumentation_scope::InstrumentationScope test_scope_1 = + *instrumentation_scope::InstrumentationScope::Create("test_scope_1"); +static instrumentation_scope::InstrumentationScope test_scope_2 = + *instrumentation_scope::InstrumentationScope::Create("test_scope_2", "1.0"); +static instrumentation_scope::InstrumentationScope test_scope_3 = + *instrumentation_scope::InstrumentationScope::Create( + "test_scope_3", + "0", + "https://opentelemetry.io/schemas/v1.18.0"); +static instrumentation_scope::InstrumentationScope test_scope_4 = + *instrumentation_scope::InstrumentationScope::Create("test_scope_4", + "0", + "https://opentelemetry.io/schemas/v1.18.0", + {attr1}); +static instrumentation_scope::InstrumentationScope test_scope_5 = + *instrumentation_scope::InstrumentationScope::Create("test_scope_5", + "0", + "https://opentelemetry.io/schemas/v1.18.0", + {attr1, attr2, attr3}); + +// This array could also directly contain the reference types, but that leads to 'uninitialized +// value was created by heap allocation' errors in Valgrind memcheck. This is a bug in Googletest +// library, see https://github.com/google/googletest/issues/3805#issuecomment-1397301790 for more +// details. Using pointers is a workaround to prevent the Valgrind warnings. +const std::array instrumentation_scopes = { + &test_scope_1, &test_scope_2, &test_scope_3, &test_scope_4, &test_scope_5, +}; + +// Test fixture for VerifyDefaultConfiguratorBehavior +class DefaultTracerConfiguratorTestFixture + : public ::testing::TestWithParam +{}; + +// verifies that the default configurator always returns the default tracer config +TEST_P(DefaultTracerConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) +{ + instrumentation_scope::InstrumentationScope *scope = GetParam(); + instrumentation_scope::ScopeConfigurator default_configurator = + instrumentation_scope::ScopeConfigurator::Builder( + trace_sdk::TracerConfig::Default()) + .Build(); + + ASSERT_EQ(default_configurator.ComputeConfig(*scope), trace_sdk::TracerConfig::Default()); +} + +INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, + DefaultTracerConfiguratorTestFixture, + ::testing::ValuesIn(instrumentation_scopes)); diff --git a/sdk/test/trace/tracer_test.cc b/sdk/test/trace/tracer_test.cc index 04b263f3e5..1f31e2336d 100644 --- a/sdk/test/trace/tracer_test.cc +++ b/sdk/test/trace/tracer_test.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,8 @@ #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/nostd/variant.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" @@ -37,8 +40,10 @@ #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/span_data.h" #include "opentelemetry/sdk/trace/tracer.h" +#include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/trace/context.h" +#include "opentelemetry/trace/noop.h" #include "opentelemetry/trace/scope.h" #include "opentelemetry/trace/span.h" #include "opentelemetry/trace/span_context.h" @@ -52,6 +57,7 @@ using namespace opentelemetry::sdk::trace; using namespace opentelemetry::sdk::resource; +using namespace opentelemetry::sdk::instrumentationscope; using opentelemetry::common::SteadyTimestamp; using opentelemetry::common::SystemTimestamp; namespace nostd = opentelemetry::nostd; @@ -103,7 +109,7 @@ class MockSampler final : public Sampler }; /** - * A Mock Custom Id Generator + * A Mock Custom ID Generator */ class MockIdGenerator : public IdGenerator { @@ -138,16 +144,20 @@ std::shared_ptr initTracer( std::unique_ptr &&exporter, // For testing, just shove a pointer over, we'll take it over. Sampler *sampler, - IdGenerator *id_generator = new RandomIdGenerator) + IdGenerator *id_generator = new RandomIdGenerator, + const ScopeConfigurator &tracer_configurator = + ScopeConfigurator::Builder(TracerConfig::Default()).Build(), + std::unique_ptr scope = InstrumentationScope::Create("")) { auto processor = std::unique_ptr(new SimpleSpanProcessor(std::move(exporter))); std::vector> processors; processors.push_back(std::move(processor)); auto resource = Resource::Create({}); - auto context = std::make_shared(std::move(processors), resource, - std::unique_ptr(sampler), - std::unique_ptr(id_generator)); - return std::shared_ptr(new Tracer(context)); + auto context = std::make_shared( + std::move(processors), resource, std::unique_ptr(sampler), + std::unique_ptr(id_generator), + std::make_unique>(tracer_configurator)); + return std::shared_ptr(new Tracer(context, std::move(scope))); } } // namespace @@ -484,6 +494,122 @@ TEST(Tracer, SpanSetEvents) ASSERT_EQ(1, span_data_events[2].GetAttributes().size()); } +TEST(Tracer, StartSpanWithDisabledConfig) +{ + InMemorySpanExporter *exporter = new InMemorySpanExporter(); + std::shared_ptr span_data = exporter->GetData(); + ScopeConfigurator disable_tracer = + ScopeConfigurator::Builder(TracerConfig::Disabled()).Build(); + auto tracer = initTracer(std::unique_ptr{exporter}, new AlwaysOnSampler(), + new RandomIdGenerator(), disable_tracer); + auto span = tracer->StartSpan("span 1"); + + std::shared_ptr noop_tracer = + std::make_shared(); + auto noop_span = noop_tracer->StartSpan("noop"); + EXPECT_TRUE(span.get() == noop_span.get()); +} + +TEST(Tracer, StartSpanWithEnabledConfig) +{ + InMemorySpanExporter *exporter = new InMemorySpanExporter(); + std::shared_ptr span_data = exporter->GetData(); + ScopeConfigurator enable_tracer = + ScopeConfigurator::Builder(TracerConfig::Enabled()).Build(); + auto tracer = initTracer(std::unique_ptr{exporter}, new AlwaysOnSampler(), + new RandomIdGenerator(), enable_tracer); + auto span = tracer->StartSpan("span 1"); + + std::shared_ptr noop_tracer = + std::make_shared(); + auto noop_span = noop_tracer->StartSpan("noop"); + EXPECT_FALSE(span.get() == noop_span.get()); +} + +TEST(Tracer, StartSpanWithCustomConfig) +{ + std::shared_ptr noop_tracer = + std::make_shared(); + auto noop_span = noop_tracer->StartSpan("noop"); + auto check_if_version_present = [](const InstrumentationScope &scope_info) { + return !scope_info.GetVersion().empty(); + }; + ScopeConfigurator custom_configurator = + ScopeConfigurator::Builder(TracerConfig::Enabled()) + .AddCondition(check_if_version_present, TracerConfig::Enabled()) + .AddConditionNameEquals("foo_library", TracerConfig::Disabled()) + .AddConditionNameEquals("", TracerConfig::Disabled()) + .Build(); + + const auto tracer_default_scope = + initTracer(std::unique_ptr{new InMemorySpanExporter()}, new AlwaysOnSampler(), + new RandomIdGenerator(), custom_configurator); + const auto span_default_scope = tracer_default_scope->StartSpan("span 1"); + EXPECT_TRUE(span_default_scope == noop_span); + + auto foo_scope = InstrumentationScope::Create("foo_library"); + const auto tracer_foo_scope = + initTracer(std::unique_ptr{new InMemorySpanExporter()}, new AlwaysOnSampler(), + new RandomIdGenerator(), custom_configurator, std::move(foo_scope)); + const auto span_foo_scope = tracer_foo_scope->StartSpan("span 1"); + EXPECT_TRUE(span_foo_scope == noop_span); + + auto foo_scope_with_version = InstrumentationScope::Create("foo_library", "1.0.0"); + const auto tracer_foo_scope_with_version = + initTracer(std::unique_ptr{new InMemorySpanExporter()}, new AlwaysOnSampler(), + new RandomIdGenerator(), custom_configurator, std::move(foo_scope_with_version)); + const auto span_foo_scope_with_version = tracer_foo_scope_with_version->StartSpan("span 1"); + EXPECT_FALSE(span_foo_scope_with_version == noop_span); + + auto bar_scope = InstrumentationScope::Create("bar_library"); + auto tracer_bar_scope = + initTracer(std::unique_ptr{new InMemorySpanExporter()}, new AlwaysOnSampler(), + new RandomIdGenerator(), custom_configurator, std::move(bar_scope)); + auto span_bar_scope = tracer_bar_scope->StartSpan("span 1"); + EXPECT_FALSE(span_bar_scope == noop_span); +} + +TEST(Tracer, StartSpanWithCustomConfigDifferingConditionOrder) +{ + std::shared_ptr noop_tracer = + std::make_shared(); + auto noop_span = noop_tracer->StartSpan("noop"); + auto check_if_version_present = [](const InstrumentationScope &scope_info) { + return !scope_info.GetVersion().empty(); + }; + // 2 configurators with same conditions, but different order + ScopeConfigurator custom_configurator_1 = + ScopeConfigurator::Builder(TracerConfig::Enabled()) + .AddCondition(check_if_version_present, TracerConfig::Enabled()) + .AddConditionNameEquals("foo_library", TracerConfig::Disabled()) + .Build(); + ScopeConfigurator custom_configurator_2 = + ScopeConfigurator::Builder(TracerConfig::Enabled()) + .AddConditionNameEquals("foo_library", TracerConfig::Disabled()) + .AddCondition(check_if_version_present, TracerConfig::Enabled()) + .Build(); + + auto foo_scope_with_version_1 = InstrumentationScope::Create("foo_library", "1.0.0"); + auto foo_scope_with_version_2 = InstrumentationScope::Create("foo_library", "1.0.0"); + + const auto tracer_foo_scope_with_version_1 = initTracer( + std::unique_ptr{new InMemorySpanExporter()}, new AlwaysOnSampler(), + new RandomIdGenerator(), custom_configurator_1, std::move(foo_scope_with_version_1)); + + const auto tracer_foo_scope_with_version_2 = initTracer( + std::unique_ptr{new InMemorySpanExporter()}, new AlwaysOnSampler(), + new RandomIdGenerator(), custom_configurator_2, std::move(foo_scope_with_version_2)); + + // Custom configurator 1 evaluates version first and enables the tracer + const auto span_foo_scope_with_version_1 = tracer_foo_scope_with_version_1->StartSpan("span 1"); + EXPECT_FALSE(span_foo_scope_with_version_1 == noop_span); + + // Custom configurator 2 evaluates the name first and therefore disables the tracer without + // evaluating other condition + const auto span_foo_scope_with_version_2 = tracer_foo_scope_with_version_2->StartSpan("span 1"); + EXPECT_TRUE(span_foo_scope_with_version_2 == noop_span); +} + TEST(Tracer, SpanSetLinks) { InMemorySpanExporter *exporter = new InMemorySpanExporter();