Merge pull request #48615 from Edouard-chin/ec-logger
Add a public API for broadcasting logs
This commit is contained in:
commit
4c72cc2b04
@ -67,7 +67,7 @@ class Railtie < Rails::Railtie # :nodoc:
|
||||
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
|
||||
console = ActiveSupport::Logger.new(STDERR)
|
||||
console.level = Rails.logger.level
|
||||
Rails.logger.extend ActiveSupport::Logger.broadcast console
|
||||
Rails.logger = ActiveSupport::BroadcastLogger.new(Rails.logger, console)
|
||||
end
|
||||
ActiveRecord.verbose_query_logs = false
|
||||
end
|
||||
|
@ -1,3 +1,38 @@
|
||||
* Add a new public API for broadcasting logs
|
||||
|
||||
This feature existed for a while but was until now a private API.
|
||||
Broadcasting log allows to send log message to difference sinks (STDOUT, a file ...) and
|
||||
is used by default in the development environment to write logs both on STDOUT and in the
|
||||
"development.log" file.
|
||||
|
||||
Basic usage:
|
||||
|
||||
```ruby
|
||||
stdout_logger = Logger.new(STDOUT)
|
||||
file_logger = Logger.new("development.log")
|
||||
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger, file_logger)
|
||||
|
||||
broadcast.info("Hello!") # The "Hello!" message is written on STDOUT and in the log file.
|
||||
```
|
||||
|
||||
Adding other sink(s) to the broadcast:
|
||||
|
||||
```ruby
|
||||
broadcast = ActiveSupport::BroadcastLogger.new
|
||||
broadcast.broadcast_to(Logger.new(STDERR))
|
||||
```
|
||||
|
||||
Remove a sink from the broadcast:
|
||||
|
||||
```ruby
|
||||
stdout_logger = Logger.new(STDOUT)
|
||||
broadcast = ActiveSupport::BroadcastLogger.new(stdout_logger)
|
||||
|
||||
broadcast.stop_broadcasting_to(stdout_logger)
|
||||
```
|
||||
|
||||
*Edouard Chin*
|
||||
|
||||
* Fix Range#overlap? not taking empty ranges into account on Ruby < 3.3
|
||||
|
||||
*Nobuyoshi Nakada*, *Shouichi Kamiya*, *Hartley McGuire*
|
||||
|
@ -28,6 +28,7 @@
|
||||
require "active_support/version"
|
||||
require "active_support/deprecator"
|
||||
require "active_support/logger"
|
||||
require "active_support/broadcast_logger"
|
||||
require "active_support/lazy_load_hooks"
|
||||
require "active_support/core_ext/date_and_time/compatibility"
|
||||
|
||||
|
206
activesupport/lib/active_support/broadcast_logger.rb
Normal file
206
activesupport/lib/active_support/broadcast_logger.rb
Normal file
@ -0,0 +1,206 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ActiveSupport
|
||||
# = Active Support Broadcast Logger
|
||||
#
|
||||
# The Broadcast logger is a logger used to write messages to multiple IO. It is commonly used
|
||||
# in development to display messages on STDOUT and also write them to a file (development.log).
|
||||
# With the Broadcast logger, you can broadcast your logs to a unlimited number of sinks.
|
||||
#
|
||||
# The BroadcastLogger acts as a standard logger and all methods you are used to are available.
|
||||
# However, all the methods on this logger will propagate and be delegated to the other loggers
|
||||
# that are part of the broadcast.
|
||||
#
|
||||
# Broadcasting your logs.
|
||||
#
|
||||
# stdout_logger = Logger.new(STDOUT)
|
||||
# file_logger = Logger.new("development.log")
|
||||
# broadcast = BroadcastLogger.new(stdout_logger, file_logger)
|
||||
#
|
||||
# broadcast.info("Hello world!") # Writes the log to STDOUT and the development.log file.
|
||||
#
|
||||
# Add a logger to the broadcast.
|
||||
#
|
||||
# stdout_logger = Logger.new(STDOUT)
|
||||
# broadcast = BroadcastLogger.new(stdout_logger)
|
||||
# file_logger = Logger.new("development.log")
|
||||
# broadcast.broadcast_to(file_logger)
|
||||
#
|
||||
# broadcast.info("Hello world!") # Writes the log to STDOUT and the development.log file.
|
||||
#
|
||||
# Modifying the log level for all broadcasted loggers.
|
||||
#
|
||||
# stdout_logger = Logger.new(STDOUT)
|
||||
# file_logger = Logger.new("development.log")
|
||||
# broadcast = BroadcastLogger.new(stdout_logger, file_logger)
|
||||
#
|
||||
# broadcast.level = Logger::FATAL # Modify the log level for the whole broadcast.
|
||||
#
|
||||
# Stop broadcasting log to a sink.
|
||||
#
|
||||
# stdout_logger = Logger.new(STDOUT)
|
||||
# file_logger = Logger.new("development.log")
|
||||
# broadcast = BroadcastLogger.new(stdout_logger, file_logger)
|
||||
# broadcast.info("Hello world!") # Writes the log to STDOUT and the development.log file.
|
||||
#
|
||||
# broadcast.stop_broadcasting_to(file_logger)
|
||||
# broadcast.info("Hello world!") # Writes the log *only* to STDOUT.
|
||||
#
|
||||
# At least one sink has to be part of the broadcast. Otherwise, your logs will not
|
||||
# be written anywhere. For instance:
|
||||
#
|
||||
# broadcast = BroadcastLogger.new
|
||||
# broadcast.info("Hello world") # The log message will appear nowhere.
|
||||
class BroadcastLogger
|
||||
include ActiveSupport::LoggerSilence
|
||||
|
||||
# Returns all the logger that are part of this broadcast.
|
||||
attr_reader :broadcasts
|
||||
attr_reader :formatter
|
||||
attr_accessor :progname
|
||||
|
||||
def initialize(*loggers)
|
||||
@broadcasts = []
|
||||
@progname = "Broadcast"
|
||||
|
||||
broadcast_to(*loggers)
|
||||
end
|
||||
|
||||
# Add logger(s) to the broadcast.
|
||||
#
|
||||
# broadcast_logger = ActiveSupport::BroadcastLogger.new
|
||||
# broadcast_logger.broadcast_to(Logger.new(STDOUT), Logger.new(STDERR))
|
||||
def broadcast_to(*loggers)
|
||||
@broadcasts.concat(loggers)
|
||||
end
|
||||
|
||||
# Remove a logger from the broadcast. When a logger is removed, messages sent to
|
||||
# the broadcast will no longer be written to its sink.
|
||||
#
|
||||
# sink = Logger.new(STDOUT)
|
||||
# broadcast_logger = ActiveSupport::BroadcastLogger.new
|
||||
#
|
||||
# broadcast_logger.stop_broadcasting_to(sink)
|
||||
def stop_broadcasting_to(logger)
|
||||
@broadcasts.delete(logger)
|
||||
end
|
||||
|
||||
def level
|
||||
@broadcasts.map(&:level).min
|
||||
end
|
||||
|
||||
def <<(message)
|
||||
dispatch { |logger| logger.<<(message) }
|
||||
end
|
||||
|
||||
def add(*args, &block)
|
||||
dispatch { |logger| logger.add(*args, &block) }
|
||||
end
|
||||
alias_method :log, :add
|
||||
|
||||
def debug(*args, &block)
|
||||
dispatch { |logger| logger.debug(*args, &block) }
|
||||
end
|
||||
|
||||
def info(*args, &block)
|
||||
dispatch { |logger| logger.info(*args, &block) }
|
||||
end
|
||||
|
||||
def warn(*args, &block)
|
||||
dispatch { |logger| logger.warn(*args, &block) }
|
||||
end
|
||||
|
||||
def error(*args, &block)
|
||||
dispatch { |logger| logger.error(*args, &block) }
|
||||
end
|
||||
|
||||
def fatal(*args, &block)
|
||||
dispatch { |logger| logger.fatal(*args, &block) }
|
||||
end
|
||||
|
||||
def unknown(*args, &block)
|
||||
dispatch { |logger| logger.unknown(*args, &block) }
|
||||
end
|
||||
|
||||
def formatter=(formatter)
|
||||
dispatch { |logger| logger.formatter = formatter }
|
||||
|
||||
@formatter = formatter
|
||||
end
|
||||
|
||||
def level=(level)
|
||||
dispatch { |logger| logger.level = level }
|
||||
end
|
||||
alias_method :sev_threshold=, :level=
|
||||
|
||||
def local_level=(level)
|
||||
dispatch do |logger|
|
||||
logger.local_level = level if logger.respond_to?(:local_level=)
|
||||
end
|
||||
end
|
||||
|
||||
def close
|
||||
dispatch { |logger| logger.close }
|
||||
end
|
||||
|
||||
# +True+ if the log level allows entries with severity Logger::DEBUG to be written
|
||||
# to at least one broadcast. +False+ otherwise.
|
||||
def debug?
|
||||
@broadcasts.any? { |logger| logger.debug? }
|
||||
end
|
||||
|
||||
# Sets the log level to Logger::DEBUG for the whole broadcast.
|
||||
def debug!
|
||||
dispatch { |logger| logger.debug! }
|
||||
end
|
||||
|
||||
# +True+ if the log level allows entries with severity Logger::INFO to be written
|
||||
# to at least one broadcast. +False+ otherwise.
|
||||
def info?
|
||||
@broadcasts.any? { |logger| logger.info? }
|
||||
end
|
||||
|
||||
# Sets the log level to Logger::INFO for the whole broadcast.
|
||||
def info!
|
||||
dispatch { |logger| logger.info! }
|
||||
end
|
||||
|
||||
# +True+ if the log level allows entries with severity Logger::WARN to be written
|
||||
# to at least one broadcast. +False+ otherwise.
|
||||
def warn?
|
||||
@broadcasts.any? { |logger| logger.warn? }
|
||||
end
|
||||
|
||||
# Sets the log level to Logger::WARN for the whole broadcast.
|
||||
def warn!
|
||||
dispatch { |logger| logger.warn! }
|
||||
end
|
||||
|
||||
# +True+ if the log level allows entries with severity Logger::ERROR to be written
|
||||
# to at least one broadcast. +False+ otherwise.
|
||||
def error?
|
||||
@broadcasts.any? { |logger| logger.error? }
|
||||
end
|
||||
|
||||
# Sets the log level to Logger::ERROR for the whole broadcast.
|
||||
def error!
|
||||
dispatch { |logger| logger.error! }
|
||||
end
|
||||
|
||||
# +True+ if the log level allows entries with severity Logger::FATAL to be written
|
||||
# to at least one broadcast. +False+ otherwise.
|
||||
def fatal?
|
||||
@broadcasts.any? { |logger| logger.fatal? }
|
||||
end
|
||||
|
||||
# Sets the log level to Logger::FATAL for the whole broadcast.
|
||||
def fatal!
|
||||
dispatch { |logger| logger.fatal! }
|
||||
end
|
||||
|
||||
private
|
||||
def dispatch(&block)
|
||||
@broadcasts.each { |logger| block.call(logger) }
|
||||
end
|
||||
end
|
||||
end
|
@ -19,64 +19,6 @@ def self.logger_outputs_to?(logger, *sources)
|
||||
sources.any? { |source| source == logger_source }
|
||||
end
|
||||
|
||||
# Broadcasts logs to multiple loggers.
|
||||
def self.broadcast(logger) # :nodoc:
|
||||
Module.new do
|
||||
define_method(:add) do |*args, &block|
|
||||
logger.add(*args, &block)
|
||||
super(*args, &block)
|
||||
end
|
||||
|
||||
define_method(:<<) do |x|
|
||||
logger << x
|
||||
super(x)
|
||||
end
|
||||
|
||||
define_method(:close) do
|
||||
logger.close
|
||||
super()
|
||||
end
|
||||
|
||||
define_method(:progname=) do |name|
|
||||
logger.progname = name
|
||||
super(name)
|
||||
end
|
||||
|
||||
define_method(:formatter=) do |formatter|
|
||||
logger.formatter = formatter
|
||||
super(formatter)
|
||||
end
|
||||
|
||||
define_method(:level=) do |level|
|
||||
logger.level = level
|
||||
super(level)
|
||||
end
|
||||
|
||||
define_method(:local_level=) do |level|
|
||||
logger.local_level = level if logger.respond_to?(:local_level=)
|
||||
super(level) if respond_to?(:local_level=)
|
||||
end
|
||||
|
||||
define_method(:silence) do |level = Logger::ERROR, &block|
|
||||
if logger.respond_to?(:silence)
|
||||
logger.silence(level) do
|
||||
if defined?(super)
|
||||
super(level, &block)
|
||||
else
|
||||
block.call(self)
|
||||
end
|
||||
end
|
||||
else
|
||||
if defined?(super)
|
||||
super(level, &block)
|
||||
else
|
||||
block.call(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(*args, **kwargs)
|
||||
super
|
||||
@formatter ||= SimpleFormatter.new
|
||||
|
@ -9,8 +9,7 @@ class BroadcastLoggerTest < TestCase
|
||||
setup do
|
||||
@log1 = FakeLogger.new
|
||||
@log2 = FakeLogger.new
|
||||
@log1.extend Logger.broadcast @log2
|
||||
@logger = @log1
|
||||
@logger = BroadcastLogger.new(@log1, @log2)
|
||||
end
|
||||
|
||||
Logger::Severity.constants.each do |level_name|
|
||||
@ -40,23 +39,40 @@ class BroadcastLoggerTest < TestCase
|
||||
end
|
||||
|
||||
test "#level= assigns the level to all loggers" do
|
||||
assert_equal ::Logger::DEBUG, logger.level
|
||||
assert_equal ::Logger::DEBUG, log1.level
|
||||
logger.level = ::Logger::FATAL
|
||||
|
||||
assert_equal ::Logger::FATAL, log1.level
|
||||
assert_equal ::Logger::FATAL, log2.level
|
||||
end
|
||||
|
||||
test "#progname= assigns to all the loggers" do
|
||||
assert_nil logger.progname
|
||||
logger.progname = ::Logger::FATAL
|
||||
test "#level returns the level of the logger with the lowest level" do
|
||||
log1.level = Logger::DEBUG
|
||||
|
||||
assert_equal ::Logger::FATAL, log1.progname
|
||||
assert_equal ::Logger::FATAL, log2.progname
|
||||
assert_equal(Logger::DEBUG, logger.level)
|
||||
|
||||
log1.level = Logger::FATAL
|
||||
log2.level = Logger::INFO
|
||||
|
||||
assert_equal(Logger::INFO, logger.level)
|
||||
end
|
||||
|
||||
test "#progname returns Broadcast literally when the user didn't change the progname" do
|
||||
assert_equal("Broadcast", logger.progname)
|
||||
end
|
||||
|
||||
test "#progname= sets the progname on the Broadcast logger but doesn't modify the inner loggers" do
|
||||
assert_nil(log1.progname)
|
||||
assert_nil(log2.progname)
|
||||
|
||||
logger.progname = "Foo"
|
||||
|
||||
assert_equal("Foo", logger.progname)
|
||||
assert_nil(log1.progname)
|
||||
assert_nil(log2.progname)
|
||||
end
|
||||
|
||||
test "#formatter= assigns to all the loggers" do
|
||||
assert_nil logger.formatter
|
||||
logger.formatter = ::Logger::FATAL
|
||||
|
||||
assert_equal ::Logger::FATAL, log1.formatter
|
||||
@ -64,24 +80,42 @@ class BroadcastLoggerTest < TestCase
|
||||
end
|
||||
|
||||
test "#local_level= assigns the local_level to all loggers" do
|
||||
assert_equal ::Logger::DEBUG, logger.local_level
|
||||
assert_equal ::Logger::DEBUG, log1.local_level
|
||||
logger.local_level = ::Logger::FATAL
|
||||
|
||||
assert_equal ::Logger::FATAL, log1.local_level
|
||||
assert_equal ::Logger::FATAL, log2.local_level
|
||||
end
|
||||
|
||||
test "severity methods get called on all loggers" do
|
||||
my_logger = Class.new(::Logger) do
|
||||
attr_reader :info_called
|
||||
|
||||
def info(msg, &block)
|
||||
@info_called = true
|
||||
end
|
||||
end.new(StringIO.new)
|
||||
|
||||
@logger.broadcast_to(my_logger)
|
||||
|
||||
assert_changes(-> { my_logger.info_called }, from: nil, to: true) do
|
||||
@logger.info("message")
|
||||
end
|
||||
ensure
|
||||
@logger.stop_broadcasting_to(my_logger)
|
||||
end
|
||||
|
||||
test "#silence does not break custom loggers" do
|
||||
new_logger = FakeLogger.new
|
||||
custom_logger = CustomLogger.new
|
||||
assert_respond_to new_logger, :silence
|
||||
assert_not_respond_to custom_logger, :silence
|
||||
|
||||
custom_logger.extend(Logger.broadcast(new_logger))
|
||||
logger = BroadcastLogger.new(custom_logger, new_logger)
|
||||
|
||||
custom_logger.silence do
|
||||
custom_logger.error "from error"
|
||||
custom_logger.unknown "from unknown"
|
||||
logger.silence do
|
||||
logger.error "from error"
|
||||
logger.unknown "from unknown"
|
||||
end
|
||||
|
||||
assert_equal [[::Logger::ERROR, "from error", nil], [::Logger::UNKNOWN, "from unknown", nil]], custom_logger.adds
|
||||
@ -117,6 +151,99 @@ class BroadcastLoggerTest < TestCase
|
||||
assert_equal [[::Logger::FATAL, "seen", nil]], log2.adds
|
||||
end
|
||||
|
||||
test "stop broadcasting to a logger" do
|
||||
@logger.stop_broadcasting_to(@log2)
|
||||
|
||||
@logger.info("Hello")
|
||||
|
||||
assert_equal([[1, "Hello", nil]], @log1.adds)
|
||||
assert_empty(@log2.adds)
|
||||
end
|
||||
|
||||
test "#broadcast on another broadcasted logger" do
|
||||
@log3 = FakeLogger.new
|
||||
@log4 = FakeLogger.new
|
||||
@broadcast2 = ActiveSupport::BroadcastLogger.new(@log3, @log4)
|
||||
|
||||
@logger.broadcast_to(@broadcast2)
|
||||
@logger.info("Hello")
|
||||
|
||||
assert_equal([[1, "Hello", nil]], @log1.adds)
|
||||
assert_equal([[1, "Hello", nil]], @log2.adds)
|
||||
assert_equal([[1, "Hello", nil]], @log3.adds)
|
||||
assert_equal([[1, "Hello", nil]], @log4.adds)
|
||||
end
|
||||
|
||||
test "#debug? is true when at least one logger's level is at or above DEBUG level" do
|
||||
@log1.level = Logger::DEBUG
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_predicate(@logger, :debug?)
|
||||
end
|
||||
|
||||
test "#debug? is false when all loggers are below DEBUG level" do
|
||||
@log1.level = Logger::ERROR
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_not_predicate(@logger, :debug?)
|
||||
end
|
||||
|
||||
test "#info? is true when at least one logger's level is at or above INFO level" do
|
||||
@log1.level = Logger::DEBUG
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_predicate(@logger, :info?)
|
||||
end
|
||||
|
||||
test "#info? is false when all loggers are below INFO" do
|
||||
@log1.level = Logger::ERROR
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_not_predicate(@logger, :info?)
|
||||
end
|
||||
|
||||
test "#warn? is true when at least one logger's level is at or above WARN level" do
|
||||
@log1.level = Logger::DEBUG
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_predicate(@logger, :warn?)
|
||||
end
|
||||
|
||||
test "#warn? is false when all loggers are below WARN" do
|
||||
@log1.level = Logger::ERROR
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_not_predicate(@logger, :warn?)
|
||||
end
|
||||
|
||||
test "#error? is true when at least one logger's level is at or above ERROR level" do
|
||||
@log1.level = Logger::DEBUG
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_predicate(@logger, :error?)
|
||||
end
|
||||
|
||||
test "#error? is false when all loggers are below ERROR" do
|
||||
@log1.level = Logger::FATAL
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_not_predicate(@logger, :error?)
|
||||
end
|
||||
|
||||
test "#fatal? is true when at least one logger's level is at or above FATAL level" do
|
||||
@log1.level = Logger::DEBUG
|
||||
@log2.level = Logger::FATAL
|
||||
|
||||
assert_predicate(@logger, :fatal?)
|
||||
end
|
||||
|
||||
test "#fatal? is false when all loggers are below FATAL" do
|
||||
@log1.level = Logger::UNKNOWN
|
||||
@log2.level = Logger::UNKNOWN
|
||||
|
||||
assert_not_predicate(@logger, :fatal?)
|
||||
end
|
||||
|
||||
class CustomLogger
|
||||
attr_reader :adds, :closed, :chevrons
|
||||
attr_accessor :level, :progname, :formatter, :local_level
|
||||
|
@ -176,13 +176,13 @@ def test_logger_silencing_works_for_broadcast
|
||||
another_output = StringIO.new
|
||||
another_logger = ActiveSupport::Logger.new(another_output)
|
||||
|
||||
@logger.extend ActiveSupport::Logger.broadcast(another_logger)
|
||||
logger = ActiveSupport::BroadcastLogger.new(@logger, another_logger)
|
||||
|
||||
@logger.debug "CORRECT DEBUG"
|
||||
@logger.silence do |logger|
|
||||
assert_kind_of ActiveSupport::Logger, logger
|
||||
@logger.debug "FAILURE"
|
||||
@logger.error "CORRECT ERROR"
|
||||
logger.debug "CORRECT DEBUG"
|
||||
logger.silence do |logger|
|
||||
assert_kind_of ActiveSupport::BroadcastLogger, logger
|
||||
logger.debug "FAILURE"
|
||||
logger.error "CORRECT ERROR"
|
||||
end
|
||||
|
||||
assert_includes @output.string, "CORRECT DEBUG"
|
||||
@ -198,13 +198,13 @@ def test_broadcast_silencing_does_not_break_plain_ruby_logger
|
||||
another_output = StringIO.new
|
||||
another_logger = ::Logger.new(another_output)
|
||||
|
||||
@logger.extend ActiveSupport::Logger.broadcast(another_logger)
|
||||
logger = ActiveSupport::BroadcastLogger.new(@logger, another_logger)
|
||||
|
||||
@logger.debug "CORRECT DEBUG"
|
||||
@logger.silence do |logger|
|
||||
assert_kind_of ActiveSupport::Logger, logger
|
||||
@logger.debug "FAILURE"
|
||||
@logger.error "CORRECT ERROR"
|
||||
logger.debug "CORRECT DEBUG"
|
||||
logger.silence do |logger|
|
||||
assert_kind_of ActiveSupport::BroadcastLogger, logger
|
||||
logger.debug "FAILURE"
|
||||
logger.error "CORRECT ERROR"
|
||||
end
|
||||
|
||||
assert_includes @output.string, "CORRECT DEBUG"
|
||||
|
@ -222,10 +222,10 @@ class TaggedLoggingWithoutBlockTest < ActiveSupport::TestCase
|
||||
|
||||
test "keeps broadcasting functionality" do
|
||||
broadcast_output = StringIO.new
|
||||
broadcast_logger = ActiveSupport::TaggedLogging.new(Logger.new(broadcast_output))
|
||||
@logger.extend(ActiveSupport::Logger.broadcast(broadcast_logger))
|
||||
broadcast_logger = ActiveSupport::BroadcastLogger.new(Logger.new(broadcast_output), @logger)
|
||||
logger_with_tags = ActiveSupport::TaggedLogging.new(broadcast_logger)
|
||||
|
||||
tagged_logger = @logger.tagged("OMG")
|
||||
tagged_logger = logger_with_tags.tagged("OMG")
|
||||
tagged_logger.info "Broadcasting..."
|
||||
|
||||
assert_equal "[OMG] Broadcasting...\n", @output.string
|
||||
|
@ -80,7 +80,7 @@ def log_to_stdout
|
||||
console.level = Rails.logger.level
|
||||
|
||||
unless ActiveSupport::Logger.logger_outputs_to?(Rails.logger, STDERR, STDOUT)
|
||||
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
|
||||
Rails.logger = ActiveSupport::BroadcastLogger.new(Rails.logger, console)
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user