Ruby Logger
last modified October 18, 2026
The logger library in Ruby provides a simple, configurable way to
record messages from a program. It supports multiple severity levels, custom
formatting, program names, exception logging, and log file rotation. This guide
covers the common Logger features with examples.
Basic Logging
The logger library is part of the Ruby standard library, so it is loaded with require 'logger'. A Logger writes messages to any IO object or file. Here we pass $stdout, and we set a custom formatter so that each line shows only the severity and the message. The default format includes a timestamp and process ID, which change on every run; the custom formatter keeps the output the same each time.
#!/usr/bin/env ruby
require 'logger'
logger = Logger.new($stdout)
logger.formatter = proc do |severity, time, progname, msg|
"#{severity}: #{msg}\n"
end
logger.info("Application started")
logger.warn("Disk space is low")
logger.error("Failed to open file")
The Logger.new($stdout) call creates a logger that writes to standard output. The formatter is a proc receiving four arguments: the severity string, the time, the program name, and the message. Here the proc returns "#{severity}: #{msg}\n", so only the severity and message appear. The info, warn, and error methods each log one message at the corresponding level.
$ ./basic_logging.rb INFO: Application started WARN: Disk space is low ERROR: Failed to open file
Log Levels
Logger supports six levels in increasing order of severity: DEBUG, INFO, WARN, ERROR, FATAL, and UNKNOWN. A message is written only when its level is at or above the logger's level. The level= method changes the threshold, and it accepts either a constant such as Logger::INFO or a symbol.
#!/usr/bin/env ruby
require 'logger'
logger = Logger.new($stdout)
logger.formatter = proc do |severity, time, progname, msg|
"#{severity}: #{msg}\n"
end
logger.level = Logger::INFO
logger.debug("This debug message is hidden")
logger.info("Info message is shown")
logger.warn("Warning message is shown")
logger.error("Error message is shown")
logger.fatal("Fatal message is shown")
logger.unknown("Unknown message is shown")
puts "Current level: #{logger.level}"
Setting logger.level = Logger::INFO hides the DEBUG message, so it is not printed. The remaining levels are printed. The UNKNOWN level is labeled ANY in the output, because the default label for that severity is ANY. The final puts prints the numeric value of the current level, where INFO is 1.
$ ./levels.rb INFO: Info message is shown WARN: Warning message is shown ERROR: Error message is shown FATAL: Fatal message is shown ANY: Unknown message is shown Current level: 1
Program Name
The progname identifies the source of a log message, for example a service or a module. It is set with logger.progname=, and the formatter receives it as the third argument. Including the program name makes it easier to filter logs from a larger application.
#!/usr/bin/env ruby
require 'logger'
logger = Logger.new($stdout)
logger.formatter = proc do |severity, time, progname, msg|
"#{severity} [#{progname}]: #{msg}\n"
end
logger.progname = "ServiceA"
logger.info("Service started")
logger.progname = "Auth"
logger.warn("Token expires soon")
logger.progname = "Database"
logger.error("Connection lost")
The formatter here prints the program name in square brackets. Before each group of messages, logger.progname is set to a different name, and every subsequent message uses that name until it is changed again. The output shows each severity with its associated program name.
$ ./progname.rb INFO [ServiceA]: Service started WARN [Auth]: Token expires soon ERROR [Database]: Connection lost
Custom Formatter and File Logging
Passing a file name to Logger.new writes the log to that file instead of standard output. The formatter works the same way for files. Logger can also rotate log files: a second argument of 'daily' or a number of old files to keep, optionally followed by a maximum size in bytes, controls rotation. The rotation configuration is shown in the configure_rotation method below; it is defined but not called, so it produces no output.
#!/usr/bin/env ruby
require 'logger'
log_file = 'app.log'
File.delete(log_file) if File.exist?(log_file)
logger = Logger.new(log_file)
logger.formatter = proc do |severity, time, progname, msg|
"#{severity}: #{msg}\n"
end
logger.info("Written to a file")
logger.warn("Second line in the file")
logger.close
# Skip the header line that Logger writes with a timestamp
File.foreach(log_file) do |line|
puts line unless line.start_with?('#')
end
File.delete(log_file)
# Rotation configuration (not called here)
def configure_rotation
Logger.new('app_daily.log', 'daily') # rotate every day
Logger.new('app_age.log', 5) # keep 5 old files
Logger.new('app_size.log', 3, 1024 * 1024) # keep 3 files, rotate at 1 MB
end
The example logs two messages to app.log, closes the logger, and reads the file back. Logger writes a header line containing a timestamp when a file is created; the loop skips lines starting with # so the printed output stays the same on every run. The configure_rotation method shows the rotation forms: 'daily' rotates once a day, 5 keeps five old files, and 3, 1024 * 1024 keeps three files and rotates when a file reaches 1 MB.
$ ./file_logging.rb INFO: Written to a file WARN: Second line in the file
Logging Exceptions
When an exception is rescued, it can be logged with its class and message. Logging the error inside the rescue block records the problem and lets the program continue. The message is built from e.class and e.message.
#!/usr/bin/env ruby
require 'logger'
logger = Logger.new($stdout)
logger.formatter = proc do |severity, time, progname, msg|
"#{severity}: #{msg}\n"
end
def divide(a, b)
a / b
end
begin
divide(10, 0)
rescue ZeroDivisionError => e
logger.error("#{e.class}: #{e.message}")
end
logger.info("Program continues after the exception")
Dividing by zero raises ZeroDivisionError. The rescue clause catches it and logs the class and message at the ERROR level. Execution continues after the begin block, so the final info call runs as well.
$ ./exceptions.rb ERROR: ZeroDivisionError: divided by 0 INFO: Program continues after the exception
List all Ruby tutorials.
Author
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.