Class Logger provides a simple but sophisticated logging utility that you can use to create one or more event logs for your program. Each such log contains a chronological sequence of entries that provides a record of the program’s activities.
All examples on this page assume that Logger has been required:
require 'logger'
Create a log with Logger.new
:
# Single log file. logger = Logger.new('t.log') # Size-based rotated logging: 3 10-megabyte files. logger = Logger.new('t.log', 3, 10485760) # Period-based rotated logging: daily (also allowed: 'weekly', 'monthly'). logger = Logger.new('t.log', 'daily') # Log to an IO stream. logger = Logger.new($stdout)
Add entries (level, message) with Logger#add
:
logger.add(Logger::DEBUG, 'Maximal debugging info') logger.add(Logger::INFO, 'Non-error information') logger.add(Logger::WARN, 'Non-error warning') logger.add(Logger::ERROR, 'Non-fatal error') logger.add(Logger::FATAL, 'Fatal error') logger.add(Logger::UNKNOWN, 'Most severe')
Close the log with Logger#close
:
logger.close
You can add entries with method Logger#add
:
logger.add(Logger::DEBUG, 'Maximal debugging info') logger.add(Logger::INFO, 'Non-error information') logger.add(Logger::WARN, 'Non-error warning') logger.add(Logger::ERROR, 'Non-fatal error') logger.add(Logger::FATAL, 'Fatal error') logger.add(Logger::UNKNOWN, 'Most severe')
These shorthand methods also add entries:
logger.debug('Maximal debugging info') logger.info('Non-error information') logger.warn('Non-error warning') logger.error('Non-fatal error') logger.fatal('Fatal error') logger.unknown('Most severe')
When you call any of these methods, the entry may or may not be written to the log, depending on the entry’s severity and on the log level; see Log Level
An entry always has:
A severity (the required argument to add
).
An automatically created timestamp.
And may also have:
A message.
A program name.
Example:
logger = Logger.new($stdout) logger.add(Logger::INFO, 'My message.', 'mung') # => I, [2022-05-07T17:21:46.536234 #20536] INFO -- mung: My message.
The default format for an entry is:
"%s, [%s #%d] %5s -- %s: %s\n"
where the values to be formatted are:
Severity (one letter).
Timestamp.
Process
id.
Severity (word).
Program name.
Message.
You can use a different entry format by:
Setting a custom format proc (affects following entries); see formatter=.
Calling any of the methods above with a block (affects only the one entry). Doing so can have two benefits:
Context: the block can evaluate the entire program context and create a context-dependent message.
Performance: the block is not evaluated unless the log level permits the entry actually to be written:
logger.error { my_slow_message_generator }
Contrast this with the string form, where the string is always evaluated, regardless of the log level:
logger.error("#{my_slow_message_generator}")
The severity of a log entry has two effects:
Determines whether the entry is selected for inclusion in the log; see Log Level.
Indicates to any log reader (whether a person or a program) the relative importance of the entry.
The timestamp for a log entry is generated automatically when the entry is created.
The logged timestamp is formatted by method Time#strftime
using this format string:
'%Y-%m-%dT%H:%M:%S.%6N'
Example:
logger = Logger.new($stdout) logger.add(Logger::INFO) # => I, [2022-05-07T17:04:32.318331 #20536] INFO -- : nil
You can set a different format using method datetime_format=
.
The message is an optional argument to an entry method:
logger = Logger.new($stdout) logger.add(Logger::INFO, 'My message') # => I, [2022-05-07T18:15:37.647581 #20536] INFO -- : My message
For the default entry formatter, Logger::Formatter
, the message object may be:
A string: used as-is.
An Exception: message.message
is used.
Anything else: message.inspect
is used.
Note: Logger::Formatter
does not escape or sanitize the message passed to it. Developers should be aware that malicious data (user input) may be in the message, and should explicitly escape untrusted data.
You can use a custom formatter to escape message data; see the example at formatter=.
The program name is an optional argument to an entry method:
logger = Logger.new($stdout) logger.add(Logger::INFO, 'My message', 'mung') # => I, [2022-05-07T18:17:38.084716 #20536] INFO -- mung: My message
The default program name for a new logger may be set in the call to Logger.new
via optional keyword argument progname
:
logger = Logger.new('t.log', progname: 'mung')
The default program name for an existing logger may be set by a call to method progname=
:
logger.progname = 'mung'
The current program name may be retrieved with method progname:
logger.progname # => "mung"
The log level setting determines whether an entry is actually written to the log, based on the entry’s severity.
These are the defined severities (least severe to most severe):
logger = Logger.new($stdout) logger.add(Logger::DEBUG, 'Maximal debugging info') # => D, [2022-05-07T17:57:41.776220 #20536] DEBUG -- : Maximal debugging info logger.add(Logger::INFO, 'Non-error information') # => I, [2022-05-07T17:59:14.349167 #20536] INFO -- : Non-error information logger.add(Logger::WARN, 'Non-error warning') # => W, [2022-05-07T18:00:45.337538 #20536] WARN -- : Non-error warning logger.add(Logger::ERROR, 'Non-fatal error') # => E, [2022-05-07T18:02:41.592912 #20536] ERROR -- : Non-fatal error logger.add(Logger::FATAL, 'Fatal error') # => F, [2022-05-07T18:05:24.703931 #20536] FATAL -- : Fatal error logger.add(Logger::UNKNOWN, 'Most severe') # => A, [2022-05-07T18:07:54.657491 #20536] ANY -- : Most severe
The default initial level setting is Logger::DEBUG, the lowest level, which means that all entries are to be written, regardless of severity:
logger = Logger.new($stdout) logger.level # => 0 logger.add(0, "My message") # => D, [2022-05-11T15:10:59.773668 #20536] DEBUG -- : My message
You can specify a different setting in a new logger using keyword argument level
with an appropriate value:
logger = Logger.new($stdout, level: Logger::ERROR) logger = Logger.new($stdout, level: 'error') logger = Logger.new($stdout, level: :error) logger.level # => 3
With this level, entries with severity Logger::ERROR and higher are written, while those with lower severities are not written:
logger = Logger.new($stdout, level: Logger::ERROR) logger.add(3) # => E, [2022-05-11T15:17:20.933362 #20536] ERROR -- : nil logger.add(2) # Silent.
You can set the log level for an existing logger with method level=
:
logger.level = Logger::ERROR
These shorthand methods also set the level:
logger.debug! # => 0 logger.info! # => 1 logger.warn! # => 2 logger.error! # => 3 logger.fatal! # => 4
You can retrieve the log level with method level
.
logger.level = Logger::ERROR logger.level # => 3
These methods return whether a given level is to be written:
logger.level = Logger::ERROR logger.debug? # => false logger.info? # => false logger.warn? # => false logger.error? # => true logger.fatal? # => true
File
Rotation By default, a log file is a single file that grows indefinitely (until explicitly closed); there is no file rotation.
To keep log files to a manageable size, you can use log file rotation, which uses multiple log files:
Each log file has entries for a non-overlapping time interval.
Only the most recent log file is open and active; the others are closed and inactive.
For size-based log file rotation, call Logger.new
with:
Argument logdev
as a file path.
Argument shift_age
with a positive integer: the number of log files to be in the rotation.
Argument shift_size
as a positive integer: the maximum size (in bytes) of each log file; defaults to 1048576 (1 megabyte).
Examples:
logger = Logger.new('t.log', 3) # Three 1-megabyte files. logger = Logger.new('t.log', 5, 10485760) # Five 10-megabyte files.
For these examples, suppose:
logger = Logger.new('t.log', 3)
Logging begins in the new log file, t.log
; the log file is “full” and ready for rotation when a new entry would cause its size to exceed shift_size
.
The first time t.log
is full:
t.log
is closed and renamed to t.log.0
.
A new file t.log
is opened.
The second time t.log
is full:
+t.log.0 is renamed as t.log.1
.
t.log
is closed and renamed to t.log.0
.
A new file t.log
is opened.
Each subsequent time that t.log
is full, the log files are rotated:
t.log.1
is removed.
+t.log.0 is renamed as t.log.1
.
t.log
is closed and renamed to t.log.0
.
A new file t.log
is opened.
For periodic rotation, call Logger.new
with:
Argument logdev
as a file path.
Argument shift_age
as a string period indicator.
Examples:
logger = Logger.new('t.log', 'daily') # Rotate log files daily. logger = Logger.new('t.log', 'weekly') # Rotate log files weekly. logger = Logger.new('t.log', 'monthly') # Rotate log files monthly.
Example:
logger = Logger.new('t.log', 'daily')
When the given period expires:
The base log file, t.log
is closed and renamed with a date-based suffix such as t.log.20220509
.
A new log file t.log
is opened.
Nothing is removed.
The default format for the suffix is '%Y%m%d'
, which produces a suffix similar to the one above. You can set a different format using create-time option shift_period_suffix
; see details and suggestions at Time#strftime
.
Syslog::Logger
is a Logger
work-alike that logs via syslog instead of to a file. You can use Syslog::Logger
to aggregate logs between multiple machines.
By default, Syslog::Logger
uses the program name ‘ruby’, but this can be changed via the first argument to Syslog::Logger.new
.
NOTE! You can only set the Syslog::Logger
program name when you initialize Syslog::Logger
for the first time. This is a limitation of the way Syslog::Logger
uses syslog (and in some ways, a limitation of the way syslog(3) works). Attempts to change Syslog::Logger
‘s program name after the first initialization will be ignored.
The following will log to syslogd on your local machine:
require 'syslog/logger' log = Syslog::Logger.new 'my_program' log.info 'this line will be logged via syslog(3)'
Also the facility may be set to specify the facility level which will be used:
log.info 'this line will be logged using Syslog default facility level' log_local1 = Syslog::Logger.new 'my_program', Syslog::LOG_LOCAL1 log_local1.info 'this line will be logged using local1 facility level'
You may need to perform some syslog.conf setup first. For a BSD machine add the following lines to /etc/syslog.conf:
!my_program *.* /var/log/my_program.log
Then touch /var/log/my_program.log and signal syslogd with a HUP (killall -HUP syslogd, on FreeBSD).
If you wish to have logs automatically roll over and archive, see the newsyslog.conf(5) and newsyslog(8) man pages.
Device used for logging messages.
Default formatter for log messages.
Logging severity.
Default formatter for log messages.
Returns true
if the log level allows entries with severity Logger::ERROR to be written, false
otherwise. See Log Level.
Sets the log level to Logger::ERROR. See Log Level.
Equivalent to calling add
with severity Logger::ERROR
.
Closes the logger; returns nil
:
logger = Logger.new('t.log') logger.close # => nil logger.info('foo') # Prints "log writing failed. closed stream"
Related: Logger#reopen
.
Logging severity threshold (e.g. Logger::INFO
).
Sets the log level; returns severity
. See Log Level.
Argument severity
may be an integer, a string, or a symbol:
logger.level = Logger::ERROR # => 3 logger.level = 3 # => 3 logger.level = 'error' # => "error" logger.level = :error # => :error
Logger#sev_threshold=
is an alias for Logger#level=
.
Returns true
if the log level allows entries with severity Logger::DEBUG to be written, false
otherwise. See Log Level.
Sets the log level to Logger::DEBUG. See Log Level.
Returns true
if the log level allows entries with severity Logger::INFO to be written, false
otherwise. See Log Level.
Sets the log level to Logger::INFO. See Log Level.
Returns true
if the log level allows entries with severity Logger::WARN to be written, false
otherwise. See Log Level.
Sets the log level to Logger::WARN. See Log Level.
Returns true
if the log level allows entries with severity Logger::FATAL to be written, false
otherwise. See Log Level.
Sets the log level to Logger::FATAL. See Log Level.
With the single argument logdev
, returns a new logger with all default options:
Logger.new('t.log') # => #<Logger:0x000001e685dc6ac8>
Argument logdev
must be one of:
A string filepath: entries are to be written to the file at that path; if the file at that path exists, new entries are appended.
An IO
stream (typically +$stdout+, +$stderr+. or an open file): entries are to be written to the given stream.
nil
or File::NULL
: no entries are to be written.
Examples:
Logger.new('t.log') Logger.new($stdout)
The keyword options are:
level
: sets the log level; default value is Logger::DEBUG. See Log Level:
Logger.new('t.log', level: Logger::ERROR)
progname
: sets the default program name; default is nil
. See Program Name:
Logger.new('t.log', progname: 'mung')
formatter
: sets the entry formatter; default is nil
. See formatter=.
datetime_format
: sets the format for entry timestamp; default is nil
. See datetime_format=
.
binmode
: sets whether the logger writes in binary mode; default is false
.
shift_period_suffix
: sets the format for the filename suffix for periodic log file rotation; default is '%Y%m%d'
. See Periodic Rotation.
Sets the logger’s output stream:
If logdev
is nil
, reopens the current output stream.
If logdev
is a filepath, opens the indicated file for append.
If logdev
is an IO
stream (usually $stdout
, $stderr
, or an open File
object), opens the stream for append.
Example:
logger = Logger.new('t.log') logger.add(Logger::ERROR, 'one') logger.close logger.add(Logger::ERROR, 'two') # Prints 'log writing failed. closed stream' logger.reopen logger.add(Logger::ERROR, 'three') logger.close File.readlines('t.log') # => # ["# Logfile created on 2022-05-12 14:21:19 -0500 by logger.rb/v1.5.0\n", # "E, [2022-05-12T14:21:27.596726 #22428] ERROR -- : one\n", # "E, [2022-05-12T14:23:05.847241 #22428] ERROR -- : three\n"]
Creates a log entry, which may or may not be written to the log, depending on the entry’s severity and on the log level. See Log Level and Entries for details.
Examples:
logger = Logger.new($stdout, progname: 'mung') logger.add(Logger::INFO) logger.add(Logger::ERROR, 'No good') logger.add(Logger::ERROR, 'No good', 'gnum')
Output:
I, [2022-05-12T16:25:31.469726 #36328] INFO -- mung: mung E, [2022-05-12T16:25:55.349414 #36328] ERROR -- mung: No good E, [2022-05-12T16:26:35.841134 #36328] ERROR -- gnum: No good
These convenience methods have implicit severity: