Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions node/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ tikv-jemalloc-ctl = { version = "0.6", optional = true, features = ["stats"] }

[target.'cfg(target_os = "linux")'.dependencies]
procfs = { version = "0.17.0", optional = true }
libc = { version = "0.2", optional = true }

[dev-dependencies]
commonware-macros.workspace = true
Expand Down Expand Up @@ -146,6 +147,7 @@ prom = [
"http",
"eyre",
"procfs",
"libc",
"summit-types/prom",
]
tokio-console = ["console-subscriber"]
Expand Down
54 changes: 54 additions & 0 deletions node/src/prom/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ impl Default for HooksBuilder {
Box::new(|| Collector::default().collect()),
Box::new(collect_memory_stats),
Box::new(collect_io_stats),
Box::new(collect_disk_stats),
],
}
}
Expand Down Expand Up @@ -175,3 +176,56 @@ fn collect_io_stats() {

#[cfg(not(target_os = "linux"))]
const fn collect_io_stats() {}

#[cfg(target_os = "linux")]
fn collect_disk_stats() {
use metrics::gauge;
use std::ffi::CString;
use std::fs;
use std::mem::MaybeUninit;
use tracing::error;

let Ok(contents) = fs::read_to_string("/proc/mounts")
.map_err(|error| error!(%error, "Failed to read /proc/mounts"))
else {
return;
};

for line in contents.lines() {
let fields: Vec<&str> = line.split_whitespace().collect();
if fields.len() < 3 {
continue;
}
let device = fields[0];
if !device.starts_with('/') {
continue;
}
let mount_point = fields[1];

let Some(c_path) = CString::new(mount_point).ok() else {
continue;
};

let mut buf = MaybeUninit::<libc::statvfs>::uninit();
let ret = unsafe { libc::statvfs(c_path.as_ptr(), buf.as_mut_ptr()) };
if ret != 0 {
continue;
}
let stat = unsafe { buf.assume_init() };

let block_size = stat.f_frsize;
let total = stat.f_blocks * block_size;
let free = stat.f_bfree * block_size;
let available = stat.f_bavail * block_size;
let used = total.saturating_sub(free);

gauge!("disk.total_bytes", "mountpoint" => mount_point.to_string()).set(total as f64);
gauge!("disk.free_bytes", "mountpoint" => mount_point.to_string()).set(free as f64);
gauge!("disk.available_bytes", "mountpoint" => mount_point.to_string())
.set(available as f64);
gauge!("disk.used_bytes", "mountpoint" => mount_point.to_string()).set(used as f64);
}
}

#[cfg(not(target_os = "linux"))]
const fn collect_disk_stats() {}
28 changes: 28 additions & 0 deletions node/src/prom/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl MetricServer {
Collector::default().describe();
describe_memory_stats();
describe_io_stats();
describe_disk_stats();

Ok(())
}
Expand Down Expand Up @@ -262,6 +263,33 @@ fn describe_io_stats() {
#[cfg(not(target_os = "linux"))]
const fn describe_io_stats() {}

#[cfg(target_os = "linux")]
fn describe_disk_stats() {
describe_gauge!(
"disk.total_bytes",
Unit::Bytes,
"Total size of the filesystem"
);
describe_gauge!(
"disk.free_bytes",
Unit::Bytes,
"Free bytes on the filesystem (including reserved)"
);
describe_gauge!(
"disk.available_bytes",
Unit::Bytes,
"Bytes available to non-root users"
);
describe_gauge!(
"disk.used_bytes",
Unit::Bytes,
"Used bytes on the filesystem"
);
}

#[cfg(not(target_os = "linux"))]
const fn describe_disk_stats() {}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading