123456789_123456789_123456789_123456789_123456789_

Class: ActiveSupport::BroadcastLogger

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Inherits: Object
Defined in: activesupport/lib/active_support/broadcast_logger.rb

Overview

===============================

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 an 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]

Constant Summary

LoggerSilence - Attributes & Methods

Class Method Summary

Instance Attribute Summary

LoggerThreadSafeLevel - self

Instance Method Summary

LoggerThreadSafeLevel - self

#initialize, #initialize_copy, #level,
#log_at

Change the thread-local level for the duration of the given block.

LoggerSilence - Included

#silence

Silences the logger for the duration of the block.

Constructor Details

.new(*loggers) ⇒ BroadcastLogger

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 95

def initialize(*loggers)
  @broadcasts = []
  @progname = "Broadcast"

  broadcast_to(*loggers)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name) (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 299

def method_missing(name, ...)
  loggers = @broadcasts.select { |logger| logger.respond_to?(name) }

  if loggers.none?
    super
  elsif loggers.one?
    loggers.first.send(name, ...)
  else
    loggers.map { |logger| logger.send(name, ...) }
  end
end

Class Attribute Details

.silencer (rw) Also known as: #silencer

[ GitHub ]

  
# File 'activesupport/lib/active_support/logger_silence.rb', line 13

cattr_accessor :silencer, default: true

Instance Attribute Details

#broadcasts (readonly)

Returns all the logger that are part of this broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 92

attr_reader :broadcasts

#debug? ⇒ Boolean (readonly)

True if the log level allows entries with severity Logger::DEBUG to be written to at least one broadcast. False otherwise.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 179

def debug?
  @broadcasts.any? { |logger| logger.debug? }
end

#error? ⇒ Boolean (readonly)

True if the log level allows entries with severity Logger::ERROR to be written to at least one broadcast. False otherwise.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 212

def error?
  @broadcasts.any? { |logger| logger.error? }
end

#fatal? ⇒ Boolean (readonly)

True if the log level allows entries with severity Logger::FATAL to be written to at least one broadcast. False otherwise.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 223

def fatal?
  @broadcasts.any? { |logger| logger.fatal? }
end

#formatter=(formatter) (writeonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 168

def formatter=(formatter)
  @broadcasts.each { |logger| logger.formatter = formatter }
end

#info? ⇒ Boolean (readonly)

True if the log level allows entries with severity Logger::INFO to be written to at least one broadcast. False otherwise.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 190

def info?
  @broadcasts.any? { |logger| logger.info? }
end

#level (rw)

Returns the lowest level of all the loggers in the broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 173

def level
  @broadcasts.map(&:level).min
end

#level=(level) (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 160

def level=(level)
  @broadcasts.each { |logger| logger.level = level }
end

#local_level (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 131

def local_level
  loggers = @broadcasts.select { |logger| logger.respond_to?(:local_level) }

  loggers.map do |logger|
    logger.local_level
  end.first
end

#local_level=(level) (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 125

def local_level=(level)
  @broadcasts.each do |logger|
    logger.local_level = level if logger.respond_to?(:local_level=)
  end
end

#progname (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 93

attr_accessor :progname

#sev_threshold=(level) (writeonly)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 164

def sev_threshold=(level)
  @broadcasts.each { |logger| logger.sev_threshold = level }
end

#silencer (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/logger_silence.rb', line 13

cattr_accessor :silencer, default: true

#warn? ⇒ Boolean (readonly)

True if the log level allows entries with severity Logger::WARN to be written to at least one broadcast. False otherwise.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 201

def warn?
  @broadcasts.any? { |logger| logger.warn? }
end

Instance Method Details

#_tagged(loggers, index, tags, &block) (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 289

def _tagged(loggers, index, tags, &block)
  if index >= loggers.size
    yield self
  else
    loggers[index].tagged(*tags) do
      _tagged(loggers, index + 1, tags, &block)
    end
  end
end

#broadcast_to(*loggers)

Add logger(s) to the broadcast.

broadcast_logger = {ActiveSupport::BroadcastLogger.new}
broadcast_logger.broadcast_to(Logger.new(STDOUT), {Logger.new}(STDERR))
[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 108

def broadcast_to(*loggers)
  @broadcasts.concat(loggers)
end

#debug!

Sets the log level to Logger::DEBUG for the whole broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 184

def debug!
  @broadcasts.each(&:debug!)
end

#dispatch_block(method, *args, **kwargs) (private)

Complex dispatch for multiple loggers with a block given. The first logger to yield executes the block as normal. Subsequent loggers reuse the initial result

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 269

def dispatch_block(method, *args, **kwargs)
  result = nil
  called, memo = false, nil

  @broadcasts.each_with_index do |logger, i|
    value = logger.send(method, *args, **kwargs) do |*args, **kwargs|
      if called
        memo
      else
        called = true
        memo = yield(*args, **kwargs)
      end
    end

    result = value if i.zero?
  end

  result
end

#error!

Sets the log level to Logger::ERROR for the whole broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 217

def error!
  @broadcasts.each(&:error!)
end

#fatal!

Sets the log level to Logger::FATAL for the whole broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 228

def fatal!
  @broadcasts.each(&:fatal!)
end

#info!

Sets the log level to Logger::INFO for the whole broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 195

def info!
  @broadcasts.each(&:info!)
end

#initialize_copy(other)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 258

def initialize_copy(other)
  @broadcasts = []
  @progname = other.progname.dup

  broadcast_to(*other.broadcasts.map(&:dup))
end

#respond_to_missing?(method, include_all) ⇒ Boolean (private)

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 311

def respond_to_missing?(method, include_all)
  @broadcasts.any? { |logger| logger.respond_to?(method, include_all) }
end

#stop_broadcasting_to(logger)

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)
[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 121

def stop_broadcasting_to(logger)
  @broadcasts.delete(logger)
end

#tagged(*tags, &block)

Add the tags to every broadcast that supports tagged logging.

Without a block, it returns a new BroadcastLogger whose tagging-capable broadcasts are tagged, so the result can be logged to directly. Broadcasts that don't support tagging are kept as-is so they keep receiving messages.

broadcast.tagged("BCX").info("Hello") # All broadcasts log "[BCX] Hello"

With a block, the tags are applied to every tagging-capable broadcast for the duration of the block, which is yielded once with the broadcast logger.

broadcast.tagged("BCX") { |logger| logger.info("Hello") }
[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 247

def tagged(*tags, &block)
  if block
    tagging_loggers = @broadcasts.select { |logger| logger.respond_to?(:tagged) }

    _tagged(tagging_loggers, 0, tags, &block)
  else
    loggers = @broadcasts.map { |logger| logger.respond_to?(:tagged) ? logger.tagged(*tags) : logger }
    self.class.new(*loggers)
  end
end

#warn!

Sets the log level to Logger::WARN for the whole broadcast.

[ GitHub ]

  
# File 'activesupport/lib/active_support/broadcast_logger.rb', line 206

def warn!
  @broadcasts.each(&:warn!)
end