1+ use sysinfo:: { System , Disks } ;
2+ use std:: { fs, thread, time:: Duration } ;
3+
4+ // --- Helper: Baca LIMIT (Hanya perlu dipanggil sekali) ---
5+
6+ fn get_docker_memory_limit ( ) -> Option < u64 > {
7+ // Cgroup V2
8+ if let Ok ( contents) = fs:: read_to_string ( "/sys/fs/cgroup/memory.max" ) {
9+ let val = contents. trim ( ) ;
10+ if val == "max" { return None ; }
11+ return val. parse :: < u64 > ( ) . ok ( ) ;
12+ }
13+ // Cgroup V1
14+ if let Ok ( contents) = fs:: read_to_string ( "/sys/fs/cgroup/memory/memory.limit_in_bytes" ) {
15+ let val = contents. trim ( ) ;
16+ if val. len ( ) > 15 { return None ; }
17+ return val. parse :: < u64 > ( ) . ok ( ) ;
18+ }
19+ None
20+ }
21+
22+ fn get_docker_cpu_limit ( ) -> Option < f64 > {
23+ // Cgroup V2
24+ if let Ok ( contents) = fs:: read_to_string ( "/sys/fs/cgroup/cpu.max" ) {
25+ let parts: Vec < & str > = contents. trim ( ) . split_whitespace ( ) . collect ( ) ;
26+ if parts. len ( ) == 2 && parts[ 0 ] != "max" {
27+ let quota = parts[ 0 ] . parse :: < f64 > ( ) . unwrap_or ( 0.0 ) ;
28+ let period = parts[ 1 ] . parse :: < f64 > ( ) . unwrap_or ( 100000.0 ) ;
29+ return Some ( quota / period) ;
30+ }
31+ }
32+ // Cgroup V1
33+ if let Ok ( quota_str) = fs:: read_to_string ( "/sys/fs/cgroup/cpu/cpu.cfs_quota_us" ) {
34+ let quota = quota_str. trim ( ) . parse :: < f64 > ( ) . unwrap_or ( -1.0 ) ;
35+ if quota > 0.0 {
36+ if let Ok ( period_str) = fs:: read_to_string ( "/sys/fs/cgroup/cpu/cpu.cfs_period_us" ) {
37+ let period = period_str. trim ( ) . parse :: < f64 > ( ) . unwrap_or ( 100000.0 ) ;
38+ return Some ( quota / period) ;
39+ }
40+ }
41+ }
42+ None
43+ }
44+
45+ // --- Helper: Baca USAGE Realtime (Dipanggil di dalam Loop) ---
46+
47+ fn get_container_memory_usage ( ) -> Option < u64 > {
48+ // Cgroup V2
49+ if let Ok ( contents) = fs:: read_to_string ( "/sys/fs/cgroup/memory.current" ) {
50+ return contents. trim ( ) . parse :: < u64 > ( ) . ok ( ) ;
51+ }
52+ // Cgroup V1
53+ if let Ok ( contents) = fs:: read_to_string ( "/sys/fs/cgroup/memory/memory.usage_in_bytes" ) {
54+ return contents. trim ( ) . parse :: < u64 > ( ) . ok ( ) ;
55+ }
56+ None
57+ }
58+
59+ #[ test] // Gunakan cargo test --test resource -- --nocapture
60+ pub fn monitor_realtime_dashboard ( ) {
61+ let mut sys = System :: new_all ( ) ;
62+
63+ // 1. SETUP AWAL (Ambil data statis sekali saja)
64+ let os_name = System :: name ( ) . unwrap_or ( "Unknown" . into ( ) ) ;
65+ let host_name = System :: host_name ( ) . unwrap_or ( "Unknown" . into ( ) ) ;
66+ let cpu_cores = sys. cpus ( ) . len ( ) ;
67+
68+ // PERBAIKAN: Tambahkan .to_string()
69+ let cpu_model = sys. cpus ( ) . first ( )
70+ . map ( |c| c. brand ( ) . to_string ( ) ) // Ubah &str jadi String (Copy)
71+ . unwrap_or ( "Unknown" . to_string ( ) ) ; // Default juga harus String
72+
73+ // Ambil Limit Docker (Statis)
74+ let docker_mem_limit_bytes = get_docker_memory_limit ( ) ;
75+ let docker_cpu_limit = get_docker_cpu_limit ( ) ;
76+
77+ // Setup format limit string agar tidak diproses berulang
78+ let limit_str = match docker_mem_limit_bytes {
79+ Some ( b) => format ! ( "{:.2} GB" , b as f64 / 1_073_741_824.0 ) ,
80+ None => "Unlimited (Host RAM)" . to_string ( ) ,
81+ } ;
82+
83+ println ! ( "Mempersiapkan monitoring... (Tunggu 1 detik untuk kalibrasi CPU)" ) ;
84+ sys. refresh_all ( ) ;
85+ thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
86+
87+ // 2. LOOP REALTIME
88+ loop {
89+ // Clear Screen (ANSI Code) -> Supaya jadi dashboard diam
90+ print ! ( "\x1B [2J\x1B [1;1H" ) ;
91+
92+ println ! ( "==============================================" ) ;
93+ println ! ( " 🚀 SERVER RESOURCE MONITOR (CTRL+C to Stop)" ) ;
94+ println ! ( "==============================================" ) ;
95+ println ! ( "Host: {} | OS: {}" , host_name, os_name) ;
96+ println ! ( "CPU Model: {} ({} Cores)" , cpu_model, cpu_cores) ;
97+
98+ // --- UPDATE DATA DINAMIS ---
99+ sys. refresh_cpu ( ) ; // Hanya refresh CPU
100+ sys. refresh_memory ( ) ; // Hanya refresh RAM
101+
102+ // A. CPU USAGE
103+ let global_cpu = sys. global_cpu_info ( ) . cpu_usage ( ) ;
104+ let cpu_bar_len = ( global_cpu / 5.0 ) as usize ; // Visualisasi Bar sederhana
105+ let cpu_bar: String = "█" . repeat ( cpu_bar_len) ;
106+
107+ println ! ( "\n [CPU USAGE]" ) ;
108+ println ! ( "Host Load : {:.2}% | {}" , global_cpu, cpu_bar) ;
109+ if let Some ( limit) = docker_cpu_limit {
110+ println ! ( "Docker Lim: {:.2} vCPU" , limit) ;
111+ } else {
112+ println ! ( "Docker Lim: Unlimited" ) ;
113+ }
114+
115+ // B. MEMORY USAGE (Host vs Container)
116+ println ! ( "\n [MEMORY USAGE]" ) ;
117+
118+ // Host Info (sysinfo baca /proc/meminfo)
119+ let host_total_gb = sys. total_memory ( ) as f64 / 1_073_741_824.0 ;
120+ let host_used_gb = sys. used_memory ( ) as f64 / 1_073_741_824.0 ;
121+ let host_percent = ( host_used_gb / host_total_gb) * 100.0 ;
122+
123+ println ! ( "HOST : {:.2} GB / {:.2} GB ({:.1}%)" , host_used_gb, host_total_gb, host_percent) ;
124+
125+ // Container Info (Baca Cgroups)
126+ if let Some ( container_bytes) = get_container_memory_usage ( ) {
127+ let container_gb = container_bytes as f64 / 1_073_741_824.0 ;
128+
129+ // Hitung persentase pemakaian terhadap limit container
130+ let container_percent_str = if let Some ( limit_bytes) = docker_mem_limit_bytes {
131+ let p = ( container_bytes as f64 / limit_bytes as f64 ) * 100.0 ;
132+ format ! ( "{:.1}% of Limit" , p)
133+ } else {
134+ "N/A" . to_string ( )
135+ } ;
136+
137+ println ! ( "CONTAINER : {:.2} GB (Limit: {}) -> {}" , container_gb, limit_str, container_percent_str) ;
138+ } else {
139+ println ! ( "CONTAINER : Gagal membaca Cgroup usage" ) ;
140+ }
141+
142+ println ! ( "\n [DISK INFO]" ) ;
143+ let disks = Disks :: new_with_refreshed_list ( ) ; // Disk jarang berubah, tapi ok lah di refresh pelan2
144+ for disk in & disks {
145+ let total_gb = disk. total_space ( ) as f64 / 1_073_741_824.0 ;
146+ let avail_gb = disk. available_space ( ) as f64 / 1_073_741_824.0 ;
147+ println ! ( "{:<10}: Free {:.2} GB / Total {:.2} GB" , format!( "{:?}" , disk. mount_point( ) ) , avail_gb, total_gb) ;
148+ }
149+
150+ println ! ( "==============================================" ) ;
151+ println ! ( "Updated: {:?}" , std:: time:: SystemTime :: now( ) ) ;
152+
153+ // Jeda 1 Detik
154+ thread:: sleep ( Duration :: from_secs ( 1 ) ) ;
155+ }
156+ }
0 commit comments