Module: ErrorRadar::Tracking
- Defined in:
- lib/error_radar/tracking.rb
Overview
The capture facade. Auto-classifies common exception types, lets the host app plug in custom rules, then persists/rolls-up an ErrorLog. Never raises.
Class Method Summary collapse
- .capture(exception, source: nil, category: nil, severity: nil, context: {}) ⇒ Object
- .categorize(exception) ⇒ Object
- .default_severity(_exception, category) ⇒ Object
- .infer_source(exception) ⇒ Object
-
.monitor(source, category: nil, severity: nil, context: {}) ⇒ Object
Wrap a block (rake task, cron script, manual maintenance) so any exception is captured and then re-raised.
- .network_error?(exception) ⇒ Boolean
-
.notify(message, category: :application, severity: :error, source: nil, context: {}) ⇒ Object
Log a problem without an exception object.
- .safe_call(callable, *args) ⇒ Object
- .warn_internal(message) ⇒ Object
Class Method Details
.capture(exception, source: nil, category: nil, severity: nil, context: {}) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/error_radar/tracking.rb', line 9 def capture(exception, source: nil, category: nil, severity: nil, context: {}) return nil unless ErrorRadar.config.enabled category ||= categorize(exception) severity ||= default_severity(exception, category) attrs = { category: category, severity: severity, error_class: exception.class.name, source: source || infer_source(exception), message: exception., backtrace: Array(exception.backtrace).first(ErrorRadar.config.backtrace_lines), context: context } ErrorRadar.config.detail_extractors.each do |extractor| extra = safe_call(extractor, exception) attrs.merge!(extra.compact) if extra.is_a?(Hash) end ErrorRadar::ErrorLog.record(**attrs) rescue StandardError => e warn_internal("capture failed: #{e.class}: #{e.}") nil end |
.categorize(exception) ⇒ Object
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/error_radar/tracking.rb', line 56 def categorize(exception) ErrorRadar.config.categorizers.each do |rule| cat = safe_call(rule, exception) return cat if cat end if defined?(ActiveRecord::ActiveRecordError) && exception.is_a?(ActiveRecord::ActiveRecordError) :database elsif exception.is_a?(SyntaxError) || exception.is_a?(NameError) || exception.is_a?(ArgumentError) || exception.is_a?(TypeError) :syntax elsif network_error?(exception) :network else :application end end |
.default_severity(_exception, category) ⇒ Object
89 90 91 92 93 94 95 |
# File 'lib/error_radar/tracking.rb', line 89 def default_severity(_exception, category) case category.to_sym when :syntax, :database then :critical when :network, :external_api then :warning else :error end end |
.infer_source(exception) ⇒ Object
97 98 99 100 101 102 |
# File 'lib/error_radar/tracking.rb', line 97 def infer_source(exception) line = Array(exception.backtrace).find { |l| l.include?('/app/') } || Array(exception.backtrace).first return 'unknown' if line.nil? || line.empty? line.split(':').first.to_s.split('/app/').last || line end |
.monitor(source, category: nil, severity: nil, context: {}) ⇒ Object
Wrap a block (rake task, cron script, manual maintenance) so any exception is captured and then re-raised. Use at boundaries the web/Sidekiq handlers don’t cover.
39 40 41 42 43 44 |
# File 'lib/error_radar/tracking.rb', line 39 def monitor(source, category: nil, severity: nil, context: {}) yield rescue StandardError => e capture(e, source: source, category: category, severity: severity, context: context) raise end |
.network_error?(exception) ⇒ Boolean
74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/error_radar/tracking.rb', line 74 def network_error?(exception) names = %w[ Net::OpenTimeout Net::ReadTimeout Errno::ECONNREFUSED Errno::ECONNRESET Errno::EHOSTUNREACH Errno::ETIMEDOUT SocketError Timeout::Error OpenSSL::SSL::SSLError EOFError Faraday::ConnectionFailed Faraday::TimeoutError ] klass = exception.class while klass return true if names.include?(klass.name) klass = klass.superclass end false end |
.notify(message, category: :application, severity: :error, source: nil, context: {}) ⇒ Object
Log a problem without an exception object.
47 48 49 50 51 52 53 54 |
# File 'lib/error_radar/tracking.rb', line 47 def notify(, category: :application, severity: :error, source: nil, context: {}) return nil unless ErrorRadar.config.enabled ErrorRadar::ErrorLog.record(category: category, severity: severity, message: , source: source, context: context) rescue StandardError => e warn_internal("notify failed: #{e.class}: #{e.}") nil end |
.safe_call(callable, *args) ⇒ Object
104 105 106 107 108 109 |
# File 'lib/error_radar/tracking.rb', line 104 def safe_call(callable, *args) callable.call(*args) rescue StandardError => e warn_internal("custom rule failed: #{e.class}: #{e.}") nil end |
.warn_internal(message) ⇒ Object
111 112 113 114 |
# File 'lib/error_radar/tracking.rb', line 111 def warn_internal() logger = defined?(Rails) && Rails.respond_to?(:logger) ? Rails.logger : nil logger ? logger.error("[ErrorRadar] #{}") : warn("[ErrorRadar] #{}") end |