# frozen_string_literal: truemoduleActiveSupport# = 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.## If you are adding a custom logger with custom methods to the broadcast,# the `BroadcastLogger` will proxy them and return the raw value, or an array# of raw values, depending on how many loggers in the broadcasts responded to# the method:## class MyLogger < ::Logger# def loggable?# true# end# end## logger = BroadcastLogger.new# logger.loggable? # => A NoMethodError exception is raised because no loggers in the broadcasts could respond.## logger.broadcast_to(MyLogger.new(STDOUT))# logger.loggable? # => true# logger.broadcast_to(MyLogger.new(STDOUT))# puts logger.broadcasts # => [MyLogger, MyLogger]# logger.loggable? # [true, true]classBroadcastLoggerincludeActiveSupport::LoggerSilence# Returns all the logger that are part of this broadcast.attr_reader:broadcastsattr_reader:formatterattr_accessor:prognamedefinitialize(*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))defbroadcast_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)defstop_broadcasting_to(logger)@broadcasts.delete(logger)enddeflevel@broadcasts.map(&:level).minenddef<<(message)dispatch{|logger|logger.<<(message)}enddefadd(*args,&block)dispatch{|logger|logger.add(*args,&block)}endalias_method:log,:adddefdebug(*args,&block)dispatch{|logger|logger.debug(*args,&block)}enddefinfo(*args,&block)dispatch{|logger|logger.info(*args,&block)}enddefwarn(*args,&block)dispatch{|logger|logger.warn(*args,&block)}enddeferror(*args,&block)dispatch{|logger|logger.error(*args,&block)}enddeffatal(*args,&block)dispatch{|logger|logger.fatal(*args,&block)}enddefunknown(*args,&block)dispatch{|logger|logger.unknown(*args,&block)}enddefformatter=(formatter)dispatch{|logger|logger.formatter=formatter}@formatter=formatterenddeflevel=(level)dispatch{|logger|logger.level=level}endalias_method:sev_threshold=,:level=deflocal_level=(level)dispatchdo|logger|logger.local_level=leveliflogger.respond_to?(:local_level=)endenddefclosedispatch{|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.defdebug?@broadcasts.any?{|logger|logger.debug?}end# Sets the log level to Logger::DEBUG for the whole broadcast.defdebug!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.definfo?@broadcasts.any?{|logger|logger.info?}end# Sets the log level to Logger::INFO for the whole broadcast.definfo!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.defwarn?@broadcasts.any?{|logger|logger.warn?}end# Sets the log level to Logger::WARN for the whole broadcast.defwarn!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.deferror?@broadcasts.any?{|logger|logger.error?}end# Sets the log level to Logger::ERROR for the whole broadcast.deferror!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.deffatal?@broadcasts.any?{|logger|logger.fatal?}end# Sets the log level to Logger::FATAL for the whole broadcast.deffatal!dispatch{|logger|logger.fatal!}enddefinitialize_copy(other)@broadcasts=[]@progname=other.progname.dup@formatter=other.formatter.dupbroadcast_to(*other.broadcasts.map(&:dup))endprivatedefdispatch(&block)@broadcasts.each{|logger|block.call(logger)}enddefmethod_missing(name,*args,**kwargs,&block)loggers=@broadcasts.select{|logger|logger.respond_to?(name)}ifloggers.none?super(name,*args,**kwargs,&block)elsifloggers.one?loggers.first.send(name,*args,**kwargs,&block)elseloggers.map{|logger|logger.send(name,*args,**kwargs,&block)}endenddefrespond_to_missing?(method,include_all)@broadcasts.any?{|logger|logger.respond_to?(method,include_all)}endendend