Module: DeadBro::Collectors::ProcessInfo

Defined in:
lib/dead_bro/collectors/process_info.rb

Overview

ProcessInfo collector exposes Ruby / Rails / process level metrics such as RSS, thread count, file descriptor count, GC stats and uptime.

All methods are best-effort and will return nil on failure rather than raising exceptions.

Class Method Summary collapse

Class Method Details

.collectObject



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/dead_bro/collectors/process_info.rb', line 16

def collect
  now = Time.now.utc

  {
    kind: DeadBro.process_kind,
    pid: Process.pid,
    hostname: DeadBro.safe_hostname,
    boot_time: rails_boot_time,
    uptime_s: uptime_seconds(now),
    ruby_version: RUBY_VERSION,
    rails_version: safe_rails_version,
    app_env: DeadBro.env,
    rss_bytes: rss_bytes,
    thread_count: thread_count,
    fd_count: fd_count,
    gc: gc_stats
  }
rescue => e
  {
    error_class: e.class.name,
    error_message: e.message.to_s[0, 500]
  }
end

.fd_countObject



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/dead_bro/collectors/process_info.rb', line 143

def fd_count
  if linux? && File.directory?("/proc/self/fd")
    Dir.entries("/proc/self/fd").size - 2 # exclude . and ..
  elsif macos? && File.directory?("/dev/fd")
    Dir.entries("/dev/fd").size - 2
  else
    # Best-effort: count file descriptors under /proc when available
    nil
  end
rescue
  nil
end

.gc_statsObject



156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/dead_bro/collectors/process_info.rb', line 156

def gc_stats
  return {} unless defined?(GC) && GC.respond_to?(:stat)

  stats = GC.stat
  {
    heap_live_slots: stats[:heap_live_slots],
    heap_free_slots: stats[:heap_free_slots],
    total_allocated_objects: stats[:total_allocated_objects],
    major_gc_count: stats[:major_gc_count],
    minor_gc_count: stats[:minor_gc_count]
  }
rescue
  {}
end

.linux?Boolean

Returns:

  • (Boolean)


40
41
42
43
44
45
# File 'lib/dead_bro/collectors/process_info.rb', line 40

def linux?
  host_os = RbConfig::CONFIG["host_os"].to_s.downcase
  host_os.include?("linux")
rescue
  false
end

.macos?Boolean

Returns:

  • (Boolean)


47
48
49
50
51
52
# File 'lib/dead_bro/collectors/process_info.rb', line 47

def macos?
  host_os = RbConfig::CONFIG["host_os"].to_s.downcase
  host_os.include?("darwin")
rescue
  false
end

.parse_proc_status_for_rss(path) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/dead_bro/collectors/process_info.rb', line 97

def parse_proc_status_for_rss(path)
  File.foreach(path) do |line|
    next unless line.start_with?("VmRSS:")

    parts = line.split
    value_kb = begin
      Integer(parts[1])
    rescue
      nil
    end
    return value_kb * 1024 if value_kb
  end
  nil
rescue
  nil
end

.process_start_timeObject



67
68
69
# File 'lib/dead_bro/collectors/process_info.rb', line 67

def process_start_time
  @process_start_time ||= Time.now.utc
end

.rails_boot_timeObject



54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/dead_bro/collectors/process_info.rb', line 54

def rails_boot_time
  return nil unless defined?(Rails)

  if Rails.respond_to?(:application) && Rails.application.respond_to?(:config)
    # Rails does not expose boot time directly; approximate with process start
    process_start_time
  else
    process_start_time
  end
rescue
  nil
end

.rss_bytesObject



87
88
89
90
91
92
93
94
95
# File 'lib/dead_bro/collectors/process_info.rb', line 87

def rss_bytes
  if linux? && File.readable?("/proc/self/status")
    parse_proc_status_for_rss("/proc/self/status")
  else
    rss_from_ps
  end
rescue
  nil
end

.rss_from_psObject



114
115
116
117
118
119
120
121
# File 'lib/dead_bro/collectors/process_info.rb', line 114

def rss_from_ps
  rss_kb = `ps -o rss= -p #{Process.pid}`.to_i
  return nil if rss_kb <= 0

  rss_kb * 1024
rescue
  nil
end

.safe_rails_versionObject



77
78
79
80
81
82
83
84
85
# File 'lib/dead_bro/collectors/process_info.rb', line 77

def safe_rails_version
  if defined?(Rails) && Rails.respond_to?(:version)
    Rails.version
  elsif defined?(Rails::VERSION) && Rails::VERSION::STRING
    Rails::VERSION::STRING
  end
rescue
  nil
end

.thread_countObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/dead_bro/collectors/process_info.rb', line 123

def thread_count
  if linux? && File.readable?("/proc/self/status")
    File.foreach("/proc/self/status") do |line|
      next unless line.start_with?("Threads:")

      parts = line.split
      begin
        return Integer(parts[1])
      rescue
        nil
      end
    end
    nil
  else
    Thread.list.size
  end
rescue
  nil
end

.uptime_seconds(now = Time.now.utc) ⇒ Object



71
72
73
74
75
# File 'lib/dead_bro/collectors/process_info.rb', line 71

def uptime_seconds(now = Time.now.utc)
  (now.to_f - process_start_time.to_f).round(2)
rescue
  nil
end