やわらかテック

興味のあること。業務を通して得られた発見。個人的に試してみたことをアウトプットしています🍵

ピュアなRubyだけで複数のLoggerに出力する

RubyにはLoggerクラスが組み込みライブラリとして提供されていて、ログをいい感じに出力することができます。
しかし、複数のロガーに対してログを出力することはLoggerクラスだけでは出来ません。RailsであればActiveSupport::Logger.broadcastが使えるそうですが、ピュアなRubyでも同じことがやりたいです。

blog.takady.net

利用シーンとしてはライブラリや自作gemを想定しています。
またピュアなRubyだけで実装することで配布時のファイルサイズを削減することが可能です。

できたもの

ということで...複数ロガーを管理するクラスを定義しました。
ただロガーの一覧を配列で管理しつつ、全てのロガーのメソッドを順に呼び出すという作りになっています。
呼び出し順は重要ではないため、この方法を採用しました。

require 'logger'

class MultiLogger
  def initialize
    file = File.open('application.log', 'a')
    @loggers = [
      Logger.new($stdout),
      Logger.new(file, 'daily'),
    ]
  end

  [:debug, :info, :warn, :error, :fatal, :unknown].each do |level|
    define_method(level) do |progname|
      @loggers.each { |logger| logger.send(level, progname) }
    end
  end
end

1つ1つメソッドを定義するのが嫌だったので、ログレベルの一覧からdefine_methodを使って動的に定義するようにしました。内部ではLoggerクラスに定義された同名のメソッドを呼び出しています。

使い方

インスタンスを作成後、Loggerクラスと同じようにログを出力することが可能です。

loggers = MultiLogger.new

loggers.debug(":::debug")
loggers.info(":::info")
loggers.warn(":::warn")
loggers.error(":::error")
loggers.fatal(":::fatal")
loggers.unknown(":::unknown")

# D, [2023-08-13T12:51:43.457365 #61639] DEBUG -- : :::debug
# I, [2023-08-13T12:51:43.457486 #61639]  INFO -- : :::info
# :
D, [2023-08-13T12:52:39.254382 #61724] DEBUG -- : :::debug
I, [2023-08-13T12:52:39.254424 #61724]  INFO -- : :::info
:

標準出力とファイルへの出力の両方に成功しました。
他にロガーを追加したければ@loggersに別途、ロガーを追加すればOKです。
現時点ではあえてリッチな実装はしていないですが、途中でロガーを追加・削除したいなどの要望があれば、ロガーの一覧を動的に管理する実装をするか、別クラスを定義するれば良いかなと思います。

少しでも「ええな〜」と思ったらはてなスター・はてなブックマーク・シェアを頂けると励みになります。