kakudooo docs

Rails Semantic Loggerはどのようにloggerを置き換えているのか

Rails Semantic Loggerについて

Rails.loggerをSemantic Loggerで置き換えるgem。 Rails.loggerを個別で呼び出す際はもちろんだが、RackやControllerのAction呼び出し、ActiveJobの実行などRailsの標準ログ機構を網羅的にSemantic Loggerで置き換えてくれる。

Rails.loggerの置き換え

何がどのように置き換えられているのか気になったので、Semantic Loggerを導入することで、Rails.loggerや各ログ出力箇所をどのように置き換えているのかを調べてみることにする。

rails_semantic_loggerのソースコードを見ていく。 lib/rails_semantic_logger/engine.rb::Rails::Engineを継承したクラスで諸々の置き換えが定義されていることがわかる。

Rails::Engineクラスを継承すると、gemのパス上にEngineが存在することをRails側から把握することができ、起動時にマウントされる仕組みになっている。 rails_semantic_loggerではこの仕組みを利用して、Railsアプリケーションの初期化フローの中で、Rails.loggerと各ログ出力箇所の置き換えを行っている。

参考: Rails Engine

Rails.loggerの置き換え

以下は該当箇所

https://github.com/reidmorrison/rails_semantic_logger/blob/c60ff0d7c7c960ad1115dcee2664d217d1d5a4a7/lib/rails_semantic_logger/engine.rb#L43

...
initializer :initialize_logger, group: :all do
  ...
  Rails.logger = config.logger = 
    begin
      ...
    end
end
...

DelayedJobのloggerの置き換え

以下は該当箇所

https://github.com/reidmorrison/rails_semantic_logger/blob/c60ff0d7c7c960ad1115dcee2664d217d1d5a4a7/lib/rails_semantic_logger/engine.rb#L143

...
# Replace the DelayedJob logger
if defined?(Delayed::Worker)
  Delayed::Worker.logger = SemanticLogger[Delayed::Worker]
  Delayed::Worker.plugins << RailsSemanticLogger::DelayedJob::Plugin
end
...

パッチ

lib/rails_semantic_logger/extensions 配下にあるパッチを当てることで、ログ機能の拡張を行っている。

以下は該当箇所

https://github.com/reidmorrison/rails_semantic_logger/blob/c60ff0d7c7c960ad1115dcee2664d217d1d5a4a7/lib/rails_semantic_logger/engine.rb#L171

config.after_initialize do
  # Rails Patches
  ...
  require("rails_semantic_logger/extensions/active_job/logging") if defined?(::ActiveJob)
  ...
end

https://github.com/reidmorrison/rails_semantic_logger/blob/c60ff0d7c7c960ad1115dcee2664d217d1d5a4a7/lib/rails_semantic_logger/extensions/active_job/logging.rb#L4

# Patch ActiveJob logger
require "active_job/logging"

module ActiveJob
  module Logging
    include SemanticLogger::Loggable

    private

    undef_method :tag_logger
    def tag_logger(*tags, &block)
      if logger.respond_to?(:tagged)
        logger.tagged(*tags, &block)
      else
        yield
      end
    end
  end
end

Log Subscriberの置き換え

ActiveSupport::LogSubscriberの継承クラスが存在する場合(ActiveRecord::LogSubscriberなど)については、Rails Semantic Loggerで定義されたLogSubscriberクラスに置き換える。

lib/rails_semantic_logger配下に各内部フレームワークのLog Subscriberを置き換えるためのLog Subscriberクラスが配置されている。

以下は該当箇所

config.after_initialize do
  ...
  if defined?(::ActiveRecord)
    require "active_record/log_subscriber"

    RailsSemanticLogger.swap_subscriber(
      ::ActiveRecord::LogSubscriber,
      RailsSemanticLogger::ActiveRecord::LogSubscriber,
      :active_record
    )
  end
  ...
end

まとめ