Radar User's Guide
Overview
Radar is a tool which provides a drop-in solution to catching and reporting errors in your Ruby application via customizable mediums.
Installation
Install via RubyGems:
gem install radar
Or specify in your own library or application's Gemfile
or gemspec
so
that the gem is included properly.
Basic Usage
Setup
First, as early as possible in your application so that Radar can begin
catching exceptions right away, create a new Radar::Application
instance for your own app, replacing my_application
with a unique name for your
application.
Radar::Application.new(:my_application)
But this alone won't do anything, since you haven't configured any reporters
for the application. Reporters are what handle taking an ExceptionEvent
and doing something with it (such as storing it in a file, reporting it to a
remote server, etc.). Radar comes with some built-in reporters. Below, we configure
the application to log errors to a file (by default at ~/.radar/errors/my_application
):
Radar::Application.new(:my_application) do |app|
app.config.reporters.use Radar::Reporter::FileReporter
end
Reporting Errors
Once the application is setup, there are two methods to report errors:
- Manually call the report method on the application.
- Tell the application to rescue at exit so Radar automatically catches any exceptions before your application crashes.
Calling the report method manually:
app = Radar::Application.new(:my_application)
app.report(exception)
The use case for this is in a rescue
block somewhere, and forces Radar
to report the given exception.
Telling Radar to catch exceptions on exit is equally simple, and can be used in conjunction with the above method as well:
app = Radar::Application.new(:my_application)
app.rescue_at_exit!
Now, whenever your application is about to crash (an exception not caught by
a rescue
), Radar will catch your exception and report it just prior to
crashing.
Features
Reporters
On its own, Radar does nothing but catch and shuttle exceptions to reporters. Without reporters, Radar is basically useless! A reporter is a class which takes an Radar::ExceptionEvent and does something with it. Its easier to get a better idea of what this means with a few examples:
FileReporter
- Stores the data from an exception on the filesystem for each exception.ServerReporter
- Transfers information about an exception to some remote server.
Enabling and Configuring a Reporter
Reporters are enabled using the appilication configuration:
Radar::Application.new(:my_application) do |app|
app.config.reporters.use FileReporter
end
And can be configured by passing a block to the reporter, which is yielded with the instance of that reporter:
Radar::Application.new(:my_application) do |app|
app.config.reporters.use FileReporter do |reporter|
reporter.output_directory = "~/.radar/exceptions"
end
end
Radar also allows multiple reporters to be used, which are then called in the order they are defined when an exception occurs:
Radar::Application.new(:my_application) do |app|
app.config.reporters.use FileReporter
app.config.reporters.use AnotherReporter
end
Built-in Reporters
FileReporter
FileReporter outputs exception information as JSON to a file
on the local filesystem. The filename is in the format of timestamp-uniquehash.txt
,
where timestamp
is the time that the exception occurred and uniquehash
is the
Radar::ExceptionEvent#uniqueness_hash.
The directory where these files will be stored is configurable:
Radar::Application.new(:my_application) do |app|
app.config.reporters.use Radar::Reporter::FileReporter do |reporter|
reporter.output_directory = "~/my_application_errors"
end
end
You may also use a lambda. Below is also the default value for the FileReporter:
reporter.output_directory = lambda { |event| "~/.radar/#{event.application.name}" }
A few notes:
- The FileReporter does not automatically handle cleaning up old exception files or reporting these files to any remote server. It is up to you, if you wish, to clean up old exception information.
- The JSON output is compressed JSON, and is not pretty printed. It is up to you to take the JSON and pretty print it if you wish it to be easily human readable. There are services out there to do this.
For complete documentation on this reporter, please see the actual Radar::Reporter::FileReporter page.
Custom Reporters
It is very easy to write custom reporters. A reporter is simply a class which
responds to report
and takes a single Radar::ExceptionEvent as a parameter.
Below is an example of a reporter which simply prints out that an error
occurred:
class StdoutReporter
def report(event)
puts "An exception occurred! Message: #{event.exception.}"
end
end
And then using that reporter is just as easy:
Radar::Application.new(:my_application) do |app|
app.config.reporters.use StdoutReporter
end
Data Extensions
Data extensions allow you to easily extend the data represented by Radar::ExceptionEvent#to_hash. By default, Radar only sends a small amount of information about the host environment and the exception when an exception occurs. Often its more helpful to get more information about the environment to more easily track down a bug. Some examples:
- HTTP request headers for a web application
- Configuration settings for a desktop application
Defining Data Extensions
Data extensions are defined with classes which are expected to be initialized
with a reference to a Radar::ExceptionEvent and must implement the to_hash
method. Below is a data extension to add the output of uname -a
to the event:
class UnameExtension
def initialize(event)
@event = event
end
def to_hash
{ :uname => `uname -a` }
end
end
Enabling Data Extensions
Data extensions are enabled via the application configuration like most other things:
Radar::Application.new(:my_application) do |app|
app.config.data_extensions.use UnameExtension
end
Built-In Data Extensions
By default, Radar::ExceptionEvent#to_hash (view the source) returns very little
information on its own. To be as general and extensible as possible, even the data
such as information about the host are created using built-in data extensions.
Some of these are enabled by default, which are designated by the *
on the name.
- HostEnvironment* - Adds information about the host such as Ruby version and operating system.
*
: Enabled by default on every application.
Matchers
Matchers allow Radar applications to conditionally match exceptions so that a Radar application doesn't catch unwanted exceptions, such as exceptions which may not be caused by the library in question, or perhaps exceptions which aren't really exceptional.
Enabling a Matcher
Matchers are enabled in the application configuration:
Radar::Application.new(:app) do |app|
app.config.match :class, StandardError
app.config.match :backtrace, /file.rb$/
end
As you can see, multiple matchers may be enabled. In this case, as long as at
least one matches, then the exception will be reported. The first argument to
match is a symbol or class of a matcher. If it is a symbol,
the symbol is constantized and expects to exist under the Radar::Matchers
namespace.
If it is a class, that class will be used as the matcher. Any additional arguments
are passed directly into the initializer of the matcher. For more information
on writing a custom matcher, see the section below.
If no matchers are specified (the default), then all exceptions are caught.
Built-in Matchers
:backtrace
A matcher which matches against the backtrace of the exception. It allows:
- Match that a string is a substring of a line in the backtrace
- Match that a regexp matches a line in the backtrace
- Match one of the above up to a maximum depth in the backtrace
Examples of each are shown below (respective to the above order):
app.config.match :backtrace, "my_file.rb"
app.config.match :backtrace, /.+_test.rb/
app.config.match :backtrace, /.+_test.rb/, :depth => 5
If an exception doesn't have a backtrace (can happen if you don't actually
raise
an exception, but instantiate one) then the matcher always returns
false
.
:class
A matcher which matches against the class of the exception. It is configurable so it can check against:
- An exact match
- Match class or any subclasses
- Match a regexp name of a class
Examples of each are shown below (in the above order):
app.config.match :class, StandardError
app.config.match :class, StandardError, :include_subclasses => true
app.config.match :class, /.*Error/
Custom Matchers
Matchers are simply classes which respond to matches?
which returns a boolean
noting if the given Radar::ExceptionEvent matches. If true, then the exception
is reported, otherwise other matchers are tried, or if there are no other matchers,
the exception is ignored.
Below is a simple custom matcher which only matches exceptions with the configured message:
class ErrorMessageMatcher
def initialize()
@message =
end
def matches?(event)
event.exception. == @message
end
end
And the usage is shown below:
Radar::Application.new(:app) do |app|
app.config.match ErrorMessageMatcher, "sample message"
end
And this results in the following behavior:
raise "Hello, World" # not reported
raise "sample message" # reported since it matches the message