require 'rollbar/notifier' require 'rollbar/scrubbers/params' require 'rollbar/util' module Rollbar class Item class Locals # :nodoc: class << self def exception_frames Rollbar.notifier.exception_bindings end def locals_for_location(filename, lineno) if (frame = frame_for_location(filename, lineno)) scrub(locals_for(frame[:binding])) else {} end end def frame_for_location(filename, lineno) while (frame = exception_frames.pop) return nil unless frame return frame if matching_frame?(frame, filename, lineno) end nil end private def matching_frame?(frame, filename, lineno) frame[:path] == filename && frame[:lineno].to_i <= lineno.to_i end def locals_for(frame) {}.tap do |hash| frame.local_variables.map do |var| hash[var] = prepare_value(frame.local_variable_get(var)) end end end # Prepare objects to be handled by the payload serializer. # # Hashes and Arrays are traversed. Then all types execpt strings and # immediates are exported using #inspect. Sending the object itself to the # serializer can result in large recursive expansions, especially in Rails # environments with ActiveRecord, ActiveSupport, etc. on the stack. # Other export options could be #to_s, #to_h, and #as_json. Several of these # will omit the class name, or are not implemented for many types. # # #inspect has the advantage that it is specifically intended for debugging # output. If the user wants more or different information in the payload # about a specific type, #inspect is the correct place to implement it. # Likewise the default implementation should be oriented toward usefulness # in debugging. # # Because #inspect outputs a string, it can be handled well by the string # truncation strategy for large payloads. # def prepare_value(value) return simple_value?(value) ? value : value.inspect unless value.is_a?(Hash) || value.is_a?(Array) cloned_value = ::Rollbar::Util.deep_copy(value) ::Rollbar::Util.iterate_and_update_with_block(cloned_value) do |v| simple_value?(v) ? v : v.inspect end cloned_value end def simple_classes if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.4.0') [String, Symbol, Integer, Float, TrueClass, FalseClass, NilClass] else [String, Symbol, Fixnum, Bignum, Float, TrueClass, FalseClass, NilClass] # rubocop:disable Lint/UnifiedInteger end end def simple_value?(value) simple_classes.each do |klass| # Use instance_of? so that subclasses and module containers will # be treated like complex object types, not simple values. return true if value.instance_of?(klass) end false end def scrub(hash) Rollbar::Scrubbers::Params.call( :params => hash, :config => Rollbar.configuration.scrub_fields, :whitelist => Rollbar.configuration.scrub_whitelist ) end end end end end