ruby-on-railsloggingruby-on-rails-7

Rails 7.1, log to STDOUT and log/production.log


In a new Rails 7.1.2 app, the following lines can be found in config/environments/production.rb:

config.logger = ActiveSupport::Logger.new(STDOUT)
  .tap  { |logger| logger.formatter = ::Logger::Formatter.new }
  .then { |logger| ActiveSupport::TaggedLogging.new(logger) }

This tells the Rails logger to log to STDOUT.

I would like to configure it so that it ALSO logs to log/production.log, but I can't for the life of me figure it out...

In this article by Fly.io it says to add these lines:

logger = ActiveSupport::Logger.new(STDOUT)
logger.formatter = config.log_formatter
volume_logger = ActiveSupport::Logger.new("/logs/production.log", 3)
logger = logger.extend ActiveSupport::Logger.broadcast(volume_logger)

But it seems like these instructions are for Rails < 7.1, since I get the error NoMethodError: undefined method broadcast' for ActiveSupport::Logger:Class`.

How can I do this in Rails 7.1?


Solution

  • Rails v7.1 added new BroadcastLogger class to handle broadcasting:

    stdout_logger           = ActiveSupport::Logger.new(STDOUT)
    stdout_logger.formatter = ::Logger::Formatter.new
    
    file_logger             = ActiveSupport::Logger.new("log/production.log")
    file_logger.formatter   = ::Logger::Formatter.new
    
    tagged_stdout_logger    = ActiveSupport::TaggedLogging.new(stdout_logger)
    tagged_file_logger      = ActiveSupport::TaggedLogging.new(file_logger)
    
    broadcast_logger = ActiveSupport::BroadcastLogger.new(tagged_stdout_logger, tagged_file_logger)
    config.logger    = broadcast_logger
    

    https://api.rubyonrails.org/classes/ActiveSupport/BroadcastLogger.html

    https://guides.rubyonrails.org/upgrading_ruby_on_rails.html#rails-logger-now-returns-an-activesupport-broadcastlogger-instance


    Since rails v7.1 you could pass formatter to new, which makes the setup much cleaner: https://github.com/rails/rails/commit/3b012a52540f7e4564d70f1955785bde32269a3d:

    config.logger = ActiveSupport::BroadcastLogger.new(
      ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new($stdout,              formatter: Logger::Formatter.new)),
      ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new("log/production.log", formatter: Logger::Formatter.new))
    )