LoginSignup
2
0

More than 3 years have passed since last update.

[Ruby] DelegateClass を使って僕だけの Logger を実装する (追記あり)

Last updated at Posted at 2019-07-09

標準出力に出力するための Logger オブジェクトを作りたい :raised_hands_tone1:

require 'logger'

logger = Logger.new(STDOUT)
logger.formatter = Logger::Formatter.new
logger.datetime_format = '%Y/%m/%d %H:%M:%S '

logger.info('BONFIRE LIT')
logger.error('YOU DIED')
I, [2019-07-09T21:40:12.663123 #18929]  INFO -- : BONFIRE LIT
E, [2019-07-09T21:40:13.036144 #18929] ERROR -- : YOU DIED

このような形で毎回 logger オブジェクトを作るのは大変だから、クラス化しよう :smirk_cat:

require 'forwardable'
require 'logger'

class StdoutLogger
  extend Forwardable

  def_delegators :logger, :debug, :info, :warn, :error, :fatal

  private

  def logger
    @logger ||=
      Logger.new(STDOUT).tap do |logger|
        logger.formatter = Logger::Formatter.new
        logger.datetime_format = '%Y/%m/%d %H:%M:%S '
      end
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

以前はこのように「Logger オブジェクトである logger を所有し、infoerror などのメソッドを logger に委譲する」というクラスを実装していました。しかし、この方法では委譲したいメソッドが増えた場合、例えば Logger#unknown も委譲したい場合などに def_delegators に追記しなくてはいけません :weary:

そこで便利なのが Kernel#DelegateClass です。

require 'delegate'
require 'logger'

class StdoutLogger < DelegateClass(Logger)
  def initialize
    super(logger)
  end

  private

  def logger
    @logger ||=
      Logger.new(STDOUT).tap do |logger|
        logger.formatter = Logger::Formatter.new
        logger.datetime_format = '%Y/%m/%d %H:%M:%S '
      end
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

DelegateClass(Logger) を継承することで、Logger のインスタンスメソッドはすべて logger に委譲されます。

ちなみに Logger クラスを継承する方法では

最後に

DelegateClass は非常に便利で、例えば Array のラッパークラスを定義したい場合にも使えます。これについては以前書いた

という記事を参照してください。

Logger 関連でよろしければこちらの記事もご覧ください。

追記: 通常の継承の方がいいような……

この場合 Logger クラスを継承する方法でもいいような……。

require 'logger'

class StdoutLogger < Logger
  def initialize
    super(STDOUT, formatter: Formatter.new, datetime_format: '%Y/%m/%d %H:%M:%S ')
  end
end

logger = StdoutLogger.new
logger.info('BONFIRE LIT')
logger.error('YOU DIED')

違いのひとつとして、Logger クラスのサブクラスかどうかが異なる。

# DelegateClass(Logger) を継承する場合
logger.is_a?(Logger)
#=> false

# Logger を継承する場合
logger.is_a?(Logger)
#=> true

この場合、むしろ Logger オブジェクトであると見なしたほうが適切かもしれない。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0