2

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"

これは正常に動作しますが、1 つのメソッド呼び出しを 2 つのメソッド呼び出し (1 つのアクセサー; ロガーを返す) と交換したことを除きます。私はまだ LoggerProxy.:debug, :info, ... を呼び出さなければなりません。これにより、元のロガーが呼び出されます! したがって、2 つのメソッド呼び出しがありましたが、1 つありました。

Logger クラスをモンキーにしたり、オーバーロードしたりしたくありません。将来、他のロガー、syslog、独自のロールなどを使用したいからです。

パフォーマンスのためにメソッド呼び出しの数を減らす方法はありますか?

-ダニエル

更新: パフォーマンスに関する質問への回答として、サンプル テストを示します。

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)
4

2 に答える 2

4

ロガー自体をリセットする代わりに、その出力をフラッシュして再度開きます。

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

Signal.trap('HUP') {
  logfile.flush
  logfile.reopen 'my_file.log', 'w+'
}
于 2010-12-04T18:00:19.600 に答える
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

しかし、繰り返しますが、これは複雑さを増す価値があるとは思えません。

(関連:これは @tenderlove が ARel gem を最適化していることを示す非常に素晴らしいプレゼンテーションです)

于 2010-12-10T00:33:10.090 に答える