class Honeybadger::Notice

Constants

BACKTRACE_FILTERS

@api private A list of backtrace filters to run all the time.

GEM_ROOT_CACHE

@api private Cache gem path substitutions for backtrace lines.

PROJECT_ROOT_CACHE

@api private Cache project path substitutions for backtrace lines.

TAG_SANITIZER

@api private The Regexp used to strip invalid characters from individual tags.

TAG_SEPERATOR

@api private The String character used to split tag strings.

Attributes

action[RW]

The action (if any) that was called in this request.

api_key[RW]

The API key used to deliver this notice.

backtrace[RW]

The backtrace from the given exception or hash.

breadcrumbs[RW]

@return [Breadcrumbs::Collector] The collection of captured breadcrumbs

cause[R]

The exception cause if available.

causes[R]

@return [Cause] A list of exception causes (see {Cause})

cgi_data[RW]

CGI variables such as HTTP_METHOD.

component[RW]

The component (if any) which was used in this request (usually the controller).

config[R]
context[RW]

The context Hash.

controller[RW]

The component (if any) which was used in this request (usually the controller).

controller=[RW]

The component (if any) which was used in this request (usually the controller).

details[RW]

Custom details data

error_class[RW]

The name of the class of error (example: RuntimeError).

error_message[RW]

The message from the exception, or a general description of the error.

exception[R]

The exception that caused this notice, if any.

fingerprint[RW]

Custom fingerprint for error, used to group similar errors together.

id[R]

The unique ID of this notice which can be used to reference the error in Honeybadger.

local_variables[RW]

Local variables are extracted from first frame of backtrace.

now[R]
opts[R]
parameters[RW]

A hash of parameters from the query string or post body.

params[RW]

A hash of parameters from the query string or post body.

pid[R]
rack_env[R]
request_sanitizer[R]
session[RW]

A hash of session data from the request.

stats[R]
tags[R]

Tags which will be applied to error.

url[RW]

The URL at which the error occurred (if any).

Public Class Methods

new(config, opts = {}) click to toggle source

@api private

# File lib/honeybadger/notice.rb, line 187
def initialize(config, opts = {})
  @now   = Time.now.utc
  @pid   = Process.pid
  @id    = SecureRandom.uuid
  @stats = Util::Stats.all

  @opts = opts
  @config = config

  @rack_env = opts.fetch(:rack_env, nil)
  @request_sanitizer = Util::Sanitizer.new(filters: params_filters)

  @exception = unwrap_exception(opts[:exception])

  self.error_class = exception_attribute(:error_class, 'Notice') {|exception| exception.class.name }
  self.error_message = exception_attribute(:error_message, 'No message provided') do |exception|
    message = exception.respond_to?(:detailed_message) ?
      exception.detailed_message(highlight: false).sub(" (#{exception.class.name})", '') # Gems like error_highlight append the exception class name
      : exception.message
    "#{exception.class.name}: #{message}"
  end
  self.backtrace = exception_attribute(:backtrace, caller)
  self.cause = opts.key?(:cause) ? opts[:cause] : (exception_cause(@exception) || $!)

  self.context = construct_context_hash(opts, exception)
  self.local_variables = local_variables_from_exception(exception, config)
  self.api_key = opts[:api_key] || config[:api_key]
  self.tags = construct_tags(opts[:tags]) | construct_tags(context[:tags])

  self.url       = opts[:url]        || request_hash[:url]      || nil
  self.action    = opts[:action]     || request_hash[:action]   || nil
  self.component = opts[:controller] || opts[:component]        || request_hash[:component] || nil
  self.params    = opts[:parameters] || opts[:params]           || request_hash[:params] || {}
  self.session   = opts[:session]    || request_hash[:session]  || {}
  self.cgi_data  = opts[:cgi_data]   || request_hash[:cgi_data] || {}
  self.details   = opts[:details]    || {}

  self.session = opts[:session][:data] if opts[:session] && opts[:session][:data]

  self.breadcrumbs = opts[:breadcrumbs] || Breadcrumbs::Collector.new(config)

  # Fingerprint must be calculated last since callback operates on `self`.
  self.fingerprint = fingerprint_from_opts(opts)
