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
# File 'lib/dead_bro/collectors/process_info.rb', line 17

def collect
  now = Time.now.utc

  {
    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



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

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



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

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



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

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



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

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

.rails_boot_timeObject



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

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



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

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



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

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



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

def safe_hostname
  Socket.gethostname
rescue
  "unknown"
end

.safe_rails_versionObject



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

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



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

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



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

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