How can I have ruby logger log output to stdout as well as file?
You can write a pseudo IO
class that will write to multiple IO
objects. Something like:
class MultiIO def initialize(*targets) @targets = targets end def write(*args) @targets.each {|t| t.write(*args)} end def close @targets.each(&:close) endend
Then set that as your log file:
log_file = File.open("log/debug.log", "a")Logger.new MultiIO.new(STDOUT, log_file)
Every time Logger
calls puts
on your MultiIO
object, it will write to both STDOUT
and your log file.
Edit: I went ahead and figured out the rest of the interface. A log device must respond to write
and close
(not puts
). As long as MultiIO
responds to those and proxies them to the real IO objects, this should work.
@David's solution is very good. I've made a generic delegator class for multiple targets based on his code.
require 'logger'class MultiDelegator def initialize(*targets) @targets = targets end def self.delegate(*methods) methods.each do |m| define_method(m) do |*args| @targets.map { |t| t.send(m, *args) } end end self end class <<self alias to new endendlog_file = File.open("debug.log", "a")log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
If you're in Rails 3 or 4, as this blog post points out, Rails 4 has this functionality built in. So you can do:
# config/environment/production.rbfile_logger = Logger.new(Rails.root.join("log/alternative-output.log"))config.logger.extend(ActiveSupport::Logger.broadcast(file_logger))
Or if you're on Rails 3, you can backport it:
# config/initializers/alternative_output_log.rb# backported from rails4module ActiveSupport class Logger < ::Logger # Broadcasts logs to multiple loggers. Returns a module to be # `extended`'ed into other logger instances. def self.broadcast(logger) Module.new do define_method(:add) do |*args, &block| logger.add(*args, &block) super(*args, &block) end define_method(:<<) do |x| logger << x super(x) end define_method(:close) do logger.close super() end define_method(:progname=) do |name| logger.progname = name super(name) end define_method(:formatter=) do |formatter| logger.formatter = formatter super(formatter) end define_method(:level=) do |level| logger.level = level super(level) end end end endendfile_logger = Logger.new(Rails.root.join("log/alternative-output.log"))Rails.logger.extend(ActiveSupport::Logger.broadcast(file_logger))