Module: Errsight::SourceContext

Defined in:
lib/errsight/source_context.rb

Overview

Reads source files to attach ‘pre_context`, `context_line`, and `post_context` to in_app backtrace frames — the snippet of code around the failing line that turns “stack trace at user.rb:42” into “here’s what user.rb:42 looked like when it crashed.”

Reference: sentry-ruby’s ‘Sentry::LineCache`. Their cache is per-process bounded LRU; ours is the same idea, smaller, no dependencies.

Constant Summary collapse

PRE_CONTEXT_LINES =

5 lines before, 5 lines after — sentry’s default and a comfortable debugging window. Larger values bloat events without adding value.

5
POST_CONTEXT_LINES =
5
MAX_LINE_BYTES =

Cap each emitted line to keep events bounded. A 1MB minified JS line accidentally landing in a Ruby backtrace (it happens via Sprockets asset pipeline errors) shouldn’t blow our 512KB ingestion limit.

256
CACHE_SIZE =

LRU bound. 100 files × ~100 lines avg × ~80 bytes per line = ~800KB cache footprint per process. Cheap. A request typically reads <10 unique files for context; the cache absorbs cross-request sharing.

100

Class Method Summary collapse

Class Method Details

.fetch(filename, lineno) ⇒ Object

Returns { pre_context:, context_line:, post_context: } or nil if the file can’t be read (missing, eval’d, internal, permission denied, malformed encoding, …). Never raises — source-context failure must not cascade into a failed event capture.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/errsight/source_context.rb', line 30

def fetch(filename, lineno)
  return nil unless filename.is_a?(String) && !filename.empty?
  return nil unless lineno.is_a?(Integer) && lineno > 0
  # Synthetic frames have no readable source.
  return nil if filename.start_with?("<", "(")

  lines = read_lines(filename)
  return nil unless lines

  idx = lineno - 1
  return nil if idx < 0 || idx >= lines.size

  {
    pre_context:  slice_with_truncation(lines, [ idx - PRE_CONTEXT_LINES, 0 ].max, idx - 1),
    context_line: truncate(lines[idx]),
    post_context: slice_with_truncation(lines, idx + 1, [ idx + POST_CONTEXT_LINES, lines.size - 1 ].min)
  }
rescue StandardError
  nil
end

.reset_cache!Object

Test-only: drop the cache so a test that mutates a fixture file gets the fresh contents on the next fetch.



53
54
55
56
# File 'lib/errsight/source_context.rb', line 53

def reset_cache!
  @cache = nil
  @order = nil
end