class Logger
})
“#{datetime}: #{msg}n”
Logger.new(logdev, formatter: proc {|severity, datetime, progname, msg|
or via the constructor.
# e.g. “2005-09-22 08:51:08 +0900: hello world”
end
“#{datetime}: #{msg}n”
logger.formatter = proc do |severity, datetime, progname, msg|
Or, you may change the overall format via the #formatter= method.
Logger.new(logdev, datetime_format: ‘%Y-%m-%d %H:%M:%S’)
or via the constructor.
# e.g. “2004-01-03 00:54:26”
logger.datetime_format = ‘%Y-%m-%d %H:%M:%S’
You may change the date and time format via #datetime_format=.
I, [1999-03-03T02:34:24.895701 #19074] INFO – Main: info.
Log sample:
SeverityID, [DateTime #pid] SeverityLabel – ProgName: message
Log format:
default. The default format and a sample are shown below:
Log messages are rendered in the output stream in a certain format by
== Format
Logger.new(logdev, level: ‘INFO’)
Logger.new(logdev, level: :info)
Logger.new(logdev, level: Logger::INFO)
4. Constructor
# :debug < :info < :warn < :error < :fatal < :unknown
logger.level = ‘INFO’
logger.level = :info
3. Symbol or String (case insensitive)
# DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
logger.level = Logger::INFO
2. Log4r (somewhat) compatible interface.
logger.sev_threshold = Logger::WARN
1. Original interface.
=== Setting severity threshold
logger.close
=== How to close a logger
level is not set to show the debug message.
Here, the string concatenation is done every time, even if the log
logger.debug(“This is a ” + potentially + “ expensive operation”)
and the entire block will not even be evaluated. Compare to this:
If the logger’s level isINFO
or higher, no debug messages will be logged,
logger.debug { “This is a ” + potentially + “ expensive operation” }
logged. For example, if we have the following:
but to delay their evaluation until and unless the message is
The block form allows you to create potentially complex log messages,
logger.add(Logger::FATAL) { ‘Fatal error!’ }
4. With severity.
logger.info(‘initialize’) { “Initializing…” }
3. With progname.
logger.error “Argument #{@foo} mismatch.”
2. Message as a string.
logger.fatal { “Argument ‘foo’ not given.” }
1. Message in a block.
dynamic) level.debug
.add
is used below to log a message of an arbitrary (perhaps
messages of various levels? Other methods in this family arewarn
and
Notice the different methods (fatal
,error
,info
) being used to log
=== How to log a message
logger = Logger.new(‘foo.log’, ‘monthly’)
logger = Logger.new(‘foo.log’, ‘weekly’)
logger = Logger.new(‘foo.log’, ‘daily’)
5. Create a logger which ages the logfile daily/weekly/monthly.
logger = Logger.new(‘foo.log’, 10, 1024000)
Leave 10 “old” log files where each file is about 1,024,000 bytes.
4. Create a logger which ages the logfile once it reaches a certain size.
logger = Logger.new(file)
# file = File.open(‘foo.log’, File::WRONLY | File::APPEND | File::CREAT)
# To create new logfile, add File::CREAT like:
file = File.open(‘foo.log’, File::WRONLY | File::APPEND)
3. Create a logger for the specified file.
logger = Logger.new(‘logfile.log’)
2. Create a logger for the file which has the specified name.
logger = Logger.new(STDOUT)
logger = Logger.new(STDERR)
1. Create a logger which logs messages to STDERR/STDOUT.
complexity.
The options below give you various choices, in more or less increasing
=== How to create a logger
== HOWTOs
shows you how to achieve these things.
specifying a program name in conjunction with the message. The next section
auto-rolling of log files, setting the format of log messages, and
There are several interesting features that Logger provides, like
=== Features
discarded.
fatal messages are recorded. The debug and info messages are silently
Because the Logger’s level is set toWARN
, only the warning, error, and
end
logger.fatal(err)
logger.fatal(“Caught exception; exiting”)
rescue => err
end
end
logger.error(“Line in wrong format: #{line.chomp}”)
unless line =~ /^(w+) = (.*)$/
File.foreach(path) do |line|
begin
path = “a_non_existent_file”
logger.warn(“Nothing to do!”)
logger.info(“Program started”)
logger.debug(“Created logger”)
logger.level = Logger::WARN
logger = Logger.new(STDOUT)
require ‘logger’
level ofWARN
:
This creates a Logger that outputs to the standard output stream, with a
=== Example
logger.info(input)
}
original_formatter.call(severity, datetime, progname, msg.dump)
logger.formatter = proc { |severity, datetime, progname, msg|
original_formatter = Logger::Formatter.new
You can use #formatter= for escaping all data.
logger.info(“User-input: %p” % input)
logger.info(“User-input: #{input.dump}”)
is passed to Logger, and manually escape the untrusted data:
Developers should be aware of when potentially malicious data (user-input)
Note: Logger does not escape or sanitize any messages passed to it.DEBUG
.
want to know about the program’s internal state, and would set the Logger to
When you are developing the system, however, you probablyINFO
or evenWARN
.
For instance, in a production system, you may have your Logger set toDEBUG
- Low-level information for developers.
INFO
- Generic (useful) information about system operation.
WARN
- A warning.
ERROR
- A handleable error condition.
FATAL
- An unhandleable error that results in a program crash.
UNKNOWN
-
An unknown message that should always be logged.
The levels are:
at that level or higher will be printed.
their importance. You can then give the Logger a level, and only messages
The messages have associated levels, such asINFO
orERROR
that indicate
you can use to output messages.
The Logger class provides a simple but sophisticated logging utility that
== Description
- An unhandleable error that results in a program crash.
- A handleable error condition.
- A warning.
- Generic (useful) information about system operation.
- Low-level information for developers.
def <<(msg)
device exists, return +nil+.
Dump given message to the log device without any formatting. If no log
def <<(msg) @logdev&.write(msg) end
def add(severity, message = nil, progname = nil)
* If the OS supports multi I/O, records possibly may be mixed.
* Append open does not need to lock file.
* Logfile is not locked.
=== Bugs
implementation if required.
including message, class, and backtrace. See #msg2str for the
A special case is an +Exception+ object, which will be printed in detail,
if the given object is not a String.
converted to a String in order to log it. Generally, +inspect+ is used
Message format: +message+ can be any object, but it has to be
#error, and #fatal.
logging method. Users will be more inclined to use #debug, #info, #warn,
Log a message if the given severity is high enough. This is the generic
=== Description
log no message, and return +true+.
When the given severity is not high enough (for this particular logger),
=== Return
Can be omitted. Called to get a message string if +message+ is nil.
+block+::
+message+ and +block+ are given.
Program name string. Can be omitted. Treated as a message if no
+progname+::
The log message. A String or Exception.
+message+::
+WARN+, +ERROR+, +FATAL+, or +UNKNOWN+.
Severity. Constants are defined in Logger namespace: +DEBUG+, +INFO+,
+severity+::
=== Args
Logger#add(severity, message = nil, progname = nil) { ... }
:call-seq:
def add(severity, message = nil, progname = nil) severity ||= UNKNOWN if @logdev.nil? or severity < level return true end if progname.nil? progname = @progname end if message.nil? if block_given? message = yield else message = progname progname = @progname end end @logdev.write( format_message(format_severity(severity), Time.now, progname, message)) true end
def close
Close the logging device.
def close @logdev&.close end
def datetime_format
def datetime_format @default_formatter.datetime_format end
def datetime_format=(datetime_format)
Set date-time format.
def datetime_format=(datetime_format) @default_formatter.datetime_format = datetime_format end
def debug(progname = nil, &block)
See #info for more information.
Log a +DEBUG+ message.
def debug(progname = nil, &block) add(DEBUG, nil, progname, &block) end
def debug!; self.level = DEBUG; end
def debug!; self.level = DEBUG; end
def debug?; level <= DEBUG; end
Returns +true+ iff the current severity level allows for the printing of
def debug?; level <= DEBUG; end
def error(progname = nil, &block)
See #info for more information.
Log an +ERROR+ message.
def error(progname = nil, &block) add(ERROR, nil, progname, &block) end
def error!; self.level = ERROR; end
def error!; self.level = ERROR; end
def error?; level <= ERROR; end
Returns +true+ iff the current severity level allows for the printing of
def error?; level <= ERROR; end
def fatal(progname = nil, &block)
See #info for more information.
Log a +FATAL+ message.
def fatal(progname = nil, &block) add(FATAL, nil, progname, &block) end
def fatal!; self.level = FATAL; end
def fatal!; self.level = FATAL; end
def fatal?; level <= FATAL; end
Returns +true+ iff the current severity level allows for the printing of
def fatal?; level <= FATAL; end
def format_message(severity, datetime, progname, msg)
def format_message(severity, datetime, progname, msg) (@formatter || @default_formatter).call(severity, datetime, progname, msg) end
def format_severity(severity)
def format_severity(severity) SEV_LABEL[severity] || 'ANY' end
def info(progname = nil, &block)
See #add.
=== Return
program name (which you can do with #progname= as well).
You'll probably stick to the second form above, unless you want to provide a
logger.info { "User typed #{input}" }
# ...
logger.info "Waiting for input from user"
# ...
logger.info("MainApp") { "Received connection from #{ip}" }
=== Examples
are only called when the logger is configured to show them.
allows you to create potentially expensive logging messages that
the logger's level is sufficient to log the message. This
+block+:: Evaluates to the message to log. This is not evaluated unless
log message. The default can be set with #progname=.
+progname+:: In the block form, this is the #progname to use in the
+message+:: The message to log; does not need to be a String.
Log an +INFO+ message.
info(progname, &block)
info(message)
:call-seq:
def info(progname = nil, &block) add(INFO, nil, progname, &block) end
def info!; self.level = INFO; end
def info!; self.level = INFO; end
def info?; level <= INFO; end
Returns +true+ iff the current severity level allows for the printing of
def info?; level <= INFO; end
def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG,
Create an instance.
=== Description
Default is '%Y%m%d'.
The log file suffix format for +daily+, +weekly+ or +monthly+ rotation.
+shift_period_suffix+::
Use binary mode on the log device. Default value is false.
+binmode+::
Date and time format. Default value is '%Y-%m-%d %H:%M:%S'.
+datetime_format+::
Logging formatter. Default values is an instance of Logger::Formatter.
+formatter+::
Program name to include in log messages. Default value is nil.
+progname+::
Logging severity threshold. Default values is Logger::DEBUG.
+level+::
Integer). Defaults to +1048576+ (1MB).
Maximum logfile size in bytes (only applies when +shift_age+ is a positive
+shift_size+::
rotation.
+weekly+ or +monthly+). Default value is 0, which disables log file
Number of old log files to keep, *or* frequency of rotation (+daily+,
+shift_age+::
+STDOUT+, +STDERR+, or an open file).
The log device. This is a filename (String) or IO object (typically
+logdev+::
=== Args
Logger.new(logdev, datetime_format: '%Y-%m-%d %H:%M:%S')
Logger.new(logdev, formatter: formatter)
Logger.new(logdev, progname: 'progname')
Logger.new(logdev, level: :info)
Logger.new(logdev, shift_age = 'weekly')
Logger.new(logdev, shift_age = 0, shift_size = 1048576)
:call-seq:
def initialize(logdev, shift_age = 0, shift_size = 1048576, level: DEBUG, progname: nil, formatter: nil, datetime_format: nil, binmode: false, shift_period_suffix: '%Y%m%d') self.level = level self.progname = progname @default_formatter = Formatter.new self.datetime_format = datetime_format self.formatter = formatter @logdev = nil if logdev @logdev = LogDevice.new(logdev, shift_age: shift_age, shift_size: shift_size, shift_period_suffix: shift_period_suffix, binmode: binmode) end end
def level=(severity)
Set logging severity threshold.
def level=(severity) if severity.is_a?(Integer) @level = severity else case severity.to_s.downcase when 'debug' @level = DEBUG when 'info' @level = INFO when 'warn' @level = WARN when 'error' @level = ERROR when 'fatal' @level = FATAL when 'unknown' @level = UNKNOWN else raise ArgumentError, "invalid log level: #{severity}" end end end
def reopen(logdev = nil)
Reopen a log device.
=== Description
it is +nil+, do nothing for IO. Default is +nil+.
+STDOUT+, +STDERR+, or an open file). reopen the same filename if
The log device. This is a filename (String) or IO object (typically
+logdev+::
=== Args
Logger#reopen(logdev)
Logger#reopen
:call-seq:
def reopen(logdev = nil) @logdev.reopen(logdev) self end
def unknown(progname = nil, &block)
See #info for more information.
level is.
Log an +UNKNOWN+ message. This will be printed no matter what the logger's
def unknown(progname = nil, &block) add(UNKNOWN, nil, progname, &block) end
def warn(progname = nil, &block)
See #info for more information.
Log a +WARN+ message.
def warn(progname = nil, &block) add(WARN, nil, progname, &block) end
def warn!; self.level = WARN; end
def warn!; self.level = WARN; end
def warn?; level <= WARN; end
Returns +true+ iff the current severity level allows for the printing of
def warn?; level <= WARN; end