2010-11-30 6 views
2

Wie berechtige ich den Ruby Logger und halte die Leistung?Ruby: Proxy-Muster, reduzierende Methodenaufrufe

So haben wir eine Anforderung bei der Arbeit, ganz in Ordnung. Wenn ein Programm gesendet wird, wird das Signal HUP das Protokoll gelöscht und neu gestartet.

Das Problem ist, dass, wenn context.logger zurückgesetzt wird, @logger immer noch auf den alten verweist.

Also, ich dachte, ich würde Proxy-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" 

Dies funktioniert gut, außer ich ein Methodenaufruf für 2 Methodenaufrufe gehandelt haben (1 Accessor, die den Logger zurückgibt). Ich muss noch LoggerProxy.:debug,: info, ... aufrufen, was wiederum den ursprünglichen Logger aufruft! Ergo, 2 Methoden Anrufe, wo es einen gab.

Ich möchte nicht mit Logger-Klasse, oder überladen es, weil ich andere Logger in der Zukunft, syslog verwenden möchte, rollen meine eigenen oder einige solcher.

Gibt es eine Möglichkeit, die Anzahl der Methodenaufrufe für die Leistung zu reduzieren?

-Daniel

Update: als Reaktion auf eine Leistung Frage, hier ist die Probe-Test.

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. Wenn ich mich nicht irre, hat das nichts mit Bloggen zu tun, also habe ich das "Blogger" -Tag entfernt. –

+0

@ Jonathan: Wenn Sie "Logger" in die Tag-Liste eingeben und Tab drücken, erhalten Sie "Blogger", wie es höher in der Liste ist (98 gegenüber 84 verwendet). –

+0

Ich sehe! Das macht Sinn. –

Antwort

2

Erstens: Ihre Frage riecht wie Sie vorzeitig zu optimieren sind. Sie sollten nur optimieren, wenn Sie wissen Ihr Code ist zu langsam. (Und Ihre Benchmark-Show nur ein winziger Unterschied)

Das heißt, Sie den Kontext jeder Proxy mitteilen machen könnte, wenn Logger jemals aktualisiert:

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 

aber wieder, ist dies scheint nicht wirklich lohnt sich die zusätzliche Komplexität .

(verwandt: this is a very nice presentation showing @tenderlove optimizing the ARel gem)

+0

+1 für die vorzeitige Optimierung Bemerkung. Und wenn Sie feststellen, dass Sie Ihren Code verschachteln, um einen Methodenaufruf zu eliminieren, sollten Sie vielleicht eine andere Sprache als Ruby in Betracht ziehen. –

4

Statt der Logger selbst zurückzusetzen, bündig und öffnen Sie seine Ausgabe:

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

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

Das Problem ist in der Zukunft, ich möchte etwas hinzufügen, die möglicherweise nicht wieder öffnen können. Say, syslog ... – Daniel

Verwandte Themen