Class: Lapsoss::BacktraceProcessor
- Inherits:
-
Object
- Object
- Lapsoss::BacktraceProcessor
- Defined in:
- lib/lapsoss/backtrace_processor.rb
Constant Summary collapse
- DEFAULT_CONFIG =
{ context_lines: 3, max_frames: 100, enable_code_context: true, strip_load_path: true, in_app_patterns: [], exclude_patterns: [ # Common test/debug patterns to exclude /rspec/, /minitest/, /test-unit/, /cucumber/, /pry/, /byebug/, /debug/, /<internal:/, /kernel_require\.rb/ ], dedupe_frames: true, include_gems_in_context: false }.freeze
Instance Attribute Summary collapse
-
#config ⇒ Object
readonly
Returns the value of attribute config.
Instance Method Summary collapse
- #clear_cache ⇒ Object
- #clear_cache! ⇒ Object
- #format_frames(frames, format = :sentry) ⇒ Object
-
#get_code_context(filename, line_number, context_lines = 3) ⇒ Object
Get code context around a specific line number using ActiveSupport::Cache.
-
#initialize(config = {}) ⇒ BacktraceProcessor
constructor
A new instance of BacktraceProcessor.
- #process_backtrace(backtrace) ⇒ Object (also: #process)
- #process_exception_backtrace(exception, follow_cause: false) ⇒ Object (also: #process_exception)
- #stats ⇒ Object
- #to_bugsnag_format(frames) ⇒ Object
- #to_hash_array(frames) ⇒ Object
- #to_rollbar_format(frames) ⇒ Object
- #to_sentry_format(frames) ⇒ Object
Constructor Details
#initialize(config = {}) ⇒ BacktraceProcessor
Returns a new instance of BacktraceProcessor.
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/lapsoss/backtrace_processor.rb', line 33 def initialize(config = {}) # Handle different config formats for backward compatibility config_hash = if config.respond_to?(:backtrace_context_lines) # Configuration object passed { context_lines: config.backtrace_context_lines, max_frames: config.backtrace_max_frames, enable_code_context: config.backtrace_enable_code_context, in_app_patterns: config.backtrace_in_app_patterns, exclude_patterns: config.backtrace_exclude_patterns, strip_load_path: config.backtrace_strip_load_path } else # Hash passed config end @config = DEFAULT_CONFIG.merge(config_hash) @file_cache = ActiveSupport::Cache::MemoryStore.new( size: (@config[:file_cache_size] || 100) * 1024 * 1024, # Convert to bytes expires_in: 1.hour ) end |
Instance Attribute Details
#config ⇒ Object (readonly)
Returns the value of attribute config.
31 32 33 |
# File 'lib/lapsoss/backtrace_processor.rb', line 31 def config @config end |
Instance Method Details
#clear_cache ⇒ Object
193 194 195 |
# File 'lib/lapsoss/backtrace_processor.rb', line 193 def clear_cache @file_cache.clear end |
#clear_cache! ⇒ Object
107 108 109 |
# File 'lib/lapsoss/backtrace_processor.rb', line 107 def clear_cache! @file_cache.clear end |
#format_frames(frames, format = :sentry) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/lapsoss/backtrace_processor.rb', line 115 def format_frames(frames, format = :sentry) case format when :sentry to_sentry_format(frames) when :rollbar (frames) when :bugsnag to_bugsnag_format(frames) else to_sentry_format(frames) end end |
#get_code_context(filename, line_number, context_lines = 3) ⇒ Object
Get code context around a specific line number using ActiveSupport::Cache
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 |
# File 'lib/lapsoss/backtrace_processor.rb', line 198 def get_code_context(filename, line_number, context_lines = 3) return nil unless filename lines = @file_cache.fetch(filename) do read_file_safely(filename) end return nil unless lines # Convert to 0-based index line_index = line_number - 1 return nil if line_index.negative? || line_index >= lines.length # Calculate context range start_line = [ 0, line_index - context_lines ].max end_line = [ lines.length - 1, line_index + context_lines ].min { pre_context: lines[start_line...line_index], context_line: lines[line_index], post_context: lines[(line_index + 1)..end_line], line_number: line_number, start_line: start_line + 1, end_line: end_line + 1 } rescue StandardError # Return nil on any file read error nil end |
#process_backtrace(backtrace) ⇒ Object Also known as: process
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/lapsoss/backtrace_processor.rb', line 57 def process_backtrace(backtrace) return [] unless backtrace&.any? # Parse all frames frames = parse_frames(backtrace) # Apply filtering frames = filter_frames(frames) # Limit frame count frames = limit_frames(frames) # Add code context if enabled add_code_context(frames) if @config[:enable_code_context] # Deduplicate if enabled frames = dedupe_frames(frames) if @config[:dedupe_frames] frames end |
#process_exception_backtrace(exception, follow_cause: false) ⇒ Object Also known as: process_exception
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/lapsoss/backtrace_processor.rb', line 81 def process_exception_backtrace(exception, follow_cause: false) return [] unless exception&.backtrace frames = process_backtrace(exception.backtrace) # Wrap frames with exception-specific context frames = frames.map.with_index do |frame, index| ExceptionBacktraceFrame.new( frame, exception_class: exception.class.name, is_crash_frame: index.zero? ) end # Follow exception causes if requested if follow_cause && exception.respond_to?(:cause) && exception.cause cause_frames = process_exception_backtrace(exception.cause, follow_cause: true) frames.concat(cause_frames) end frames end |
#stats ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 |
# File 'lib/lapsoss/backtrace_processor.rb', line 180 def stats { file_cache: { # ActiveSupport::Cache::MemoryStore doesn't expose detailed stats # but we can provide basic info type: "ActiveSupport::Cache::MemoryStore", configured_size: @file_cache.[:size] }, config: @config, load_paths: @load_paths } end |
#to_bugsnag_format(frames) ⇒ Object
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/lapsoss/backtrace_processor.rb', line 160 def to_bugsnag_format(frames) frames.map do |frame| data = { file: frame.filename, lineNumber: frame.line_number, method: frame.function || frame.method_name, inProject: frame.in_app }.compact # Add code context for Bugsnag/Insight Hub if frame.code_context data[:code] = { frame.code_context[:line_number] => frame.code_context[:context_line] } end data end end |
#to_hash_array(frames) ⇒ Object
111 112 113 |
# File 'lib/lapsoss/backtrace_processor.rb', line 111 def to_hash_array(frames) frames.map(&:to_h) end |
#to_rollbar_format(frames) ⇒ Object
149 150 151 152 153 154 155 156 157 158 |
# File 'lib/lapsoss/backtrace_processor.rb', line 149 def (frames) frames.map do |frame| { filename: frame.filename, lineno: frame.line_number, method: frame.function || frame.method_name, code: frame.code_context&.dig(:context_line) }.compact end end |
#to_sentry_format(frames) ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/lapsoss/backtrace_processor.rb', line 128 def to_sentry_format(frames) frames.map do |frame| data = { filename: frame.filename, lineno: frame.line_number, function: frame.function || frame.method_name, module: frame.module_name, in_app: frame.in_app }.compact # Add code context for Sentry if frame.code_context data[:pre_context] = frame.code_context[:pre_context] data[:context_line] = frame.code_context[:context_line] data[:post_context] = frame.code_context[:post_context] end data end.reverse # Sentry expects oldest frame first end |