From 89614f08dd4b2f00d196d7effe3bd53bec52ae15 Mon Sep 17 00:00:00 2001 From: George Ogata Date: Wed, 22 Mar 2023 01:14:05 -0400 Subject: [PATCH] Expose the logdev The motivation here is to fix a Rails issue caused by the way ActiveSupport extends the ruby Logger to add broadcasting of messages to multiple destinations. [1] I believe the problem could be much more elegantly solved if Logger exposed the underlying LogDevice. Going by repo history, the existence of this object hasn't changed since 2003, so I think it's stable enough to expose. In addition to letting you read the logdev, this also lets you pass a logdev in. To implement broadcasting, we could now define a LogDevice subclass that delegates its 3 methods to the LogDevices of a list of underlying loggers, and simply create a new logger with this device. [1]: https://github.com/rails/rails/blob/ba19dbc49956a73f417abd68c7a5f33e302eacd3/activesupport/lib/active_support/logger.rb#L23 --- lib/logger.rb | 19 +++++++++++++++---- test/logger/test_logger.rb | 10 ++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/lib/logger.rb b/lib/logger.rb index 4be5c33..707fdff 100644 --- a/lib/logger.rb +++ b/lib/logger.rb @@ -545,6 +545,7 @@ def fatal!; self.level = FATAL; end # new entries are appended. # - An IO stream (typically +$stdout+, +$stderr+. or an open file): # entries are to be written to the given stream. + # - An instance of Logger::LogDevice, such as the #logdev of another Logger. # - +nil+ or +File::NULL+: no entries are to be written. # # Examples: @@ -586,13 +587,23 @@ def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG, @logdev = nil @level_override = {} if logdev && logdev != File::NULL - @logdev = LogDevice.new(logdev, shift_age: shift_age, - shift_size: shift_size, - shift_period_suffix: shift_period_suffix, - binmode: binmode) + if logdev.is_a?(LogDevice) + @logdev = logdev + else + @logdev = LogDevice.new(logdev, shift_age: shift_age, + shift_size: shift_size, + shift_period_suffix: shift_period_suffix, + binmode: binmode) + end end end + # The underlying log device. + # + # This is the first argument passed to the constructor, wrapped in a + # Logger::LogDevice, along with the binmode flag and rotation options. + attr_reader :logdev + # Sets the logger's output stream: # # - If +logdev+ is +nil+, reopens the current output stream. diff --git a/test/logger/test_logger.rb b/test/logger/test_logger.rb index 37d0f58..aefe5e6 100644 --- a/test/logger/test_logger.rb +++ b/test/logger/test_logger.rb @@ -168,6 +168,16 @@ def test_initialize assert_nil(logger.datetime_format) end + def test_logdev + logger = Logger.new(STDERR) + assert_instance_of(Logger::LogDevice, logger.logdev) + + logdev = Logger::LogDevice.new(STDERR) + logger = Logger.new(logdev) + assert_instance_of(Logger::LogDevice, logger.logdev) + assert_equal(STDERR, logger.logdev.dev) + end + def test_initialize_with_level # default logger = Logger.new(STDERR)