server_stats 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +5 -0
- data/LICENSE +21 -0
- data/README.md +397 -0
- data/README_EN.md +405 -0
- data/Rakefile +10 -0
- data/bin/server-stats +4 -0
- data/bin/server_stats +9 -0
- data/lib/server_stats/cli.rb +193 -0
- data/lib/server_stats/collector.rb +61 -0
- data/lib/server_stats/formatter.rb +54 -0
- data/lib/server_stats/models.rb +205 -0
- data/lib/server_stats/os.rb +31 -0
- data/lib/server_stats/providers/base.rb +94 -0
- data/lib/server_stats/providers/linux.rb +220 -0
- data/lib/server_stats/providers/macos.rb +225 -0
- data/lib/server_stats/version.rb +5 -0
- data/lib/server_stats.rb +100 -0
- data/server_stats.gemspec +25 -0
- metadata +64 -0
data/README_EN.md
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
# server_stats
|
|
2
|
+
|
|
3
|
+
[English](README_EN.md) | [Tiếng Việt](README.md)
|
|
4
|
+
|
|
5
|
+
A lightweight, **zero-dependency** Ruby gem designed to collect server statistics and resource utilization on **Ubuntu (Linux)** and **macOS**.
|
|
6
|
+
|
|
7
|
+
Supports both a **Command Line Interface (CLI)** and a **Ruby API** for direct integration into Rails, Sinatra, monitoring scripts, Sidekiq background jobs, health-check endpoints, and more.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 🚀 Key Features
|
|
12
|
+
|
|
13
|
+
- **CPU Metrics**:
|
|
14
|
+
- Total CPU core count (Logical cores & Physical cores).
|
|
15
|
+
- CPU usage percentage (`usage_percent`).
|
|
16
|
+
- CPU model / chip architecture (Apple Silicon, Intel, AMD).
|
|
17
|
+
- System load averages (1m, 5m, 15m).
|
|
18
|
+
- **Memory (RAM) Metrics**:
|
|
19
|
+
- Total RAM (`total_bytes` & `total_human`).
|
|
20
|
+
- Actual free RAM (`free_bytes` & `free_human`).
|
|
21
|
+
- Available RAM (`available_bytes` & `available_human`).
|
|
22
|
+
- Used RAM (`used_bytes` & `used_human`).
|
|
23
|
+
- RAM usage percentage (`usage_percent`).
|
|
24
|
+
- Deep breakdown: Buffers, Cached, Swap (Linux) / Wired, Active, Inactive, Compressed (macOS).
|
|
25
|
+
- **Disk Storage Metrics**:
|
|
26
|
+
- Total disk space (`total_bytes` & `total_human`).
|
|
27
|
+
- Available / Free disk space (`available_bytes` & `available_human`).
|
|
28
|
+
- Used disk space (`used_bytes` & `used_human`).
|
|
29
|
+
- Disk usage percentage (`usage_percent`).
|
|
30
|
+
- Filesystem & mount path (inspect any custom mount point, defaults to `/`).
|
|
31
|
+
- **Operating System & Hardware Info**:
|
|
32
|
+
- OS name and release version (Ubuntu 20.04/22.04/24.04, macOS Sonoma/Sequoia,...).
|
|
33
|
+
- Server hostname.
|
|
34
|
+
- Architecture (x86_64, arm64, aarch64,...).
|
|
35
|
+
- Kernel release.
|
|
36
|
+
- System uptime (`uptime_seconds` & `uptime_human`).
|
|
37
|
+
- **Zero Dependencies**: Pure Ruby standard library implementation (`optparse`, `json`, `etc`, `socket`), requiring no native C extensions or third-party gem installations.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 📋 System Requirements
|
|
42
|
+
|
|
43
|
+
- Ruby `>= 2.5.0` (fully compatible with Ruby 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4+).
|
|
44
|
+
- Operating Systems:
|
|
45
|
+
- **Ubuntu / Debian / Linux** (x86_64, aarch64).
|
|
46
|
+
- **macOS** (both Apple Silicon M1/M2/M3/M4 and Intel).
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 📦 Installation
|
|
51
|
+
|
|
52
|
+
### Method 1: Install directly via `gem`
|
|
53
|
+
```bash
|
|
54
|
+
gem install server_stats
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Method 2: Add to your application's `Gemfile`
|
|
58
|
+
Add the following line to your `Gemfile`:
|
|
59
|
+
```ruby
|
|
60
|
+
gem 'server_stats'
|
|
61
|
+
```
|
|
62
|
+
Then install via Bundler:
|
|
63
|
+
```bash
|
|
64
|
+
bundle install
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Method 3: Build from source
|
|
68
|
+
```bash
|
|
69
|
+
git clone https://github.com/ntamvl/server_stats.git
|
|
70
|
+
cd server_stats
|
|
71
|
+
gem build server_stats.gemspec
|
|
72
|
+
gem install server_stats-0.1.0.gem
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 🖥️ Command Line Interface (CLI) Usage
|
|
78
|
+
|
|
79
|
+
After installing, run `server_stats` (or its alias `server-stats`).
|
|
80
|
+
|
|
81
|
+
### 1. Full System Overview (Default)
|
|
82
|
+
```bash
|
|
83
|
+
server_stats
|
|
84
|
+
```
|
|
85
|
+
**Example Output:**
|
|
86
|
+
```text
|
|
87
|
+
============================================================
|
|
88
|
+
SERVER STATS REPORT
|
|
89
|
+
============================================================
|
|
90
|
+
OS: macOS 26.7.1 (arm64)
|
|
91
|
+
Hostname: Tams-MacBook-Pro-M3-Max.local
|
|
92
|
+
Uptime: 2h 49m 36s (Kernel: 25.6.0)
|
|
93
|
+
------------------------------------------------------------
|
|
94
|
+
CPU Cores: 16 cores (Physical: 16)
|
|
95
|
+
CPU Model: Apple M3 Max
|
|
96
|
+
CPU Usage: 13.00%
|
|
97
|
+
Load Avg: 2.89 (1m), 3.35 (5m), 3.43 (15m)
|
|
98
|
+
------------------------------------------------------------
|
|
99
|
+
RAM Total: 48.00 GB
|
|
100
|
+
RAM Used: 32.08 GB (66.83%)
|
|
101
|
+
RAM Available: 15.92 GB
|
|
102
|
+
RAM Free: 822.83 MB
|
|
103
|
+
------------------------------------------------------------
|
|
104
|
+
Disk Total: 926.35 GB (Mount: /)
|
|
105
|
+
Disk Used: 11.78 GB (1.27%)
|
|
106
|
+
Disk Available: 50.84 GB
|
|
107
|
+
Filesystem: /dev/disk3s1s1
|
|
108
|
+
============================================================
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 2. CPU Metrics Only
|
|
112
|
+
```bash
|
|
113
|
+
server_stats --cpu
|
|
114
|
+
```
|
|
115
|
+
**Example Output:**
|
|
116
|
+
```text
|
|
117
|
+
CPU Cores: 16 (Physical: 16)
|
|
118
|
+
CPU Model: Apple M3 Max
|
|
119
|
+
CPU Usage: 14.00%
|
|
120
|
+
Load Average: 2.97 (1m), 3.33 (5m), 3.42 (15m)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. Memory (RAM) Metrics Only
|
|
124
|
+
```bash
|
|
125
|
+
server_stats --memory
|
|
126
|
+
```
|
|
127
|
+
**Example Output:**
|
|
128
|
+
```text
|
|
129
|
+
RAM Total: 48.00 GB
|
|
130
|
+
RAM Used: 32.42 GB (67.54%)
|
|
131
|
+
RAM Available: 15.58 GB
|
|
132
|
+
RAM Free: 509.45 MB
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Display raw bytes instead of human-readable units:
|
|
136
|
+
```bash
|
|
137
|
+
server_stats --memory --raw
|
|
138
|
+
```
|
|
139
|
+
```text
|
|
140
|
+
RAM Total: 51539607552 bytes
|
|
141
|
+
RAM Used: 34326265856 bytes (66.60%)
|
|
142
|
+
RAM Available: 17213341696 bytes
|
|
143
|
+
RAM Free: 602013696 bytes
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### 4. Disk Storage Metrics Only
|
|
147
|
+
Check root partition (`/`):
|
|
148
|
+
```bash
|
|
149
|
+
server_stats --disk
|
|
150
|
+
```
|
|
151
|
+
Check a specific mount point (e.g. `/data` or `/var`):
|
|
152
|
+
```bash
|
|
153
|
+
server_stats --disk /data
|
|
154
|
+
```
|
|
155
|
+
**Example Output:**
|
|
156
|
+
```text
|
|
157
|
+
Mount Path: /data
|
|
158
|
+
Filesystem: /dev/sdb1
|
|
159
|
+
Disk Total: 500.00 GB
|
|
160
|
+
Disk Used: 120.50 GB (24.10%)
|
|
161
|
+
Disk Available: 379.50 GB
|
|
162
|
+
Disk Free: 379.50 GB
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 5. Operating System & System Info Only
|
|
166
|
+
```bash
|
|
167
|
+
server_stats --system
|
|
168
|
+
```
|
|
169
|
+
**Example Output:**
|
|
170
|
+
```text
|
|
171
|
+
OS: macOS 26.7.1
|
|
172
|
+
Hostname: Tams-MacBook-Pro-M3-Max.local
|
|
173
|
+
Architecture: arm64
|
|
174
|
+
Kernel: 25.6.0
|
|
175
|
+
Uptime: 2h 50m 3s (10203s)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 6. JSON Output (Ideal for Prometheus, Datadog, Zabbix, or CI/CD pipelines)
|
|
179
|
+
Output formatted JSON:
|
|
180
|
+
```bash
|
|
181
|
+
server_stats --json
|
|
182
|
+
```
|
|
183
|
+
**Example JSON Output:**
|
|
184
|
+
```json
|
|
185
|
+
{
|
|
186
|
+
"timestamp": "2026-10-05T18:06:48Z",
|
|
187
|
+
"system": {
|
|
188
|
+
"os": "macOS",
|
|
189
|
+
"os_family": "macos",
|
|
190
|
+
"os_version": "26.7.1",
|
|
191
|
+
"hostname": "Tams-MacBook-Pro-M3-Max.local",
|
|
192
|
+
"architecture": "arm64",
|
|
193
|
+
"kernel": "25.6.0",
|
|
194
|
+
"uptime_seconds": 10196,
|
|
195
|
+
"uptime_human": "2h 49m 56s"
|
|
196
|
+
},
|
|
197
|
+
"cpu": {
|
|
198
|
+
"cores": 16,
|
|
199
|
+
"physical_cores": 16,
|
|
200
|
+
"usage_percent": 14.0,
|
|
201
|
+
"load_average": {
|
|
202
|
+
"one_minute": 3.14,
|
|
203
|
+
"five_minutes": 3.37,
|
|
204
|
+
"fifteen_minutes": 3.43
|
|
205
|
+
},
|
|
206
|
+
"model": "Apple M3 Max"
|
|
207
|
+
},
|
|
208
|
+
"memory": {
|
|
209
|
+
"total_bytes": 51539607552,
|
|
210
|
+
"free_bytes": 638238720,
|
|
211
|
+
"available_bytes": 17006346240,
|
|
212
|
+
"used_bytes": 34533261312,
|
|
213
|
+
"usage_percent": 67.0,
|
|
214
|
+
"total_human": "48.00 GB",
|
|
215
|
+
"free_human": "608.67 MB",
|
|
216
|
+
"available_human": "15.84 GB",
|
|
217
|
+
"used_human": "32.16 GB",
|
|
218
|
+
"details": {
|
|
219
|
+
"page_size_bytes": 16384,
|
|
220
|
+
"active_bytes": 16198729728,
|
|
221
|
+
"inactive_bytes": 16148070400,
|
|
222
|
+
"wired_bytes": 4961730560,
|
|
223
|
+
"purgeable_bytes": 220037120,
|
|
224
|
+
"compressed_bytes": 12622397440
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"disk": {
|
|
228
|
+
"path": "/",
|
|
229
|
+
"filesystem": "/dev/disk3s1s1",
|
|
230
|
+
"total_bytes": 994662584320,
|
|
231
|
+
"used_bytes": 12647350272,
|
|
232
|
+
"free_bytes": 54586494976,
|
|
233
|
+
"available_bytes": 54586494976,
|
|
234
|
+
"usage_percent": 1.27,
|
|
235
|
+
"total_human": "926.35 GB",
|
|
236
|
+
"used_human": "11.78 GB",
|
|
237
|
+
"free_human": "50.84 GB",
|
|
238
|
+
"available_human": "50.84 GB"
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Filter specific components with JSON output:
|
|
244
|
+
```bash
|
|
245
|
+
server_stats --cpu --json
|
|
246
|
+
server_stats --memory --json
|
|
247
|
+
server_stats --disk /data --json
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Output single-line compact JSON:
|
|
251
|
+
```bash
|
|
252
|
+
server_stats --json --compact
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 7. Custom CPU Sampling Interval
|
|
256
|
+
Sample CPU over 1.0 second for higher accuracy:
|
|
257
|
+
```bash
|
|
258
|
+
server_stats --cpu --interval 1.0
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### 8. CLI Options Reference
|
|
262
|
+
|
|
263
|
+
| Flag | Short | Description | Default |
|
|
264
|
+
|---|---|---|---|
|
|
265
|
+
| `--all` | `-a` | Display all server statistics | Default |
|
|
266
|
+
| `--cpu` | `-c` | Show CPU metrics only | - |
|
|
267
|
+
| `--memory`, `--ram` | `-m` | Show RAM/memory metrics only | - |
|
|
268
|
+
| `--disk [PATH]` | `-d` | Show disk storage metrics for path | `/` |
|
|
269
|
+
| `--system` | `-s` | Show operating system & hardware info | - |
|
|
270
|
+
| `--json` | `-j` | Format output as JSON | `false` |
|
|
271
|
+
| `--compact` | | Format JSON as a single line (when used with `-j`) | `false` |
|
|
272
|
+
| `--raw` | `-r` | Show raw byte numbers instead of human-readable units | `false` |
|
|
273
|
+
| `--interval SEC` | `-i` | CPU sampling interval in seconds | `0.5` |
|
|
274
|
+
| `--version` | `-v` | Display version | - |
|
|
275
|
+
| `--help` | `-h` | Display help banner | - |
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## 🛠️ Ruby Application Integration (Ruby API)
|
|
280
|
+
|
|
281
|
+
### 1. Basic Usage
|
|
282
|
+
```ruby
|
|
283
|
+
require 'server_stats'
|
|
284
|
+
|
|
285
|
+
# 1. CPU Metrics
|
|
286
|
+
cpu = ServerStats.cpu
|
|
287
|
+
puts "CPU Cores: #{cpu.cores} (Physical: #{cpu.physical_cores})"
|
|
288
|
+
puts "CPU Usage: #{cpu.usage_percent}%"
|
|
289
|
+
puts "Load (1m): #{cpu.load_average[:one_minute]}"
|
|
290
|
+
|
|
291
|
+
# 2. RAM / Memory Metrics
|
|
292
|
+
mem = ServerStats.memory # or ServerStats.ram
|
|
293
|
+
puts "RAM Total: #{mem.total_human} (#{mem.total_bytes} bytes)"
|
|
294
|
+
puts "RAM Available: #{mem.available_human}"
|
|
295
|
+
puts "RAM Free: #{mem.free_human}"
|
|
296
|
+
puts "RAM Used: #{mem.used_human} (#{mem.usage_percent}%)"
|
|
297
|
+
|
|
298
|
+
# 3. Disk Storage Metrics
|
|
299
|
+
disk = ServerStats.disk('/') # or custom mount: ServerStats.disk('/mnt/data')
|
|
300
|
+
puts "Disk Total: #{disk.total_human}"
|
|
301
|
+
puts "Disk Available: #{disk.available_human}"
|
|
302
|
+
puts "Disk Used: #{disk.used_human} (#{disk.usage_percent}%)"
|
|
303
|
+
|
|
304
|
+
# 4. OS & Hardware Info
|
|
305
|
+
sys = ServerStats.system
|
|
306
|
+
puts "OS: #{sys.os} #{sys.os_version} (#{sys.architecture})"
|
|
307
|
+
puts "Hostname: #{sys.hostname}"
|
|
308
|
+
puts "Uptime: #{sys.uptime_human}"
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 2. Hash and JSON Serialization
|
|
312
|
+
```ruby
|
|
313
|
+
require 'server_stats'
|
|
314
|
+
|
|
315
|
+
# Convert entire snapshot into a Ruby Hash
|
|
316
|
+
stats_hash = ServerStats.to_h
|
|
317
|
+
|
|
318
|
+
# Export as a JSON string
|
|
319
|
+
json_data = ServerStats.to_json
|
|
320
|
+
|
|
321
|
+
# Pretty-printed JSON string
|
|
322
|
+
pretty_json = ServerStats.to_json(pretty: true)
|
|
323
|
+
|
|
324
|
+
# Formatted ASCII summary table
|
|
325
|
+
puts ServerStats.summary
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### 3. Example: Rails Health-Check Endpoint
|
|
329
|
+
```ruby
|
|
330
|
+
# app/controllers/health_controller.rb
|
|
331
|
+
class HealthController < ApplicationController
|
|
332
|
+
def show
|
|
333
|
+
stats = ServerStats.all
|
|
334
|
+
|
|
335
|
+
# Return HTTP 503 if RAM or Disk usage exceeds 90%
|
|
336
|
+
status = if stats.memory.usage_percent > 90.0 || stats.disk.usage_percent > 90.0
|
|
337
|
+
:service_unavailable
|
|
338
|
+
else
|
|
339
|
+
:ok
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
render json: {
|
|
343
|
+
status: status == :ok ? "healthy" : "warning",
|
|
344
|
+
stats: stats.to_h
|
|
345
|
+
}, status: status
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### 4. Example: Resource Alerting Script (Slack / Telegram / Discord)
|
|
351
|
+
```ruby
|
|
352
|
+
require 'server_stats'
|
|
353
|
+
require 'net/http'
|
|
354
|
+
require 'json'
|
|
355
|
+
|
|
356
|
+
cpu = ServerStats.cpu(interval: 1.0)
|
|
357
|
+
mem = ServerStats.memory
|
|
358
|
+
disk = ServerStats.disk('/')
|
|
359
|
+
|
|
360
|
+
if cpu.usage_percent > 85.0 || mem.usage_percent > 85.0
|
|
361
|
+
message = <<~MSG
|
|
362
|
+
⚠️ SERVER RESOURCE ALERT: #{ServerStats.system.hostname}
|
|
363
|
+
- CPU: #{cpu.usage_percent}% (#{cpu.cores} cores)
|
|
364
|
+
- RAM: #{mem.used_human} / #{mem.total_human} (#{mem.usage_percent}%)
|
|
365
|
+
- Disk: #{disk.used_human} / #{disk.total_human} (#{disk.usage_percent}%)
|
|
366
|
+
MSG
|
|
367
|
+
puts message
|
|
368
|
+
# Send webhook request to Slack / Telegram / Discord here...
|
|
369
|
+
end
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 🔬 Technical Data Collection Mechanisms
|
|
375
|
+
|
|
376
|
+
| Component | Ubuntu / Linux | macOS |
|
|
377
|
+
|---|---|---|
|
|
378
|
+
| **RAM Total & Free** | Reads `/proc/meminfo` directly (`MemTotal`, `MemAvailable`, `MemFree`, `Buffers`, `Cached`). | `sysctl -n hw.memsize` combined with page calculation from `vm_stat` (`Pages free`, `inactive`, `speculative`, `purgeable`). |
|
|
379
|
+
| **CPU Cores** | `Etc.nprocessors` combined with `/proc/cpuinfo` for physical cores. | `sysctl -n hw.ncpu` and `sysctl -n hw.physicalcpu`. |
|
|
380
|
+
| **CPU Usage %** | Two consecutive samples from `/proc/stat` calculating `delta_idle` and `delta_total`. Zero subprocesses required. | Sampled using `iostat -c 2` (or snapshot `top`), reliable within sandbox. |
|
|
381
|
+
| **Disk Space** | POSIX standard: `df -P -k <path>`, parses 1024-byte blocks precisely. | POSIX standard: `df -P -k <path>`. |
|
|
382
|
+
| **Uptime** | Reads directly from `/proc/uptime`. | Computed from boot timestamp via `sysctl -n kern.boottime`. |
|
|
383
|
+
| **OS Info** | Parsed from standard `/etc/os-release`. | Gathered via `sw_vers` and `uname`. |
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## 🧪 Running Tests
|
|
388
|
+
|
|
389
|
+
The test suite is completely standalone and requires no external testing gems:
|
|
390
|
+
|
|
391
|
+
```bash
|
|
392
|
+
# Run test suite directly
|
|
393
|
+
ruby test/run_all.rb
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
Or via Rake:
|
|
397
|
+
```bash
|
|
398
|
+
rake test
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## 📄 License
|
|
404
|
+
|
|
405
|
+
Distributed under the [MIT License](LICENSE).
|
data/Rakefile
ADDED
data/bin/server-stats
ADDED
data/bin/server_stats
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "optparse"
|
|
4
|
+
require "json"
|
|
5
|
+
require_relative "../server_stats"
|
|
6
|
+
|
|
7
|
+
module ServerStats
|
|
8
|
+
class CLI
|
|
9
|
+
def self.start(args = ARGV)
|
|
10
|
+
new(args).run
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(args = ARGV)
|
|
14
|
+
@args = args.dup
|
|
15
|
+
@options = {
|
|
16
|
+
mode: :all,
|
|
17
|
+
format: :text,
|
|
18
|
+
disk_path: "/",
|
|
19
|
+
interval: 0.5,
|
|
20
|
+
raw: false,
|
|
21
|
+
pretty: true
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def parse_options
|
|
26
|
+
opt_parser = OptionParser.new do |opts|
|
|
27
|
+
opts.banner = "Cách dùng: server_stats [tuỳ chọn]"
|
|
28
|
+
opts.separator ""
|
|
29
|
+
opts.separator "Tuỳ chọn hiển thị:"
|
|
30
|
+
|
|
31
|
+
opts.on("-a", "--all", "Hiển thị đầy đủ thông số hệ thống (mặc định)") do
|
|
32
|
+
@options[:mode] = :all
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
opts.on("-c", "--cpu", "Chỉ hiển thị thông số CPU") do
|
|
36
|
+
@options[:mode] = :cpu
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
opts.on("-m", "--memory", "--ram", "Chỉ hiển thị thông số RAM") do
|
|
40
|
+
@options[:mode] = :memory
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
opts.on("-d", "--disk [PATH]", "Chỉ hiển thị thông số ổ cứng (mặc định: /)") do |path|
|
|
44
|
+
@options[:mode] = :disk
|
|
45
|
+
@options[:disk_path] = path if path && !path.empty?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
opts.on("-s", "--system", "Chỉ hiển thị thông tin hệ điều hành và phần cứng") do
|
|
49
|
+
@options[:mode] = :system
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
opts.separator ""
|
|
53
|
+
opts.separator "Tuỳ chọn định dạng:"
|
|
54
|
+
|
|
55
|
+
opts.on("-j", "--json", "Xuất dữ liệu ở định dạng JSON") do
|
|
56
|
+
@options[:format] = :json
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
opts.on("--compact", "Xuất JSON trên 1 dòng (khi dùng với -j)") do
|
|
60
|
+
@options[:pretty] = false
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
opts.on("-r", "--raw", "Hiển thị dung lượng dạng byte thô thay vì đơn vị đọc được (GB/MB)") do
|
|
64
|
+
@options[:raw] = true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
opts.on("-i", "--interval SECONDS", Float, "Thời gian lấy mẫu CPU tính bằng giây (mặc định: 0.5)") do |sec|
|
|
68
|
+
@options[:interval] = [sec.to_f, 0.1].max
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
opts.separator ""
|
|
72
|
+
opts.separator "Thông tin chung:"
|
|
73
|
+
|
|
74
|
+
opts.on("-v", "--version", "Hiển thị phiên bản gem") do
|
|
75
|
+
puts "server_stats version #{ServerStats::VERSION}"
|
|
76
|
+
exit 0
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
opts.on("-h", "--help", "Hiển thị hướng dẫn sử dụng") do
|
|
80
|
+
puts opts
|
|
81
|
+
exit 0
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
opt_parser.parse!(@args)
|
|
86
|
+
# If extra non-option argument given, treat as disk path if disk mode was selected
|
|
87
|
+
@options[:disk_path] = @args.first if @options[:mode] == :disk && @args.first
|
|
88
|
+
@options
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def run
|
|
92
|
+
parse_options
|
|
93
|
+
execute_command
|
|
94
|
+
rescue OptionParser::InvalidOption, OptionParser::MissingArgument => e
|
|
95
|
+
warn "Lỗi cú pháp: #{e.message}"
|
|
96
|
+
warn "Chạy 'server_stats --help' để xem danh sách tuỳ chọn."
|
|
97
|
+
exit 1
|
|
98
|
+
rescue StandardError => e
|
|
99
|
+
warn "Lỗi khi lấy thông số: #{e.message}"
|
|
100
|
+
exit 1
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def execute_command
|
|
104
|
+
case @options[:format]
|
|
105
|
+
when :json
|
|
106
|
+
output_json
|
|
107
|
+
else
|
|
108
|
+
output_text
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
private
|
|
113
|
+
|
|
114
|
+
def output_json
|
|
115
|
+
data = case @options[:mode]
|
|
116
|
+
when :cpu
|
|
117
|
+
ServerStats.cpu(interval: @options[:interval]).to_h
|
|
118
|
+
when :memory
|
|
119
|
+
ServerStats.memory.to_h
|
|
120
|
+
when :disk
|
|
121
|
+
ServerStats.disk(path: @options[:disk_path]).to_h
|
|
122
|
+
when :system
|
|
123
|
+
ServerStats.system.to_h
|
|
124
|
+
else
|
|
125
|
+
ServerStats.to_h(path: @options[:disk_path], interval: @options[:interval])
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
puts @options[:pretty] ? JSON.pretty_generate(data) : data.to_json
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def output_text
|
|
132
|
+
case @options[:mode]
|
|
133
|
+
when :cpu
|
|
134
|
+
print_cpu(ServerStats.cpu(interval: @options[:interval]))
|
|
135
|
+
when :memory
|
|
136
|
+
print_memory(ServerStats.memory)
|
|
137
|
+
when :disk
|
|
138
|
+
print_disk(ServerStats.disk(path: @options[:disk_path]))
|
|
139
|
+
when :system
|
|
140
|
+
print_system(ServerStats.system)
|
|
141
|
+
else
|
|
142
|
+
puts ServerStats.summary(path: @options[:disk_path], interval: @options[:interval])
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def print_cpu(cpu)
|
|
147
|
+
puts "CPU Cores: #{cpu.cores} (Physical: #{cpu.physical_cores})"
|
|
148
|
+
puts "CPU Model: #{cpu.model || 'Unknown'}" if cpu.model
|
|
149
|
+
puts "CPU Usage: #{Formatter.format_percent(cpu.usage_percent)}"
|
|
150
|
+
if cpu.load_average.any?
|
|
151
|
+
puts "Load Average: #{cpu.load_average[:one_minute]} (1m), #{cpu.load_average[:five_minutes]} (5m), #{cpu.load_average[:fifteen_minutes]} (15m)"
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def print_memory(mem)
|
|
156
|
+
if @options[:raw]
|
|
157
|
+
puts "RAM Total: #{mem.total_bytes} bytes"
|
|
158
|
+
puts "RAM Used: #{mem.used_bytes} bytes (#{Formatter.format_percent(mem.usage_percent)})"
|
|
159
|
+
puts "RAM Available: #{mem.available_bytes} bytes"
|
|
160
|
+
puts "RAM Free: #{mem.free_bytes} bytes"
|
|
161
|
+
else
|
|
162
|
+
puts "RAM Total: #{mem.total_human}"
|
|
163
|
+
puts "RAM Used: #{mem.used_human} (#{Formatter.format_percent(mem.usage_percent)})"
|
|
164
|
+
puts "RAM Available: #{mem.available_human}"
|
|
165
|
+
puts "RAM Free: #{mem.free_human}"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def print_disk(disk)
|
|
170
|
+
puts "Mount Path: #{disk.path}"
|
|
171
|
+
puts "Filesystem: #{disk.filesystem}"
|
|
172
|
+
if @options[:raw]
|
|
173
|
+
puts "Disk Total: #{disk.total_bytes} bytes"
|
|
174
|
+
puts "Disk Used: #{disk.used_bytes} bytes (#{Formatter.format_percent(disk.usage_percent)})"
|
|
175
|
+
puts "Disk Available: #{disk.available_bytes} bytes"
|
|
176
|
+
puts "Disk Free: #{disk.free_bytes} bytes"
|
|
177
|
+
else
|
|
178
|
+
puts "Disk Total: #{disk.total_human}"
|
|
179
|
+
puts "Disk Used: #{disk.used_human} (#{Formatter.format_percent(disk.usage_percent)})"
|
|
180
|
+
puts "Disk Available: #{disk.available_human}"
|
|
181
|
+
puts "Disk Free: #{disk.free_human}"
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def print_system(sys)
|
|
186
|
+
puts "OS: #{sys.os} #{sys.os_version}"
|
|
187
|
+
puts "Hostname: #{sys.hostname}"
|
|
188
|
+
puts "Architecture: #{sys.architecture}"
|
|
189
|
+
puts "Kernel: #{sys.kernel}"
|
|
190
|
+
puts "Uptime: #{sys.uptime_human} (#{sys.uptime_seconds}s)"
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "os"
|
|
4
|
+
require_relative "models"
|
|
5
|
+
require_relative "providers/base"
|
|
6
|
+
require_relative "providers/linux"
|
|
7
|
+
require_relative "providers/macos"
|
|
8
|
+
|
|
9
|
+
module ServerStats
|
|
10
|
+
class Collector
|
|
11
|
+
attr_accessor :provider
|
|
12
|
+
|
|
13
|
+
def initialize(provider: nil)
|
|
14
|
+
@provider = provider || default_provider
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def cpu(interval: 0.5)
|
|
18
|
+
@provider.cpu(interval)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def memory
|
|
22
|
+
@provider.memory
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
alias ram memory
|
|
26
|
+
|
|
27
|
+
def disk(path = "/")
|
|
28
|
+
path = path[:path] if path.is_a?(Hash) && path.key?(:path)
|
|
29
|
+
@provider.disk(path)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def system
|
|
33
|
+
@provider.system
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def snapshot(path: "/", interval: 0.5)
|
|
37
|
+
Snapshot.new(
|
|
38
|
+
cpu: cpu(interval: interval),
|
|
39
|
+
memory: memory,
|
|
40
|
+
disk: disk(path: path),
|
|
41
|
+
system: system
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
alias all snapshot
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def default_provider
|
|
50
|
+
case OS.type
|
|
51
|
+
when :macos
|
|
52
|
+
Providers::MacOs.new
|
|
53
|
+
when :linux
|
|
54
|
+
Providers::Linux.new
|
|
55
|
+
else
|
|
56
|
+
# Fallback to Linux-like or base provider
|
|
57
|
+
File.exist?("/proc/meminfo") ? Providers::Linux.new : Providers::Base.new
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|