diff --git a/CHANGELOG.md b/CHANGELOG.md index c51a88bb02..0664817783 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,9 @@ Increment the: * [DEVCONTAINER] Support customization and run as non-root user [#3270](https://github.com/open-telemetry/opentelemetry-cpp/pull/3270) +* [SDK] Add logger scope configurator + [#3282](https://github.com/open-telemetry/opentelemetry-cpp/pull/3282) + Important changes: * [SDK] Support OTEL_SDK_DISABLED environment variable diff --git a/sdk/include/opentelemetry/sdk/logs/logger.h b/sdk/include/opentelemetry/sdk/logs/logger.h index ed43d1ce55..cb8ff6ab35 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger.h +++ b/sdk/include/opentelemetry/sdk/logs/logger.h @@ -6,8 +6,10 @@ #include #include +#include "logger_config.h" #include "opentelemetry/logs/log_record.h" #include "opentelemetry/logs/logger.h" +#include "opentelemetry/logs/noop.h" #include "opentelemetry/nostd/string_view.h" #include "opentelemetry/nostd/unique_ptr.h" #include "opentelemetry/sdk/instrumentationscope/instrumentation_scope.h" @@ -65,6 +67,8 @@ class Logger final : public opentelemetry::logs::Logger // logger-context. std::unique_ptr instrumentation_scope_; std::shared_ptr context_; + LoggerConfig logger_config_; + static opentelemetry::logs::NoopLogger kNoopLogger; }; } // namespace logs diff --git a/sdk/include/opentelemetry/sdk/logs/logger_config.h b/sdk/include/opentelemetry/sdk/logs/logger_config.h new file mode 100644 index 0000000000..6504102995 --- /dev/null +++ b/sdk/include/opentelemetry/sdk/logs/logger_config.h @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include "opentelemetry/version.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ +/** + * LoggerConfig defines various configurable aspects of a Logger's behavior. + * This class should not be used directly to configure a Logger's behavior, instead a + * ScopeConfigurator should be used to compute the desired LoggerConfig which can then be used to + * configure a Logger. + */ +class OPENTELEMETRY_EXPORT LoggerConfig +{ +public: + bool operator==(const LoggerConfig &other) const noexcept; + + /** + * Returns if the Logger is enabled or disabled. Loggers are enabled by default. + * @return a boolean indicating if the Logger is enabled. Defaults to true. + */ + bool IsEnabled() const noexcept; + + /** + * Returns a LoggerConfig that represents an enabled Logger. + * @return a static constant LoggerConfig that represents an enabled logger. + */ + static LoggerConfig Enabled(); + + /** + * Returns a LoggerConfig that represents a disabled Logger. A disabled logger behaves like a + * no-op logger. + * @return a static constant LoggerConfig that represents a disabled logger. + */ + static LoggerConfig Disabled(); + + /** + * Returns a LoggerConfig that represents a Logger configured with the default behavior. + * The default behavior is guided by the OpenTelemetry specification. + * @return a static constant LoggerConfig that represents a logger configured with default + * behavior. + */ + static LoggerConfig Default(); + +private: + explicit LoggerConfig(const bool disabled = false) : disabled_(disabled) {} + + bool disabled_; +}; +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/logs/logger_context.h b/sdk/include/opentelemetry/sdk/logs/logger_context.h index 11eca41d16..038da2d948 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_context.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_context.h @@ -7,6 +7,8 @@ #include #include +#include "logger_config.h" +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/resource/resource.h" #include "opentelemetry/version.h" @@ -33,9 +35,15 @@ namespace logs class LoggerContext { public: - explicit LoggerContext(std::vector> &&processors, - const opentelemetry::sdk::resource::Resource &resource = - opentelemetry::sdk::resource::Resource::Create({})) noexcept; + explicit LoggerContext( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource = + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr> logger_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder( + LoggerConfig::Default()) + .Build())) noexcept; /** * Attaches a log processor to list of configured processors to this logger context. @@ -61,6 +69,13 @@ class LoggerContext */ const opentelemetry::sdk::resource::Resource &GetResource() const noexcept; + /** + * Obtain the ScopeConfigurator with this logger context. + * @return The ScopeConfigurator for this logger context. + */ + const instrumentationscope::ScopeConfigurator &GetLoggerConfigurator() + const noexcept; + /** * Force all active LogProcessors to flush any buffered logs * within the given timeout. @@ -76,6 +91,8 @@ class LoggerContext // order of declaration is important here - resource object should be destroyed after processor. opentelemetry::sdk::resource::Resource resource_; std::unique_ptr processor_; + + std::unique_ptr> logger_configurator_; }; } // namespace logs } // namespace sdk diff --git a/sdk/include/opentelemetry/sdk/logs/logger_context_factory.h b/sdk/include/opentelemetry/sdk/logs/logger_context_factory.h index 5565be8a62..333c76f511 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_context_factory.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_context_factory.h @@ -24,17 +24,36 @@ class LoggerContextFactory { public: /** - * Create a LoggerContext. + * Create a LoggerContext with specified LogRecordProcessors. + * @param processors A vector of log processors that can parse the logs. + * @return A unique pointer to the created LoggerContext object. */ static std::unique_ptr Create( std::vector> &&processors); /** - * Create a LoggerContext. + * Create a LoggerContext with specified LogRecordProcessors and OpenTelemetry resource. + * @param processors A vector of log processors that can parse the logs. + * @param resource The OpenTelemetry resource responsible for generating the logs. + * @return A unique pointer to the created LoggerContext object. */ static std::unique_ptr Create( std::vector> &&processors, const opentelemetry::sdk::resource::Resource &resource); + + /** + * Create a LoggerContext with specified LogRecordProcessors, OpenTelemetry resource and a logger + * ScopeConfigurator. + * @param processors A vector of log processors that can parse the logs. + * @param resource The OpenTelemetry resource responsible for generating the logs. + * @param logger_configurator A ScopeConfigurator that can be used compute the LoggerConfig for a + * given InstrumentationScope. + * @return A unique pointer to the created LoggerContext object. + */ + static std::unique_ptr Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> logger_configurator); }; } // namespace logs diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider.h b/sdk/include/opentelemetry/sdk/logs/logger_provider.h index b3e217e2e8..ee5165ebd2 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_provider.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider.h @@ -32,18 +32,37 @@ class OPENTELEMETRY_EXPORT LoggerProvider final : public opentelemetry::logs::Lo { public: /** - * Initialize a new logger provider - * @param processor The span processor for this logger provider. This must + * Initialize a new logger provider. + * @param processor The log record processor for this logger provider. This must * not be a nullptr. * @param resource The resources for this logger provider. */ - explicit LoggerProvider(std::unique_ptr &&processor, - const opentelemetry::sdk::resource::Resource &resource = - opentelemetry::sdk::resource::Resource::Create({})) noexcept; + explicit LoggerProvider( + std::unique_ptr &&processor, + const opentelemetry::sdk::resource::Resource &resource = + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr> logger_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder( + LoggerConfig::Default()) + .Build())) noexcept; - explicit LoggerProvider(std::vector> &&processors, - const opentelemetry::sdk::resource::Resource &resource = - opentelemetry::sdk::resource::Resource::Create({})) noexcept; + /** + * Initialize a new logger provider. + * @param processors A list of log record processors for this logger provider. + * @param resource The resources for this logger provider. + * @param logger_configurator The scope configurator used to determine the configs for loggers + * created using this logger provider. + */ + explicit LoggerProvider( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource = + opentelemetry::sdk::resource::Resource::Create({}), + std::unique_ptr> logger_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder( + LoggerConfig::Default()) + .Build())) noexcept; /** * Initialize a new logger provider. A processor must later be assigned diff --git a/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h b/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h index 490393209c..6745b39cee 100644 --- a/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h +++ b/sdk/include/opentelemetry/sdk/logs/logger_provider_factory.h @@ -37,6 +37,14 @@ class OPENTELEMETRY_EXPORT LoggerProviderFactory std::unique_ptr &&processor, const opentelemetry::sdk::resource::Resource &resource); + /** + * Create a LoggerProvider. + */ + static std::unique_ptr Create( + std::unique_ptr &&processor, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> logger_configurator); + /** * Create a LoggerProvider. */ @@ -50,6 +58,14 @@ class OPENTELEMETRY_EXPORT LoggerProviderFactory std::vector> &&processors, const opentelemetry::sdk::resource::Resource &resource); + /** + * Create a LoggerProvider. + */ + static std::unique_ptr Create( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> logger_configurator); + /** * Create a LoggerProvider. */ diff --git a/sdk/src/logs/CMakeLists.txt b/sdk/src/logs/CMakeLists.txt index 05dbab211b..178b28f830 100644 --- a/sdk/src/logs/CMakeLists.txt +++ b/sdk/src/logs/CMakeLists.txt @@ -15,6 +15,7 @@ add_library( simple_log_record_processor_factory.cc batch_log_record_processor.cc batch_log_record_processor_factory.cc + logger_config.cc logger_context.cc logger_context_factory.cc multi_log_record_processor.cc diff --git a/sdk/src/logs/logger.cc b/sdk/src/logs/logger.cc index eca45c55fb..1c0ce3ab26 100644 --- a/sdk/src/logs/logger.cc +++ b/sdk/src/logs/logger.cc @@ -9,12 +9,15 @@ #include "opentelemetry/context/context_value.h" #include "opentelemetry/context/runtime_context.h" #include "opentelemetry/logs/log_record.h" +#include "opentelemetry/logs/noop.h" #include "opentelemetry/nostd/shared_ptr.h" #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/logs/logger.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/recordable.h" @@ -31,22 +34,34 @@ namespace logs namespace trace_api = opentelemetry::trace; namespace common = opentelemetry::common; +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_name_(std::string(name)), instrumentation_scope_(std::move(instrumentation_scope)), - context_(std::move(context)) + context_(std::move(context)), + logger_config_(context_->GetLoggerConfigurator().ComputeConfig(*instrumentation_scope_)) {} const opentelemetry::nostd::string_view Logger::GetName() noexcept { + if (!logger_config_.IsEnabled()) + { + return kNoopLogger.GetName(); + } return logger_name_; } opentelemetry::nostd::unique_ptr Logger::CreateLogRecord() noexcept { + if (!logger_config_.IsEnabled()) + { + return kNoopLogger.CreateLogRecord(); + } + auto recordable = context_->GetProcessor().MakeRecordable(); recordable->SetObservedTimestamp(std::chrono::system_clock::now()); @@ -90,6 +105,11 @@ opentelemetry::nostd::unique_ptr Logger::CreateL void Logger::EmitLogRecord( opentelemetry::nostd::unique_ptr &&log_record) noexcept { + if (!logger_config_.IsEnabled()) + { + return kNoopLogger.EmitLogRecord(std::move(log_record)); + } + if (!log_record) { return; diff --git a/sdk/src/logs/logger_config.cc b/sdk/src/logs/logger_config.cc new file mode 100644 index 0000000000..2bc8aeb5f4 --- /dev/null +++ b/sdk/src/logs/logger_config.cc @@ -0,0 +1,41 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/logs/logger_config.h" + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace sdk +{ +namespace logs +{ + +OPENTELEMETRY_EXPORT bool LoggerConfig::operator==(const LoggerConfig &other) const noexcept +{ + return disabled_ == other.disabled_; +} + +OPENTELEMETRY_EXPORT bool LoggerConfig::IsEnabled() const noexcept +{ + return !disabled_; +} + +OPENTELEMETRY_EXPORT LoggerConfig LoggerConfig::Enabled() +{ + return Default(); +} + +OPENTELEMETRY_EXPORT LoggerConfig LoggerConfig::Disabled() +{ + static const auto kDisabledConfig = LoggerConfig(true); + return kDisabledConfig; +} + +OPENTELEMETRY_EXPORT LoggerConfig LoggerConfig::Default() +{ + static const auto kDefaultConfig = LoggerConfig(); + return kDefaultConfig; +} + +} // namespace logs +} // namespace sdk +OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/src/logs/logger_context.cc b/sdk/src/logs/logger_context.cc index a6eb063661..f7ad9db0c6 100644 --- a/sdk/src/logs/logger_context.cc +++ b/sdk/src/logs/logger_context.cc @@ -6,6 +6,8 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/multi_log_record_processor.h" #include "opentelemetry/sdk/logs/processor.h" @@ -19,10 +21,13 @@ namespace logs { LoggerContext::LoggerContext(std::vector> &&processors, - const opentelemetry::sdk::resource::Resource &resource) noexcept + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> + logger_configurator) noexcept : resource_(resource), processor_( - std::unique_ptr(new MultiLogRecordProcessor(std::move(processors)))) + std::unique_ptr(new MultiLogRecordProcessor(std::move(processors)))), + logger_configurator_(std::move(logger_configurator)) {} void LoggerContext::AddProcessor(std::unique_ptr processor) noexcept @@ -41,6 +46,12 @@ const opentelemetry::sdk::resource::Resource &LoggerContext::GetResource() const return resource_; } +const instrumentationscope::ScopeConfigurator &LoggerContext::GetLoggerConfigurator() + const noexcept +{ + return *logger_configurator_; +} + bool LoggerContext::ForceFlush(std::chrono::microseconds timeout) noexcept { return processor_->ForceFlush(timeout); diff --git a/sdk/src/logs/logger_context_factory.cc b/sdk/src/logs/logger_context_factory.cc index f11852be7f..ccdfe87e8d 100644 --- a/sdk/src/logs/logger_context_factory.cc +++ b/sdk/src/logs/logger_context_factory.cc @@ -5,6 +5,8 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/logger_context_factory.h" #include "opentelemetry/sdk/logs/processor.h" @@ -28,7 +30,20 @@ std::unique_ptr LoggerContextFactory::Create( std::vector> &&processors, const opentelemetry::sdk::resource::Resource &resource) { - std::unique_ptr context(new LoggerContext(std::move(processors), resource)); + auto logger_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(LoggerConfig::Default()) + .Build()); + return Create(std::move(processors), resource, std::move(logger_configurator)); +} + +std::unique_ptr LoggerContextFactory::Create( + std::vector> &&processors, + const resource::Resource &resource, + std::unique_ptr> logger_configurator) +{ + std::unique_ptr context( + new LoggerContext(std::move(processors), resource, std::move(logger_configurator))); return context; } diff --git a/sdk/src/logs/logger_provider.cc b/sdk/src/logs/logger_provider.cc index da282ab655..159fd2b165 100644 --- a/sdk/src/logs/logger_provider.cc +++ b/sdk/src/logs/logger_provider.cc @@ -13,7 +13,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/logs/logger.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/processor.h" @@ -26,18 +28,27 @@ namespace sdk namespace logs { -LoggerProvider::LoggerProvider(std::unique_ptr &&processor, - const opentelemetry::sdk::resource::Resource &resource) noexcept +LoggerProvider::LoggerProvider( + std::unique_ptr &&processor, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> + logger_configurator) noexcept { std::vector> processors; processors.emplace_back(std::move(processor)); - context_ = std::make_shared(std::move(processors), resource); + context_ = std::make_shared(std::move(processors), resource, + std::move(logger_configurator)); OTEL_INTERNAL_LOG_DEBUG("[LoggerProvider] LoggerProvider created."); } -LoggerProvider::LoggerProvider(std::vector> &&processors, - const opentelemetry::sdk::resource::Resource &resource) noexcept - : context_{std::make_shared(std::move(processors), resource)} +LoggerProvider::LoggerProvider( + std::vector> &&processors, + const opentelemetry::sdk::resource::Resource &resource, + std::unique_ptr> + logger_configurator) noexcept + : context_{std::make_shared(std::move(processors), + resource, + std::move(logger_configurator))} {} LoggerProvider::LoggerProvider() noexcept diff --git a/sdk/src/logs/logger_provider_factory.cc b/sdk/src/logs/logger_provider_factory.cc index 530d1c2db4..f80dd43b5d 100644 --- a/sdk/src/logs/logger_provider_factory.cc +++ b/sdk/src/logs/logger_provider_factory.cc @@ -5,6 +5,8 @@ #include #include +#include "opentelemetry/sdk/instrumentationscope/scope_configurator.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_context.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/logger_provider_factory.h" @@ -28,9 +30,21 @@ std::unique_ptr LoggerProviderFactory: std::unique_ptr LoggerProviderFactory::Create( std::unique_ptr &&processor, const opentelemetry::sdk::resource::Resource &resource) +{ + auto logger_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(LoggerConfig::Default()) + .Build()); + return Create(std::move(processor), resource, std::move(logger_configurator)); +} + +std::unique_ptr LoggerProviderFactory::Create( + std::unique_ptr &&processor, + const resource::Resource &resource, + std::unique_ptr> logger_configurator) { std::unique_ptr provider( - new LoggerProvider(std::move(processor), resource)); + new LoggerProvider(std::move(processor), resource, std::move(logger_configurator))); return provider; } @@ -44,9 +58,21 @@ std::unique_ptr LoggerProviderFactory: std::unique_ptr LoggerProviderFactory::Create( std::vector> &&processors, const opentelemetry::sdk::resource::Resource &resource) +{ + auto logger_configurator = + std::make_unique>( + instrumentationscope::ScopeConfigurator::Builder(LoggerConfig::Default()) + .Build()); + return Create(std::move(processors), resource, std::move(logger_configurator)); +} + +std::unique_ptr LoggerProviderFactory::Create( + std::vector> &&processors, + const resource::Resource &resource, + std::unique_ptr> logger_configurator) { std::unique_ptr provider( - new LoggerProvider(std::move(processors), resource)); + new LoggerProvider(std::move(processors), resource, std::move(logger_configurator))); return provider; } diff --git a/sdk/test/logs/BUILD b/sdk/test/logs/BUILD index e47cceca74..26c837ea89 100644 --- a/sdk/test/logs/BUILD +++ b/sdk/test/logs/BUILD @@ -93,3 +93,18 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "logger_config_test", + srcs = [ + "logger_config_test.cc", + ], + tags = [ + "logs", + "test", + ], + deps = [ + "//sdk/src/logs", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/sdk/test/logs/CMakeLists.txt b/sdk/test/logs/CMakeLists.txt index 1164e8b5b7..8ef79df68f 100644 --- a/sdk/test/logs/CMakeLists.txt +++ b/sdk/test/logs/CMakeLists.txt @@ -3,9 +3,13 @@ foreach( testname - logger_provider_set_test logger_provider_sdk_test logger_sdk_test - log_record_test simple_log_record_processor_test - batch_log_record_processor_test) + logger_provider_set_test + logger_provider_sdk_test + logger_sdk_test + log_record_test + simple_log_record_processor_test + batch_log_record_processor_test + logger_config_test) add_executable(${testname} "${testname}.cc") target_link_libraries( ${testname} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} diff --git a/sdk/test/logs/logger_config_test.cc b/sdk/test/logs/logger_config_test.cc new file mode 100644 index 0000000000..f9fd21ac5c --- /dev/null +++ b/sdk/test/logs/logger_config_test.cc @@ -0,0 +1,95 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +#include "opentelemetry/sdk/logs/logger_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 logs_sdk = opentelemetry::sdk::logs; +namespace instrumentation_scope = opentelemetry::sdk::instrumentationscope; + +/** Test to verify the basic behavior of logs_sdk::LoggerConfig */ + +TEST(LoggerConfig, CheckDisabledWorksAsExpected) +{ + logs_sdk::LoggerConfig disabled_config = logs_sdk::LoggerConfig::Disabled(); + ASSERT_FALSE(disabled_config.IsEnabled()); +} + +TEST(LoggerConfig, CheckEnabledWorksAsExpected) +{ + logs_sdk::LoggerConfig enabled_config = logs_sdk::LoggerConfig::Enabled(); + ASSERT_TRUE(enabled_config.IsEnabled()); +} + +TEST(LoggerConfig, CheckDefaultConfigWorksAccToSpec) +{ + logs_sdk::LoggerConfig default_config = logs_sdk::LoggerConfig::Default(); + ASSERT_TRUE(default_config.IsEnabled()); +} + +/** Tests to verify the behavior of logs_sdk::LoggerConfig::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 DefaultLoggerConfiguratorTestFixture + : public ::testing::TestWithParam +{}; + +// verifies that the default configurator always returns the default meter config +TEST_P(DefaultLoggerConfiguratorTestFixture, VerifyDefaultConfiguratorBehavior) +{ + instrumentation_scope::InstrumentationScope *scope = GetParam(); + instrumentation_scope::ScopeConfigurator default_configurator = + instrumentation_scope::ScopeConfigurator::Builder( + logs_sdk::LoggerConfig::Default()) + .Build(); + + ASSERT_EQ(default_configurator.ComputeConfig(*scope), logs_sdk::LoggerConfig::Default()); +} + +INSTANTIATE_TEST_SUITE_P(InstrumentationScopes, + DefaultLoggerConfiguratorTestFixture, + ::testing::ValuesIn(instrumentation_scopes)); diff --git a/sdk/test/logs/logger_sdk_test.cc b/sdk/test/logs/logger_sdk_test.cc index 53f9cf4638..b4bdf2f420 100644 --- a/sdk/test/logs/logger_sdk_test.cc +++ b/sdk/test/logs/logger_sdk_test.cc @@ -3,7 +3,10 @@ #include #include +#include +#include #include +#include #include #include #include @@ -12,16 +15,20 @@ #include "opentelemetry/common/timestamp.h" #include "opentelemetry/logs/event_logger.h" #include "opentelemetry/logs/event_logger_provider.h" +#include "opentelemetry/logs/log_record.h" #include "opentelemetry/logs/logger.h" #include "opentelemetry/logs/logger_provider.h" +#include "opentelemetry/logs/noop.h" #include "opentelemetry/logs/severity.h" #include "opentelemetry/nostd/shared_ptr.h" #include "opentelemetry/nostd/span.h" #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/logs/event_logger_provider.h" #include "opentelemetry/sdk/logs/logger.h" +#include "opentelemetry/sdk/logs/logger_config.h" #include "opentelemetry/sdk/logs/logger_provider.h" #include "opentelemetry/sdk/logs/processor.h" #include "opentelemetry/sdk/logs/recordable.h" @@ -38,6 +45,7 @@ #include "opentelemetry/trace/tracer.h" using namespace opentelemetry::sdk::logs; +using namespace opentelemetry::sdk::instrumentationscope; namespace logs_api = opentelemetry::logs; namespace nostd = opentelemetry::nostd; @@ -275,6 +283,237 @@ TEST(LoggerSDK, LogToAProcessor) now); } +TEST(LoggerSDK, LoggerWithDisabledConfig) +{ + ScopeConfigurator disabled_all_scopes = + ScopeConfigurator::Builder(LoggerConfig::Disabled()).Build(); + // Set a processor for the LoggerProvider + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto log_processor = std::unique_ptr(new MockProcessor(shared_recordable)); + + // Create an API LoggerProvider and logger + const auto resource = opentelemetry::sdk::resource::Resource::Create({}); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto api_lp = std::shared_ptr( + new LoggerProvider(std::move(log_processor), resource, + std::make_unique>(disabled_all_scopes))); + auto logger = api_lp->GetLogger("logger", "opentelelemtry_library", "", schema_url); + + auto noop_logger = logs_api::NoopLogger(); + + // Test Logger functions for the constructed logger + // This logger should behave like a noop logger + ASSERT_EQ(logger->GetName(), noop_logger.GetName()); + + // Since the logger is disabled, when creating a LogRecord, the observed timestamp will not be + // set in the underlying LogRecordable + auto log_record = logger->CreateLogRecord(); + logger->EmitLogRecord(std::move(log_record)); + ASSERT_EQ(shared_recordable->GetObservedTimestamp(), std::chrono::system_clock::from_time_t(0)); + + // Since this logger should behave like a noop logger, no values within the recordable would be + // set. + logger->EmitLogRecord(logs_api::Severity::kWarn, "Log Message"); + ASSERT_EQ(shared_recordable->GetBody(), ""); + ASSERT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kInvalid); + ASSERT_EQ(shared_recordable->GetObservedTimestamp(), std::chrono::system_clock::from_time_t(0)); +} + +TEST(LoggerSDK, LoggerWithEnabledConfig) +{ + ScopeConfigurator enabled_all_scopes = + ScopeConfigurator::Builder(LoggerConfig::Enabled()).Build(); + // Set a processor for the LoggerProvider + auto shared_recordable = std::shared_ptr(new MockLogRecordable()); + auto log_processor = std::unique_ptr(new MockProcessor(shared_recordable)); + + // Create an API LoggerProvider and logger + const auto resource = opentelemetry::sdk::resource::Resource::Create({}); + const std::string schema_url{"https://opentelemetry.io/schemas/1.11.0"}; + auto api_lp = std::shared_ptr( + new LoggerProvider(std::move(log_processor), resource, + std::make_unique>(enabled_all_scopes))); + auto logger = api_lp->GetLogger("test-logger", "opentelemetry_library", "", schema_url); + + // Test Logger functions for the constructed logger + ASSERT_EQ(logger->GetName(), "test-logger"); + + // Since the logger is enabled, when creating a LogRecord, the observed timestamp will be set + // in the underlying LogRecordable. + auto reference_ts = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + auto log_record = logger->CreateLogRecord(); + logger->EmitLogRecord(std::move(log_record)); + // Since log_record was created after recording reference timestamp, expect that observed + // timestamp is greater + ASSERT_GE(shared_recordable->GetObservedTimestamp().time_since_epoch().count(), + reference_ts.count()); + + // Since this logger should behave like a valid logger, values within the recordable would be set. + logger->EmitLogRecord(logs_api::Severity::kWarn, "Log Message"); + ASSERT_EQ(shared_recordable->GetBody(), "Log Message"); + ASSERT_EQ(shared_recordable->GetSeverity(), opentelemetry::logs::Severity::kWarn); + ASSERT_GE(shared_recordable->GetObservedTimestamp().time_since_epoch().count(), + reference_ts.count()); +} + +static std::unique_ptr create_mock_log_recordable( + const std::string &body, + opentelemetry::logs::Severity severity) +{ + auto mock_log_recordable = std::make_unique(); + mock_log_recordable->SetBody(body); + mock_log_recordable->SetSeverity(severity); + return mock_log_recordable; +} + +class CustomLogConfiguratorTestData +{ +public: + InstrumentationScope instrumentation_scope_; + MockLogRecordable test_log_recordable_; + MockLogRecordable expected_log_recordable_; + bool expected_disabled_for_scope_; + + CustomLogConfiguratorTestData(const InstrumentationScope &instrumentation_scope, + const MockLogRecordable &test_log_recordable, + const MockLogRecordable &expected_log_recordable, + const bool expected_disabled_for_scope) + : instrumentation_scope_(instrumentation_scope), + test_log_recordable_(test_log_recordable), + expected_log_recordable_(expected_log_recordable), + expected_disabled_for_scope_(expected_disabled_for_scope) + {} +}; + +// constants used in VerifyCustomConfiguratorBehavior test +static auto noop_logger = logs_api::NoopLogger(); +const std::string schema{"https://opentelemetry.io/schemas/1.11.0"}; + +// Generate test case data +// Test Case 1 +static auto instrumentation_scope_1 = + *InstrumentationScope::Create("opentelemetry_library", "1.0.0", schema); +static auto test_log_recordable_1 = + create_mock_log_recordable("Log Message", opentelemetry::logs::Severity::kWarn); +static auto expected_log_recordable_1 = + create_mock_log_recordable("Log Message", opentelemetry::logs::Severity::kWarn); +static auto custom_log_configurator_test_data_1 = + CustomLogConfiguratorTestData(instrumentation_scope_1, + *test_log_recordable_1, + *expected_log_recordable_1, + false); +// Test Case 2 +static auto instrumentation_scope_2 = *InstrumentationScope::Create("bar_library", "1.0.0", schema); +static auto test_log_recordable_2 = + create_mock_log_recordable("", opentelemetry::logs::Severity::kDebug); +static auto expected_log_recordable_2 = + create_mock_log_recordable("", opentelemetry::logs::Severity::kDebug); +static auto custom_log_configurator_test_data_2 = + CustomLogConfiguratorTestData(instrumentation_scope_2, + *test_log_recordable_2, + *expected_log_recordable_2, + false); +// Test Case 3 +static auto instrumentation_scope_3 = *InstrumentationScope::Create("foo_library", "", schema); +static auto test_log_recordable_3 = + create_mock_log_recordable("Info message", opentelemetry::logs::Severity::kInfo); +static auto expected_log_recordable_3 = + create_mock_log_recordable("", opentelemetry::logs::Severity::kInvalid); +static auto custom_log_configurator_test_data_3 = + CustomLogConfiguratorTestData(instrumentation_scope_3, + *test_log_recordable_3, + *expected_log_recordable_3, + true); +// Test Case 4 +static auto instrumentation_scope_4 = *InstrumentationScope::Create("allowed_library", "", schema); +static auto test_log_recordable_4 = + create_mock_log_recordable("Scope version missing", opentelemetry::logs::Severity::kInfo); +static auto expected_log_recordable_4 = + create_mock_log_recordable("", opentelemetry::logs::Severity::kInvalid); +static auto custom_log_configurator_test_data_4 = + CustomLogConfiguratorTestData(instrumentation_scope_4, + *test_log_recordable_4, + *expected_log_recordable_4, + true); + +// 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. +constexpr std::array log_configurator_test_cases = { + &custom_log_configurator_test_data_1, &custom_log_configurator_test_data_2, + &custom_log_configurator_test_data_3, &custom_log_configurator_test_data_4}; + +// Test fixture for VerifyCustomConfiguratorBehavior +class CustomLoggerConfiguratorTestFixture + : public ::testing::TestWithParam +{}; + +TEST_P(CustomLoggerConfiguratorTestFixture, VerifyCustomConfiguratorBehavior) +{ + // lambda checks if version is present in scope information + auto check_if_version_present = [](const InstrumentationScope &scope_info) { + return !scope_info.GetVersion().empty(); + }; + // custom scope configurator that only disables loggers for library name "foo_library" or do not + // have version information + auto test_scope_configurator = ScopeConfigurator( + ScopeConfigurator::Builder(LoggerConfig::Disabled()) + .AddConditionNameEquals("foo_library", LoggerConfig::Disabled()) + .AddCondition(check_if_version_present, LoggerConfig::Enabled()) + .Build()); + + // Get the test case data from fixture + CustomLogConfiguratorTestData *test_case = GetParam(); + auto test_instrumentation_scope = test_case->instrumentation_scope_; + auto test_log_recordable = test_case->test_log_recordable_; + + // Set a processor for the LoggerProvider + auto shared_recordable_under_test = std::shared_ptr(new MockLogRecordable()); + auto log_processor_test = + std::unique_ptr(new MockProcessor(shared_recordable_under_test)); + + // Create an API LoggerProvider and logger + const auto resource = opentelemetry::sdk::resource::Resource::Create({}); + auto api_lp = std::shared_ptr(new LoggerProvider( + std::move(log_processor_test), resource, + std::make_unique>(test_scope_configurator))); + + // Create logger and make assertions + auto logger_under_test = api_lp->GetLogger("test-logger", test_instrumentation_scope.GetName(), + test_instrumentation_scope.GetVersion(), + test_instrumentation_scope.GetSchemaURL()); + auto reference_ts = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()); + auto log_record = logger_under_test->CreateLogRecord(); + logger_under_test->EmitLogRecord(std::move(log_record)); + + // Test Logger functions for the constructed logger + if (test_case->expected_disabled_for_scope_) + { + ASSERT_EQ(logger_under_test->GetName(), noop_logger.GetName()); + ASSERT_EQ(shared_recordable_under_test->GetObservedTimestamp(), + std::chrono::system_clock::from_time_t(0)); + } + else + { + ASSERT_EQ(logger_under_test->GetName(), "test-logger"); + ASSERT_GE(shared_recordable_under_test->GetObservedTimestamp().time_since_epoch().count(), + reference_ts.count()); + } + + logger_under_test->EmitLogRecord(test_log_recordable.GetBody(), + test_log_recordable.GetSeverity()); + ASSERT_EQ(shared_recordable_under_test->GetBody(), test_case->expected_log_recordable_.GetBody()); + ASSERT_EQ(shared_recordable_under_test->GetSeverity(), + test_case->expected_log_recordable_.GetSeverity()); +} + +INSTANTIATE_TEST_SUITE_P(CustomLogConfiguratorTestData, + CustomLoggerConfiguratorTestFixture, + ::testing::ValuesIn(log_configurator_test_cases)); + #if OPENTELEMETRY_ABI_VERSION_NO < 2 TEST(LoggerSDK, EventLog) {