class Raygun::Client

client for the Raygun REST APIv1 as per raygun.com/documentation/product-guides/crash-reporting/api/

Constants

ENV_IP_ADDRESS_KEYS
MAX_BREADCRUMBS_SIZE
NO_API_KEY_MESSAGE

Public Class Methods

new() click to toggle source
# File lib/raygun/client.rb, line 12
def initialize
  @api_key = require_api_key
  @headers = {
    "X-ApiKey" => @api_key
  }

  enable_http_proxy if Raygun.configuration.proxy_settings[:address]
  self.class.base_uri Raygun.configuration.api_url
  self.class.default_timeout(Raygun.configuration.error_report_send_timeout)
end

Public Instance Methods

require_api_key() click to toggle source
# File lib/raygun/client.rb, line 23
def require_api_key
  Raygun.configuration.api_key || print_api_key_warning
end
track_exception(exception_instance, env = {}, user = nil) click to toggle source
# File lib/raygun/client.rb, line 27
def track_exception(exception_instance, env = {}, user = nil)
  create_entry(build_payload_hash(exception_instance, env, user))
end

Private Instance Methods

action_dispatch_params(env) click to toggle source
# File lib/raygun/client.rb, line 131
def action_dispatch_params(env)
  env["action_dispatch.request.parameters"]
end
affected_user_present?(env) click to toggle source
# File lib/raygun/client.rb, line 82
def affected_user_present?(env)
  !!env["raygun.affected_user"]
end
build_payload_hash(exception_instance, env = {}, user = nil) click to toggle source

see raygun.com/documentation/product-guides/crash-reporting/api/

# File lib/raygun/client.rb, line 168
def build_payload_hash(exception_instance, env = {}, user = nil)
  Raygun.log('building payload hash')
  custom_data = filter_custom_data(env) || {}
  exception_custom_data = if exception_instance.respond_to?(:raygun_custom_data)
                            exception_instance.raygun_custom_data
                          else
                            {}
                          end

  tags = env.delete(:tags) || []

  if rails_env
    tags << rails_env
  else
    tags << rack_env
  end

  combined_tags = []

  if Raygun.configuration.tags.is_a?(Proc)
    configuration_tags = Raygun.configuration.tags.call(exception_instance, env)
  else
    configuration_tags = Raygun.configuration.tags
  end

  combined_tags.concat(configuration_tags)

  Raygun.log('set tags')

  grouping_key = env.delete(:grouping_key)
  correlation_id = env.delete(:correlation_id)

  configuration_custom_data = Raygun.configuration.custom_data
  configured_custom_data = if configuration_custom_data.is_a?(Proc)
                            configuration_custom_data.call(exception_instance, env)
                           else
                            configuration_custom_data
                           end

  Raygun.log('set custom data')

  error_details = {
      machineName:    hostname,
      version:        version,
      client:         client_details,
      error:          error_details(exception_instance),
      userCustomData: exception_custom_data.merge(custom_data).merge(configured_custom_data),
      tags:           combined_tags.concat(tags).compact.uniq,
      request:        request_information(env),
      environment:    {
        utcOffset: Time.now.utc_offset / 3600
      }
  }
  store = ::Raygun::Breadcrumbs::Store
  error_details[:breadcrumbs] = store.take_until_size(MAX_BREADCRUMBS_SIZE).map(&:build_payload) if store.any?

  Raygun.log('set details and breadcrumbs')

  error_details.merge!(groupingKey: grouping_key) if grouping_key
  error_details.merge!(correlationId: correlation_id) if correlation_id

  user_details = if affected_user_present?(env)
                   user_information(env)
                 elsif user != nil
                   AffectedUser.information_hash(user)
                 end
  error_details.merge!(user: user_details) unless user_details == nil

  Raygun.log('set user details')

  if Raygun.configuration.filter_payload_with_whitelist
    Raygun.log('filtering payload with whitelist')
    error_details = filter_payload_with_whitelist(error_details)
  end

  {
    occurredOn: Time.now.utc.iso8601,
    details:    error_details
  }
end
client_details() click to toggle source
# File lib/raygun/client.rb, line 40
def client_details
  {
    name:      Raygun::CLIENT_NAME,
    version:   Raygun::VERSION,
    clientUrl: Raygun::CLIENT_URL
  }
end
create_entry(payload_hash) click to toggle source
# File lib/raygun/client.rb, line 249
def create_entry(payload_hash)
  Raygun.log('sending payload to api')

  self.class.post(
    "/entries",
    verify_peer: true,
    verify: true,
    headers: @headers,
    body: JSON.generate(payload_hash),
  )
end
enable_http_proxy() click to toggle source
# File lib/raygun/client.rb, line 33
def enable_http_proxy
  self.class.http_proxy(Raygun.configuration.proxy_settings[:address],
                        Raygun.configuration.proxy_settings[:port] || "80",
                        Raygun.configuration.proxy_settings[:username],
                        Raygun.configuration.proxy_settings[:password])
end
error_details(exception) click to toggle source
# File lib/raygun/client.rb, line 48
def error_details(exception)
  details = {
    className:  exception.class.to_s,
    message:    exception.message.to_s.encode('UTF-16', :undef => :replace, :invalid => :replace).encode('UTF-8'),
    stackTrace: (exception.backtrace || []).map { |line| stack_trace_for(line) },
  }

  details.update(innerError: error_details(exception.cause)) if exception.respond_to?(:cause) && exception.cause

  details
