Escalate
A simple and lightweight gem to escalate rescued exceptions. This implementation is an abstract interface that can be used on it's own, or attached to more concrete implementations like Honeybadger, Airbrake, or Sentry in order to not just log exceptions in an easy to parse way, but also escalate the appropriate information to third party systems.
Installation
Add this line to your application's Gemfile:
gem 'escalate'
And then execute:
$ bundle install
Or install it yourself as:
$ gem install escalate
Usage
Adding Escalate to Your Gem
All you need to do is extend Escalate.mixin
within your gem and you're all set.
module SomeGem
include Escalate.mixin
end
Using .escalate
This will expose the Escalate#escalate
method within your gem to be used instead
of using logger.error
.
module SomeGem
include Escalate.mixin
class << self
attr_accessor :logger
end
class SomeClass
def something_dangerous(key)
# code here...
rescue => exception
SomeGem.escalate(exception,
"Exception raised in SomeGem::SomeClass#something_dangerous",
context: { key: key })
end
end
end
The following arguments are supported by .escalate
:
argument | default | type | description |
---|---|---|---|
exception |
Exception |
The exception to escalate. | |
location_message |
String |
A message providing information about where and why this exception is being escalated. | |
context: |
{} |
Hash |
An optional hash of context. This will be logged with the exception. |
When SomeGem.escalate
above is triggered, it will use the logger returned by SomeGem.logger
or
default to a STDERR
logger and do the following:
- [optional] Log an error containing the exception, location_message, and context hash
- Trigger any
on_escalate_callbacks
configured on theEscalate
gem
Step (1) is optional. It will happen if either of these is true:
- by default if no
on_escalate_callbacks
have been registered; or - if any of the
on_escalate_callbacks
was registered withon_escalate(log_first: true)
.
Using .rescue_and_escalate
The above pattern of rescue
with escalate
is very common, so a single method is provided to do both.
This is equivalent to the code above:
class SomeClass
def something_dangerous(key)
SomeGem.rescue_and_escalate("Exception raised in SomeGem::SomeClass#something_dangerous",
context: { key: key }) do
# code here...
end
end
end
The following arguments are supported by .rescue_and_escalate
:
argument | default | type | description |
---|---|---|---|
location_message |
required | String |
A message providing information about where and why this exception is being escalated. |
context: |
{} |
Hash |
An optional hash of context. This will be logged with the exception. |
exceptions: |
StandardError |
Class or Array(Class)
|
The Class or Array(Class) to rescue. Class must be Exception or a sub-class. |
pass_through_exceptions: |
[SystemExit, SystemStackError, NoMemoryError, SecurityError, SignalException] |
Class or Array(Class)
|
The Class or Array(Class) to pass through without rescuing. Class must be Exception , or a sub-class. These take precedence over exceptions: . |
Registering an Escalate Callback
If you are using an error reporting service, you can register an on_escalate
callback to escalate exceptions.
You have the option to handle logging yourself, or to let escalate
log first, before yielding to callbacks.
The following arguments are supported by .on_escalate
:
argument | default | type | description |
---|---|---|---|
name: |
block.source_location |
String or Array
|
Globally unique name for this callback. |
log_first: |
true |
Boolean |
Whether escalate should log the error first. false means the block will take care of this. |
&block |
required | Proc |
The callback block to yield to from escalate . |
Leave the Logging to the Escalate Gem
Here is an example that uses the default log_first: true
so that logging is handled by the Escalate
gem first:
Escalate.on_escalate do |exception, location_message, **context|
# send exception, location_message, **context to the error reporting service here
end
Callback Uniqueness
Each callback may be named with the name:
keyword argument.
If a callback with the same name has been registered before, it will be overwritten with the new one.
Escalate.on_escalate(name: 'abc gem') do |exception, location_message, **context|
# send exception, location_message, **context to the error reporting service here
end
If not given, the name:
defaults to the .source_location
property of the passed-in block.
Handle the Logging in the on_escalate
Callback
Here is an example that handles logging itself with log_first: false
:
Escalate.on_escalate(log_first: false) do |exception, location_message, **context|
# log here first
# send exception, location_message, **context to the error reporting service here
end
Development
After checking out the repo, run bin/setup
to install dependencies. Then, run rake spec
to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install
. To release a new version, update the version number in version.rb
, and then run bundle exec rake release
, which will create a git tag for the version, push git commits and the created tag, and push the .gem
file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/invoca/escalate.