Class: RailsErrorDashboard::Services::ErrorHashGenerator
- Inherits:
-
Object
- Object
- RailsErrorDashboard::Services::ErrorHashGenerator
- Defined in:
- lib/rails_error_dashboard/services/error_hash_generator.rb
Overview
Pure algorithm: Generate consistent hash for error deduplication
No database access — accepts exception data, returns a hash string. Same hash = same error type for grouping purposes.
Two entry points:
-
‘.call(exception, …)` — used by LogError command (exception-based)
-
‘.from_attributes(…)` — used by ErrorLog model callback (attribute-based)
Class Method Summary collapse
-
.call(exception, controller_name: nil, action_name: nil, application_id: nil, context: {}) ⇒ String
Generate hash from an exception object (used by LogError command).
-
.extract_app_frame(backtrace) ⇒ String?
Extract first meaningful app code frame from backtrace.
-
.extract_app_frame_from_locations(exception) ⇒ String?
Extract first meaningful app code frame using backtrace_locations More reliable than string parsing — uses Location#absolute_path directly.
-
.from_attributes(error_type:, message: nil, backtrace: nil, controller_name: nil, action_name: nil, application_id: nil) ⇒ String
Generate hash from error attributes (used by ErrorLog model callback) Uses ErrorNormalizer for smarter normalization and significant frame extraction.
-
.normalize_message(message) ⇒ String?
Normalize dynamic values in error messages for consistent hashing.
Class Method Details
.call(exception, controller_name: nil, action_name: nil, application_id: nil, context: {}) ⇒ String
Generate hash from an exception object (used by LogError command)
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/rails_error_dashboard/services/error_hash_generator.rb', line 25 def self.call(exception, controller_name: nil, action_name: nil, application_id: nil, context: {}) # Check for custom fingerprint lambda custom = try_custom_fingerprint(exception, context) return custom if custom = (exception.) file_path = extract_app_frame_from_locations(exception) || extract_app_frame(exception.backtrace) digest_input = [ exception.class.name, , file_path, controller_name, action_name, application_id.to_s ].compact.join("|") Digest::SHA256.hexdigest(digest_input)[0..15] end |
.extract_app_frame(backtrace) ⇒ String?
Extract first meaningful app code frame from backtrace
106 107 108 109 110 111 112 113 114 |
# File 'lib/rails_error_dashboard/services/error_hash_generator.rb', line 106 def self.extract_app_frame(backtrace) return nil if backtrace.nil? first_app_frame = backtrace.find { |frame| !frame.include?("/gems/") } first_app_frame&.split(":")&.first end |
.extract_app_frame_from_locations(exception) ⇒ String?
Extract first meaningful app code frame using backtrace_locations More reliable than string parsing — uses Location#absolute_path directly.
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/rails_error_dashboard/services/error_hash_generator.rb', line 86 def self.extract_app_frame_from_locations(exception) locations = exception.backtrace_locations return nil if locations.nil? || locations.empty? first_app_location = locations.find { |loc| path = loc.absolute_path || loc.path !path&.include?("/gems/") } first_app_location && (first_app_location.absolute_path || first_app_location.path) rescue => e RailsErrorDashboard::Logger.debug( "[RailsErrorDashboard] extract_app_frame_from_locations failed: #{e.}" ) nil end |
.from_attributes(error_type:, message: nil, backtrace: nil, controller_name: nil, action_name: nil, application_id: nil) ⇒ String
Generate hash from error attributes (used by ErrorLog model callback) Uses ErrorNormalizer for smarter normalization and significant frame extraction
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/rails_error_dashboard/services/error_hash_generator.rb', line 54 def self.from_attributes(error_type:, message: nil, backtrace: nil, controller_name: nil, action_name: nil, application_id: nil) = ErrorNormalizer.normalize() significant_frames = ErrorNormalizer.extract_significant_frames(backtrace, count: 3) digest_input = [ error_type, , significant_frames, controller_name, action_name, application_id.to_s ].compact.join("|") Digest::SHA256.hexdigest(digest_input)[0..15] end |
.normalize_message(message) ⇒ String?
Normalize dynamic values in error messages for consistent hashing
73 74 75 76 77 78 79 80 |
# File 'lib/rails_error_dashboard/services/error_hash_generator.rb', line 73 def self.() &.gsub(/0x[0-9a-f]+/i, "HEX") # Replace hex addresses (before numbers) &.gsub(/#<[^>]+>/, "#<OBJ>") # Replace object inspections &.gsub(/\d+/, "N") # Replace numbers &.gsub(/"[^"]*"/, '""') # Replace double-quoted strings &.gsub(/'[^']*'/, "''") # Replace single-quoted strings end |