app/services/loggable.rb
Rama develop · réplica de lectura del repositorio original.
# Gives a class or module its own logger that prefixes every line with the
# class/module name centrally — no per-message "[ClassName]" literals to write
# or forget.
#
# class Foo
# include Loggable # for instance-method logging
# extend Loggable # for class-method logging
# end
#
# Foo.new.logger.info('hi') # => "[Foo] hi"
# Foo.logger.info('hi') # => "[Foo] hi"
#
# Why a literal prefix and not Rails.logger.tagged(name)? Under `bin/dev`,
# `rails server` broadcasts the development log to a *plain* STDOUT logger
# (ActiveSupport::Logger.new($stdout)). ActiveSupport::BroadcastLogger#tagged
# dispatches only to sinks that respond to `tagged`, so a tagged logger reaches
# development.log but never the terminal. Sending a literal prefix straight
# through Rails.logger hits every broadcast sink, so the line shows up both in
# the file and in the `bin/dev` terminal.
#
# Do NOT use in ActiveJob or ActionController subclasses — they already define
# `logger`; use their framework logger instead.
module Loggable
# The standard severities. Only debug/info/warn/error are used today; fatal
# and unknown are wired up for completeness.
LEVELS = %i[debug info warn error fatal unknown].freeze
def logger
# self is a Class/Module when extended (class-method context), an instance
# when included (instance-method context).
PrefixedLogger.new(is_a?(Module) ? name : self.class.name)
end
# Forwards each severity to Rails.logger, prefixing the message with the tag.
# Logging through Rails.logger (rather than a tagged sub-logger) is what keeps
# the line visible on every broadcast sink — see the note above.
class PrefixedLogger
def initialize(tag)
@tag = tag
end
LEVELS.each do |level|
define_method(level) do |message = nil, &block|
Rails.logger.public_send(level) { "[#{@tag}] #{block ? block.call : message}" }
end
end
end
end