diff --git a/CHANGELOG.md b/CHANGELOG.md index 90ea49e17c..0a4fc9b228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,9 @@ Increment the: * [SDK] Support OTEL_SDK_DISABLED environment variable [#3245](https://github.com/open-telemetry/opentelemetry-cpp/pull/3245) +* [SDK] Add meter scope configurator + [#3268](https://github.com/open-telemetry/opentelemetry-cpp/pull/3268) + Important changes: * [SDK] Support OTEL_SDK_DISABLED environment variable diff --git a/sdk/include/opentelemetry/sdk/metrics/meter.h b/sdk/include/opentelemetry/sdk/metrics/meter.h index 2e9153e151..bf1b0e6c37 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter.h @@ -19,6 +19,7 @@ #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/metrics/instrument_metadata_validator.h" #include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/state/async_metric_storage.h" #include "opentelemetry/sdk/resource/resource.h" @@ -138,12 +139,15 @@ class Meter final : public opentelemetry::metrics::Meter // Mapping between instrument-name and Aggregation Storage. std::unordered_map> storage_registry_; std::shared_ptr observable_registry_; + MeterConfig meter_config_; std::unique_ptr RegisterSyncMetricStorage( InstrumentDescriptor &instrument_descriptor); std::unique_ptr RegisterAsyncMetricStorage( InstrumentDescriptor &instrument_descriptor); opentelemetry::common::SpinLockMutex storage_lock_; + static opentelemetry::metrics::NoopMeter kNoopMeter; + static nostd::shared_ptr GetNoopObservableInsrument() { diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_config.h b/sdk/include/opentelemetry/sdk/metrics/meter_config.h new file mode 100644 index 0000000000..abb22154d2 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/metrics/meter_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 metrics +{ +/** + * MeterConfig defines various configurable aspects of a Meter's behavior. + * This class should not be used directly to configure a Meter's behavior, instead a + * ScopeConfigurator should be used to compute the desired MeterConfig which can then be used to + * configure a Meter. + */ +class MeterConfig +{ +public: + bool operator==(const MeterConfig &other) const noexcept; + + /** + * Returns if the Meter is enabled or disabled. Meters are enabled by default. + * @return a boolean indicating if the Meter is enabled. Defaults to true. + */ + bool IsEnabled() const noexcept; + + /** + * Returns a MeterConfig that represents a disabled Meter. A disabled meter behaves like a + * no-op meter. + * @return a static constant MeterConfig that represents a disabled meter. + */ + static MeterConfig Disabled(); + + /** + * Returns a MeterConfig that represents an enabled Meter. + * @return a static constant MeterConfig that represents an enabled meter. + */ + static MeterConfig Enabled(); + + /** + * Returns a MeterConfig that represents a Meter configured with the default behavior. + * The default behavior is guided by the OpenTelemetry specification. + * @return a static constant MeterConfig that represents a meter configured with default + * behavior. + */ + static MeterConfig Default(); + +private: + explicit MeterConfig(const bool disabled = false) : disabled_(disabled) {} + bool disabled_; + static const MeterConfig kDefaultConfig; + static const MeterConfig kDisabledConfig; +}; +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_context.h b/sdk/include/opentelemetry/sdk/metrics/meter_context.h index 8a19cd7466..8925059a96 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_context.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_context.h @@ -13,6 +13,8 @@ #include "opentelemetry/nostd/function_ref.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include "opentelemetry/sdk/metrics/state/metric_collector.h" #include "opentelemetry/sdk/metrics/view/instrument_selector.h" @@ -48,14 +50,17 @@ class MeterContext : public std::enable_shared_from_this public: /** * Initialize a new meter provider - * @param readers The readers to be configured with meter context. * @param views The views to be configured with meter context. * @param resource The resource for this meter context. */ MeterContext( std::unique_ptr views = std::unique_ptr(new ViewRegistry()), const opentelemetry::sdk::resource::Resource &resource = - opentelemetry::sdk::resource::Resource::Create({})) noexcept; + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr> meter_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(MeterConfig::Default()) + .Build())) noexcept; /** * Obtain the resource associated with this meter context. @@ -70,13 +75,19 @@ class MeterContext : public std::enable_shared_from_this ViewRegistry *GetViewRegistry() const noexcept; /** - * NOTE - INTERNAL method, can change in future. + * Obtain the ScopeConfigurator with this meter context. + * @return The ScopeConfigurator for this meter context. + */ + const instrumentationscope::ScopeConfigurator &GetMeterConfigurator() const noexcept; + + /** + * NOTE - INTERNAL method, can change in the future. * Process callback for each meter in thread-safe manner */ bool ForEachMeter(nostd::function_ref &meter)> callback) noexcept; /** - * NOTE - INTERNAL method, can change in future. + * NOTE - INTERNAL method, can change in the future. * Get the configured meters. * This method is NOT thread safe, and only called through MeterProvider * @@ -154,6 +165,7 @@ class MeterContext : public std::enable_shared_from_this std::vector> collectors_; std::unique_ptr views_; opentelemetry::common::SystemTimestamp sdk_start_ts_; + std::unique_ptr> meter_configurator_; std::vector> meters_; #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_context_factory.h b/sdk/include/opentelemetry/sdk/metrics/meter_context_factory.h index 1185a6075a..3b6544b984 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_context_factory.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_context_factory.h @@ -23,15 +23,39 @@ class OPENTELEMETRY_EXPORT MeterContextFactory { public: /** - * Create a MeterContext. + * Create a MeterContext with valid defaults. + * @return A unique pointer to the created MeterContext object. */ static std::unique_ptr Create(); + /** + * Create a MeterContext with specified views. + * @param views ViewRegistry containing OpenTelemetry views registered with this meter context. + */ static std::unique_ptr Create(std::unique_ptr views); + /** + * Create a MeterContext with specified views and resource. + * @param views ViewRegistry containing OpenTelemetry views registered with this meter context. + * @param resource The OpenTelemetry resource associated with this meter context. + * @return A unique pointer to the created MeterContext object. + */ static std::unique_ptr Create( std::unique_ptr views, const opentelemetry::sdk::resource::Resource &resource); + + /** + * Create a MeterContext with specified views, resource and meter scope configurator. + * @param views ViewRegistry containing OpenTelemetry views registered with this meter context. + * @param resource The OpenTelemetry resource associated with this meter context. + * @param meter_configurator A scope configurator defining the behavior of a meter associated with + * this meter context. + * @return A unique pointer to the created MeterContext object. + */ + static std::unique_ptr Create( + std::unique_ptr views, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> meter_configurator); }; } // namespace metrics diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h index 46b4efc93c..5b794fa809 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h @@ -20,6 +20,9 @@ #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/version.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/meter.h" + #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW # include "opentelemetry/sdk/metrics/exemplar/filter_type.h" #endif @@ -34,13 +37,19 @@ class OPENTELEMETRY_EXPORT MeterProvider final : public opentelemetry::metrics:: { public: /** - * Initialize a new meter provider + * Initialize a new meter provider. * @param views The views for this meter provider * @param resource The resources for this meter provider. + * @param meter_configurator Provides access to a function that computes the MeterConfig for + * Meters provided by this MeterProvider. */ MeterProvider( std::unique_ptr views = std::unique_ptr(new ViewRegistry()), - const sdk::resource::Resource &resource = sdk::resource::Resource::Create({})) noexcept; + const sdk::resource::Resource &resource = sdk::resource::Resource::Create({}), + std::unique_ptr> meter_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(MeterConfig::Default()) + .Build())) noexcept; /** * Initialize a new meter provider with a specified context diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_provider_factory.h b/sdk/include/opentelemetry/sdk/metrics/meter_provider_factory.h index 499c87ae6a..f31b06ae10 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_provider_factory.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_provider_factory.h @@ -30,6 +30,11 @@ class OPENTELEMETRY_EXPORT MeterProviderFactory std::unique_ptr views, const opentelemetry::sdk::resource::Resource &resource); + static std::unique_ptr Create( + std::unique_ptr views, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> meter_configurator); + static std::unique_ptr Create( std::unique_ptr context); }; diff --git a/sdk/src/metrics/CMakeLists.txt b/sdk/src/metrics/CMakeLists.txt index 5a726b4eee..f1ba16a116 100644 --- a/sdk/src/metrics/CMakeLists.txt +++ b/sdk/src/metrics/CMakeLists.txt @@ -8,6 +8,7 @@ add_library( meter_provider.cc meter_provider_factory.cc meter.cc + meter_config.cc meter_context.cc meter_context_factory.cc metric_reader.cc diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index 2b95802fe0..f89de3b6b0 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -21,10 +21,12 @@ #include "opentelemetry/nostd/unique_ptr.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/metrics/async_instruments.h" #include "opentelemetry/sdk/metrics/data/metric_data.h" #include "opentelemetry/sdk/metrics/instruments.h" #include "opentelemetry/sdk/metrics/meter.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/state/async_metric_storage.h" #include "opentelemetry/sdk/metrics/state/metric_collector.h" @@ -49,19 +51,37 @@ namespace metrics namespace metrics = opentelemetry::metrics; +metrics::NoopMeter Meter::kNoopMeter = metrics::NoopMeter(); + Meter::Meter( std::weak_ptr meter_context, std::unique_ptr instrumentation_scope) noexcept : scope_{std::move(instrumentation_scope)}, meter_context_{std::move(meter_context)}, - observable_registry_(new ObservableRegistry()) -{} + observable_registry_(new ObservableRegistry()), + meter_config_(MeterConfig::Default()) +{ + if (auto meter_context_locked_ptr = meter_context_.lock()) + { + meter_config_ = meter_context_locked_ptr->GetMeterConfigurator().ComputeConfig(*scope_); + } + else + { + OTEL_INTERNAL_LOG_ERROR("[Meter::Meter()] - Error during initialization." + << "The metric context is invalid") + } +} opentelemetry::nostd::unique_ptr> Meter::CreateUInt64Counter( opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateUInt64Counter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateUInt64Counter - failed. Invalid parameters." @@ -83,6 +103,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateDoubleCo opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleCounter - failed. Invalid parameters." @@ -105,6 +130,11 @@ Meter::CreateInt64ObservableCounter(opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateInt64ObservableCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64ObservableCounter - failed. Invalid parameters." @@ -126,6 +156,11 @@ Meter::CreateDoubleObservableCounter(opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleObservableCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleObservableCounter - failed. Invalid parameters." @@ -147,6 +182,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateUInt opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateUInt64Histogram(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateUInt64Histogram - failed. Invalid parameters." @@ -169,6 +209,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateDouble opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleHistogram(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleHistogram - failed. Invalid parameters." @@ -192,6 +237,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateInt64Gaug opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateInt64Gauge(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64Gauge - failed. Invalid parameters." @@ -213,6 +263,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateDoubleGaug opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleGauge(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleGauge - failed. Invalid parameters." @@ -235,6 +290,11 @@ Meter::CreateInt64ObservableGauge(opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateInt64ObservableGauge(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64ObservableGauge - failed. Invalid parameters." @@ -256,6 +316,11 @@ Meter::CreateDoubleObservableGauge(opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleObservableGauge(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleObservableGauge - failed. Invalid parameters." @@ -277,6 +342,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateI opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateInt64UpDownCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateInt64UpDownCounter - failed. Invalid parameters." @@ -299,6 +369,11 @@ opentelemetry::nostd::unique_ptr> Meter::CreateDo opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleUpDownCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR("Meter::CreateDoubleUpDownCounter - failed. Invalid parameters." @@ -321,6 +396,11 @@ Meter::CreateInt64ObservableUpDownCounter(opentelemetry::nostd::string_view name opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateInt64ObservableUpDownCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR( @@ -342,6 +422,11 @@ Meter::CreateDoubleObservableUpDownCounter(opentelemetry::nostd::string_view nam opentelemetry::nostd::string_view description, opentelemetry::nostd::string_view unit) noexcept { + if (!meter_config_.IsEnabled()) + { + return kNoopMeter.CreateDoubleObservableUpDownCounter(name, description, unit); + } + if (!ValidateInstrument(name, description, unit)) { OTEL_INTERNAL_LOG_ERROR( @@ -486,6 +571,10 @@ std::unique_ptr Meter::RegisterAsyncMetricStorage( std::vector Meter::Collect(CollectorHandle *collector, opentelemetry::common::SystemTimestamp collect_ts) noexcept { + if (!meter_config_.IsEnabled()) + { + return std::vector(); + } observable_registry_->Observe(collect_ts); std::vector metric_data_list; auto ctx = meter_context_.lock(); diff --git a/sdk/src/metrics/meter_config.cc b/sdk/src/metrics/meter_config.cc new file mode 100644 index 0000000000..cef0d60da1 --- /dev/null +++ b/sdk/src/metrics/meter_config.cc @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/metrics/meter_config.h" +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace metrics +{ + +const MeterConfig MeterConfig::kDefaultConfig = MeterConfig(); +const MeterConfig MeterConfig::kDisabledConfig = MeterConfig(true); + +bool MeterConfig::operator==(const MeterConfig &other) const noexcept +{ + return disabled_ == other.disabled_; +} + +bool MeterConfig::IsEnabled() const noexcept +{ + return !disabled_; +} + +MeterConfig MeterConfig::Disabled() +{ + return kDisabledConfig; +} + +MeterConfig MeterConfig::Enabled() +{ + return kDefaultConfig; +} + +MeterConfig MeterConfig::Default() +{ + return kDefaultConfig; +} + +} // namespace metrics +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/metrics/meter_context.cc b/sdk/src/metrics/meter_context.cc index 31dd1af8b1..1aa4782c34 100644 --- a/sdk/src/metrics/meter_context.cc +++ b/sdk/src/metrics/meter_context.cc @@ -17,7 +17,9 @@ #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/metrics/meter.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/metric_reader.h" #include "opentelemetry/sdk/metrics/state/metric_collector.h" @@ -35,8 +37,13 @@ namespace metrics { MeterContext::MeterContext(std::unique_ptr views, - const opentelemetry::sdk::resource::Resource &resource) noexcept - : resource_{resource}, views_(std::move(views)), sdk_start_ts_{std::chrono::system_clock::now()} + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> + meter_configurator) noexcept + : resource_{resource}, + views_(std::move(views)), + sdk_start_ts_{std::chrono::system_clock::now()}, + meter_configurator_(std::move(meter_configurator)) {} const resource::Resource &MeterContext::GetResource() const noexcept @@ -49,6 +56,12 @@ ViewRegistry *MeterContext::GetViewRegistry() const noexcept return views_.get(); } +const instrumentationscope::ScopeConfigurator &MeterContext::GetMeterConfigurator() + const noexcept +{ + return *meter_configurator_; +} + bool MeterContext::ForEachMeter( nostd::function_ref &meter)> callback) noexcept { diff --git a/sdk/src/metrics/meter_context_factory.cc b/sdk/src/metrics/meter_context_factory.cc index 59e97ce264..27efbc965f 100644 --- a/sdk/src/metrics/meter_context_factory.cc +++ b/sdk/src/metrics/meter_context_factory.cc @@ -4,6 +4,9 @@ #include #include +#include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/meter_context_factory.h" #include "opentelemetry/sdk/metrics/view/view_registry.h" @@ -33,7 +36,19 @@ std::unique_ptr MeterContextFactory::Create( std::unique_ptr views, const opentelemetry::sdk::resource::Resource &resource) { - std::unique_ptr context(new MeterContext(std::move(views), resource)); + auto meter_configurator = std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(MeterConfig::Default()) + .Build()); + return Create(std::move(views), resource, std::move(meter_configurator)); +} + +std::unique_ptr MeterContextFactory::Create( + std::unique_ptr views, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> meter_configurator) +{ + std::unique_ptr context( + new MeterContext(std::move(views), resource, std::move(meter_configurator))); return context; } diff --git a/sdk/src/metrics/meter_provider.cc b/sdk/src/metrics/meter_provider.cc index 1e0df0f6f3..82af065f30 100644 --- a/sdk/src/metrics/meter_provider.cc +++ b/sdk/src/metrics/meter_provider.cc @@ -12,7 +12,9 @@ #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/metrics/meter.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/metric_reader.h" @@ -36,8 +38,11 @@ MeterProvider::MeterProvider(std::unique_ptr context) noexcept {} MeterProvider::MeterProvider(std::unique_ptr views, - const sdk::resource::Resource &resource) noexcept - : context_(std::make_shared(std::move(views), resource)) + const sdk::resource::Resource &resource, + std::unique_ptr> + meter_configurator) noexcept + : context_( + std::make_shared(std::move(views), resource, std::move(meter_configurator))) { OTEL_INTERNAL_LOG_DEBUG("[MeterProvider] MeterProvider created."); } diff --git a/sdk/src/metrics/meter_provider_factory.cc b/sdk/src/metrics/meter_provider_factory.cc index c5e368aca6..052639e341 100644 --- a/sdk/src/metrics/meter_provider_factory.cc +++ b/sdk/src/metrics/meter_provider_factory.cc @@ -4,6 +4,9 @@ #include #include +#include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/meter_config.h" #include "opentelemetry/sdk/metrics/meter_context.h" #include "opentelemetry/sdk/metrics/meter_provider.h" #include "opentelemetry/sdk/metrics/meter_provider_factory.h" @@ -34,9 +37,21 @@ std::unique_ptr MeterProviderFactory std::unique_ptr MeterProviderFactory::Create( std::unique_ptr views, const opentelemetry::sdk::resource::Resource &resource) +{ + auto meter_configurator = std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(MeterConfig::Default()) + .Build()); + return Create(std::move(views), resource, std::move(meter_configurator)); +} + +std::unique_ptr MeterProviderFactory::Create( + std::unique_ptr views, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> meter_configurator) { std::unique_ptr provider( - new opentelemetry::sdk::metrics::MeterProvider(std::move(views), resource)); + new opentelemetry::sdk::metrics::MeterProvider(std::move(views), resource, + std::move(meter_configurator))); return provider; } diff --git a/sdk/test/metrics/BUILD b/sdk/test/metrics/BUILD index 70d0cc063a..929511a89b 100644 --- a/sdk/test/metrics/BUILD +++ b/sdk/test/metrics/BUILD @@ -18,6 +18,21 @@ cc_library( ], ) +cc_test( + name = "meter_config_test", + srcs = [ + "meter_config_test.cc", + ], + tags = [ + "metrics", + "test", + ], + deps = [ + "metrics_common_test_utils", + "@com_google_googletest//:gtest_main", + ], +) + cc_test( name = "all_tests", srcs = glob(["*_test.cc"]), diff --git a/sdk/test/metrics/CMakeLists.txt b/sdk/test/metrics/CMakeLists.txt index 186c6cbbbd..f82a386816 100644 --- a/sdk/test/metrics/CMakeLists.txt +++ b/sdk/test/metrics/CMakeLists.txt @@ -6,6 +6,7 @@ target_link_libraries(metrics_common_test_utils opentelemetry_metrics) foreach( testname + meter_config_test meter_provider_set_test meter_provider_sdk_test meter_test diff --git a/sdk/test/metrics/meter_config_test.cc b/sdk/test/metrics/meter_config_test.cc new file mode 100644 index 0000000000..c16b1d93f0 --- /dev/null +++ b/sdk/test/metrics/meter_config_test.cc @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/metrics/meter_config.h" +#include +#include +#include +#include +#include +#include +#include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/nostd/string_view.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" + +namespace metrics_sdk = opentelemetry::sdk::metrics; +namespace instrumentation_scope = opentelemetry::sdk::instrumentationscope; + +/** Test to verify the basic behavior of metrics_sdk::MeterConfig */ + +TEST(MeterConfig, CheckDisabledWorksAsExpected) +{ + metrics_sdk::MeterConfig disabled_config = metrics_sdk::MeterConfig::Disabled(); + ASSERT_FALSE(disabled_config.IsEnabled()); +} + +TEST(MeterConfig, CheckEnabledWorksAsExpected) +{ + metrics_sdk::MeterConfig enabled_config = metrics_sdk::MeterConfig::Enabled(); + ASSERT_TRUE(enabled_config.IsEnabled()); +} + +TEST(MeterConfig, CheckDefaultConfigWorksAccToSpec) +{ + metrics_sdk::MeterConfig enabled_config = metrics_sdk::MeterConfig::Default(); + ASSERT_TRUE(enabled_config.IsEnabled()); +} + +/** Tests to verify the behavior of metrics_sdk::MeterConfig::Default */ + +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 DefaultMeterConfiguratorTestFixture + : public ::testing::TestWithParam +{}; + +// verifies that the default configurator always returns the default meter config +TEST_P(DefaultMeterConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) +{ + instrumentation_scope::InstrumentationScope *scope = GetParam(); + instrumentation_scope::ScopeConfigurator default_configurator = + instrumentation_scope::ScopeConfigurator::Builder( + metrics_sdk::MeterConfig::Default()) + .Build(); + + ASSERT_EQ(default_configurator.ComputeConfig(*scope), metrics_sdk::MeterConfig::Default()); +} + +INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, + DefaultMeterConfiguratorTestFixture, + ::testing::ValuesIn(instrumentation_scopes)); diff --git a/sdk/test/metrics/meter_test.cc b/sdk/test/metrics/meter_test.cc index 078ef809cd..e3335b9a0b 100644 --- a/sdk/test/metrics/meter_test.cc +++ b/sdk/test/metrics/meter_test.cc @@ -13,8 +13,19 @@ #include #include "common.h" +#include +#include "opentelemetry/context/context.h" #include "opentelemetry/metrics/async_instruments.h" #include "opentelemetry/metrics/meter.h" +#include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/metrics/instruments.h" +#include "opentelemetry/sdk/metrics/meter_config.h" +#include "opentelemetry/sdk/metrics/view/view_registry.h" +#include "opentelemetry/sdk/resource/resource.h" + +#include + #include "opentelemetry/metrics/meter_provider.h" #include "opentelemetry/metrics/observer_result.h" #include "opentelemetry/metrics/sync_instruments.h" // IWYU pragma: keep @@ -44,12 +55,37 @@ nostd::shared_ptr InitMeter(MetricReader **metricReaderPtr, return meter; } -void asyc_generate_measurements(opentelemetry::metrics::ObserverResult observer, void * /* state */) +void asyc_generate_measurements_long(opentelemetry::metrics::ObserverResult observer, + void * /* state */) { auto observer_long = nostd::get>>(observer); observer_long->Observe(10); } + +void asyc_generate_measurements_double(opentelemetry::metrics::ObserverResult observer, + void * /* state */) +{ + auto observer_double = + nostd::get>>(observer); + observer_double->Observe(10.2f); +} + +std::shared_ptr GetMeterProviderWithScopeConfigurator( + const ScopeConfigurator &meter_configurator, + MetricReader **metric_reader_ptr) +{ + auto views = ViewRegistryFactory::Create(); + auto resource = sdk::resource::Resource::Create({}); + std::unique_ptr metric_reader(new MockMetricReader()); + *metric_reader_ptr = metric_reader.get(); + std::shared_ptr provider( + new MeterProvider(std::move(views), resource, + std::make_unique>(meter_configurator))); + auto p = std::static_pointer_cast(provider); + p->AddMetricReader(std::move(metric_reader)); + return p; +} } // namespace TEST(MeterTest, BasicAsyncTests) @@ -57,7 +93,7 @@ TEST(MeterTest, BasicAsyncTests) MetricReader *metric_reader_ptr = nullptr; auto meter = InitMeter(&metric_reader_ptr); auto observable_counter = meter->CreateInt64ObservableCounter("observable_counter"); - observable_counter->AddCallback(asyc_generate_measurements, nullptr); + observable_counter->AddCallback(asyc_generate_measurements_long, nullptr); size_t count = 0; metric_reader_ptr->Collect([&count](ResourceMetrics &metric_data) { @@ -73,7 +109,7 @@ TEST(MeterTest, BasicAsyncTests) } return true; }); - observable_counter->RemoveCallback(asyc_generate_measurements, nullptr); + observable_counter->RemoveCallback(asyc_generate_measurements_long, nullptr); } constexpr static unsigned MAX_THREADS = 25; @@ -110,7 +146,7 @@ TEST(MeterTest, StressMultiThread) std::cout << "\n creating async thread " << std::to_string(numIterations); auto observable_instrument = meter->CreateInt64ObservableUpDownCounter( "test_gauge_" + std::to_string(instrument_id)); - observable_instrument->AddCallback(asyc_generate_measurements, nullptr); + observable_instrument->AddCallback(asyc_generate_measurements_long, nullptr); observable_instruments.push_back(std::move(observable_instrument)); do_collect.store(true); instrument_id++; @@ -133,3 +169,206 @@ TEST(MeterTest, StressMultiThread) } } } + +TEST(MeterTest, MeterWithDisabledConfig) +{ + ScopeConfigurator disable_all_scopes = + ScopeConfigurator::Builder(MeterConfig::Disabled()).Build(); + MetricReader *metric_reader_ptr = nullptr; + std::shared_ptr meter_provider = + GetMeterProviderWithScopeConfigurator(disable_all_scopes, &metric_reader_ptr); + + auto meter = meter_provider->GetMeter("foo", "0.1.0", "https://opentelemetry.io/schemas/1.24.0"); + + // Test all supported instruments from this meter - create instruments + auto double_counter = meter->CreateDoubleCounter("double_counter"); + auto double_histogram = meter->CreateDoubleHistogram("double_histogram"); + auto double_up_down_counter = meter->CreateDoubleUpDownCounter("double_up_down_counter"); + auto double_obs_counter = meter->CreateDoubleObservableCounter("double_obs_counter"); + auto double_obs_gauge = meter->CreateDoubleObservableGauge("double_obs_gauge"); + auto double_obs_up_down_counter = + meter->CreateDoubleObservableUpDownCounter("double_obs_up_down_counter"); + + auto uint64_counter = meter->CreateUInt64Counter("uint64_counter"); + auto uint64_histogram = meter->CreateUInt64Histogram("uint64_histogram"); + auto int64_up_down_counter = meter->CreateInt64UpDownCounter("int64_up_down_counter"); + auto int64_obs_counter = meter->CreateInt64ObservableCounter("int64_obs_counter"); + auto int64_obs_gauge = meter->CreateInt64ObservableGauge("int64_obs_gauge"); + auto int64_obs_up_down_counter = + meter->CreateInt64ObservableUpDownCounter("int64_obs_up_down_counter"); + + // Invoke the created instruments + double_counter->Add(1.0f); + double_histogram->Record(23.2f, {}); + double_up_down_counter->Add(-2.4f); + double_obs_counter->AddCallback(asyc_generate_measurements_double, nullptr); + double_obs_gauge->AddCallback(asyc_generate_measurements_double, nullptr); + double_obs_up_down_counter->AddCallback(asyc_generate_measurements_double, nullptr); + + uint64_counter->Add(1); + uint64_histogram->Record(23, {}); + int64_up_down_counter->Add(-2); + int64_obs_counter->AddCallback(asyc_generate_measurements_long, nullptr); + int64_obs_gauge->AddCallback(asyc_generate_measurements_long, nullptr); + int64_obs_up_down_counter->AddCallback(asyc_generate_measurements_long, nullptr); + + // No active instruments are expected - since all scopes are disabled. + metric_reader_ptr->Collect([&](ResourceMetrics &metric_data) { + EXPECT_EQ(metric_data.scope_metric_data_.size(), 0); + return true; + }); +} + +TEST(MeterTest, MeterWithEnabledConfig) +{ + ScopeConfigurator enable_all_scopes = + ScopeConfigurator::Builder(MeterConfig::Enabled()).Build(); + MetricReader *metric_reader_ptr = nullptr; + std::shared_ptr meter_provider = + GetMeterProviderWithScopeConfigurator(enable_all_scopes, &metric_reader_ptr); + + auto meter = meter_provider->GetMeter("foo", "0.1.0", "https://opentelemetry.io/schemas/1.24.0"); + + // Test all supported instruments from this meter - create instruments + auto double_counter = meter->CreateDoubleCounter("double_counter"); + auto double_histogram = meter->CreateDoubleHistogram("double_histogram"); + auto double_up_down_counter = meter->CreateDoubleUpDownCounter("double_up_down_counter"); + auto double_obs_counter = meter->CreateDoubleObservableCounter("double_obs_counter"); + auto double_obs_gauge = meter->CreateDoubleObservableGauge("double_obs_gauge"); + auto double_obs_up_down_counter = + meter->CreateDoubleObservableUpDownCounter("double_obs_up_down_counter"); + + auto uint64_counter = meter->CreateUInt64Counter("uint64_counter"); + auto uint64_histogram = meter->CreateUInt64Histogram("uint64_histogram"); + auto int64_up_down_counter = meter->CreateInt64UpDownCounter("int64_up_down_counter"); + auto int64_obs_counter = meter->CreateInt64ObservableCounter("int64_obs_counter"); + auto int64_obs_gauge = meter->CreateInt64ObservableGauge("int64_obs_gauge"); + auto int64_obs_up_down_counter = + meter->CreateInt64ObservableUpDownCounter("int64_obs_up_down_counter"); + + // Invoke the created instruments + double_counter->Add(1.0f); + double_histogram->Record(23.2f, {}); + double_up_down_counter->Add(-2.4f); + double_obs_counter->AddCallback(asyc_generate_measurements_double, nullptr); + double_obs_gauge->AddCallback(asyc_generate_measurements_double, nullptr); + double_obs_up_down_counter->AddCallback(asyc_generate_measurements_double, nullptr); + + uint64_counter->Add(1); + uint64_histogram->Record(23, {}); + int64_up_down_counter->Add(-2); + int64_obs_counter->AddCallback(asyc_generate_measurements_long, nullptr); + int64_obs_gauge->AddCallback(asyc_generate_measurements_long, nullptr); + int64_obs_up_down_counter->AddCallback(asyc_generate_measurements_long, nullptr); + + // Expected active instruments + std::vector> active_scope_instrument_pairs; + active_scope_instrument_pairs.emplace_back("foo", "double_counter"); + active_scope_instrument_pairs.emplace_back("foo", "double_histogram"); + active_scope_instrument_pairs.emplace_back("foo", "double_up_down_counter"); + active_scope_instrument_pairs.emplace_back("foo", "double_obs_up_down_counter"); + active_scope_instrument_pairs.emplace_back("foo", "double_obs_counter"); + active_scope_instrument_pairs.emplace_back("foo", "double_obs_gauge"); + active_scope_instrument_pairs.emplace_back("foo", "uint64_counter"); + active_scope_instrument_pairs.emplace_back("foo", "uint64_histogram"); + active_scope_instrument_pairs.emplace_back("foo", "int64_up_down_counter"); + active_scope_instrument_pairs.emplace_back("foo", "int64_obs_up_down_counter"); + active_scope_instrument_pairs.emplace_back("foo", "int64_obs_counter"); + active_scope_instrument_pairs.emplace_back("foo", "int64_obs_gauge"); + + metric_reader_ptr->Collect([&](const ResourceMetrics &metric_data) { + bool unexpected_instrument_found = false; + std::string curr_scope_name = metric_data.scope_metric_data_.at(0).scope_->GetName(); + EXPECT_EQ(metric_data.scope_metric_data_.size(), 1); + EXPECT_EQ(metric_data.scope_metric_data_.at(0).scope_->GetName(), "foo"); + EXPECT_EQ(metric_data.scope_metric_data_.at(0).metric_data_.size(), 12); + for (const MetricData &md : metric_data.scope_metric_data_.at(0).metric_data_) + { + auto found_instrument = std::make_pair(curr_scope_name, md.instrument_descriptor.name_); + // confirm if the found instrument is expected + auto it = std::find(active_scope_instrument_pairs.begin(), + active_scope_instrument_pairs.end(), found_instrument); + if (it == active_scope_instrument_pairs.end()) + { + // found instrument is not expected + unexpected_instrument_found = true; + break; + } + } + EXPECT_FALSE(unexpected_instrument_found); + return true; + }); +} + +TEST(MeterTest, MeterWithCustomConfig) +{ + // within the same call + auto check_if_version_present = [](const InstrumentationScope &scope_info) { + return !scope_info.GetVersion().empty(); + }; + + // custom scope configurator that only disables meters with name "foo_library" or do not have + // version information + ScopeConfigurator custom_scope_configurator = + ScopeConfigurator::Builder(MeterConfig::Disabled()) + .AddConditionNameEquals("foo_library", MeterConfig::Disabled()) + .AddCondition(check_if_version_present, MeterConfig::Enabled()) + .Build(); + MetricReader *metric_reader_ptr = nullptr; + std::shared_ptr meter_provider = + GetMeterProviderWithScopeConfigurator(custom_scope_configurator, &metric_reader_ptr); + + // The meter has version information and name is not "foo_library". + // All instruments from this meter should be active and recording metrics. + auto meter_enabled_expected_bar = + meter_provider->GetMeter("bar_library", "0.1.0", "https://opentelemetry.io/schemas/1.24.0"); + + // The meter has version information and name is "foo_library". + // All instruments from this meter should be noop. + auto meter_disabled_expected_foo = + meter_provider->GetMeter("foo_library", "0.1.0", "https://opentelemetry.io/schemas/1.24.0"); + + // This meter has no version information. + // All instruments from this meter should be noop. + auto meter_disabled_expected_baz = + meter_provider->GetMeter("baz_library", "", "https://opentelemetry.io/schemas/1.24.0"); + + // Create instruments from all meters + auto double_counter_bar = meter_enabled_expected_bar->CreateDoubleCounter("double_counter"); + auto double_counter_foo = meter_disabled_expected_foo->CreateDoubleCounter("double_counter"); + auto double_counter_baz = meter_disabled_expected_baz->CreateDoubleCounter("double_counter"); + + // Invoke created instruments at least once + double_counter_bar->Add(1.0f); + double_counter_foo->Add(1.0f); + double_counter_baz->Add(1.0f); + + std::vector> active_scope_instrument_pairs; + active_scope_instrument_pairs.emplace_back("bar_library", "double_counter"); + + metric_reader_ptr->Collect([&](const ResourceMetrics &metric_data) { + int found_instruments = 0; + bool unexpected_instrument_found = false; + for (const ScopeMetrics &sm : metric_data.scope_metric_data_) + { + std::string curr_scope = sm.scope_->GetName(); + for (const MetricData &md : sm.metric_data_) + { + found_instruments++; + auto found_instrument = std::make_pair(curr_scope, md.instrument_descriptor.name_); + // confirm if the found instrument is expected + auto it = std::find(active_scope_instrument_pairs.begin(), + active_scope_instrument_pairs.end(), found_instrument); + if (it == active_scope_instrument_pairs.end()) + { + // found instrument is not expected + unexpected_instrument_found = true; + break; + } + } + } + EXPECT_EQ(found_instruments, active_scope_instrument_pairs.size()); + EXPECT_FALSE(unexpected_instrument_found); + return true; + }); +}