class Rollbar::Item

This class represents the payload to be sent to the API. It contains the logic to build the payload, trucante it and dump the JSON.

Attributes

configuration[R]
context[R]
exception[R]
extra[R]
level[R]
logger[R]
message[R]
notifier[R]
payload[W]
scope[R]

Public Class Methods

build_with(payload, options = {}) click to toggle source
# File lib/rollbar/item.rb, line 32
def build_with(payload, options = {})
  new(options).tap do |item|
    item.payload = item.add_access_token_to_payload(payload)
  end
end
new(options) click to toggle source
# File lib/rollbar/item.rb, line 39
def initialize(options)
  @level = options[:level]
  @message = options[:message]
  @exception = options[:exception]
  @extra = options[:extra]
  @configuration = options[:configuration]
  @logger = options[:logger]
  @scope = options[:scope]
  @payload = nil
  @notifier = options[:notifier]
  @context = options[:context]
end

Public Instance Methods

add_access_token_to_payload(payload) click to toggle source
# File lib/rollbar/item.rb, line 174
def add_access_token_to_payload(payload)
  # Some use cases remain where the token is needed in the payload. For example:
  #
  # When using async senders, if the access token is changed dynamically in
  # the main process config, the sender process won't see that change.
  #
  # Until the delayed sender interface is changed to allow passing dynamic
  # config options, this workaround allows the main process to set the token
  # by adding it to the payload.
  if configuration && configuration.use_payload_access_token
    payload['access_token'] ||= configuration.access_token
  end

  payload
end
build() click to toggle source
# File lib/rollbar/item.rb, line 56
def build
  data = build_data
  self.payload = add_access_token_to_payload({ 'data' => data })

  enforce_valid_utf8
  transform
  payload
end
build_data() click to toggle source
# File lib/rollbar/item.rb, line 65
def build_data
  data = initial_data

  build_optional_data(data)

  Util.deep_merge(data, configuration.payload_options)
  Util.deep_merge(data, scope)

  # Our API doesn't allow null context values, so just delete
  # the key if value is nil.
  data.delete(:context) unless data[:context]

  data
end
build_optional_data(data) click to toggle source
# File lib/rollbar/item.rb, line 97
def build_optional_data(data)
  if configuration.project_gem_paths.any?
    data[:project_package_paths] = configuration.project_gem_paths
  end

  data[:code_version] = configuration.code_version if configuration.code_version

  return unless defined?(SecureRandom) && SecureRandom.respond_to?(:uuid)

  data[:uuid] = SecureRandom.uuid
end
configured_options() click to toggle source
# File lib/rollbar/item.rb, line 109
def configured_options
  if Gem.loaded_specs['activesupport'] &&
     Gem.loaded_specs['activesupport'].version < Gem::Version.new('4.1')
    # There are too many types that crash ActiveSupport JSON serialization,
    # and not worth the risk just to send this diagnostic object.
    # In versions < 4.1, ActiveSupport hooks Ruby's JSON.generate so deeply
    # that there's no workaround.
    'not serialized in ActiveSupport < 4.1'
  elsif configuration.use_async && !configuration.async_json_payload
    # The setting allows serialization to be performed by each handler,
    # and this usually means it is actually performed by ActiveSupport,
    # which cannot safely serialize this key.
    'not serialized when async_json_payload is not set'
  else
    scrub(configuration.configured_options.configured)
  end
end
dump() click to toggle source
# File lib/rollbar/item.rb, line 127
def dump
  # Ensure all keys are strings since we can receive the payload inline or
  # from an async handler job, which can be serialized.
  stringified_payload = Util::Hash.deep_stringify_keys(payload)
  attempts = []
  result = Truncation.truncate(stringified_payload, attempts)

  return result unless Truncation.truncate?(result)

  handle_too_large_payload(stringified_payload, result, attempts)

  nil
end
handle_too_large_payload(stringified_payload, _final_payload, attempts) click to toggle source
# File lib/rollbar/item.rb, line 141
def handle_too_large_payload(stringified_payload, _final_payload, attempts)
  uuid = stringified_payload['data']['uuid']
  host = stringified_payload['data'].fetch('server', {})['host']

  original_error = {
    :message => message,
    :exception => exception,
    :configuration => configuration,
    :uuid => uuid,
    :host => host
  }

  notifier.send_failsafe(too_large_payload_string(attempts), nil, original_error)

  logger.error('[Rollbar] Payload too large to be sent for UUID ' \
    "#{uuid}: #{Rollbar::JSON.dump(payload)}")
end
ignored?() click to toggle source
# File lib/rollbar/item.rb, line 165
def ignored?
  data = payload['data']

  return unless data[:person]

  person_id = data[:person][configuration.person_id_method.to_sym]
  configuration.ignored_person_ids.include?(person_id)
