Class: RailsErrorDashboard::Services::SystemHealthSnapshot

Inherits:
Object
  • Object
show all
Defined in:
lib/rails_error_dashboard/services/system_health_snapshot.rb

Overview

Pure algorithm: Capture runtime health metrics at error time

Captures GC stats, process memory (RSS/swap/peak), thread count, connection pool, Puma stats, job queue, RubyVM/YJIT, ActionCable, file descriptors, system load, system memory pressure, GC context, and TCP connection states.

NOT memoized — fresh data every call (unlike EnvironmentSnapshot). Every metric call individually wrapped in rescue => nil.

Safety contract (from HOST_APP_SAFETY.md):

  • Total snapshot < 1ms budget (~0.3ms typical on Linux)

  • NEVER ObjectSpace.each_object or ObjectSpace.count_objects (heap scan)

  • NEVER Thread.list.map(&:backtrace) (GVL hold)

  • Thread.list.count only (O(1), safe)

  • Process/system metrics: Linux procfs ONLY, no fork/subprocess ever

  • All procfs reads guarded with File.exist? — returns nil on macOS/non-Linux

  • TCP file size guard (skip if > 1MB) to protect against connection leak scenarios

  • No new gems, no global state, no Thread.current, no mutex

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.captureHash

Capture current system health metrics

Returns:

  • (Hash)

    Health snapshot (always safe, never raises)



26
27
28
29
30
31
# File 'lib/rails_error_dashboard/services/system_health_snapshot.rb', line 26

def self.capture
  new.capture
rescue => e
  RailsErrorDashboard::Logger.debug("[RailsErrorDashboard] SystemHealthSnapshot.capture failed: #{e.message}")
  { captured_at: Time.current.iso8601 }
end

Instance Method Details

#captureHash

Returns Health snapshot.

Returns:

  • (Hash)

    Health snapshot



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/rails_error_dashboard/services/system_health_snapshot.rb', line 34

def capture
  mem = process_memory
  {
    gc: gc_stats,
    gc_latest: gc_latest,
    process_memory: mem,
    process_memory_mb: mem&.dig(:rss_mb),  # backward compat
    thread_count: thread_count,
    connection_pool: connection_pool_stats,
    puma: puma_stats,
    job_queue: job_queue_stats,
    ruby_vm: ruby_vm_stats,
    yjit: yjit_stats,
    actioncable: actioncable_stats,
    file_descriptors: file_descriptors,
    system_load: system_load,
    system_memory: system_memory,
    tcp_connections: tcp_connections,
    captured_at: Time.current.iso8601
  }
end