I had started to write this for a Rake task, where I wanted the option of both logging to the console versus the file. Suppose for sake of this argument that you don’t want the same thing to go to both places. (If you did, then the answer for that is at the bottom). Suppose you had the following, and then realized that you wanted to add a method called error. But that’s just too much duplicated code!
def warn(message = nil, &block)
if @console_mode
if message.nil? && block_given?
message = yield
end
puts "WARNING: #{message}"
else
Rails.logger.warn(message, &block)
end
end
def info(message = nil, &block)
if @console_mode
if message.nil? && block_given?
message = yield
end
puts message
else
Rails.logger.info(message, &block)
end
end
Here’s the solution. I encourage you to try to develop this on your own.
There’s a few key tricks:
- Read up a little on what
class_eval
does in Ruby. - Pass in the
FILE
andLINE + 1
so you get a stack trace. - Understand how and when to escape the
#
when doing string interpolation. Take a look at the lineputs "#{severity}: \#{message}"
. Do you see how the\#{message}
needs to get quoted? Try with and without this this\
. - Consider that the class_eval is really taking a STRING, so you need to consider when you need to put an actual string in quotes. Especially see the call below to
Rails.logger.send
where the#{severity.downcase}
is quoted.
%w(INFO WARN ERROR).each do |severity|
class_eval <<-EOT, __FILE__, __LINE__ + 1
def #{severity.downcase}(message = nil, &block) # def info(message = nil, &block)
if @console_mode
if message.nil? && block_given?
message = yield
end
puts "#{severity}: \#{message}"
else
Rails.logger.send("#{severity.downcase}", message, &block)
end
end
EOT
end
Incidentally, if you want to just change the logger to STDOUT for a rake task, you can call:
Rails.logger = Logger.new(STDOUT)
However, that ends up logging all your queries to STDOUT as well, which might swamp the messages you want to see.