diff --git a/logs_api/lib/opentelemetry/logs/logger.rb b/logs_api/lib/opentelemetry/logs/logger.rb index c6caa34a7..6374b296b 100644 --- a/logs_api/lib/opentelemetry/logs/logger.rb +++ b/logs_api/lib/opentelemetry/logs/logger.rb @@ -8,6 +8,11 @@ module OpenTelemetry module Logs # No-op implementation of logger. class Logger + def initialize + @mutex = Mutex.new + @instrument_registry = {} + end + # rubocop:disable Style/EmptyMethod # Emit a {LogRecord} to the processing pipeline. diff --git a/logs_api/lib/opentelemetry/logs/logger_provider.rb b/logs_api/lib/opentelemetry/logs/logger_provider.rb index 13343407d..9c9b9c79e 100644 --- a/logs_api/lib/opentelemetry/logs/logger_provider.rb +++ b/logs_api/lib/opentelemetry/logs/logger_provider.rb @@ -9,7 +9,9 @@ module Logs # No-op implementation of a logger provider. class LoggerProvider NOOP_LOGGER = OpenTelemetry::Logs::Logger.new - private_constant :NOOP_LOGGER + # TODO: Make NOOP_LOGGER private again + # NOOP_LOGGER is used in the SDK logger at this time until the ProxyLogger has been created + # private_constant :NOOP_LOGGER # Returns an {OpenTelemetry::Logs::Logger} instance. # diff --git a/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb b/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb index 99e7df10e..3a3c10856 100644 --- a/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb +++ b/logs_sdk/lib/opentelemetry/sdk/logs/logger_provider.rb @@ -9,6 +9,9 @@ module SDK module Logs # The SDK implementation of OpenTelemetry::Logs::LoggerProvider. class LoggerProvider < OpenTelemetry::Logs::LoggerProvider + Key = Struct.new(:name, :version) + private_constant(:Key) + UNEXPECTED_ERROR_MESSAGE = 'unexpected error in ' \ 'OpenTelemetry::SDK::Logs::LoggerProvider#%s' @@ -25,6 +28,8 @@ def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) @mutex = Mutex.new @resource = resource @stopped = false + @registry = {} + @registry_mutex = Mutex.new end # Returns an {OpenTelemetry::SDK::Logs::Logger} instance. @@ -34,14 +39,22 @@ def initialize(resource: OpenTelemetry::SDK::Resources::Resource.create) # # @return [OpenTelemetry::SDK::Logs::Logger] def logger(name:, version: nil) - version ||= '' + if @stopped + # TODO: Return a ProxyLogger instead once that's available + OpenTelemetry.logger.warn('calling LoggerProvider#logger after shutdown, a noop logger will be returned') + OpenTelemetry::Logs::LoggerProvider::NOOP_LOGGER + else + version ||= '' + + if !name.is_a?(String) || name.empty? + OpenTelemetry.logger.warn('LoggerProvider#logger called with an ' \ + "invalid name. Name provided: #{name.inspect}") + end - if !name.is_a?(String) || name.empty? - OpenTelemetry.logger.warn('LoggerProvider#logger called with an ' \ - "invalid name. Name provided: #{name.inspect}") + @registry_mutex.synchronize do + @registry[Key.new(name, version)] ||= Logger.new(name, version, self) + end end - - Logger.new(name, version, self) end # Adds a new log record processor to this LoggerProvider's diff --git a/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb b/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb index 023a9a67c..e9b5380da 100644 --- a/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb +++ b/logs_sdk/test/opentelemetry/sdk/logs/logger_provider_test.rb @@ -215,4 +215,40 @@ def mock_context.value(key); OpenTelemetry::Trace::Span::INVALID; end # rubocop: end end end + + describe 'instrument registry' do + # On the first call, create a logger with an empty string for name and + # version and add to the registry. The second call returns that logger + # from the registry instead of creating a new instance. + it 'reuses the same logger if called twice when name and version are nil' do + logger = logger_provider.logger(name: nil, version: nil) + logger2 = logger_provider.logger(name: nil, version: nil) + + assert_instance_of(Logs::Logger, logger) + assert_same(logger, logger2) + end + + describe 'when stopped' do + it 'logs a warning' do + logger_provider.instance_variable_set(:@stopped, true) + + OpenTelemetry::TestHelpers.with_test_logger do |log_stream| + logger_provider.logger(name: '') + assert_match( + /calling LoggerProvider#logger after shutdown/, + log_stream.string + ) + end + end + + it 'does not add a new logger to the registry' do + before_stopped_size = logger_provider.instance_variable_get(:@registry).keys.size + logger_provider.instance_variable_set(:@stopped, true) + logger_provider.logger(name: 'new_logger') + after_stopped_size = logger_provider.instance_variable_get(:@registry).keys.size + + assert_equal(before_stopped_size, after_stopped_size) + end + end + end end