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



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

def collect
  now = Time.now.utc

  {
    kind: DeadBro.process_kind,
    pid: Process.pid,
    hostname: 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



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/dead_bro/collectors/process_info.rb', line 150

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



163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/dead_bro/collectors/process_info.rb', line 163

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)


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

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

.macos?Boolean

Returns:

  • (Boolean)


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

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



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/dead_bro/collectors/process_info.rb', line 104

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



74
75
76
# File 'lib/dead_bro/collectors/process_info.rb', line 74

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

.rails_boot_timeObject



61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/dead_bro/collectors/process_info.rb', line 61

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



94
95
96
97
98
99
100
101
102
# File 'lib/dead_bro/collectors/process_info.rb', line 94

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



121
122
123
124
125
126
127
128
# File 'lib/dead_bro/collectors/process_info.rb', line 121

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_hostnameObject



55
56
57
58
59
# File 'lib/dead_bro/collectors/process_info.rb', line 55

def safe_hostname
  Socket.gethostname
rescue
  "unknown"
end

.safe_rails_versionObject



84
85
86
87
88
89
90
91
92
# File 'lib/dead_bro/collectors/process_info.rb', line 84

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



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/dead_bro/collectors/process_info.rb', line 130

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



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

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