end

Public Instance Methods

as_json(*args) click to toggle source

@api private Template used to create JSON payload.

@return [Hash] JSON representation of notice.

# File lib/honeybadger/notice.rb, line 236
def as_json(*args)
  request = construct_request_hash
  request[:context] = s(context)
  request[:local_variables] = local_variables if local_variables

  {
    api_key: s(api_key),
    notifier: NOTIFIER,
    breadcrumbs: sanitized_breadcrumbs,
    error: {
      token: id,
      class: s(error_class),
      message: s(error_message),
      backtrace: s(parsed_backtrace),
      fingerprint: fingerprint_hash,
      tags: s(tags),
      causes: s(prepare_causes(causes))
    },
    details: s(details),
    request: request,
    server: {
      project_root: s(config[:root]),
      revision: s(config[:revision]),
      environment_name: s(config[:env]),
      hostname: s(config[:hostname]),
      stats: stats,
      time: now,
      pid: pid
    }
  }
end
cause=(cause) click to toggle source
# File lib/honeybadger/notice.rb, line 82
def cause=(cause)
  @cause = cause
  @causes = unwrap_causes(cause)
end
halt!() click to toggle source

Halts the notice and the before_notify callback chain.

Returns

Returns nothing.

# File lib/honeybadger/notice.rb, line 284
def halt!
  @halted ||= true
end
halted?() click to toggle source

@api private Determines if this notice will be discarded.

# File lib/honeybadger/notice.rb, line 290
def halted?
  !!@halted
end
ignore?() click to toggle source

@api private Determines if this notice should be ignored.

# File lib/honeybadger/notice.rb, line 277
def ignore?
  ignore_by_origin? || ignore_by_class? || ignore_by_callbacks?
end
parsed_backtrace() click to toggle source

The parsed exception backtrace. Lines in this backtrace that are from installed gems have the base path for gem installs replaced by “[GEM_ROOT]”, while those in the project have “[PROJECT_ROOT]”. @return [Array<{:number, :file, :method => String}>]

# File lib/honeybadger/notice.rb, line 151
def parsed_backtrace
  @parsed_backtrace ||= parse_backtrace(backtrace)
end
tags=(tags) click to toggle source
# File lib/honeybadger/notice.rb, line 98
def tags=(tags)
  @tags = construct_tags(tags)
end
to_json(*a) click to toggle source

Converts the notice to JSON.

@return [Hash] The JSON representation of the notice.

# File lib/honeybadger/notice.rb, line 271
def to_json(*a)
  ::JSON.generate(as_json(*a))
end

Private Instance Methods

construct_backtrace_filters(opts) click to toggle source
# File lib/honeybadger/notice.rb, line 361
def construct_backtrace_filters(opts)
  [
    config.backtrace_filter
  ].compact | BACKTRACE_FILTERS
end
construct_context_hash(opts, exception) click to toggle source
# File lib/honeybadger/notice.rb, line 411
def construct_context_hash(opts, exception)
  context = {}
  context.merge!(Context(opts[:global_context]))
  context.merge!(exception_context(exception))
  context.merge!(Context(opts[:context]))
  context
end
construct_request_hash() click to toggle source

Construct the request data.

Returns

Returns Hash request data.

# File lib/honeybadger/notice.rb, line 374
def construct_request_hash
  request = {
    url: url,
    component: component,
    action: action,
    params: params,
    session: session,
    cgi_data: cgi_data,
    sanitizer: request_sanitizer
  }
  request.delete_if {|k,v| config.excluded_request_keys.include?(k) }
  Util::RequestPayload.build(request)
end
construct_tags(tags) click to toggle source
# File lib/honeybadger/notice.rb, line 435
def construct_tags(tags)
  ret = []
  Array(tags).flatten.each do |val|
    val.to_s.split(TAG_SEPERATOR).each do |tag|
      tag.gsub!(TAG_SANITIZER, STRING_EMPTY)
      ret << tag if tag =~ NOT_BLANK
    end
  end

  ret
