Dav*_*vid 122
您可以编写一个IO
将写入多个IO
对象的伪类.就像是:
class MultiIO
def initialize(*targets)
@targets = targets
end
def write(*args)
@targets.each {|t| t.write(*args)}
end
def close
@targets.each(&:close)
end
end
Run Code Online (Sandbox Code Playgroud)
然后将其设置为您的日志文件:
log_file = File.open("log/debug.log", "a")
Logger.new MultiIO.new(STDOUT, log_file)
Run Code Online (Sandbox Code Playgroud)
每次Logger
调用puts
您的MultiIO
对象时,它都会写入STDOUT
您的日志文件.
编辑:我继续前进并找出了界面的其余部分.日志设备必须响应write
和close
(而不是puts
).只要MultiIO
响应那些并将它们代理到真正的IO对象,这应该有效.
jon*_*054 47
@David的解决方案非常好.我根据他的代码为多个目标创建了一个通用委托者类.
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
end
end
log_file = File.open("debug.log", "a")
log = Logger.new MultiDelegator.delegate(:write, :close).to(STDOUT, log_file)
Run Code Online (Sandbox Code Playgroud)
phi*_*ker 33
如果您正在使用Rails 3或4,正如此博客文章指出的那样,Rails 4内置了此功能.所以你可以这样做:
# config/environment/production.rb
file_logger = Logger.new(Rails.root.join("log/alternative-output.log"))
config.logger.extend(ActiveSupport::Logger.broadcast(file_logger))
Run Code Online (Sandbox Code Playgroud)
或者,如果您使用的是Rails 3,则可以向后移植它:
# config/initializers/alternative_output_log.rb
# backported from rails4
module 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
end
end
file_logger = Logger.new(Rails.root.join("log/alternative-output.log"))
Rails.logger.extend(ActiveSupport::Logger.broadcast(file_logger))
Run Code Online (Sandbox Code Playgroud)
Igo*_*gor 13
对于那些喜欢它简单的人:
log = Logger.new("| tee test.log") # note the pipe ( '|' )
log.info "hi" # will log to both STDOUT and test.log
Run Code Online (Sandbox Code Playgroud)
或者在Logger格式化程序中打印消息:
log = Logger.new("test.log")
log.formatter = proc do |severity, datetime, progname, msg|
puts msg
msg
end
log.info "hi" # will log to both STDOUT and test.log
Run Code Online (Sandbox Code Playgroud)
我实际上使用这种技术打印到日志文件,云记录器服务(logentries),如果它是开发环境 - 也打印到STDOUT.
dsz*_*dsz 12
虽然我非常喜欢其他建议,但我发现我有同样的问题,但希望能够为STDERR和文件提供不同的日志记录级别(就像我可以使用更大的日志框架,如NLog).我最终得到了一个路由策略,它在记录器级别而不是在IO级别进行多路复用,这样每个记录器就可以在独立的日志级别运行:
class MultiLogger
def initialize(*targets)
@targets = targets
end
%w(log debug info warn error fatal unknown).each do |m|
define_method(m) do |*args|
@targets.map { |t| t.send(m, *args) }
end
end
end
stderr_log = Logger.new(STDERR)
file_log = Logger.new(File.open('logger.log', 'a'))
stderr_log.level = Logger::INFO
file_log.level = Logger::DEBUG
log = MultiLogger.new(stderr_log, file_log)
Run Code Online (Sandbox Code Playgroud)
Ram*_*lle 11
您还可以将多个设备日志记录功能直接添加到Logger中:
require 'logger'
class Logger
# Creates or opens a secondary log file.
def attach(name)
@logdev.attach(name)
end
# Closes a secondary log file.
def detach(name)
@logdev.detach(name)
end
class LogDevice # :nodoc:
attr_reader :devs
def attach(log)
@devs ||= {}
@devs[log] = open_logfile(log)
end
def detach(log)
@devs ||= {}
@devs[log].close
@devs.delete(log)
end
alias_method :old_write, :write
def write(message)
old_write(message)
@devs ||= {}
@devs.each do |log, dev|
dev.write(message)
end
end
end
end
Run Code Online (Sandbox Code Playgroud)
例如:
logger = Logger.new(STDOUT)
logger.warn('This message goes to stdout')
logger.attach('logfile.txt')
logger.warn('This message goes both to stdout and logfile.txt')
logger.detach('logfile.txt')
logger.warn('This message goes just to stdout')
Run Code Online (Sandbox Code Playgroud)
这是另一个实现,受@ jonas054的回答启发.
这使用类似的模式Delegator
.这样您就不必列出要委派的所有方法,因为它将委托在任何目标对象中定义的所有方法:
class Tee < DelegateToAllClass(IO)
end
$stdout = Tee.new(STDOUT, File.open("#{__FILE__}.log", "a"))
Run Code Online (Sandbox Code Playgroud)
你也应该能够将它与Logger一起使用.
delegate_to_all.rb可从此处获得:https://gist.github.com/TylerRick/4990898