Reason #86 • March 27th, 2026

Logging with Logger

The logger standard gem provides a straightforward way to add logging to your Ruby applications. It supports multiple log levels and formatting options and can be easily extended with custom log devices and formatters.

Ruby
require "logger"

logger = Logger.new(STDOUT)
logger.level = Logger::INFO

logger.debug "This is a debug message"
# won't be logged since the log level is set to INFO

logger.info "an info message"
# I, [2026-03-23T12:00:00.000000 #12345]  INFO -- : an info message
logger.warn "a warning"
# W, [2026-03-23T12:00:00.000000 #12345]  WARN -- : a warning
logger.error "an error"
# E, [2026-03-23T12:00:00.000000 #12345] ERROR -- : an error
logger.fatal "a fatal error"
# F, [2026-03-23T12:00:00.000000 #12345] FATAL -- : a fatal error

# We can also pass in the message with a block, which will only be
# evaluated if the log level is high enough:
logger.debug { "won't be evaluated" }
logger.info { "hello from block" }
# I, [2026-03-23T12:00:00.000000 #12345]  INFO -- : hello from block

# If we pass both a message and a block, the message is used as
# "program name" and the block is used as the actual log message:
logger.info("MyApp") { "info message" }
# I, [2026-03-23T12:00:00.000000 #12345]  INFO -- MyApp: info message
    

Adding custom formatting is as easy as assigning a proc to the formatter attribute of the logger:

Ruby
# Simple formatter
logger.formatter = proc do |severity, time, _program_name, msg|
  "#{time.iso8601} #{severity}: #{msg}\n"
end

logger.info "custom formatting FTW!"
# 2026-03-23T12:00:00Z INFO: custom formatting FTW!

# JSON formatter
require "json"

logger.formatter = proc do |severity, time, program_name, message|
  timestamp = time.iso8601
  { severity:, timestamp:, program_name:, message: }.to_json + "\n"
end

logger.info("MyApp") { "hello in JSON" }
# { "severity": "INFO", "timestamp": "2026-03-23T12:00:00Z", ... }
    

If you're running in some kind of boomer environment and want to log to a file instead of STDOUT, you can either pass a file path to Logger.new or pass an IO object:

Ruby
logger = Logger.new("application.log")
logger.info "This will be logged to application.log"

file = File.open("another.log", "a")
another_logger = Logger.new(file)
another_logger.info "This will be logged to another.log"
    

History

The Logger class was added to the Ruby standard library in Ruby 1.8.1, released on Christmas Day 2003.

Reason #87 ?