class RedmineAirbrakeBackend::IosReport

iOS Report received by airbrake

Public Class Methods

handle_header(key, value, error, application) click to toggle source

rubocop:enable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength

# File lib/redmine_airbrake_backend/ios_report.rb, line 68
def self.handle_header(key, value, error, application)
  case key
  when 'Exception Type'
    error[:type] = value
  when 'Exception Codes'
    error[:message] = value
  when 'Incident Identifier'
    return value
  when 'Identifier'
    application[:name] = value
  when 'Version'
    application[:version] = value
  end

  nil
end
new(options) click to toggle source
Calls superclass method
# File lib/redmine_airbrake_backend/ios_report.rb, line 8
def initialize(options)
  error, application, attachments = self.class.parse(options[:report])

  super(
    errors:      [Error.new(error)],
    context:     options[:context],
    application: application,
    attachments: attachments
  )
end
parse(data) click to toggle source

rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength

# File lib/redmine_airbrake_backend/ios_report.rb, line 20
def self.parse(data)
  error       = { backtrace: [] }
  application = {}
  attachments = []

  header_finished      = false
  next_line_is_message = false
  crashed_thread       = false
  indicent_identifier  = nil

  data.split("\n").each do |line|
    if line.match?(/^(Application Specific Information|Last Exception Backtrace|Thread \d+( Crashed)?):$/)
      header_finished = true
    end

    unless header_finished
      ii = parse_header_line(line, error, application)
      indicent_identifier ||= ii if ii
    end

    if next_line_is_message
      next_line_is_message = false

      parse_message(line, error)
    end

    crashed_thread = false if line.match?(/^Thread \d+:$/)

    if crashed_thread
      backtrace = parse_backtrace_element(line)
      error[:backtrace] << backtrace if backtrace
    end

    if error[:backtrace].compact.blank? && line.match?(/^(Last Exception Backtrace|Thread \d+ Crashed):$/)
      crashed_thread = true
    end

    next_line_is_message = true if line.match?(/^Application Specific Information:$/)
  end

  return nil if error.blank?

  attachments << { filename: "#{indicent_identifier}.crash", data: data } if indicent_identifier.present?

  [error, application, attachments]
end
parse_backtrace_element(line) click to toggle source
# File lib/redmine_airbrake_backend/ios_report.rb, line 104
def self.parse_backtrace_element(line)
  return nil unless line =~ /^(\d+)\s+([^\s]+)\s+(0x[0-9a-f]+)\s+(.+) \+ (\d+)$/

  {
    file:     Regexp.last_match(2),
    function: Regexp.last_match(4),
    line:     Regexp.last_match(5)
  }
end
parse_header_line(line, error, application) click to toggle source
# File lib/redmine_airbrake_backend/ios_report.rb, line 85
def self.parse_header_line(line, error, application)
  key, value = line.split(':', 2).map(&:strip)

  return nil if key.blank? || value.blank?

  handle_header(key, value, error, application)
end
parse_message(line, error) click to toggle source
# File lib/redmine_airbrake_backend/ios_report.rb, line 93
def self.parse_message(line, error)
  error[:message] = line

  if line =~ /^\*\*\* Terminating app due to uncaught exception '([^']+)', reason: '\*\*\* (.*)'$/
    error[:type]    = Regexp.last_match(1)
    error[:message] = Regexp.last_match(2)
  else
    error[:message] = line
  end
end