2010-11-30 3 views
2

Comment puis-je proxy l'enregistreur de rubis et conserver les performances? Donc, nous avons une exigence au travail, tout à fait raisonnable. Lorsqu'un programme est envoyé le signal HUP le journal est vidé et redémarré. Le problème, c'est que si context.logger est réinitialisé, @logger pointe toujours vers l'ancien.Ruby: modèle de proxy, réduction des appels de méthode

Alors, je pensais logger proxy:

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" 

Cela fonctionne très bien, sauf que j'ai échangé un appel de méthode pour 2 appels de méthode (1 accesseurs, qui renvoie l'enregistreur). Je dois encore appeler LoggerProxy.:debug,: info, ..., qui à son tour appelle le logger d'origine! Ergo, 2 appels de méthodes, où il y en avait un.

Je ne veux pas singe avec la classe Logger, ou la surcharger, parce que je veux utiliser d'autres loggers dans le futur, syslog, rouler le mien, ou quelque chose comme ça.

Y a-t-il un moyen de réduire le nombre d'appels de méthode pour les performances?

-Daniel

Mise à jour: en réponse à une question sur la performance, voici le test de l'échantillon.

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. À moins de me tromper, cela n'a rien à voir avec les blogs, alors j'ai supprimé la balise "blogger". –

+0

@Jonathan: Si vous tapez "logger" dans la liste des tags et appuyez sur tab, vous obtenez "blogger" car il est plus haut dans la liste (98 contre 84 utilisations). –

+0

Je vois! Ça a du sens. –

Répondre

2

Première: votre question sent comme vous optimisez prématurément. Vous devriez seulement optimiser si vous connaissez votre code est trop lent. (Et votre émission de référence seulement une différence minuscule)

Cela dit, vous pouvez faire le contexte notifier chaque proxy si enregistreur est jamais mis à jour:

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 

mais encore une fois, cela ne semble pas vraiment la peine la complexité supplémentaire .

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

+0

+1 pour la remarque d'optimisation prématurée. Et OMI, si vous vous trouvez compliquer votre code afin d'éliminer un appel de méthode, vous devriez peut-être envisager d'autres langues que Ruby. –

4

Au lieu de remettre l'enregistreur lui-même, chasse d'eau et de rouvrir sa sortie:

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

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

Le problème est dans l'avenir, je veux ajouter quelque chose, qui peut ne pas avoir une capacité de réouverture. Say, syslog ... – Daniel

Questions connexes