Ruby: шаблон Proxy, сокращающий количество вызовов методов - PullRequest
2 голосов
/ 01 декабря 2010

Как мне прокси-сервер ruby ​​logger и поддерживать производительность?

Итак, у нас есть требования к работе, вполне разумные.Когда программа отправляет сигнал 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, вызовы двух методов, где был один.

Я не хочу связываться с классом Logger или перегружать его, потому что я хочу использовать другие регистраторы в будущем, syslog, roll your own илинекоторые такие.

Есть ли способ уменьшить количество вызовов методов для повышения производительности?

-даниель

Обновление: в ответ на вопрос о производительности, вот примертест.

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)

Ответы [ 2 ]

4 голосов
/ 04 декабря 2010

Вместо сброса самого регистратора сбросьте и снова откройте его вывод:

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

Signal.trap('HUP') {
  logfile.flush
  logfile.reopen 'my_file.log', 'w+'
}
2 голосов
/ 10 декабря 2010

Во-первых: ваш вопрос пахнет так, как будто вы преждевременно оптимизируете. Вам следует оптимизировать только если вы знаете ваш код слишком медленный. (И ваш тест показывает только небольшую разницу)

Тем не менее, вы можете сделать так, чтобы Контекст уведомлял каждого прокси, если когда-либо обновляется логгер:

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

но опять же, это, похоже, не стоит дополнительной сложности.

(связанный: это очень хорошая презентация, показывающая @tenderlove, оптимизирующий драгоценный камень ARel )

...