warn, warnings and log levels
warn writes to stderr
puts is the program's output and warn is the program's complaint. They go to different streams, which is why a warning does not corrupt a report a script is printing:
ruby -e 'warn "careful"; puts "ok"'
careful
ok
Both lines appear in a terminal because both streams point at it. Split them and the difference is clear:
ruby -e 'warn "careful"; puts "ok"' 2>/dev/null
ok
warn adds a newline and returns nil. $stderr.puts does the same thing with the stream named, and STDERR is the constant version. A warning that a reader should see even when stdout is piped into a file belongs on stderr.
ruby -w
The interpreter has its own warnings, and -w turns the full set on. An assigned variable that is never read is the smallest example:
x = 1
ruby -w w.rb
w.rb:1: warning: assigned but unused variable - x
The warning names the file and the line, which is what makes a warning worth reading. A run of a script with -w is a cheap lint pass: it finds a shadowed variable, a redefined method, an unused argument and a constant assigned twice.
ruby -W0 turns them off, and -W:deprecated sets one category. Warning.warn is the hook a library overrides to route interpreter warnings into its own log:
ruby -e 'Warning.warn("note\n"); puts "after"'
note
after
Logger and levels
A long-running program wants more than a line on stderr: a level, a timestamp and a way to silence the noise. The standard library has Logger for exactly that:
require "logger"
log = Logger.new($stderr)
log.level = Logger::WARN
log.info("hidden")
log.warn("careful")
ruby -e 'require "logger"; log = Logger.new($stderr); log.level = Logger::WARN; log.info("hidden"); log.warn("careful")'
W, [2026-11-05T00:12:31.785495 #462] WARN -- : careful
The debug line never appears because the level is set to WARN. The timestamp and the process id in the line change on every run. The order is DEBUG, INFO, WARN, ERROR, FATAL; set the level from the environment (ENV.fetch("LOG_LEVEL", "INFO")) so the same build is quiet in production and loud on a laptop. Lesson 40, later in the course, covers the configuration side, and lesson 147 goes deeper into formatters and rotation.
Warning or exception
An exception means the operation cannot continue: the caller must do something. A warning means the operation continues and the reader should know: a deprecated option, a value that was clamped, a fallback that was used. The test is whether the program has a correct answer to give.
- Exception. A missing required setting, a failed write, a parse error: there is no value to return.
- Warning. An option ignored, a default applied, a slow path taken: there is a value, and it is worth a note.
- Neither. A message a user asked for. That is puts, or the report the program exists to print.
What to watch for
A warning that fires on every request is noise, and noise is ignored. Log
it once, at the point where the configuration is read, or raise the level
so it does not print in production.
puts in a library is a bug: it writes to the
caller's stdout. A gem writes to its logger, and the application decides
where that goes.
Next: Debugging with the debug gem
Article author: Arthur Isaev