Simplify service disk usage detection - remove all estimation fallbacks
- Replace complex multi-strategy detection with single deterministic method - Remove estimate_service_disk_usage and all fallback strategies - Use simple get_service_disk_usage method with clear logic: * Defined path exists → use only that path * Defined path fails → return None (shows as '-') * No defined path → use systemctl WorkingDirectory * No estimates or guessing ever Fixes misleading 5MB estimates when defined paths fail due to permissions.
This commit is contained in:
parent
fe18ace767
commit
47a7d5ae62
@ -322,43 +322,22 @@ impl SystemdCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get service disk usage with comprehensive detection strategies
|
||||
fn get_comprehensive_service_disk_usage(&self, service: &str) -> Option<f32> {
|
||||
// Strategy 1: Try service-specific directories first
|
||||
if let Some(size) = self.get_service_disk_usage_basic(service) {
|
||||
return Some(size);
|
||||
}
|
||||
|
||||
// Strategy 2: Check service binary and configuration directories
|
||||
if let Some(size) = self.get_service_binary_disk_usage(service) {
|
||||
return Some(size);
|
||||
}
|
||||
|
||||
// Strategy 3: Check service logs and runtime data
|
||||
if let Some(size) = self.get_service_logs_disk_usage(service) {
|
||||
return Some(size);
|
||||
}
|
||||
|
||||
// Strategy 4: Use process memory maps to find file usage
|
||||
if let Some(size) = self.get_process_file_usage(service) {
|
||||
return Some(size);
|
||||
}
|
||||
|
||||
// Strategy 5: Last resort - estimate based on service type
|
||||
self.estimate_service_disk_usage(service)
|
||||
}
|
||||
|
||||
/// Basic service disk usage detection (existing logic)
|
||||
fn get_service_disk_usage_basic(&self, service: &str) -> Option<f32> {
|
||||
// Check defined service paths FIRST (highest priority)
|
||||
let service_dirs = self.get_service_directories(service);
|
||||
for dir in service_dirs {
|
||||
if let Some(size) = self.get_directory_size(dir) {
|
||||
return Some(size);
|
||||
/// Get service disk usage - simple and deterministic
|
||||
fn get_service_disk_usage(&self, service: &str) -> Option<f32> {
|
||||
// 1. Check if service has defined directories
|
||||
let defined_dirs = self.get_service_directories(service);
|
||||
if !defined_dirs.is_empty() {
|
||||
// Service has defined paths - use ONLY those
|
||||
for dir in defined_dirs {
|
||||
if let Some(size) = self.get_directory_size(dir) {
|
||||
return Some(size);
|
||||
}
|
||||
}
|
||||
// If defined path failed, return None (shows as "-")
|
||||
return None;
|
||||
}
|
||||
|
||||
// Only if no defined path, try systemctl WorkingDirectory
|
||||
// 2. No defined path - use systemctl WorkingDirectory
|
||||
let output = Command::new("systemctl")
|
||||
.arg("show")
|
||||
.arg(format!("{}.service", service))
|
||||
@ -409,205 +388,11 @@ impl SystemdCollector {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check service binary and configuration directories
|
||||
fn get_service_binary_disk_usage(&self, service: &str) -> Option<f32> {
|
||||
let mut total_size = 0u64;
|
||||
let mut found_any = false;
|
||||
|
||||
// Check common binary locations
|
||||
let binary_paths = [
|
||||
format!("/usr/bin/{}", service),
|
||||
format!("/usr/sbin/{}", service),
|
||||
format!("/usr/local/bin/{}", service),
|
||||
format!("/opt/{}/bin/{}", service, service),
|
||||
];
|
||||
|
||||
for binary_path in &binary_paths {
|
||||
if let Ok(metadata) = std::fs::metadata(binary_path) {
|
||||
total_size += metadata.len();
|
||||
found_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check configuration directories
|
||||
let config_dirs = [
|
||||
format!("/etc/{}", service),
|
||||
format!("/usr/share/{}", service),
|
||||
format!("/var/lib/{}", service),
|
||||
format!("/opt/{}", service),
|
||||
];
|
||||
|
||||
for config_dir in &config_dirs {
|
||||
if let Some(size_gb) = self.get_directory_size(config_dir) {
|
||||
total_size += (size_gb * 1024.0 * 1024.0 * 1024.0) as u64;
|
||||
found_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if found_any {
|
||||
let size_gb = total_size as f32 / (1024.0 * 1024.0 * 1024.0);
|
||||
Some(size_gb.max(0.001)) // Minimum 1MB for visibility
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Check service logs and runtime data
|
||||
fn get_service_logs_disk_usage(&self, service: &str) -> Option<f32> {
|
||||
let mut total_size = 0u64;
|
||||
let mut found_any = false;
|
||||
|
||||
// Check systemd journal logs for this service
|
||||
let output = Command::new("journalctl")
|
||||
.arg("-u")
|
||||
.arg(format!("{}.service", service))
|
||||
.arg("--disk-usage")
|
||||
.output()
|
||||
.ok();
|
||||
|
||||
if let Some(output) = output {
|
||||
if output.status.success() {
|
||||
let output_str = String::from_utf8_lossy(&output.stdout);
|
||||
// Extract size from "Archived and active journals take up X on disk."
|
||||
if let Some(size_part) = output_str.split("take up ").nth(1) {
|
||||
if let Some(size_str) = size_part.split(" on disk").next() {
|
||||
// Parse sizes like "1.2M", "45.6K", "2.1G"
|
||||
if let Some(size_bytes) = self.parse_size_string(size_str) {
|
||||
total_size += size_bytes;
|
||||
found_any = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check common log directories
|
||||
let log_dirs = [
|
||||
format!("/var/log/{}", service),
|
||||
format!("/var/log/{}.log", service),
|
||||
"/var/log/syslog".to_string(),
|
||||
"/var/log/messages".to_string(),
|
||||
];
|
||||
|
||||
for log_path in &log_dirs {
|
||||
if let Ok(metadata) = std::fs::metadata(log_path) {
|
||||
total_size += metadata.len();
|
||||
found_any = true;
|
||||
}
|
||||
}
|
||||
|
||||
if found_any {
|
||||
let size_gb = total_size as f32 / (1024.0 * 1024.0 * 1024.0);
|
||||
Some(size_gb.max(0.001))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse size strings like "1.2M", "45.6K", "2.1G" to bytes
|
||||
fn parse_size_string(&self, size_str: &str) -> Option<u64> {
|
||||
let size_str = size_str.trim();
|
||||
if size_str.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (number_part, unit) = if size_str.ends_with('K') {
|
||||
(size_str.trim_end_matches('K'), 1024u64)
|
||||
} else if size_str.ends_with('M') {
|
||||
(size_str.trim_end_matches('M'), 1024 * 1024)
|
||||
} else if size_str.ends_with('G') {
|
||||
(size_str.trim_end_matches('G'), 1024 * 1024 * 1024)
|
||||
} else {
|
||||
(size_str, 1)
|
||||
};
|
||||
|
||||
if let Ok(number) = number_part.parse::<f64>() {
|
||||
Some((number * unit as f64) as u64)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Use process information to find file usage
|
||||
fn get_process_file_usage(&self, service: &str) -> Option<f32> {
|
||||
// Get main PID
|
||||
let output = Command::new("systemctl")
|
||||
.arg("show")
|
||||
.arg(format!("{}.service", service))
|
||||
.arg("--property=MainPID")
|
||||
.output()
|
||||
.ok()?;
|
||||
|
||||
let output_str = String::from_utf8(output.stdout).ok()?;
|
||||
for line in output_str.lines() {
|
||||
if line.starts_with("MainPID=") {
|
||||
let pid_str = line.trim_start_matches("MainPID=");
|
||||
if let Ok(pid) = pid_str.parse::<u32>() {
|
||||
if pid > 0 {
|
||||
return self.get_process_open_files_size(pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Get size of files opened by a process
|
||||
fn get_process_open_files_size(&self, pid: u32) -> Option<f32> {
|
||||
let mut total_size = 0u64;
|
||||
let mut found_any = false;
|
||||
|
||||
// Check /proc/PID/fd/ for open file descriptors
|
||||
let fd_dir = format!("/proc/{}/fd", pid);
|
||||
if let Ok(entries) = std::fs::read_dir(&fd_dir) {
|
||||
for entry in entries.flatten() {
|
||||
if let Ok(link) = std::fs::read_link(entry.path()) {
|
||||
if let Some(path_str) = link.to_str() {
|
||||
// Skip special files, focus on regular files
|
||||
if !path_str.starts_with("/dev/")
|
||||
&& !path_str.starts_with("/proc/")
|
||||
&& !path_str.starts_with("[")
|
||||
{
|
||||
if let Ok(metadata) = std::fs::metadata(&link) {
|
||||
total_size += metadata.len();
|
||||
found_any = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if found_any {
|
||||
let size_gb = total_size as f32 / (1024.0 * 1024.0 * 1024.0);
|
||||
Some(size_gb.max(0.001))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Estimate disk usage based on service type and memory usage
|
||||
fn estimate_service_disk_usage(&self, service: &str) -> Option<f32> {
|
||||
// Get memory usage to help estimate disk usage
|
||||
let memory_mb = self.get_service_memory(service).unwrap_or(0.0);
|
||||
|
||||
let estimated_gb = match service {
|
||||
// Database services typically have significant disk usage
|
||||
s if s.contains("mysql") || s.contains("postgres") || s.contains("redis") => {
|
||||
(memory_mb / 100.0).max(0.1) // Estimate based on memory
|
||||
}
|
||||
// Web services and applications
|
||||
s if s.contains("nginx") || s.contains("apache") => 0.05, // ~50MB for configs/logs
|
||||
s if s.contains("gitea") => (memory_mb / 50.0).max(0.5), // Code repositories
|
||||
s if s.contains("docker") => 1.0, // Docker has significant overhead
|
||||
// System services
|
||||
s if s.contains("ssh") || s.contains("postfix") => 0.01, // ~10MB for configs/logs
|
||||
// Default small footprint
|
||||
_ => 0.005, // ~5MB minimum
|
||||
};
|
||||
|
||||
Some(estimated_gb)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -660,7 +445,7 @@ impl Collector for SystemdCollector {
|
||||
}
|
||||
|
||||
// Service disk usage (comprehensive detection)
|
||||
if let Some(disk_gb) = self.get_comprehensive_service_disk_usage(service) {
|
||||
if let Some(disk_gb) = self.get_service_disk_usage(service) {
|
||||
metrics.push(Metric {
|
||||
name: format!("service_{}_disk_gb", service),
|
||||
value: MetricValue::Float(disk_gb),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user