🔭 Want to centralize and monitor your Ruby logs?
Head over to Logtail and start ingesting your logs in 5 minutes.
Logging is an important part of every application life cycle. Having a good logging system becomes a key feature that helps developers, sysadmins, and support teams to understand and solve appearing problems.
Every log message has an associated log level. The log level helps you understand the severity and urgency of the message. Usually, each log level has an assigned integer representing the severity of the message.
As for Ruby, it has a powerful built-in tool and plenty of great third-party packages.
In the tutorial you are will learn how to :
logging
package.Head over to Logtail and start ingesting your logs in 5 minutes.
You will need:
Ruby provides a powerful built-in option for logging — the Logger class. It is a sophisticated logging utility available from the box. The Ruby's default logger supports different logging targets, logs formatting, and structured output.
The logger creation is a pretty-straightforward. You need to import the logger
module and create an instance of the Logger
class.
The first argument that Logger.new
method accepts is the logging device. The
logging device could be a filename or an IO object, such as STDOUT
and
STDERR
.
For the example, we are going to create a logger in the main.rb
file.
main.rb
# import the logger module
require 'logger'
# create a logger instance with logs.log file as a logging target
logger = Logger.new('logs.log')
The additional information and all overloads of the Logger.new
method are
available
in the documentation.
Ruby default logger has rich configuration ability. The basic configuration is done during the creation, but you also can change the configuration in the runtime.
For our application, we are going to set the minimum logging level to INFO
and
change the date-time format to the American one from the default
%Y-%m-%d %H:%M:%S
.
main.rb
# import the logger module
require 'logger'
# create a logger instance with logs.log file as a logging target
logger = Logger.new('logs.log')
# set the minimum logging level to INFO
logger.level = Logger::INFO
# create a string variable with the American date-time format
american_datetime_format = '%m/%d/%y %H:%M:%S'
# change the date-time format to the American one
logger.datetime_format = american_datetime_format
Sometimes, you may need to fully modify the format of your logs. For a such task, Ruby provides the Formatter class. Using the class, you can overwrite the logger's message template.
We want to write our logs in the JSON format. It allows them to be treated as data sets rather than text.
Fortunately, the Formatter
class is fully compatible with
the default JSON module.
main.rb
# import the logger module
require 'logger'
# import the JSON module
require "json"
# create a logger instance with logs.log file as a logging target
logger = Logger.new('logs.log')
# set the minimum logging level to INFO
logger.level = Logger::INFO
# create a string variable with the American date-time format
american_datetime_format = '%m/%d/%y %H:%M:%S'
# change the date-time format to the American one
logger.datetime_format = american_datetime_format
# set new logs formatter
logger.formatter = proc do |severity, datetime, progname, msg|
# specify date format
date_format = datetime.strftime(american_datetime_format)
# dump the log event to JSON
JSON.dump(date: "#{date_format}", severity:"#{severity}", message: msg) + "\\n"
end
The advanced information about the Formatter
class is available
in the documentation.
After the logger is configured, you are almost ready to start logging.
The only thing you need to know before the logging is the Logger's log levels system. It consists of the 6 levels:
To demonstrate how the logger works, we will log 6 messages, one for each log
level. According to the logger configuration, the minimum log level is info
,
so the debug
logs must be omitted in the logs.log
file.
The following code should be written in the main.rb
file:
main.rb
# import the logger module
require 'logger'
# import the JSON module
require "json"
# create a logger instance with logs.log file as a logging target
logger = Logger.new('logs.log')
# set the minimum logging level to INFO
logger.level = Logger::INFO
# create a string variable with the American date-time format
american_datetime_format = '%m/%d/%y %H:%M:%S'
# change the date-time format to the American one
logger.datetime_format = american_datetime_format
# set new logs formatter
logger.formatter = proc do |severity, datetime, progname, msg|
# specify date format
date_format = datetime.strftime(american_datetime_format)
# dump the log event to JSON
JSON.dump(date: "#{date_format}", severity:"#{severity}", message: msg) + "\\n"
end
# logging
logger.debug('debug log message')
logger.info('info log message')
logger.warn('warn log message')
logger.error('error log message')
logger.fatal('fatal log message')
logger.unknown('unknown log message')
Now, it's time to test our code! Let's run the program via the following bash command:
$ ruby main.rb
After the program execution, you can display the contents of the logs.log
file.
The simplest way to do it is cat
. The command prints the contents of the
specified file to the console.
$ cat logs.log
The output should be similar to that:
Output
{"date":"05/25/21 04:45:56","severity":"INFO","message":"info log message"}
{"date":"05/25/21 04:45:56","severity":"WARN","message":"warn log message"}
{"date":"05/25/21 04:45:56","severity":"ERROR","message":"error log message"}
{"date":"05/25/21 04:45:56","severity":"FATAL","message":"fatal log message"}
{"date":"05/25/21 04:45:56","severity":"ANY","message":"unknown log message"}
The logging
library is a long-lived project based on the design of Java's
log4j
library. It is a flexible and easy-to-use library with many powerful
features, great documentation, and big
developer community.
In the long list of the logging
library features, you can find:
The logging
package and all its dependencies can be installed with the default
Ruby package manager, RubyGems.
We can install it with the following command.
$ gem install logging
The logger creation is a pretty-straightforward. You need to import the
logging
module and call the Logging.logger
method.
For the example, we are going to create a logger in the main.rb
file.
main.rb
# import the logging module
require 'logging'
# create a logger, named "Main logger"
log = Logging.logger['Main logger']
The additional information is available in the documentation.
The logger configuration consists of several steps, such as configuring appenders, restricting log levels, and applying the configuration to a logger.
The first thing we are going to do is to restrict low-severity logs to be displayed in the console.
main.rb
# import the logging module
require 'logging'
# show only "info" and higher severity messages on STDOUT
Logging.appenders.stdout(:level => :info)
# create a logger, named "Main logger"
log = Logging.logger['Main logger']
The next thing we are going to do is to configure the appenders. For the
application, we will add a logs.json
file appender.
main.rb
# import the logging module
require 'logging'
# only show "info" or higher messages on STDOUT using the Basic layout
Logging.appenders.stdout(:level => :info)
# send all log events to the development log (including debug) as JSON
Logging.appenders.file(
# filename
'logs.json',
# JSON formatting
:layout => Logging.layouts.json
)
# create a logger, named "Main logger"
log = Logging.logger['Main logger']
# add appenders to the logger
log.add_appenders 'stdout', 'logs.json'
# set the default minimum log level
log.level = :debug
After we have configured the logger we can start logging.
The only thing you need to know before the logging is the log levels system. It consists of the 5 levels:
To demonstrate how the logger works, we will log 5 messages, one for each log
level. According to the logger configuration, only info
and higher severity
log will be displayed in the console, while all logs should be available in the
logs.json
file.
The following code should be written in the main.rb
file:
main.rb
# import the logging module
require 'logging'
# only show "info" or higher messages on STDOUT using the Basic layout
Logging.appenders.stdout(:level => :info)
# send all log events to the development log (including debug) as JSON
Logging.appenders.file(
# filename
'logs.json',
# JSON formatting
:layout => Logging.layouts.json
)
# create a logger, named "Main logger"
log = Logging.logger['Main logger']
# add appenders to the logger
log.add_appenders 'stdout', 'logs.json'
# set the default minimum log level
log.level = :debug
# logging
log.debug "A very nice little debug message."
log.info "A normal informative message."
log.warn "A warning about something unusual happened."
log.error "An error occurred!"
log.fatal "A fatal error!"
Now, it's time to test our code! Let's run the program via the following bash command:
$ ruby main.rb
After the program execution, your console's output should look like this:
Output
INFO Main logger : A normal informative message.
WARN Main logger : A warning about something unusual happened.
ERROR Main logger : An error occurred!
FATAL Main logger : A fatal error!
Now, let's display the contents of the logs.json
file.
The simplest way to do it is cat
. The command prints the contents of the
specified file to the console.
$ cat logs.json
The output should be similar to that:
Output
{"timestamp":"2021-06-09T11:42:54.745101+02:00","level":"DEBUG","logger":"Main logger","message":"A very nice little debug message."}
{"timestamp":"2021-06-09T11:42:54.753815+02:00","level":"INFO","logger":"Main logger","message":"A normal informative message."}
{"timestamp":"2021-06-09T11:42:54.753912+02:00","level":"WARN","logger":"Main logger","message":"A warning about something unusual happened."}
{"timestamp":"2021-06-09T11:42:54.753960+02:00","level":"ERROR","logger":"Main logger","message":"An error occurred!"}
{"timestamp":"2021-06-09T11:42:54.754003+02:00","level":"FATAL","logger":"Main logger","message":"A fatal error!"}
Proper logging can greatly assist in the support and development of your application. This may seem like a daunting task, but Ruby has fast and configurable built-in tools.
In the tutorial, you've created a Ruby application with a complex logging system
with the built-in tools and a third-party gem package, the logging
library.
If you'd like to continue learning about logging in Ruby, you may be interested in other third-party logging libraries. You can find information about the most popular ones on The Ruby Toolbox pages.
Now developing and maintaining your Ruby applications will be much easier!