Reputation: 3478
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?
Upvotes: 10
Views: 4793
Reputation: 2184
The accepted answer causes the following issue in production when sending email with Action Mailer using deliver_later
:
activejob-7.1.3.2/lib/active_job/logging.rb:32:in `logger_tagged_by_active_job?': undefined method `current_tags' for nil (NoMethodError)
logger.formatter.current_tags.include?("ActiveJob")
The issue is with BroadcastLogger
, which means both the shorthand and the long versions of the accepted answer will be broken. The issue isn't just limited to the activejob
version above, and is present in multiple Ruby versions I've tested.
config.logger = ActiveSupport::Logger.new("log/#{Rails.env}.log")
.tap { |logger| logger.formatter = ::Logger::Formatter.new }
.then { |logger| ActiveSupport::TaggedLogging.new(logger) }
Removing the entire logger block also logs to production.log
by default.
Upvotes: 1
Reputation: 30003
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
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))
)
Upvotes: 15