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.

Best practices
Illustration of monitoring and troubleshooting application behavior

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.

Get a tailored plan

Contact us

Join the community

Scan with WeChat
to join the community

Try Guance

Start online and pay only for what you use.

Get started

Choose a Guance plan

Code hosting