Class: RailsErrorDashboard::Services::BacktraceProcessor
- Inherits:
-
Object
- Object
- RailsErrorDashboard::Services::BacktraceProcessor
- Defined in:
- lib/rails_error_dashboard/services/backtrace_processor.rb
Overview
Pure algorithm service for backtrace processing Handles truncation and signature generation with no database access.
Class Method Summary collapse
-
.calculate_signature(backtrace, locations: nil) ⇒ String?
Calculate a signature hash from backtrace for fuzzy similarity matching Extracts file paths and method names, ignoring line numbers, then produces an order-independent SHA256 digest.
-
.shorten_gem_path(line) ⇒ String
Shorten gem/ruby paths to remove user-specific prefixes.
-
.truncate(backtrace, max_lines: nil) ⇒ String?
Truncate backtrace to a maximum number of lines and shorten gem paths.
Class Method Details
.calculate_signature(backtrace, locations: nil) ⇒ String?
Calculate a signature hash from backtrace for fuzzy similarity matching Extracts file paths and method names, ignoring line numbers, then produces an order-independent SHA256 digest.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/rails_error_dashboard/services/backtrace_processor.rb', line 76 def self.calculate_signature(backtrace, locations: nil) # Try structured locations first (more reliable, no regex) if locations && !locations.empty? return signature_from_locations(locations) end return nil if backtrace.blank? lines = backtrace.is_a?(String) ? backtrace.split("\n") : backtrace frames = lines.first(20).map do |line| if line =~ %r{([^/]+\.rb):.*?in `(.+)'$} "#{Regexp.last_match(1)}:#{Regexp.last_match(2)}" elsif line =~ %r{([^/]+\.rb)} Regexp.last_match(1) end end.compact.uniq return nil if frames.empty? file_paths = frames.map { |frame| frame.split(":").first }.sort Digest::SHA256.hexdigest(file_paths.join("|"))[0..15] end |
.shorten_gem_path(line) ⇒ String
Shorten gem/ruby paths to remove user-specific prefixes. Preserves gem name + version for debugging.
Examples:
/home/user/.gem/ruby/3.4.0/gems/rack-3.2.6/lib/rack/head.rb:15
→ gems/rack-3.2.6/lib/rack/head.rb:15
/home/user/.local/share/mise/installs/ruby/4.0.2/lib/ruby/4.0.0/net/http.rb:1234
→ ruby/4.0.0/net/http.rb:1234
/home/user/myapp/app/controllers/users_controller.rb:10
→ app/controllers/users_controller.rb:10
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
# File 'lib/rails_error_dashboard/services/backtrace_processor.rb', line 51 def self.shorten_gem_path(line) # Strip everything before /gems/ (gem code) if line.include?("/gems/") line.sub(%r{^.*/gems/}, "gems/") # Strip everything before /lib/ruby/ (Ruby stdlib) elsif line.include?("/lib/ruby/") line.sub(%r{^.*/lib/ruby/}, "ruby/") # Strip everything before /app/ (application code) elsif line.include?("/app/") line.sub(%r{^.*/app/}, "app/") # Strip everything before /lib/ for app lib code (but not gem/ruby paths already handled) elsif line.include?("/lib/") line.sub(%r{^.*/lib/}, "lib/") else line end end |
.truncate(backtrace, max_lines: nil) ⇒ String?
Truncate backtrace to a maximum number of lines and shorten gem paths.
Gem paths like:
/home/user/.local/share/mise/installs/ruby/4.0.2/lib/ruby/gems/4.0.0/gems/actionpack-8.1.3/lib/...
are shortened to:
gems/actionpack-8.1.3/lib/...
This saves significant disk space without losing debugging value (issue #115).
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/rails_error_dashboard/services/backtrace_processor.rb', line 20 def self.truncate(backtrace, max_lines: nil) return nil if backtrace.nil? max_lines ||= RailsErrorDashboard.configuration.max_backtrace_lines limited_backtrace = backtrace.first(max_lines).map { |line| shorten_gem_path(line) } result = limited_backtrace.join("\n") if backtrace.length > max_lines truncation_notice = "... (#{backtrace.length - max_lines} more lines truncated)" result = result.empty? ? truncation_notice : result + "\n" + truncation_notice end result end |