Class: ErrorRadar::ErrorLog
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- ErrorRadar::ErrorLog
- Defined in:
- app/models/error_radar/error_log.rb
Overview
One row per distinct failure (collapsed by fingerprint). Doubles as a task on the triage board. Table: error_radar_error_logs.
Class Method Summary collapse
- .build_fingerprint(category:, error_class:, source:, message:) ⇒ Object
- .presence(value) ⇒ Object
-
.record(category:, message:, severity: :error, error_class: nil, source: nil, backtrace: nil, context: {}, http_status: nil, request_url: nil, api_code: nil, api_subcode: nil, fingerprint: nil) ⇒ Object
Record (or roll-up) an error.
- .severity_rank(value) ⇒ Object
Instance Method Summary collapse
Class Method Details
.build_fingerprint(category:, error_class:, source:, message:) ⇒ Object
66 67 68 69 70 71 72 73 |
# File 'app/models/error_radar/error_log.rb', line 66 def self.build_fingerprint(category:, error_class:, source:, message:) normalized = .to_s .gsub(/\d+/, '#') # ids, counts, timestamps .gsub(/0x[0-9a-f]+/i, '0x#') # object addresses .gsub(/[0-9a-f]{8}-[0-9a-f-]{27}/i, '#') # uuids .strip Digest::SHA1.hexdigest([category, error_class, source, normalized].join('|')) end |
.presence(value) ⇒ Object
79 80 81 |
# File 'app/models/error_radar/error_log.rb', line 79 def self.presence(value) value.respond_to?(:empty?) ? (value.empty? ? nil : value) : value end |
.record(category:, message:, severity: :error, error_class: nil, source: nil, backtrace: nil, context: {}, http_status: nil, request_url: nil, api_code: nil, api_subcode: nil, fingerprint: nil) ⇒ Object
Record (or roll-up) an error. Idempotent per fingerprint: identical errors increment ‘occurrences` and bump `last_seen_at` instead of creating a new row. NEVER raises — logging must not break the calling code path.
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'app/models/error_radar/error_log.rb', line 35 def self.record(category:, message:, severity: :error, error_class: nil, source: nil, backtrace: nil, context: {}, http_status: nil, request_url: nil, api_code: nil, api_subcode: nil, fingerprint: nil) now = Time.current fp = presence(fingerprint) || build_fingerprint(category: category, error_class: error_class, source: source, message: ) log = find_or_initialize_by(fingerprint: fp) if log.persisted? log.occurrences += 1 log.status = :open if log.status_resolved? || log.status_ignored? else log.assign_attributes( category: category, severity: severity, error_class: error_class, source: source, http_status: http_status, request_url: request_url, api_code: api_code, api_subcode: api_subcode, first_seen_at: now, status: :open ) end log. = .to_s.truncate(ErrorRadar.config.) log.backtrace = presence(Array(backtrace).join("\n")) || log.backtrace log.context = (log.context || {}).merge(context.presence || {}).deep_stringify_keys if context.present? || log.context log.severity = severity if log.new_record? || severity_rank(severity) > severity_rank(log.severity) log.last_seen_at = now log.save! log rescue StandardError => e ErrorRadar::Tracking.warn_internal("ErrorLog.record failed: #{e.class}: #{e.}") nil end |
.severity_rank(value) ⇒ Object
75 76 77 |
# File 'app/models/error_radar/error_log.rb', line 75 def self.severity_rank(value) severities[value.to_s] || 0 end |
Instance Method Details
#reopen! ⇒ Object
91 92 93 |
# File 'app/models/error_radar/error_log.rb', line 91 def reopen! update!(status: :open, resolved_at: nil) end |
#resolve!(by: nil, note: nil) ⇒ Object
87 88 89 |
# File 'app/models/error_radar/error_log.rb', line 87 def resolve!(by: nil, note: nil) update!(status: :resolved, resolved_at: Time.current, resolved_by: by, resolution_note: note.presence || resolution_note) end |
#short_message ⇒ Object
83 84 85 |
# File 'app/models/error_radar/error_log.rb', line 83 def .to_s.truncate(120) end |