end
filter_custom_data(env) click to toggle source
# File lib/raygun/client.rb, line 162
def filter_custom_data(env)
  params = env.delete(:custom_data) || {}
  filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"])
end
filter_params_with_array(params_hash, filter_keys) click to toggle source
# File lib/raygun/client.rb, line 284
def filter_params_with_array(params_hash, filter_keys)
  # Recursive filtering of (nested) hashes
  (params_hash || {}).inject({}) do |result, (k, v)|
    result[k] = case v
    when Hash
      filter_params_with_array(v, filter_keys)
    else
      filter_keys.any? { |fk| /#{fk}/i === k.to_s } ? "[FILTERED]" : v
    end
    result
  end
end
filter_params_with_blacklist(params_hash = {}, extra_filter_keys = nil) click to toggle source
# File lib/raygun/client.rb, line 261
def filter_params_with_blacklist(params_hash = {}, extra_filter_keys = nil)
  filter_parameters = Raygun.configuration.filter_parameters

  if filter_parameters.is_a? Proc
    filter_parameters.call(params_hash)
  else
    filter_keys = (Array(extra_filter_keys) + filter_parameters).map(&:to_s)

    filter_params_with_array(params_hash, filter_keys)
  end
end
filter_payload_with_whitelist(payload_hash) click to toggle source
# File lib/raygun/client.rb, line 273
def filter_payload_with_whitelist(payload_hash)
  shape = Raygun.configuration.whitelist_payload_shape

  if shape.is_a? Proc
    shape.call(payload_hash)
  else
    # Always keep the client hash, so force it to true here
    Services::ApplyWhitelistFilterToPayload.new.call(shape.merge(client: true), payload_hash)
  end
end
form_params(env) click to toggle source
# File lib/raygun/client.rb, line 124
def form_params(env)
  Raygun.log('retrieving form params')

  params = action_dispatch_params(env) || rack_params(env) || {}
  filter_params_with_blacklist(params, env["action_dispatch.parameter_filter"])
end
headers(rack_env) click to toggle source
# File lib/raygun/client.rb, line 110
def headers(rack_env)
  rack_env.select { |k, v| k.to_s.start_with?("HTTP_") }.inject({}) do |hsh, (k, v)|
    hsh[normalize_raygun_header_key(k)] = v
    hsh
  end
end
hostname() click to toggle source
# File lib/raygun/client.rb, line 70
def hostname
  Socket.gethostname
end
ip_address_from(env_hash) click to toggle source
# File lib/raygun/client.rb, line 297
def ip_address_from(env_hash)
  ENV_IP_ADDRESS_KEYS.each do |key_to_try|
    return env_hash[key_to_try] unless env_hash[key_to_try].nil? || env_hash[key_to_try] == ""
  end
  "(Not Available)"
end
normalize_raygun_header_key(key) click to toggle source
# File lib/raygun/client.rb, line 117
def normalize_raygun_header_key(key)
  key.sub(/^HTTP_/, '')
     .sub(/_/, ' ')
     .split.map(&:capitalize).join(' ')
     .sub(/ /, '-')
end
print_api_key_warning() click to toggle source
rack_env() click to toggle source
# File lib/raygun/client.rb, line 86
def rack_env
  ENV["RACK_ENV"]
end
rack_params(env) click to toggle source
# File lib/raygun/client.rb, line 135
def rack_params(env)
  request = Rack::Request.new(env)
  request.params if env["rack.input"]
end
rails_env() click to toggle source
# File lib/raygun/client.rb, line 90
def rails_env
  ENV["RAILS_ENV"]
end
raw_data(rack_env) click to toggle source
# File lib/raygun/client.rb, line 140
def raw_data(rack_env)
  Raygun.log('retrieving raw data')
  request = Rack::Request.new(rack_env)

  return unless Raygun.configuration.record_raw_data
  return if request.get?
  Raygun.log('passed raw_data checks')

  input = rack_env['rack.input']

  if input && !request.form_data?
    input.rewind

    body = input.read(4096) || ''
    input.rewind

    body
  else
    {}
  end
end
request_information(env) click to toggle source
# File lib/raygun/client.rb, line 94
def request_information(env)
  Raygun.log('retrieving request information')

  return {} if env.nil? || env.empty?
  {
    hostName:    env["SERVER_NAME"],
    url:         env["PATH_INFO"],
    httpMethod:  env["REQUEST_METHOD"],
    iPAddress:   "#{ip_address_from(env)}",
    queryString: Rack::Utils.parse_nested_query(env["QUERY_STRING"]),
    headers:     headers(env),
    form:        form_params(env),
    rawData:     raw_data(env)
  }
end
stack_trace_for(line) click to toggle source
# File lib/raygun/client.rb, line 60
def stack_trace_for(line)
  # see http://www.ruby-doc.org/core-2.0/Exception.html#method-i-backtrace
  file_name, line_number, method = line.split(":")
  {
    lineNumber: line_number,
    fileName:   file_name,
    methodName: method ? method.gsub(/^in `(.*?)'$/, "\\1") : "(none)"
  }
end
user_information(env) click to toggle source
# File lib/raygun/client.rb, line 78
def user_information(env)
  env["raygun.affected_user"]
end
version() click to toggle source
# File lib/raygun/client.rb, line 74
def version
  Raygun.configuration.version
end