2010-11-30 35 views
2

如何代理ruby記錄器並保持性能?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類來猴子,或者重載它,因爲我想在將來使用其他記錄器,syslog,滾動我自己的,或者其他的。

有沒有辦法減少性能方法調用的次數?

-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

Ohmygoodness。除非我錯誤得多,否則這與博客無關,所以我刪除了「博客」標籤。 – 2010-12-01 00:13:17

+0

@Jonathan:如果你在標籤列表中輸入「logger」,然後按tab,你會得到「博主」,因爲它在列表中位置較高(98與84使用)。 – 2010-12-01 05:52:16

+0

我明白了!這就說得通了。 – 2010-12-03 04:14:43

回答

2

第一:你的問題聞起來像你過早優化。你只應該優化,如果你知道你的代碼太慢了。 (和你的基準測試顯示只有細微差別)

這就是說,你可以使上下文通知每一個代理,如果記錄儀是不斷更新:

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

4

而是自身重置記錄器,沖洗,然後重新打開它的輸出:

logfile = File.open 'my_file.log', 'w+' 
context.logger = Logger.new logfile 

Signal.trap('HUP') { 
    logfile.flush 
    logfile.reopen 'my_file.log', 'w+' 
}