end
exception_attribute(attribute, default = nil, &block) click to toggle source

Gets a property named “attribute” of an exception, either from the args hash or actual exception (in order of precedence).

attribute

A Symbol existing as a key in args and/or attribute on Exception.

default

Default value if no other value is found (optional).

block

An optional block which receives an Exception and returns the desired value.

Returns

Returns attribute value from args or exception, otherwise default.

# File lib/honeybadger/notice.rb, line 320
def exception_attribute(attribute, default = nil, &block)
  opts[attribute] || (exception && from_exception(attribute, &block)) || default
end
exception_cause(exception) click to toggle source

Fetch cause from exception.

exception

Exception to fetch cause from.

Returns

Returns the Exception cause.

# File lib/honeybadger/notice.rb, line 528
def exception_cause(exception)
  e = exception
  if e.respond_to?(:cause) && e.cause && e.cause.is_a?(Exception)
    e.cause
  elsif e.respond_to?(:original_exception) && e.original_exception && e.original_exception.is_a?(Exception)
    e.original_exception
  elsif e.respond_to?(:continued_exception) && e.continued_exception && e.continued_exception.is_a?(Exception)
    e.continued_exception
  end
end
exception_context(exception) click to toggle source

Get optional context from exception.

Returns

Returns the Hash context.

# File lib/honeybadger/notice.rb, line 391
def exception_context(exception)
  # This extra check exists because the exception itself is not expected to
  # convert to a hash.
  object = exception if exception.respond_to?(:to_honeybadger_context)
  object ||= {}.freeze

  Context(object)
end
fingerprint_from_opts(opts) click to toggle source
# File lib/honeybadger/notice.rb, line 419
def fingerprint_from_opts(opts)
  callback = opts[:fingerprint]
  callback ||= config.exception_fingerprint

  if callback.respond_to?(:call)
    callback.call(self)
  else
    callback
  end
end
fingerprint_hash() click to toggle source
# File lib/honeybadger/notice.rb, line 430
def fingerprint_hash
  return unless fingerprint
  Digest::SHA1.hexdigest(fingerprint.to_s)
end
from_exception(attribute) { |exception| ... } click to toggle source

Gets a property named attribute from an exception.

If a block is given, it will be used when getting the property from an exception. The block should accept and exception and return the value for the property.

If no block is given, a method with the same name as attribute will be invoked for the value.

# File lib/honeybadger/notice.rb, line 332
def from_exception(attribute)
  return unless exception

  if block_given?
    yield(exception)
  else
    exception.send(attribute)
  end
end
ignore_by_callbacks?() click to toggle source
# File lib/honeybadger/notice.rb, line 305
def ignore_by_callbacks?
  config.exception_filter &&
    config.exception_filter.call(self)
end
ignore_by_class?(ignored_class = nil) click to toggle source

Determines if error class should be ignored.

ignored_class_name

The name of the ignored class. May be a string or regexp (optional).

Returns

Returns true or false.

# File lib/honeybadger/notice.rb, line 348
def ignore_by_class?(ignored_class = nil)
  @ignore_by_class ||= Proc.new do |ignored_class|
    case error_class
    when (ignored_class.respond_to?(:name) ? ignored_class.name : ignored_class)
      true
    else
      exception && ignored_class.is_a?(Class) && exception.class < ignored_class
    end
  end

  ignored_class ? @ignore_by_class.call(ignored_class) : config.ignored_classes.any?(&@ignore_by_class)
end
ignore_by_origin?() click to toggle source
# File lib/honeybadger/notice.rb, line 299
def ignore_by_origin?
  return false if opts[:origin] != :rake
  return false if config[:'exceptions.rescue_rake']
  true
end
local_variables_from_exception(exception, config) click to toggle source

Fetch local variables from first frame of backtrace.

exception

The Exception containing the bindings stack.

Returns

Returns a Hash of local variables.

