Write Ruby logs to a file and the console
Configure two Logger destinations, add a small message fan-out wrapper, or use Rails BroadcastLogger without duplicating ingestion.
A standard Ruby Logger writes to one destination. To send an event to both a file and the console, configure two loggers and call both, or place a small wrapper in front of them.
Configure each destination
require 'logger'
file_logger = Logger.new('application.log', 'daily')
file_logger.level = Logger::INFO
file_logger.formatter = proc do |severity, time, progname, message|
"#{time.strftime('%Y-%m-%d %H:%M:%S %z')} [#{severity}] #{message}\n"
end
console_logger = Logger.new($stdout)
console_logger.level = Logger::DEBUG
message = 'Order accepted'
file_logger.info(message)
console_logger.info(message)
Each destination applies its own level and formatter. The console can display debug detail while the file keeps only informational events and above.
Add a small fan-out wrapper
class MultiLogger
def initialize(*loggers)
@loggers = loggers
end
%i[debug info warn error fatal unknown].each do |level|
define_method(level) do |message|
@loggers.each { |logger| logger.public_send(level, message) }
end
end
end
logger = MultiLogger.new(file_logger, console_logger)
logger.info('Written to both eligible destinations')
This wrapper intentionally supports only direct message calls. It is not a complete replacement for the Logger API: it does not implement lazy blocks, progname arguments, add, lifecycle methods, or unified configuration. Use a maintained logging abstraction when a framework expects those features. Delivery to multiple destinations is also not atomic; one destination can fail independently.
When configuring collection, choose whether to ingest stdout or the file. Collecting both without a deduplication strategy produces two stored events for one application action.
Rails and rotation
Rails 7.1 and later provide ActiveSupport::BroadcastLogger for broadcasting to multiple loggers:
# In a Rails configuration context where ActiveSupport is available.
config.logger = ActiveSupport::BroadcastLogger.new(file_logger, console_logger)
Retain your application's required tagging and formatting. A third-party library may offer structured fields and richer routing, but its configuration and error behavior need separate review.
For files, use daily rotation or a size-based Logger such as Logger.new('app.log', 10, 100 * 1024 * 1024). A byte threshold is portable plain Ruby; 100.megabytes needs ActiveSupport. It is reasonable to emit JSON to a file and a shorter format to the terminal, as long as both retain the fields you need.
References: Ruby Logger and ActiveSupport::BroadcastLogger.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan