@@ -2,6 +2,8 @@ use futures::StreamExt;
22use heim:: units:: information:: byte;
33#[ cfg( not( windows) ) ]
44use heim:: units:: ratio:: ratio;
5+ #[ cfg( unix) ]
6+ use nix:: sys:: statvfs:: statvfs;
57use vector_lib:: { configurable:: configurable_component, metric_tags} ;
68
79use super :: { FilterList , HostMetrics , default_all_devices, example_devices, filter_result} ;
@@ -128,8 +130,28 @@ impl HostMetrics {
128130 output. gauge (
129131 "filesystem_used_ratio" ,
130132 usage. ratio ( ) . get :: < ratio > ( ) as f64 ,
131- tags,
133+ tags. clone ( ) ,
132134 ) ;
135+
136+ // Inode metrics (Unix only)
137+ #[ cfg( unix) ]
138+ if let Ok ( stat) = statvfs ( partition. mount_point ( ) ) {
139+ let inodes_total = stat. files ( ) as f64 ;
140+ let inodes_free = stat. files_free ( ) as f64 ;
141+ let inodes_used = inodes_total - inodes_free;
142+ let inodes_used_ratio = if inodes_total > 0.0 {
143+ inodes_used / inodes_total
144+ } else {
145+ 0.0
146+ } ;
147+
148+ output. gauge ( "filesystem_inodes_total" , inodes_total, tags. clone ( ) ) ;
149+ output. gauge ( "filesystem_inodes_free" , inodes_free, tags. clone ( ) ) ;
150+ output. gauge ( "filesystem_inodes_used" , inodes_used, tags. clone ( ) ) ;
151+ output. gauge ( "filesystem_inodes_used_ratio" , inodes_used_ratio, tags) ;
152+ }
153+ #[ cfg( windows) ]
154+ drop ( tags) ;
133155 }
134156 }
135157 Err ( error) => {
@@ -161,17 +183,40 @@ mod tests {
161183 . await ;
162184 let metrics = buffer. metrics ;
163185 assert ! ( !metrics. is_empty( ) ) ;
164- assert ! ( metrics. len( ) . is_multiple_of( 4 ) ) ;
165186 assert ! ( all_gauges( & metrics) ) ;
166187
167- // There are exactly three filesystem_* names
168- for name in & [
188+ // Base metrics (these are always present)
189+ let base_metrics = [
169190 "filesystem_free_bytes" ,
170191 "filesystem_total_bytes" ,
171192 "filesystem_used_bytes" ,
172193 "filesystem_used_ratio" ,
173- ] {
174- assert_eq ! ( count_name( & metrics, name) , metrics. len( ) / 4 , "name={name}" ) ;
194+ ] ;
195+
196+ // Each filesystem should have all 4 base metrics
197+ let num_filesystems = count_name ( & metrics, "filesystem_free_bytes" ) ;
198+ assert ! ( num_filesystems > 0 ) ;
199+ for name in & base_metrics {
200+ assert_eq ! ( count_name( & metrics, name) , num_filesystems, "name={name}" ) ;
201+ }
202+
203+ // Inode metrics are present for filesystems that support statvfs
204+ // (some virtual filesystems like /proc, /sys might not)
205+ let inode_metrics = [
206+ "filesystem_inodes_total" ,
207+ "filesystem_inodes_free" ,
208+ "filesystem_inodes_used" ,
209+ "filesystem_inodes_used_ratio" ,
210+ ] ;
211+ let num_inode_total = count_name ( & metrics, "filesystem_inodes_total" ) ;
212+ assert ! (
213+ num_inode_total > 0 ,
214+ "Expected at least one filesystem to report inode metrics"
215+ ) ;
216+
217+ // For filesystems that report inodes, all 4 inode metrics should be present
218+ for name in & inode_metrics {
219+ assert_eq ! ( count_name( & metrics, name) , num_inode_total, "name={name}" ) ;
175220 }
176221
177222 // They should all have "filesystem" and "mountpoint" tags
0 commit comments