Class: RailsOrbit::Backtrace

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_orbit/backtrace.rb

Overview

Parses a raw exception backtrace (the newline-joined Ruby backtrace that solid_errors stores on each occurrence) into structured frames, flagging which frames belong to the host application versus third-party gems.

It reads no source files — only file, line, and method are extracted — so the dashboard stays fast and works even when the rendering server does not have the source on disk. It also depends only on the raw text, never on solid_errors’ own Backtrace/BacktraceLine classes, so it is unaffected by internal changes in that gem.

Defined Under Namespace

Classes: Frame

Constant Summary collapse

LINE_FORMAT =

Matches “path/to/file.rb:42:in ‘method’” or “:in ‘method’” (Ruby 3.4+), with the method portion optional. A leading “X:” Windows drive is allowed.

/\A((?:[A-Za-z]:)?[^:]+):(\d+)(?::in [`']([^']+)')?\z/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(frames) ⇒ Backtrace

Returns a new instance of Backtrace.



103
104
105
# File 'lib/rails_orbit/backtrace.rb', line 103

def initialize(frames)
  @frames = frames
end

Instance Attribute Details

#framesObject (readonly)

Returns the value of attribute frames.



101
102
103
# File 'lib/rails_orbit/backtrace.rb', line 101

def frames
  @frames
end

Class Method Details

.app_rootObject



89
90
91
92
93
# File 'lib/rails_orbit/backtrace.rb', line 89

def self.app_root
  return unless defined?(Rails) && Rails.respond_to?(:root) && Rails.root

  Rails.root.to_s
end

.application_file?(file) ⇒ Boolean

A frame is “application” code when it lives under the app root but not in vendored gems. Gem frames and stdlib frames are not application frames.

Returns:

  • (Boolean)


66
67
68
69
70
71
# File 'lib/rails_orbit/backtrace.rb', line 66

def self.application_file?(file)
  root = app_root
  return false unless root && file.start_with?(root)

  !file.start_with?(File.join(root, "vendor"))
end

.gem_pathsObject



95
96
97
98
99
# File 'lib/rails_orbit/backtrace.rb', line 95

def self.gem_paths
  return [] unless defined?(Gem)

  Gem.path.map(&:to_s)
end

.parse(raw) ⇒ Object



41
42
43
44
# File 'lib/rails_orbit/backtrace.rb', line 41

def self.parse(raw)
  frames = String(raw).split("\n").filter_map { |line| parse_line(line) }
  new(frames)
end

.parse_line(line) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/rails_orbit/backtrace.rb', line 46

def self.parse_line(line)
  stripped = line.to_s.strip
  return nil if stripped.empty?

  if (match = stripped.match(LINE_FORMAT))
    file = match[1]
    Frame.new(
      file: file,
      line: match[2].to_i,
      method_name: match[3],
      application: application_file?(file),
      raw: stripped
    )
  else
    Frame.new(file: nil, line: nil, method_name: nil, application: false, raw: stripped)
  end
end

.relative_to_app(file) ⇒ Object



73
74
75
76
77
78
# File 'lib/rails_orbit/backtrace.rb', line 73

def self.relative_to_app(file)
  root = app_root
  return file unless root && file.start_with?(root)

  file.sub(/\A#{Regexp.escape(root)}\/?/, "")
end

.shorten_gem(file) ⇒ Object

Trims the gem install prefix so “/…/gems/foo-1.0/lib/x.rb” reads as “foo-1.0/lib/x.rb”. Falls back to the full path if no prefix matches.



82
83
84
85
86
87
# File 'lib/rails_orbit/backtrace.rb', line 82

def self.shorten_gem(file)
  gem_paths.each do |path|
    return file.sub(/\A#{Regexp.escape(path)}\/?(?:gems\/)?/, "") if file.start_with?(path)
  end
  file
end

Instance Method Details

#application_framesObject



107
108
109
# File 'lib/rails_orbit/backtrace.rb', line 107

def application_frames
  @application_frames ||= frames.select(&:application?)
end

#empty?Boolean

Returns:

  • (Boolean)


118
119
120
# File 'lib/rails_orbit/backtrace.rb', line 118

def empty?
  frames.empty?
end

#top_locationObject

The most relevant “where did this happen” frame: the first application frame, or the first parseable frame if the trace is entirely framework/gem code, or nil when there is nothing usable.



114
115
116
# File 'lib/rails_orbit/backtrace.rb', line 114

def top_location
  application_frames.first || frames.find(&:parsed?)
end