@@ -96,7 +96,7 @@ class Logger
9696 ##
9797 # This logger does not use a formatter, but it provides a default
9898 # Logger::Formatter for API compatibility with the standard Logger.
99- attr_accessor :formatter
99+ attr_reader :formatter
100100
101101 ##
102102 # This logger does not use a formatter, but it implements this
@@ -108,6 +108,11 @@ class Logger
108108 # used to set the trace field of log entries.
109109 attr_accessor :project
110110
111+ ##
112+
113+ attr_reader :broadcasts
114+
115+
111116 ##
112117 # This logger treats progname as an alias for log_name.
113118 def progname = name
@@ -178,12 +183,38 @@ def initialize writer, log_name, resource, labels = nil
178183 @formatter = ::Logger ::Formatter . new
179184 @datetime_format = ""
180185 @silencer = true
186+ @broadcasts = [ ]
181187
182188 # The writer is usually a Project or AsyncWriter.
183189 logging = @writer . respond_to? ( :logging ) ? @writer . logging : @writer
184190 @project = logging . project if logging . respond_to? :project
185191 end
186192
193+ # Add logger(s) to the broadcast.
194+ #
195+ # broadcast_logger = ActiveSupport::BroadcastLogger.new
196+ # broadcast_logger.broadcast_to(Logger.new(STDOUT), Logger.new(STDERR))
197+ def broadcast_to ( *loggers )
198+ @broadcasts . concat loggers
199+ end
200+
201+ # Remove a logger from the broadcast. When a logger is removed, messages sent to
202+ # the broadcast will no longer be written to its sink.
203+ #
204+ # sink = Logger.new(STDOUT)
205+ # broadcast_logger = ActiveSupport::BroadcastLogger.new
206+ #
207+ # broadcast_logger.stop_broadcasting_to(sink)
208+ def stop_broadcasting_to logger
209+ @broadcasts . delete logger
210+ end
211+
212+ def formatter = formatter
213+ dispatch { |logger | logger . formatter = formatter }
214+
215+ @formatter = formatter
216+ end
217+
187218 ##
188219 # Log a `DEBUG` entry.
189220 #
@@ -307,7 +338,7 @@ def unknown message = nil, &block
307338 # to create potentially expensive logging messages that are only
308339 # called when the logger is configured to show them.
309340 #
310- def add severity , message = nil , progname = nil
341+ def add severity , message = nil , progname = nil , & block
311342 return if @closed
312343
313344 severity = derive_severity ( severity ) || ::Logger ::UNKNOWN
@@ -317,6 +348,7 @@ def add severity, message = nil, progname = nil
317348 # TODO: Figure out what to do with the progname
318349
319350 write_entry severity , message unless @closed
351+ dispatch { |logger | logger . add ( severity , message , progname , &block ) }
320352 true
321353 end
322354 alias log add
@@ -335,41 +367,48 @@ def << msg
335367 # Returns `true` if the current severity level allows for sending
336368 # `DEBUG` messages.
337369 def debug?
370+ @broadcasts . any? ( &:debug? )
371+
338372 @level <= ::Logger ::DEBUG
339373 end
340374
341375 ##
342376 # Returns `true` if the current severity level allows for sending `INFO`
343377 # messages.
344378 def info?
379+ @broadcasts . any? ( &:info? )
345380 @level <= ::Logger ::INFO
346381 end
347382
348383 ##
349384 # Returns `true` if the current severity level allows for sending `WARN`
350385 # messages.
351386 def warn?
387+ @broadcasts . any? ( &:warn? )
352388 @level <= ::Logger ::WARN
353389 end
354390
355391 ##
356392 # Returns `true` if the current severity level allows for sending
357393 # `ERROR` messages.
358394 def error?
395+ @broadcasts . any? ( &:error? )
359396 @level <= ::Logger ::ERROR
360397 end
361398
362399 ##
363400 # Returns `true` if the current severity level allows for sending
364401 # `FATAL` messages.
365402 def fatal?
403+ @broadcasts . any? ( &:fatal? )
366404 @level <= ::Logger ::FATAL
367405 end
368406
369407 ##
370408 # Returns `true` if the current severity level allows for sending
371409 # `UNKNOWN` messages.
372410 def unknown?
411+ @broadcasts . any? ( &:unknown? )
373412 @level <= ::Logger ::UNKNOWN
374413 end
375414
@@ -398,17 +437,30 @@ def level= severity
398437 if new_level . nil?
399438 raise ArgumentError , "invalid log level: #{ severity } "
400439 end
440+ dispatch { |logger | logger . level = new_level }
401441 @level = new_level
402442 end
403443 alias sev_threshold = level =
404- alias local_level = level =
444+
445+ def local_level = severity
446+ new_level = derive_severity severity
447+ if new_level . nil?
448+ raise ArgumentError , "invalid log level: #{ severity } "
449+ end
450+ dispatch do |logger |
451+ logger . local_level = new_level if logger . respond_to? :local_level=
452+ end
453+ @level = new_level
454+ end
405455
406456 ##
407457 # Close the logging "device". This effectively disables logging from
408458 # this logger; any further log messages will be silently ignored. The
409459 # logger may be re-enabled by calling #reopen.
410460 #
411461 def close
462+ dispatch ( &:close )
463+
412464 @closed = true
413465 self
414466 end
@@ -601,6 +653,11 @@ def current_thread_id
601653
602654 private
603655
656+ def dispatch &block
657+ @broadcasts . each { |logger | block . call logger }
658+ true
659+ end
660+
604661 ##
605662 # @private Compute values for labels
606663 def compute_labels request_env
0 commit comments