ExceptionManager gem

Apr 14, 2015

What is this? #

ExceptionManager is a gem for getting extra information from your exception.

Source code: https://github.com/iliabylich/exception_manager

With this gem every time when you get an exception, it’s possible to grab subject of exception (the instance of class where raise happened), locals - local variables, subject_instance_variables and subject_class_variables

Examples:

require 'exception_manager'
ExceptionManager.enable!

class TestClassThatRaisesException
  @@class_variable = :class_value

  def test_error(*args)
    @instance_variable = :instance_value

    raise 'Test error'
  end
end

begin
  TestClassThatRaisesException.new.test_error(1, 2, 3)
rescue => e
  puts "Subject: #{e.subject}"
  puts "Locals: #{e.locals}"
  puts "Instance variables: #{e.subject_instance_variables}"
  puts "Class variables: #{e.subject_class_variables}"
  puts "Summary: #{e.summary.inspect}"
end

This code snippet prints:

Subject: #<TestClassThatRaisesException:0x00000001512268>
Locals: {:args=>[1, 2, 3]}
Instance variables: {:@instance_variable=>:instance_value}
Class variables: {:@@class_variable=>:class_value}
Summary: {
  :locals => {
    :klass => TestClassThatRaisesException,
    :args=> [1, 2, 3]
  },
  :subject => #<TestClassThatRaisesException:0x00000001512268>,
  :subject_instance_variables => {
    :@instance_variable => :instance_value
  },
  :subject_class_variables => {
    :@@class_variable => :class_value
  }
}

So, you can get local variables of you exception, instance where it happened and the whole context.

If you have pry installed, you can inject pry session into stored binding:

begin
  TestClassThatRaisesException.new.test_error(1, 2, 3)
rescue => e
  e._binding.pry
end

Integration with NewRelic #

Just use the following snippet:

class StandardError
  def original_exception
    message_for_new_relic = [message, summary.inspect].join(' ')
    self.exception(message_for_new_relic)
  end
end

NewRelic gem by default calls .original_exception.to_s on every caught exception. Yes, looks dirty, but saves a lot of time!

How it works #

In old versions of ruby there was a Kernel method called set_trace_func which allows to set trigger for every executed line (including c-calls).

Ruby 2.0.0 (and newer) has a class called TracePoint which allows to subscribe to any specific method call (raise in our case).

So, simplified version of the main code looks like:

class Exception
  attr_accessor :_binding
end

TracePoint.new(:raise) do |tp|
  tp.raised_exception._binding = tp.binding
end

And then we can get everything through this binding

Compatibility #

ExceptionManager is compatible (and tested on Travis CI) with the following versions of ruby

(In other words, in all versions of ruby that has TracePoint class).

Questions #

If you have any questions/suggestions feel free to create an issue on GitHub .