如何代理ruby记录器并保持性能?
所以,我们在工作中有一个要求,非常合理。当程序发送信号HUP时 刷新并重新启动日志。
class LocalObject
attr_accessor :logger
def initialize context
# one less method call! Yea! performance++
@logger = context.logger
end
def something
@logger.info "Hello world"
end
end
问题是,如果重置了context.logger,那么@logger仍然指向旧的。
所以,我以为我会代理记录器:
class LoggerProxy
attr_accessor :logger
def debug *args
@logger.send :debug, args
end
def info *args
@logger.send :info, args
end
end
context.logger = LoggerProxy.new
context.logger.logger = Logger.new 'my_file.log'
Signal.trap('HUP') {
context.logger.logger = Logger.new 'my_file.log'
}
...
@logger = context.logger
@logger.info "Hello world"
这很好用,除了我交换了一个方法调用2个方法调用(1个访问器;它返回记录器)。我仍然需要调用LoggerProxy.:debug,:info,...,然后调用原始记录器! Ergo,2个方法调用,其中有一个。
我不想使用Logger类,或者重载它,因为我希望将来使用其他记录器,系统日志,自己动手或其他类似记录器。
有没有办法减少方法调用的性能?
-daniel
更新:在回答关于性能的问题时,这是样本测试。
require 'logger'
require 'benchmark';
class MyLogger
attr_accessor :logger
def info msg
@logger.info msg
end
end
myLogger = Logger.new '/dev/null' # dev null to avoid IO issues
myLoggerProxy = MyLogger.new
myLoggerProxy.logger = myLogger
n = 100000
Benchmark.bm do | benchmarker |
# plain logger
benchmarker.report { n.times { myLogger.info 'opps' } }
# via accessor
benchmarker.report { n.times { myLoggerProxy.logger.info 'opps' } }
# via proxy
benchmarker.report { n.times { myLoggerProxy.info 'opps' } }
end
user system total real
1.580000 0.150000 1.730000 ( 1.734956)
1.600000 0.150000 1.750000 ( 1.747969)
1.610000 0.160000 1.770000 ( 1.767886)
答案 0 :(得分:4)
不是重置记录器本身,而是刷新并重新打开其输出:
logfile = File.open 'my_file.log', 'w+'
context.logger = Logger.new logfile
Signal.trap('HUP') {
logfile.flush
logfile.reopen 'my_file.log', 'w+'
}
答案 1 :(得分:2)
首先:你的问题有点像你过早地进行优化。只有在知道您的代码太慢时才应优化。 (而你的基准显示只有很小的差异)
也就是说,如果记录器被更新,你可以让Context通知每个代理:
class ProxyLogger
attr_accessor :logger
def initialize(context)
context.register(self)
end
end
class Context
attr_accessor :logger
def initialize
@proxies = []
end
def logger=(val)
@logger = val
@proxies.each { |p| p.logger = val }
end
def register(proxy)
@proxies << proxy
end
end
但同样,这似乎并不值得额外的复杂性。
(相关:this is a very nice presentation showing @tenderlove optimizing the ARel gem)