end
initial_data() click to toggle source
# File lib/rollbar/item.rb, line 80
def initial_data
  {
    :timestamp => Time.now.to_i,
    :environment => build_environment,
    :level => level,
    :language => 'ruby',
    :framework => configuration.framework,
    :server => server_data,
    :notifier => {
      :name => 'rollbar-gem',
      :version => VERSION,
      :configured_options => configured_options
    },
    :body => build_body
  }
end
payload() click to toggle source
# File lib/rollbar/item.rb, line 52
def payload
  @payload ||= build
end
too_large_payload_string(attempts) click to toggle source
# File lib/rollbar/item.rb, line 159
def too_large_payload_string(attempts)
  'Could not send payload due to it being too large after truncating attempts. ' \
    "Original size: #{attempts.first} Attempts: #{attempts.join(', ')} " \
    "Final size: #{attempts.last}"
end

Private Instance Methods

build_backtrace_body() click to toggle source
# File lib/rollbar/item.rb, line 203
def build_backtrace_body
  backtrace = Backtrace.new(exception,
                            :message => message,
                            :extra => build_extra,
                            :configuration => configuration)

  backtrace.to_h
end
build_body() click to toggle source
# File lib/rollbar/item.rb, line 199
def build_body
  exception ? build_backtrace_body : build_message_body
end
build_environment() click to toggle source
# File lib/rollbar/item.rb, line 192
def build_environment
  env = configuration.environment
  env = 'unspecified' if env.nil? || env.empty?

  env
end
build_extra() click to toggle source
# File lib/rollbar/item.rb, line 212
def build_extra
  merged_extra = Util.deep_merge(scrub(extra), scrub(error_context))

  if custom_data_method? && !Rollbar::Util.method_in_stack(:custom_data, __FILE__)
    Util.deep_merge(scrub(custom_data), merged_extra)
  else
    # avoid putting an empty {} in the payload.
    merged_extra.empty? ? nil : merged_extra
  end
end
build_message_body() click to toggle source
# File lib/rollbar/item.rb, line 266
def build_message_body
  extra = build_extra
  result = { :body => message || 'Empty message' }
  result[:extra] = extra if extra

  { :message => result }
end
custom_data() click to toggle source
# File lib/rollbar/item.rb, line 242
def custom_data
  data = if configuration.custom_data_method.arity == 3
           configuration.custom_data_method.call(message, exception, context)
         else
           configuration.custom_data_method.call
         end

  Rollbar::Util.deep_copy(data)
rescue StandardError => e
  return {} if configuration.safely?

  report_custom_data_error(e)
end
custom_data_method?() click to toggle source
# File lib/rollbar/item.rb, line 238
def custom_data_method?
  !!configuration.custom_data_method
end
enforce_valid_utf8() click to toggle source
# File lib/rollbar/item.rb, line 285
def enforce_valid_utf8
  Util.enforce_valid_utf8(payload)
end
error_context() click to toggle source
# File lib/rollbar/item.rb, line 223
def error_context
  exception.respond_to?(:rollbar_context) && exception.rollbar_context
end
report_custom_data_error(e) click to toggle source
# File lib/rollbar/item.rb, line 256
def report_custom_data_error(e)
  data = notifier.safely.error(e)

  return {} unless data.is_a?(Hash) && data[:uuid]

  uuid_url = Util.uuid_rollbar_url(data, configuration)

  { :_error_in_custom_data_method => uuid_url }
end
scrub(data) click to toggle source
# File lib/rollbar/item.rb, line 227
def scrub(data)
  return data unless data.is_a? Hash

  options = {
    :params => data,
    :config => Rollbar.configuration.scrub_fields,
    :whitelist => Rollbar.configuration.scrub_whitelist
  }
  Rollbar::Scrubbers::Params.call(options)
end
server_data() click to toggle source
# File lib/rollbar/item.rb, line 274
def server_data
  data = {
    :host => configuration.host || Socket.gethostname
  }
  data[:root] = configuration.root.to_s if configuration.root
  data[:branch] = configuration.branch if configuration.branch
  data[:pid] = Process.pid

  data
end
transform() click to toggle source
# File lib/rollbar/item.rb, line 289
def transform
  handlers = configuration.transform

  handlers.each do |handler|
    begin
      handler.call(transform_options)
    rescue StandardError => e
      logger.error("[Rollbar] Error calling the `transform` hook: #{e}")

      break
    end
  end
end
transform_options() click to toggle source
# File lib/rollbar/item.rb, line 303
def transform_options
  {
    :level => level,
    :scope => scope,
    :exception => exception,
    :message => message,
    :extra => extra,
    :payload => payload
  }
end