Module: DeadBro::Collectors::System
- Defined in:
- lib/dead_bro/collectors/system.rb
Overview
System collector provides best-effort CPU and memory statistics using cgroups when available and falling back to /proc on Linux.
CPU percentages are normalised to 0..100 across all cores. The first run may not contain a CPU percentage because there is no previous sample to diff against.
Constant Summary collapse
- CPU_SAMPLE_KEY =
"cpu"- MEMINFO_PATH =
"/proc/meminfo"
Class Method Summary collapse
- .collect ⇒ Object
-
.cpu_pct_from_samples(prev, current) ⇒ Object
Computes a CPU percentage from two /proc/stat samples.
-
.cpu_percentage ⇒ Object
CPU percentage normalised to 0..100.
- .cpu_percentage_linux ⇒ Object
- .cpu_percentage_macos ⇒ Object
- .current_time ⇒ Object
- .linux? ⇒ Boolean
- .macos? ⇒ Boolean
- .mem_available_bytes ⇒ Object
- .mem_total_bytes ⇒ Object
- .mem_used_bytes ⇒ Object
- .meminfo ⇒ Object
-
.read_proc_stat ⇒ Object
Parse the first “cpu” line from /proc/stat.
- .system_enabled? ⇒ Boolean
Class Method Details
.collect ⇒ Object
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/dead_bro/collectors/system.rb', line 20 def collect return {enabled: false} unless system_enabled? { cpu_pct: cpu_percentage, mem_used_bytes: mem_used_bytes, mem_total_bytes: mem_total_bytes, mem_available_bytes: mem_available_bytes, disk: Filesystem.collect } rescue => e { error_class: e.class.name, error_message: e..to_s[0, 500] } end |
.cpu_pct_from_samples(prev, current) ⇒ Object
Computes a CPU percentage from two /proc/stat samples. This is intentionally public so it can be unit tested.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/dead_bro/collectors/system.rb', line 118 def cpu_pct_from_samples(prev, current) prev_total = prev["total"].to_f prev_idle = prev["idle"].to_f cur_total = current["total"].to_f cur_idle = current["idle"].to_f total_delta = cur_total - prev_total idle_delta = cur_idle - prev_idle return nil if total_delta <= 0 usage = (total_delta - idle_delta) / total_delta.to_f pct = (usage * 100.0) return nil unless pct.finite? pct.round(2) rescue nil end |
.cpu_percentage ⇒ Object
CPU percentage normalised to 0..100
59 60 61 62 63 64 65 |
# File 'lib/dead_bro/collectors/system.rb', line 59 def cpu_percentage if linux? cpu_percentage_linux elsif macos? cpu_percentage_macos end end |
.cpu_percentage_linux ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/dead_bro/collectors/system.rb', line 67 def cpu_percentage_linux return nil unless File.readable?("/proc/stat") now = current_time current = read_proc_stat prev = SampleStore.load(CPU_SAMPLE_KEY) SampleStore.save(CPU_SAMPLE_KEY, {"timestamp" => now, "stat" => current}) return nil unless prev && prev["stat"].is_a?(Hash) && prev["timestamp"] cpu_pct_from_samples(prev["stat"], current) rescue nil end |
.cpu_percentage_macos ⇒ Object
82 83 84 85 86 87 88 89 90 91 |
# File 'lib/dead_bro/collectors/system.rb', line 82 def cpu_percentage_macos output = `top -l 1 -n 0 | grep "CPU usage"` # Example: CPU usage: 9.38% user, 10.93% sys, 79.68% idle if output =~ /([\d.]+)% idle/ idle = $1.to_f (100.0 - idle).round(2) end rescue nil end |
.current_time ⇒ Object
93 94 95 96 97 |
# File 'lib/dead_bro/collectors/system.rb', line 93 def current_time Process.clock_gettime(Process::CLOCK_MONOTONIC) rescue Time.now.to_f end |
.linux? ⇒ Boolean
44 45 46 47 48 49 |
# File 'lib/dead_bro/collectors/system.rb', line 44 def linux? host_os = RbConfig::CONFIG["host_os"].to_s.downcase host_os.include?("linux") rescue false end |
.macos? ⇒ Boolean
51 52 53 54 55 56 |
# File 'lib/dead_bro/collectors/system.rb', line 51 def macos? host_os = RbConfig::CONFIG["host_os"].to_s.downcase host_os.include?("darwin") rescue false end |
.mem_available_bytes ⇒ Object
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/dead_bro/collectors/system.rb', line 170 def mem_available_bytes if linux? info = meminfo avail_kb = info["MemAvailable"] || info["MemFree"] return nil unless avail_kb avail_kb * 1024 elsif macos? # vm_stat output: # Pages free: 3632. # Pages active: 138466. # Pages inactive: 134812. # ... output = `vm_stat` pages_free = output[/Pages free:\s+(\d+)/, 1].to_i pages_inactive = output[/Pages inactive:\s+(\d+)/, 1].to_i # MacOS page size is typically 4096 bytes (pages_free + pages_inactive) * 4096 else nil end rescue nil end |
.mem_total_bytes ⇒ Object
157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/dead_bro/collectors/system.rb', line 157 def mem_total_bytes if linux? info = meminfo total_kb = info["MemTotal"] return nil unless total_kb total_kb * 1024 elsif macos? `sysctl -n hw.memsize`.to_i end rescue nil end |
.mem_used_bytes ⇒ Object
195 196 197 198 199 200 201 202 203 |
# File 'lib/dead_bro/collectors/system.rb', line 195 def mem_used_bytes total = mem_total_bytes avail = mem_available_bytes return nil unless total && avail total - avail rescue nil end |
.meminfo ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/dead_bro/collectors/system.rb', line 137 def meminfo return {} unless linux? && File.readable?(MEMINFO_PATH) info = {} File.foreach(MEMINFO_PATH) do |line| key, value, _ = line.split next unless key && value key = key.sub(":", "") info[key] = begin Integer(value) rescue nil end end info rescue {} end |
.read_proc_stat ⇒ Object
Parse the first “cpu” line from /proc/stat
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/dead_bro/collectors/system.rb', line 100 def read_proc_stat File.foreach("/proc/stat") do |line| next unless line.start_with?("cpu ") fields = line.split # cpu user nice system idle iowait irq softirq steal guest guest_nice values = fields[1..-1].map { |v| v.to_i } total = values.sum idle = values[3] + values[4] # idle + iowait return {"total" => total, "idle" => idle} end {} rescue {} end |
.system_enabled? ⇒ Boolean
37 38 39 40 41 42 |
# File 'lib/dead_bro/collectors/system.rb', line 37 def system_enabled? DeadBro.configuration.respond_to?(:enable_system_stats) && DeadBro.configuration.enable_system_stats rescue false end |