# File lib/honeybadger/notice.rb, line 456
def local_variables_from_exception(exception, config)
  return nil unless send_local_variables?(config)
  return {} unless Exception === exception
  return {} unless exception.respond_to?(:__honeybadger_bindings_stack)
  return {} if exception.__honeybadger_bindings_stack.empty?

  if config[:root]
    binding = exception.__honeybadger_bindings_stack.find { |b|
      if BINDING_HAS_SOURCE_LOCATION
        b.source_location[0]
      else
        b.eval('__FILE__')
      end =~ /^#{Regexp.escape(config[:root].to_s)}/
    }
  end

  binding ||= exception.__honeybadger_bindings_stack[0]

  vars = binding.eval('local_variables')
  results =
    vars.inject([]) { |acc, arg|
      begin
        result = binding.eval(arg.to_s)
        acc << [arg, result]
      rescue NameError
        # Do Nothing
      end

      acc
    }

  result_hash = Hash[results]
  request_sanitizer.sanitize(result_hash)
end
params_filters() click to toggle source
# File lib/honeybadger/notice.rb, line 571
def params_filters
  config.params_filters + rails_params_filters
end
parse_backtrace(backtrace) click to toggle source

Parse Backtrace from exception backtrace.

backtrace

The Array backtrace from exception.

Returns

Returns the Backtrace.

# File lib/honeybadger/notice.rb, line 503
def parse_backtrace(backtrace)
  Backtrace.parse(
    backtrace,
    filters: construct_backtrace_filters(opts),
    config: config,
    source_radius: config[:'exceptions.source_radius']
  ).to_a
end
prepare_causes(causes) click to toggle source

Convert list of causes into payload format.

causes

Array of Cause instances.

Returns

Returns the Array of causes in Hash payload format.

# File lib/honeybadger/notice.rb, line 561
def prepare_causes(causes)
  causes.map {|c|
    {
      class: c.error_class,
      message: c.error_message,
      backtrace: parse_backtrace(c.backtrace)
    }
  }
end
rails_params_filters() click to toggle source
# File lib/honeybadger/notice.rb, line 575
def rails_params_filters
  rack_env && Array(rack_env['action_dispatch.parameter_filter']) or []
end
request_hash() click to toggle source
# File lib/honeybadger/notice.rb, line 367
def request_hash
  @request_hash ||= Util::RequestHash.from_env(rack_env)
end
s(data) click to toggle source
# File lib/honeybadger/notice.rb, line 447
def s(data)
  Util::Sanitizer.sanitize(data)
end
sanitized_breadcrumbs() click to toggle source

Sanitize metadata to keep it at a single level and remove any filtered parameters

# File lib/honeybadger/notice.rb, line 402
def sanitized_breadcrumbs
  sanitizer = Util::Sanitizer.new(max_depth: 1, filters: params_filters)
  breadcrumbs.each do |breadcrumb|
    breadcrumb.metadata = sanitizer.sanitize(breadcrumb.metadata)
  end

  breadcrumbs.to_h
end
send_local_variables?(config) click to toggle source

Should local variables be sent?

Returns

Returns true to send local_variables.

# File lib/honeybadger/notice.rb, line 494
def send_local_variables?(config)
  config[:'exceptions.local_variables']
end
unwrap_causes(cause) click to toggle source

Create a list of causes.

cause

The first cause to unwrap.

Returns

Returns the Array of Cause instances.

# File lib/honeybadger/notice.rb, line 544
def unwrap_causes(cause)
  causes, c, i = [], cause, 0

  while c && i < MAX_EXCEPTION_CAUSES
    causes << Cause.new(c)
    i += 1
    c = exception_cause(c)
  end

  causes
end
unwrap_exception(exception) click to toggle source

Unwrap the exception so that original exception is ignored or reported.

exception

The exception which was rescued.

Returns

Returns the Exception to report.

# File lib/honeybadger/notice.rb, line 518
def unwrap_exception(exception)
  return exception unless config[:'exceptions.unwrap']
  exception_cause(exception) || exception
end

Deprecated

↑ top

Attributes

source[R]

Excerpt from source file.