123456789_123456789_123456789_123456789_123456789_

Class: ActiveSupport::LogSubscriber

Relationships & Source Files
Namespace Children
Modules:
Extension / Inclusion / Inheritance Descendants
Subclasses:
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Subscriber
Instance Chain:
self, Subscriber
Inherits: ActiveSupport::Subscriber
Defined in: activesupport/lib/active_support/log_subscriber.rb,
activesupport/lib/active_support/log_subscriber/test_helper.rb

Overview

LogSubscriber is an object set to consume Notifications with the sole purpose of logging them. The log subscriber dispatches notifications to a registered object based on its given namespace.

An example would be Active Record log subscriber responsible for logging queries:

module ActiveRecord
  class LogSubscriber < ActiveSupport::LogSubscriber
    attach_to :active_record

    def sql(event)
      info "#{event.payload[:name]} (#{event.duration}) #{event.payload[:sql]}"
    end
  end
end

ActiveRecord::LogSubscriber.logger must be set as well, but it is assigned automatically in a Rails environment.

After configured, whenever a "sql.active_record" notification is published, it will properly dispatch the event (ActiveSupport::Notifications::Event) to the sql method.

Being an Notifications consumer, LogSubscriber exposes a simple interface to check if instrumented code raises an exception. It is common to log a different message in case of an error, and this can be achieved by extending the previous example:

module ActiveRecord
  class LogSubscriber < ActiveSupport::LogSubscriber
    def sql(event)
      exception = event.payload[:exception]

      if exception
        exception_object = event.payload[:exception_object]

        error "[ERROR] #{event.payload[:name]}: #{exception.join(', ')} " \
              "(#{exception_object.backtrace.first})"
      else
        # standard logger code
      end
    end
  end
end

LogSubscriber also has some helpers to deal with logging. For example, .flush_all! will ensure that all logs are flushed, and it is called in ::Rails::Rack::Logger after a request finishes.

Constant Summary

Class Attribute Summary

Subscriber - Inherited

Class Method Summary

Subscriber - Inherited

.attach_to

Attach the subscriber to a namespace.

.detach_from

Detach the subscriber from a namespace.

.method_added

Adds event subscribers for all new methods added to the class.

.new, .subscribers, .add_event_subscriber, .fetch_public_methods, .find_attached_subscriber, .invalid_event?, .pattern_subscribed?, .prepare_pattern, .remove_event_subscriber

Instance Attribute Summary

Subscriber - Inherited

Instance Method Summary

Subscriber - Inherited

Constructor Details

.newLogSubscriber

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 137

def initialize
  super
  @event_levels = {}
end

Class Attribute Details

.colorize_logging (rw) Also known as: #colorize_logging

[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 87

mattr_accessor :colorize_logging, default: true

.log_levels (rw)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 88

class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:

.log_levels?Boolean (rw)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 88

class_attribute :log_levels, instance_accessor: false, default: {} # :nodoc:

.logger (rw)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 97

def logger
  @logger ||= if defined?(Rails) && Rails.respond_to?(:logger)
    Rails.logger
  end
end

.logger=(value) (rw)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 109

attr_writer :logger

Class Method Details

.attach_to

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 103

def attach_to(...) # :nodoc:
  result = super
  set_event_levels
  result
end

.fetch_public_methods(subscriber, inherit_all) (private)

This method is for internal use only.
[ GitHub ]

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

def fetch_public_methods(subscriber, inherit_all)
  subscriber.public_methods(inherit_all) - LogSubscriber.public_instance_methods(true)
end

.flush_all!

This method is for internal use only.

Flush all log_subscribers’ logger.

[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 116

def flush_all!
  logger.flush if logger.respond_to?(:flush)
end

.log_subscribers

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 111

def log_subscribers
  subscribers
end

.set_event_levels (private)

This method is for internal use only.
[ GitHub ]

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

def set_event_levels
  if subscriber
    subscriber.event_levels = log_levels.transform_keys { |k| "#{k}.#{namespace}" }
  end
end

.subscribe_log_level(method, level) (private)

This method is for internal use only.
[ GitHub ]

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

def subscribe_log_level(method, level)
  self.log_levels = log_levels.merge(method => LEVEL_CHECKS.fetch(level))
  set_event_levels
end

Instance Attribute Details

#colorize_logging (rw)

[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 87

mattr_accessor :colorize_logging, default: true

#event_levels=(value) (writeonly)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 162

attr_writer :event_levels # :nodoc:

Instance Method Details

#call(event)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 150

def call(event)
  super if logger
rescue => e
  log_exception(event.name, e)
end

#color(text, color, mode_options = {}) (private)

This method is for internal use only.

Set color by using a symbol or one of the defined constants. Set modes by specifying bold, italic, or underline options. Inspired by Highline, this method will automatically clear formatting at the end of the returned ::String.

[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 176

def color(text, color, mode_options = {}) # :doc:
  return text unless colorize_logging
  color = self.class.const_get(color.upcase) if color.is_a?(Symbol)
  mode = mode_from(mode_options)
  clear = "\e[#{MODES[:clear]}m"
  "#{mode}#{color}#{text}#{clear}"
end

#log_exception(name, e) (private)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 198

def log_exception(name, e)
  if logger
    logger.error "Could not log #{name.inspect} event. #{e.class}: #{e.message} #{e.backtrace}"
  end
end

#logger

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 142

def logger
  LogSubscriber.logger
end

#mode_from(options) (private)

This method is for internal use only.
[ GitHub ]

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

def mode_from(options)
  if options.is_a?(TrueClass) || options.is_a?(FalseClass)
    ActiveSupport.deprecator.warn(<<~MSG.squish)
      Bolding log text with a positional boolean is deprecated and will be removed
      in Rails 7.2. Use an option hash instead (eg. `color("my text", :red, bold: true)`).
    MSG
    options = { bold: options }
  end

  modes = MODES.values_at(*options.compact_blank.keys)

  "\e[#{modes.join(";")}m" if modes.any?
end

#publish_event(event)

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 156

def publish_event(event)
  super if logger
rescue => e
  log_exception(event.name, e)
end

#silenced?(event) ⇒ Boolean

This method is for internal use only.
[ GitHub ]

  
# File 'activesupport/lib/active_support/log_subscriber.rb', line 146

def silenced?(event)
  logger.nil? || @event_levels[event]&.call(logger)
end