Class: RailsOrbit::Backtrace
- Inherits:
-
Object
- Object
- RailsOrbit::Backtrace
- 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
-
#frames ⇒ Object
readonly
Returns the value of attribute frames.
Class Method Summary collapse
- .app_root ⇒ Object
-
.application_file?(file) ⇒ Boolean
A frame is “application” code when it lives under the app root but not in vendored gems.
- .gem_paths ⇒ Object
- .parse(raw) ⇒ Object
- .parse_line(line) ⇒ Object
- .relative_to_app(file) ⇒ Object
-
.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”.
Instance Method Summary collapse
- #application_frames ⇒ Object
- #empty? ⇒ Boolean
-
#initialize(frames) ⇒ Backtrace
constructor
A new instance of Backtrace.
-
#top_location ⇒ Object
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.
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
#frames ⇒ Object (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_root ⇒ Object
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.
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_paths ⇒ Object
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_frames ⇒ Object
107 108 109 |
# File 'lib/rails_orbit/backtrace.rb', line 107 def application_frames @application_frames ||= frames.select(&:application?) end |
#empty? ⇒ Boolean
118 119 120 |
# File 'lib/rails_orbit/backtrace.rb', line 118 def empty? frames.empty? end |
#top_location ⇒ Object
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 |