diff --git a/CHANGELOG.md b/CHANGELOG.md index c0c080a79c..77f58a88f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ Increment the: ## [Unreleased] +* [SDK] Allow SDK provider and instrumentation constructors to throw during + initialization. `GetTracer`, `GetLogger`, and `GetMeter` stay `noexcept` and + return a pre-allocated noop object if constructing a new instrumentation + object fails, without caching the failure. + [#4361](https://github.com/open-telemetry/opentelemetry-cpp/issues/4361) * [CONFIGURATION] Add a configuration builder for the host resource detector [#4451](https://github.com/open-telemetry/opentelemetry-cpp/issues/4451) * [CONFIGURATION] Build the configured resource detectors in SdkBuilder, apply diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index 44ed4ec1ee..7ad7a7cd62 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -42,7 +42,7 @@ class Logger final : public opentelemetry::logs::Logger opentelemetry::nostd::string_view name, std::shared_ptr context, std::unique_ptr instrumentation_scope = - instrumentationscope::InstrumentationScope::Create("")) noexcept; + instrumentationscope::InstrumentationScope::Create("")); /** * Returns the name of this logger. diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider.h b/sdk/include/opentelemetry/sdk/logs/logger_provider.h index 06ef66cc6a..75454836c2 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_provider.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider.h @@ -45,7 +45,7 @@ class OPENTELEMETRY_EXPORT LoggerProvider final : public opentelemetry::logs::Lo std::make_unique>( instrumentationscope::ScopeConfigurator::Builder( LoggerConfig::Default()) - .Build())) noexcept; + .Build())); /** * Initialize a new logger provider. @@ -62,19 +62,19 @@ class OPENTELEMETRY_EXPORT LoggerProvider final : public opentelemetry::logs::Lo std::make_unique>( instrumentationscope::ScopeConfigurator::Builder( LoggerConfig::Default()) - .Build())) noexcept; + .Build())); /** * Initialize a new logger provider. A processor must later be assigned * to this logger provider via the AddProcessor() method. */ - explicit LoggerProvider() noexcept; + explicit LoggerProvider(); /** * Initialize a new logger provider with a specified context * @param context The owned logger configuration/pipeline for this provider. */ - explicit LoggerProvider(std::unique_ptr context) noexcept; + explicit LoggerProvider(std::unique_ptr context); LoggerProvider(const LoggerProvider &) = delete; LoggerProvider(LoggerProvider &&) = delete; @@ -144,6 +144,8 @@ class OPENTELEMETRY_EXPORT LoggerProvider final : public opentelemetry::logs::Lo std::vector> loggers_; std::shared_ptr context_; std::mutex lock_; + // Allocated during provider construction so GetLogger can return it without allocating. + nostd::shared_ptr noop_logger_; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/metrics/meter.h b/sdk/include/opentelemetry/sdk/metrics/meter.h index e0ed7acdbb..92d61471fa 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter.h @@ -45,7 +45,7 @@ class Meter final : public opentelemetry::metrics::Meter explicit Meter( std::weak_ptr meter_context, std::unique_ptr scope = - opentelemetry::sdk::instrumentationscope::InstrumentationScope::Create("")) noexcept; + opentelemetry::sdk::instrumentationscope::InstrumentationScope::Create("")); nostd::unique_ptr> CreateUInt64Counter( nostd::string_view name, diff --git a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h index c6871a434e..e3517101d2 100644 --- a/sdk/include/opentelemetry/sdk/metrics/meter_provider.h +++ b/sdk/include/opentelemetry/sdk/metrics/meter_provider.h @@ -59,13 +59,13 @@ class OPENTELEMETRY_EXPORT MeterProvider final : public opentelemetry::metrics:: std::unique_ptr> meter_configurator = std::make_unique>( instrumentationscope::ScopeConfigurator::Builder(MeterConfig::Default()) - .Build())) noexcept; + .Build())); /** * Initialize a new meter provider with a specified context * @param context The owned meter configuration/pipeline for this provider. */ - explicit MeterProvider(std::unique_ptr context) noexcept; + explicit MeterProvider(std::unique_ptr context); MeterProvider(const MeterProvider &) = delete; MeterProvider(MeterProvider &&) = delete; @@ -152,6 +152,8 @@ class OPENTELEMETRY_EXPORT MeterProvider final : public opentelemetry::metrics:: private: std::shared_ptr context_; std::mutex lock_; + // Allocated during provider construction so GetMeter can return it without allocating. + nostd::shared_ptr noop_meter_; #if defined(__cpp_lib_atomic_value_initialization) && \ __cpp_lib_atomic_value_initialization >= 201911L diff --git a/sdk/include/opentelemetry/sdk/trace/tracer.h b/sdk/include/opentelemetry/sdk/trace/tracer.h index 88e12ab16d..edd7eb73ec 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer.h @@ -40,7 +40,7 @@ class Tracer final : public opentelemetry::trace::Tracer, /** Construct a new Tracer with the given context pipeline. */ explicit Tracer(std::shared_ptr context, std::unique_ptr instrumentation_scope = - InstrumentationScope::Create("")) noexcept; + InstrumentationScope::Create("")); nostd::shared_ptr StartSpan( nostd::string_view name, diff --git a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h index 5b9986a4c3..8c3f6358ff 100644 --- a/sdk/include/opentelemetry/sdk/trace/tracer_provider.h +++ b/sdk/include/opentelemetry/sdk/trace/tracer_provider.h @@ -56,7 +56,7 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T instrumentationscope::ScopeConfigurator::Builder( TracerConfig::Default()) .Build()), - SpanLimits span_limits = SpanLimits::NoLimits()) noexcept; + SpanLimits span_limits = SpanLimits::NoLimits()); explicit TracerProvider( std::vector> &&processors, @@ -70,13 +70,13 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T instrumentationscope::ScopeConfigurator::Builder( TracerConfig::Default()) .Build()), - SpanLimits span_limits = SpanLimits::NoLimits()) noexcept; + SpanLimits span_limits = SpanLimits::NoLimits()); /** * Initialize a new tracer provider with a specified context * @param context The owned tracer configuration/pipeline for this provider. */ - explicit TracerProvider(std::unique_ptr context) noexcept; + explicit TracerProvider(std::unique_ptr context); TracerProvider(const TracerProvider &) = delete; TracerProvider(TracerProvider &&) = delete; @@ -155,6 +155,8 @@ class OPENTELEMETRY_EXPORT TracerProvider final : public opentelemetry::trace::T std::vector> tracers_; std::shared_ptr context_; std::mutex lock_; + // Allocated during provider construction so GetTracer can return it without allocating. + nostd::shared_ptr noop_tracer_; }; } // namespace trace } // namespace sdk diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index c04b1dfe68..c60057e6a8 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -96,10 +96,9 @@ void StampSpanContextFromVariant( opentelemetry::logs::NoopLogger Logger::kNoopLogger = opentelemetry::logs::NoopLogger(); -Logger::Logger( - opentelemetry::nostd::string_view name, - std::shared_ptr context, - std::unique_ptr instrumentation_scope) noexcept +Logger::Logger(opentelemetry::nostd::string_view name, + std::shared_ptr context, + std::unique_ptr instrumentation_scope) : logger_name_(std::string(name)), instrumentation_scope_(std::move(instrumentation_scope)), context_(std::move(context)) diff --git a/sdk/src/logs/logger_provider.cc b/sdk/src/logs/logger_provider.cc index 235c3bb4f8..37670e3ebb 100644 --- a/sdk/src/logs/logger_provider.cc +++ b/sdk/src/logs/logger_provider.cc @@ -2,10 +2,14 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include +#include #include #include +#include "opentelemetry/logs/logger.h" +#include "opentelemetry/logs/noop.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/common/global_log_handler.h" @@ -25,11 +29,39 @@ namespace sdk namespace logs { +namespace +{ + +nostd::shared_ptr CreateNoopLoggerFallback() +{ + return nostd::shared_ptr(new opentelemetry::logs::NoopLogger()); +} + +void LogGetLoggerConstructionFailure(const char *detail) noexcept +{ +#if OPENTELEMETRY_HAVE_EXCEPTIONS + try + { +#endif + OTEL_INTERNAL_LOG_ERROR("[LoggerProvider::GetLogger] Failed to construct logger: " + << detail << "; returning noop logger."); +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } + catch (const std::exception &) // NOLINT(bugprone-empty-catch) + { + // Logging can throw (typically std::bad_alloc from the string stream). + // Swallow it so the noexcept GetLogger path cannot throw. + } +#endif +} + +} // namespace + LoggerProvider::LoggerProvider( std::unique_ptr &&processor, const opentelemetry::sdk::resource::Resource &resource, - std::unique_ptr> - logger_configurator) noexcept + std::unique_ptr> logger_configurator) + : noop_logger_(CreateNoopLoggerFallback()) { std::vector> processors; processors.emplace_back(std::move(processor)); @@ -41,19 +73,20 @@ LoggerProvider::LoggerProvider( LoggerProvider::LoggerProvider( std::vector> &&processors, const opentelemetry::sdk::resource::Resource &resource, - std::unique_ptr> - logger_configurator) noexcept + std::unique_ptr> logger_configurator) : context_{std::make_shared(std::move(processors), resource, - std::move(logger_configurator))} + std::move(logger_configurator))}, + noop_logger_(CreateNoopLoggerFallback()) {} -LoggerProvider::LoggerProvider() noexcept - : context_{std::make_shared(std::vector>{})} +LoggerProvider::LoggerProvider() + : context_{std::make_shared(std::vector>{})}, + noop_logger_(CreateNoopLoggerFallback()) {} -LoggerProvider::LoggerProvider(std::unique_ptr context) noexcept - : context_(std::move(context)) +LoggerProvider::LoggerProvider(std::unique_ptr context) + : context_(std::move(context)), noop_logger_(CreateNoopLoggerFallback()) {} LoggerProvider::~LoggerProvider() @@ -94,12 +127,31 @@ opentelemetry::nostd::shared_ptr LoggerProvider::Ge } } - std::unique_ptr lib = - instrumentationscope::InstrumentationScope::Create(name, version, schema_url, attributes); - - loggers_.push_back(std::shared_ptr( - new Logger(logger_name, context_, std::move(lib)))); - return opentelemetry::nostd::shared_ptr{loggers_.back()}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + try + { +#endif + std::unique_ptr lib = + instrumentationscope::InstrumentationScope::Create(name, version, schema_url, attributes); + + auto logger = std::make_shared(logger_name, context_, std::move(lib)); + loggers_.push_back(logger); + return opentelemetry::nostd::shared_ptr{logger}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } + catch (const std::exception &ex) + { + LogGetLoggerConstructionFailure(ex.what()); + return noop_logger_; + } + // User-provided scope configurators can throw any exception type, not just + // std::exception. Catch everything so GetLogger stays noexcept. + catch (...) + { + LogGetLoggerConstructionFailure("unknown exception"); + return noop_logger_; + } +#endif } void LoggerProvider::AddProcessor(std::unique_ptr processor) noexcept diff --git a/sdk/src/metrics/meter.cc b/sdk/src/metrics/meter.cc index 0abd75265e..0f7ee6992b 100644 --- a/sdk/src/metrics/meter.cc +++ b/sdk/src/metrics/meter.cc @@ -95,9 +95,8 @@ namespace metrics = opentelemetry::metrics; metrics::NoopMeter Meter::kNoopMeter = metrics::NoopMeter(); -Meter::Meter( - std::weak_ptr meter_context, - std::unique_ptr instrumentation_scope) noexcept +Meter::Meter(std::weak_ptr meter_context, + std::unique_ptr instrumentation_scope) : scope_{std::move(instrumentation_scope)}, meter_context_{std::move(meter_context)}, observable_registry_(new ObservableRegistry()), diff --git a/sdk/src/metrics/meter_provider.cc b/sdk/src/metrics/meter_provider.cc index 98d510906c..c30d4b7627 100644 --- a/sdk/src/metrics/meter_provider.cc +++ b/sdk/src/metrics/meter_provider.cc @@ -3,11 +3,15 @@ #include #include +#include #include +#include #include #include #include "opentelemetry/common/key_value_iterable.h" // IWYU pragma: keep +#include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/noop.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #include "opentelemetry/nostd/string_view.h" @@ -39,25 +43,56 @@ namespace metrics namespace resource = opentelemetry::sdk::resource; namespace metrics_api = opentelemetry::metrics; +namespace +{ + +nostd::shared_ptr CreateNoopMeterFallback() +{ + return nostd::shared_ptr(new metrics_api::NoopMeter()); +} + +void LogGetMeterConstructionFailure(const char *detail) noexcept +{ +#if OPENTELEMETRY_HAVE_EXCEPTIONS + try + { +#endif + OTEL_INTERNAL_LOG_ERROR("[MeterProvider::GetMeter] Failed to construct meter: " + << detail << "; returning noop meter."); +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } + catch (const std::exception &) // NOLINT(bugprone-empty-catch) + { + // Logging can throw (typically std::bad_alloc from the string stream). + // Swallow it so the noexcept GetMeter path cannot throw. + } +#endif +} + +} // namespace + MeterProvider::MeterProvider() : context_(std::make_shared( std::make_unique(), resource::Resource::Create({}), std::make_unique>( instrumentationscope::ScopeConfigurator::Builder(MeterConfig::Default()) - .Build()))) + .Build()))), + noop_meter_(CreateNoopMeterFallback()) {} -MeterProvider::MeterProvider(std::unique_ptr context) noexcept - : context_(std::move(context)) +MeterProvider::MeterProvider(std::unique_ptr context) + : context_(std::move(context)), noop_meter_(CreateNoopMeterFallback()) {} -MeterProvider::MeterProvider(std::unique_ptr views, - const sdk::resource::Resource &resource, - std::unique_ptr> - meter_configurator) noexcept - : context_( - std::make_shared(std::move(views), resource, std::move(meter_configurator))) +MeterProvider::MeterProvider( + std::unique_ptr views, + const sdk::resource::Resource &resource, + std::unique_ptr> meter_configurator) + : context_(std::make_shared(std::move(views), + resource, + std::move(meter_configurator))), + noop_meter_(CreateNoopMeterFallback()) { OTEL_INTERNAL_LOG_DEBUG("[MeterProvider] MeterProvider created."); } @@ -96,13 +131,32 @@ nostd::shared_ptr MeterProvider::GetMeter( } } - instrumentationscope::InstrumentationScopeAttributes attrs_map(attributes); - auto scope = - instrumentationscope::InstrumentationScope::Create(name, version, schema_url, attrs_map); - - auto meter = std::shared_ptr(new Meter(context_, std::move(scope))); - context_->AddMeter(meter); - return nostd::shared_ptr{meter}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + try + { +#endif + instrumentationscope::InstrumentationScopeAttributes attrs_map(attributes); + auto scope = + instrumentationscope::InstrumentationScope::Create(name, version, schema_url, attrs_map); + + auto meter = std::make_shared(context_, std::move(scope)); + context_->AddMeter(meter); + return nostd::shared_ptr{meter}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } + catch (const std::exception &ex) + { + LogGetMeterConstructionFailure(ex.what()); + return noop_meter_; + } + // User-provided scope configurators can throw any exception type, not just + // std::exception. Catch everything so GetMeter stays noexcept. + catch (...) + { + LogGetMeterConstructionFailure("unknown exception"); + return noop_meter_; + } +#endif } #if OPENTELEMETRY_ABI_VERSION_NO >= 2 diff --git a/sdk/src/trace/tracer.cc b/sdk/src/trace/tracer.cc index 88e84b953d..8509dd6c5b 100644 --- a/sdk/src/trace/tracer.cc +++ b/sdk/src/trace/tracer.cc @@ -94,7 +94,7 @@ OPENTELEMETRY_BEGIN_NAMESPACE namespace sdk } // namespace Tracer::Tracer(std::shared_ptr context, - std::unique_ptr instrumentation_scope) noexcept + std::unique_ptr instrumentation_scope) : instrumentation_scope_{std::move(instrumentation_scope)}, context_{std::move(context)}, tracer_config_(context_->GetTracerConfigurator().ComputeConfig(*instrumentation_scope_)), diff --git a/sdk/src/trace/tracer_provider.cc b/sdk/src/trace/tracer_provider.cc index 47d3bb98b9..dae9996b6f 100644 --- a/sdk/src/trace/tracer_provider.cc +++ b/sdk/src/trace/tracer_provider.cc @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include +#include #include #include @@ -21,6 +23,8 @@ #include "opentelemetry/sdk/trace/tracer_config.h" #include "opentelemetry/sdk/trace/tracer_context.h" #include "opentelemetry/sdk/trace/tracer_provider.h" +#include "opentelemetry/trace/noop.h" +#include "opentelemetry/trace/tracer.h" #include "opentelemetry/version.h" OPENTELEMETRY_BEGIN_NAMESPACE @@ -31,8 +35,39 @@ namespace trace namespace resource = opentelemetry::sdk::resource; namespace trace_api = opentelemetry::trace; -TracerProvider::TracerProvider(std::unique_ptr context) noexcept - : context_(std::move(context)) +namespace +{ + +nostd::shared_ptr CreateNoopTracerFallback() +{ + // Construct as NoopTracer so enable_shared_from_this is initialized; StartSpan + // relies on shared_from_this() for the static noop span. + nostd::shared_ptr tracer(new trace_api::NoopTracer()); + return nostd::shared_ptr(std::move(tracer)); +} + +void LogGetTracerConstructionFailure(const char *detail) noexcept +{ +#if OPENTELEMETRY_HAVE_EXCEPTIONS + try + { +#endif + OTEL_INTERNAL_LOG_ERROR("[TracerProvider::GetTracer] Failed to construct tracer: " + << detail << "; returning noop tracer."); +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } + catch (const std::exception &) // NOLINT(bugprone-empty-catch) + { + // Logging can throw (typically std::bad_alloc from the string stream). + // Swallow it so the noexcept GetTracer path cannot throw. + } +#endif +} + +} // namespace + +TracerProvider::TracerProvider(std::unique_ptr context) + : context_(std::move(context)), noop_tracer_(CreateNoopTracerFallback()) { OTEL_INTERNAL_LOG_DEBUG("[TracerProvider] TracerProvider created."); } @@ -43,7 +78,8 @@ TracerProvider::TracerProvider( std::unique_ptr sampler, std::unique_ptr id_generator, std::unique_ptr> tracer_configurator, - SpanLimits span_limits) noexcept + SpanLimits span_limits) + : noop_tracer_(CreateNoopTracerFallback()) { std::vector> processors; processors.push_back(std::move(processor)); @@ -58,13 +94,14 @@ TracerProvider::TracerProvider( std::unique_ptr sampler, std::unique_ptr id_generator, std::unique_ptr> tracer_configurator, - SpanLimits span_limits) noexcept + SpanLimits span_limits) : context_(std::make_shared(std::move(processors), resource, std::move(sampler), std::move(id_generator), std::move(tracer_configurator), - span_limits)) + span_limits)), + noop_tracer_(CreateNoopTracerFallback()) {} TracerProvider::~TracerProvider() @@ -116,13 +153,32 @@ nostd::shared_ptr TracerProvider::GetTracer( } } - instrumentationscope::InstrumentationScopeAttributes attrs_map(attributes); - auto scope = - instrumentationscope::InstrumentationScope::Create(name, version, schema_url, attrs_map); - - auto tracer = std::shared_ptr(new Tracer(context_, std::move(scope))); - tracers_.push_back(tracer); - return nostd::shared_ptr{tracer}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + try + { +#endif + instrumentationscope::InstrumentationScopeAttributes attrs_map(attributes); + auto scope = + instrumentationscope::InstrumentationScope::Create(name, version, schema_url, attrs_map); + + auto tracer = std::make_shared(context_, std::move(scope)); + tracers_.push_back(tracer); + return nostd::shared_ptr{tracer}; +#if OPENTELEMETRY_HAVE_EXCEPTIONS + } + catch (const std::exception &ex) + { + LogGetTracerConstructionFailure(ex.what()); + return noop_tracer_; + } + // User-provided scope configurators can throw any exception type, not just + // std::exception. Catch everything so GetTracer stays noexcept. + catch (...) + { + LogGetTracerConstructionFailure("unknown exception"); + return noop_tracer_; + } +#endif } void TracerProvider::AddProcessor(std::unique_ptr processor) noexcept diff --git a/sdk/test/logs/logger_provider_sdk_test.cc b/sdk/test/logs/logger_provider_sdk_test.cc index 3db9dcefc2..b407440e98 100644 --- a/sdk/test/logs/logger_provider_sdk_test.cc +++ b/sdk/test/logs/logger_provider_sdk_test.cc @@ -6,8 +6,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -15,6 +17,7 @@ #include #include "opentelemetry/common/attribute_value.h" +#include "opentelemetry/common/macros.h" #include "opentelemetry/common/timestamp.h" #include "opentelemetry/logs/logger.h" #include "opentelemetry/logs/logger_provider.h" @@ -270,6 +273,29 @@ class DummyProcessor : public LogRecordProcessor bool ForceFlush(std::chrono::microseconds /* timeout */) noexcept override { return true; } bool Shutdown(std::chrono::microseconds /* timeout */) noexcept override { return true; } }; + +class CountingProcessor : public LogRecordProcessor +{ +public: + std::unique_ptr MakeRecordable() noexcept override + { + return std::unique_ptr(new DummyLogRecordable()); + } + + void OnEmit(std::unique_ptr &&record) noexcept override + { + auto record_ptr = std::move(record); + ++emit_count_; + } + + bool ForceFlush(std::chrono::microseconds /* timeout */) noexcept override { return true; } + bool Shutdown(std::chrono::microseconds /* timeout */) noexcept override { return true; } + + std::size_t emit_count() const { return emit_count_; } + +private: + std::size_t emit_count_{0}; +}; } // namespace TEST(LoggerProviderSDK, GetResource) @@ -641,3 +667,101 @@ TEST(LoggerProviderSDK, UpdateLoggerConfiguratorConcurrentEmit) stop.store(true, std::memory_order_relaxed); worker.join(); } + +TEST(LoggerProviderSDK, ConstructorsAreNotNoexcept) +{ + static_assert(!noexcept(LoggerProvider()), + "LoggerProvider construction must be allowed to throw"); + static_assert(!noexcept(LoggerProvider(std::unique_ptr{})), + "LoggerProvider construction must be allowed to throw"); + static_assert(!noexcept(Logger("", std::shared_ptr{})), + "Logger construction must be allowed to throw"); +} + +#if OPENTELEMETRY_HAVE_EXCEPTIONS +TEST(LoggerProviderSDK, GetLoggerReturnsNoopOnConstructionFailure) +{ + auto should_throw = std::make_shared(true); + auto throwing_configurator = std::make_unique>( + scope_sdk::ScopeConfigurator::Builder(LoggerConfig::Default()) + .AddCondition( + [should_throw](const scope_sdk::InstrumentationScope &scope) { + if (scope.GetName() == "throwing-scope" && *should_throw) + { + throw std::runtime_error("injected logger construction failure"); + } + return false; + }, + LoggerConfig::Default()) + .Build()); + + auto *counting_processor = new CountingProcessor(); + std::unique_ptr processor(counting_processor); + LoggerProvider provider(std::move(processor), opentelemetry::sdk::resource::Resource::Create({}), + std::move(throwing_configurator)); + + auto cached = provider.GetLogger("cached-logger", "cached-scope"); + ASSERT_NE(cached, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + ASSERT_NE(dynamic_cast(cached.get()), nullptr); +# endif + EXPECT_EQ(provider.GetLogger("cached-logger", "cached-scope"), cached); + + auto failed = provider.GetLogger("throwing-logger", "throwing-scope"); + ASSERT_NE(failed, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_EQ(dynamic_cast(failed.get()), nullptr); +# endif + EXPECT_EQ(failed->GetName(), "noop logger"); + failed->Info("should-not-emit"); + provider.ForceFlush(); + EXPECT_EQ(counting_processor->emit_count(), 0u); + + auto failed_again = provider.GetLogger("throwing-logger", "throwing-scope"); + EXPECT_EQ(failed, failed_again); + + *should_throw = false; + auto recovered = provider.GetLogger("throwing-logger", "throwing-scope"); + ASSERT_NE(recovered, nullptr); + EXPECT_NE(recovered, failed); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_NE(dynamic_cast(recovered.get()), nullptr); +# endif + recovered->Info("should-emit"); + provider.ForceFlush(); + EXPECT_EQ(counting_processor->emit_count(), 1u); + + EXPECT_EQ(provider.GetLogger("cached-logger", "cached-scope"), cached); +} + +TEST(LoggerProviderSDK, GetLoggerReturnsNoopOnNonStdConstructionFailure) +{ + auto throwing_configurator = std::make_unique>( + scope_sdk::ScopeConfigurator::Builder(LoggerConfig::Default()) + .AddCondition( + [](const scope_sdk::InstrumentationScope &scope) { + if (scope.GetName() == "throwing-scope") + { + throw 1; + } + return false; + }, + LoggerConfig::Default()) + .Build()); + + auto *counting_processor = new CountingProcessor(); + std::unique_ptr processor(counting_processor); + LoggerProvider provider(std::move(processor), opentelemetry::sdk::resource::Resource::Create({}), + std::move(throwing_configurator)); + + auto failed = provider.GetLogger("throwing-logger", "throwing-scope"); + ASSERT_NE(failed, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_EQ(dynamic_cast(failed.get()), nullptr); +# endif + EXPECT_EQ(failed->GetName(), "noop logger"); + failed->Info("should-not-emit"); + provider.ForceFlush(); + EXPECT_EQ(counting_processor->emit_count(), 0u); +} +#endif // OPENTELEMETRY_HAVE_EXCEPTIONS diff --git a/sdk/test/metrics/meter_provider_sdk_test.cc b/sdk/test/metrics/meter_provider_sdk_test.cc index 501273b597..a8bcca1917 100644 --- a/sdk/test/metrics/meter_provider_sdk_test.cc +++ b/sdk/test/metrics/meter_provider_sdk_test.cc @@ -2,7 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 #include +#include #include +#include #include #include #include @@ -14,10 +16,15 @@ #include "opentelemetry/common/macros.h" #include "opentelemetry/metrics/meter.h" +#include "opentelemetry/metrics/sync_instruments.h" #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/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/meter_provider.h" #include "opentelemetry/sdk/metrics/meter_provider_factory.h" #include "opentelemetry/sdk/metrics/metric_reader.h" @@ -25,20 +32,14 @@ #include "opentelemetry/sdk/metrics/view/instrument_selector.h" #include "opentelemetry/sdk/metrics/view/meter_selector.h" #include "opentelemetry/sdk/metrics/view/view.h" +#include "opentelemetry/sdk/metrics/view/view_registry.h" +#include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/test_common/sdk/common/scoped_test_log_handler.h" #ifdef ENABLE_METRICS_EXEMPLAR_PREVIEW # include "opentelemetry/sdk/metrics/exemplar/filter_type.h" # include "opentelemetry/sdk/metrics/meter_context_factory.h" -# ifndef NO_GETENV -# 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/view/view_registry.h" -# include "opentelemetry/sdk/resource/resource.h" -# endif - # if defined(_MSC_VER) && !defined(NO_GETENV) # include "opentelemetry/sdk/common/env_variables.h" using opentelemetry::sdk::common::setenv; @@ -55,7 +56,6 @@ using opentelemetry::sdk::common::unsetenv; # include "opentelemetry/common/attribute_value.h" # include "opentelemetry/nostd/utility.h" # include "opentelemetry/nostd/variant.h" -# include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #endif /* OPENTELEMETRY_ABI_VERSION_NO >= 2 */ using namespace opentelemetry::sdk::metrics; @@ -520,3 +520,101 @@ TEST(MeterProvider, ExplicitShutdownNotWarnOnDestructionCheck) logs = log_handler.Drain(); EXPECT_TRUE(logs.empty()); } + +TEST(MeterProvider, ConstructorsAreNotNoexcept) +{ + static_assert(!noexcept(MeterProvider()), "MeterProvider construction must be allowed to throw"); + static_assert(!noexcept(MeterProvider(std::unique_ptr{})), + "MeterProvider construction must be allowed to throw"); + static_assert(!noexcept(Meter(std::weak_ptr{})), + "Meter construction must be allowed to throw"); +} + +#if OPENTELEMETRY_HAVE_EXCEPTIONS +TEST(MeterProvider, GetMeterReturnsNoopOnConstructionFailure) +{ + auto should_throw = std::make_shared(true); + auto throwing_configurator = + std::make_unique>( + opentelemetry::sdk::instrumentationscope::ScopeConfigurator::Builder( + MeterConfig::Default()) + .AddCondition( + [should_throw]( + const opentelemetry::sdk::instrumentationscope::InstrumentationScope &scope) { + if (scope.GetName() == "throwing-scope" && *should_throw) + { + throw std::runtime_error("injected meter construction failure"); + } + return false; + }, + MeterConfig::Default()) + .Build()); + + MeterProvider provider(std::unique_ptr(new ViewRegistry()), + opentelemetry::sdk::resource::Resource::Create({}), + std::move(throwing_configurator)); + + auto cached = provider.GetMeter("cached-scope"); + ASSERT_NE(cached, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + ASSERT_NE(dynamic_cast(cached.get()), nullptr); +# endif + EXPECT_EQ(provider.GetMeter("cached-scope"), cached); + + auto failed = provider.GetMeter("throwing-scope"); + ASSERT_NE(failed, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_EQ(dynamic_cast(failed.get()), nullptr); +# endif + auto failed_counter = failed->CreateUInt64Counter("requests"); + ASSERT_NE(failed_counter, nullptr); + failed_counter->Add(1); + + auto failed_again = provider.GetMeter("throwing-scope"); + EXPECT_EQ(failed, failed_again); + + *should_throw = false; + auto recovered = provider.GetMeter("throwing-scope"); + ASSERT_NE(recovered, nullptr); + EXPECT_NE(recovered, failed); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_NE(dynamic_cast(recovered.get()), nullptr); +# endif + auto recovered_counter = recovered->CreateUInt64Counter("requests"); + ASSERT_NE(recovered_counter, nullptr); + recovered_counter->Add(1); + + EXPECT_EQ(provider.GetMeter("cached-scope"), cached); +} + +TEST(MeterProvider, GetMeterReturnsNoopOnNonStdConstructionFailure) +{ + auto throwing_configurator = + std::make_unique>( + opentelemetry::sdk::instrumentationscope::ScopeConfigurator::Builder( + MeterConfig::Default()) + .AddCondition( + [](const opentelemetry::sdk::instrumentationscope::InstrumentationScope &scope) { + if (scope.GetName() == "throwing-scope") + { + throw 1; + } + return false; + }, + MeterConfig::Default()) + .Build()); + + MeterProvider provider(std::unique_ptr(new ViewRegistry()), + opentelemetry::sdk::resource::Resource::Create({}), + std::move(throwing_configurator)); + + auto failed = provider.GetMeter("throwing-scope"); + ASSERT_NE(failed, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_EQ(dynamic_cast(failed.get()), nullptr); +# endif + auto failed_counter = failed->CreateUInt64Counter("requests"); + ASSERT_NE(failed_counter, nullptr); + failed_counter->Add(1); +} +#endif // OPENTELEMETRY_HAVE_EXCEPTIONS diff --git a/sdk/test/trace/tracer_provider_test.cc b/sdk/test/trace/tracer_provider_test.cc index 09868fd687..072b6254cd 100644 --- a/sdk/test/trace/tracer_provider_test.cc +++ b/sdk/test/trace/tracer_provider_test.cc @@ -6,16 +6,19 @@ #include #include #include +#include #include #include +#include +#include #include #include #include #include #include "opentelemetry/common/macros.h" +#include "opentelemetry/exporters/memory/in_memory_span_data.h" #include "opentelemetry/exporters/memory/in_memory_span_exporter.h" -#include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" #include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" @@ -29,6 +32,7 @@ #include "opentelemetry/sdk/trace/samplers/always_on.h" #include "opentelemetry/sdk/trace/simple_processor.h" #include "opentelemetry/sdk/trace/simple_processor_factory.h" +#include "opentelemetry/sdk/trace/span_data.h" #include "opentelemetry/sdk/trace/span_limits.h" #include "opentelemetry/sdk/trace/tracer.h" #include "opentelemetry/sdk/trace/tracer_config.h" @@ -56,6 +60,7 @@ using opentelemetry::sdk::common::unsetenv; using namespace opentelemetry::sdk::trace; using namespace opentelemetry::sdk::resource; +using opentelemetry::sdk::instrumentationscope::InstrumentationScope; using opentelemetry::sdk::instrumentationscope::ScopeConfigurator; TEST(TracerProvider, GetTracer) @@ -699,3 +704,108 @@ TEST(TracerProvider, SpanLimitsTracerProviderFactoryCreateDefault) EXPECT_EQ(limits.link_attribute_count_limit, no_limits.link_attribute_count_limit); EXPECT_EQ(limits.attribute_value_length_limit, no_limits.attribute_value_length_limit); } + +TEST(TracerProvider, ConstructorsAreNotNoexcept) +{ + static_assert(!noexcept(TracerProvider(std::unique_ptr{})), + "TracerProvider construction must be allowed to throw"); + static_assert(!noexcept(Tracer(std::shared_ptr{})), + "Tracer construction must be allowed to throw"); +} + +#if OPENTELEMETRY_HAVE_EXCEPTIONS +TEST(TracerProvider, GetTracerReturnsNoopOnConstructionFailure) +{ + auto should_throw = std::make_shared(true); + auto throwing_configurator = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()) + .AddCondition( + [should_throw](const InstrumentationScope &scope) { + if (scope.GetName() == "throwing-scope" && *should_throw) + { + throw std::runtime_error("injected tracer construction failure"); + } + return false; + }, + TracerConfig::Default()) + .Build()); + + std::unique_ptr exporter( + new opentelemetry::exporter::memory::InMemorySpanExporter()); + auto span_data = exporter->GetData(); + auto processor = SimpleSpanProcessorFactory::Create(std::move(exporter)); + TracerProvider provider( + std::move(processor), Resource::Create({}), std::unique_ptr(new AlwaysOnSampler()), + std::unique_ptr(new RandomIdGenerator()), std::move(throwing_configurator)); + + auto cached = provider.GetTracer("cached-scope"); + ASSERT_NE(cached, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + ASSERT_NE(dynamic_cast(cached.get()), nullptr); +# endif + auto cached_again = provider.GetTracer("cached-scope"); + EXPECT_EQ(cached, cached_again); + + auto failed = provider.GetTracer("throwing-scope"); + ASSERT_NE(failed, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_EQ(dynamic_cast(failed.get()), nullptr); +# endif + auto failed_span = failed->StartSpan("should-not-record"); + ASSERT_NE(failed_span, nullptr); + EXPECT_FALSE(failed_span->IsRecording()); + failed_span->End(); + provider.ForceFlush(); + EXPECT_EQ(span_data->GetSpans().size(), 0u); + + auto failed_again = provider.GetTracer("throwing-scope"); + EXPECT_EQ(failed, failed_again); + + *should_throw = false; + auto recovered = provider.GetTracer("throwing-scope"); + ASSERT_NE(recovered, nullptr); + EXPECT_NE(recovered, failed); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_NE(dynamic_cast(recovered.get()), nullptr); +# endif + auto recovered_span = recovered->StartSpan("should-record"); + EXPECT_TRUE(recovered_span->IsRecording()); + recovered_span->End(); + provider.ForceFlush(); + EXPECT_EQ(span_data->GetSpans().size(), 1u); + + EXPECT_EQ(provider.GetTracer("cached-scope"), cached); +} + +TEST(TracerProvider, GetTracerReturnsNoopOnNonStdConstructionFailure) +{ + auto throwing_configurator = std::make_unique>( + ScopeConfigurator::Builder(TracerConfig::Default()) + .AddCondition( + [](const InstrumentationScope &scope) { + if (scope.GetName() == "throwing-scope") + { + throw 1; + } + return false; + }, + TracerConfig::Default()) + .Build()); + + std::unique_ptr exporter( + new opentelemetry::exporter::memory::InMemorySpanExporter()); + auto processor = SimpleSpanProcessorFactory::Create(std::move(exporter)); + TracerProvider provider( + std::move(processor), Resource::Create({}), std::unique_ptr(new AlwaysOnSampler()), + std::unique_ptr(new RandomIdGenerator()), std::move(throwing_configurator)); + + auto failed = provider.GetTracer("throwing-scope"); + ASSERT_NE(failed, nullptr); +# ifdef OPENTELEMETRY_RTTI_ENABLED + EXPECT_EQ(dynamic_cast(failed.get()), nullptr); +# endif + auto failed_span = failed->StartSpan("should-not-record"); + ASSERT_NE(failed_span, nullptr); + EXPECT_FALSE(failed_span->IsRecording()); +} +#endif // OPENTELEMETRY_HAVE_EXCEPTIONS