diff --git a/Cargo.lock b/Cargo.lock index f4280d8..b84771c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "android-tzdata" version = "0.1.1" @@ -209,6 +218,29 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -325,6 +357,30 @@ version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +[[package]] +name = "jiff" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02000660d30638906021176af16b17498bd0d12813dbfe7b276d8bc7f3c0806" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde", +] + +[[package]] +name = "jiff-static" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c30758ddd7188629c6713fc45d1188af4f44c90582311d0c8d8c9907f60c48" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "js-sys" version = "0.3.77" @@ -467,6 +523,21 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "portable-atomic" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -505,6 +576,35 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustversion" version = "1.0.20" @@ -569,6 +669,8 @@ dependencies = [ "clap", "ctrlc", "dirs", + "env_logger", + "log", "notify", "num_cpus", "serde", diff --git a/Cargo.toml b/Cargo.toml index b88d13c..1f2979f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,5 @@ num_cpus = "1.16" ctrlc = "3.4" notify = { version = "8.0.0", features = ["serde"] } chrono = "0.4" +log = "0.4" +env_logger = "0.11" diff --git a/src/daemon.rs b/src/daemon.rs index 00b8b8d..b755226 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -4,6 +4,7 @@ use crate::conflict; use crate::core::SystemReport; use crate::engine; use crate::monitor; +use log::{LevelFilter, debug, error, info, warn}; use std::fs::File; use std::io::Write; use std::sync::Arc; @@ -19,20 +20,23 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box LevelFilter::Error, + LogLevel::Warning => LevelFilter::Warn, + LogLevel::Info => LevelFilter::Info, + LogLevel::Debug => LevelFilter::Debug, + }; + + // Update the log level filter if needed, without re-initializing the logger + log::set_max_level(level_filter); + + info!("Starting superfreq daemon..."); // Check for conflicts with other power management services let conflicts = conflict::detect_conflicts(); if conflicts.has_conflicts() { - log_message( - &effective_log_level, - LogLevel::Warning, - &conflicts.get_conflict_message(), - ); + warn!("{}", conflicts.get_conflict_message()); } // Create a flag that will be set to true when a signal is received @@ -41,27 +45,19 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box Result<(), Box { - log_message( - &effective_log_level, - LogLevel::Info, - &format!("Watching config file: {path}"), - ); + info!("Watching config file: {path}"); Some(watcher) } Err(e) => { - log_message( - &effective_log_level, - LogLevel::Warning, - &format!("Failed to initialize config file watcher: {e}"), - ); + warn!("Failed to initialize config file watcher: {e}"); None } } } else { - log_message( - &effective_log_level, - LogLevel::Warning, - "No config file found to watch for changes.", - ); + warn!("No config file found to watch for changes."); None }; @@ -119,11 +103,7 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box { - log_message( - &effective_log_level, - LogLevel::Info, - "Config file changed, updating configuration", - ); + info!("Config file changed, updating configuration"); config = new_config; // Reset polling interval after config change current_poll_interval = config.daemon.poll_interval_sec; @@ -131,11 +111,7 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box { - log_message( - &effective_log_level, - LogLevel::Error, - &format!("Error loading new configuration: {e}"), - ); + error!("Error loading new configuration: {e}"); // Continue with existing config } } @@ -144,11 +120,7 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box { - log_message( - &effective_log_level, - LogLevel::Debug, - "Collected system report, applying settings...", - ); + debug!("Collected system report, applying settings..."); // Determine current system state let current_state = determine_system_state(&report); @@ -156,40 +128,24 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box { - log_message( - &effective_log_level, - LogLevel::Debug, - "Successfully applied system settings", - ); + debug!("Successfully applied system settings"); // If system state changed or settings were applied differently, record the time if current_state != last_system_state { last_settings_change = Instant::now(); last_system_state = current_state.clone(); - log_message( - &effective_log_level, - LogLevel::Info, - &format!("System state changed to: {current_state:?}"), - ); + info!("System state changed to: {current_state:?}"); } } Err(e) => { - log_message( - &effective_log_level, - LogLevel::Error, - &format!("Error applying system settings: {e}"), - ); + error!("Error applying system settings: {e}"); } } @@ -202,25 +158,13 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box Result<(), Box { - log_message( - &effective_log_level, - LogLevel::Error, - &format!("Error collecting system report: {e}"), - ); + error!("Error collecting system report: {e}"); } } @@ -257,39 +193,22 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box matches!(msg_level, LogLevel::Error), - LogLevel::Warning => matches!(msg_level, LogLevel::Error | LogLevel::Warning), - LogLevel::Info => matches!( - msg_level, - LogLevel::Error | LogLevel::Warning | LogLevel::Info - ), - LogLevel::Debug => true, - }; - - if should_log { - match msg_level { - LogLevel::Error => eprintln!("ERROR: {message}"), - LogLevel::Warning => eprintln!("WARNING: {message}"), - LogLevel::Info => println!("INFO: {message}"), - LogLevel::Debug => println!("DEBUG: {message}"), - } +/// Initialize the logger with the appropriate level +const fn get_log_level_filter(log_level: LogLevel) -> LevelFilter { + match log_level { + LogLevel::Error => LevelFilter::Error, + LogLevel::Warning => LevelFilter::Warn, + LogLevel::Info => LevelFilter::Info, + LogLevel::Debug => LevelFilter::Debug, } } diff --git a/src/engine.rs b/src/engine.rs index 8611e47..c83d2f2 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -2,6 +2,7 @@ use crate::config::{AppConfig, ProfileConfig}; use crate::core::{OperationalMode, SystemReport, TurboSetting}; use crate::cpu::{self}; use crate::util::error::EngineError; +use log::{debug, info}; /// Determines the appropriate CPU profile based on power status or forced mode, /// and applies the settings using functions from the `cpu` module. @@ -12,8 +13,8 @@ pub fn determine_and_apply_settings( ) -> Result<(), EngineError> { // First, check if there's a governor override set if let Some(override_governor) = cpu::get_governor_override() { - println!( - "Engine: Governor override is active: '{}'. Setting governor.", + info!( + "Governor override is active: '{}'. Setting governor.", override_governor.trim() ); cpu::set_governor(override_governor.trim(), None)?; @@ -24,11 +25,11 @@ pub fn determine_and_apply_settings( if let Some(mode) = force_mode { match mode { OperationalMode::Powersave => { - println!("Engine: Forced Powersave mode selected. Applying 'battery' profile."); + info!("Forced Powersave mode selected. Applying 'battery' profile."); selected_profile_config = &config.battery; } OperationalMode::Performance => { - println!("Engine: Forced Performance mode selected. Applying 'charger' profile."); + info!("Forced Performance mode selected. Applying 'charger' profile."); selected_profile_config = &config.charger; } } @@ -41,28 +42,25 @@ pub fn determine_and_apply_settings( report.batteries.is_empty() || report.batteries.first().is_some_and(|b| b.ac_connected); if on_ac_power { - println!("Engine: On AC power, selecting Charger profile."); + info!("On AC power, selecting Charger profile."); selected_profile_config = &config.charger; } else { - println!("Engine: On Battery power, selecting Battery profile."); + info!("On Battery power, selecting Battery profile."); selected_profile_config = &config.battery; } } // Apply settings from selected_profile_config - // TODO: The println! statements are for temporary debugging/logging - // and we'd like to replace them with proper logging in the future. - if let Some(governor) = &selected_profile_config.governor { - println!("Engine: Setting governor to '{governor}'"); + info!("Setting governor to '{governor}'"); cpu::set_governor(governor, None)?; } if let Some(turbo_setting) = selected_profile_config.turbo { - println!("Engine: Setting turbo to '{turbo_setting:?}'"); + info!("Setting turbo to '{turbo_setting:?}'"); match turbo_setting { TurboSetting::Auto => { - println!("Engine: Managing turbo in auto mode based on system conditions"); + debug!("Managing turbo in auto mode based on system conditions"); manage_auto_turbo(report, selected_profile_config)?; } _ => cpu::set_turbo(turbo_setting)?, @@ -70,31 +68,31 @@ pub fn determine_and_apply_settings( } if let Some(epp) = &selected_profile_config.epp { - println!("Engine: Setting EPP to '{epp}'"); + info!("Setting EPP to '{epp}'"); cpu::set_epp(epp, None)?; } if let Some(epb) = &selected_profile_config.epb { - println!("Engine: Setting EPB to '{epb}'"); + info!("Setting EPB to '{epb}'"); cpu::set_epb(epb, None)?; } if let Some(min_freq) = selected_profile_config.min_freq_mhz { - println!("Engine: Setting min frequency to '{min_freq} MHz'"); + info!("Setting min frequency to '{min_freq} MHz'"); cpu::set_min_frequency(min_freq, None)?; } if let Some(max_freq) = selected_profile_config.max_freq_mhz { - println!("Engine: Setting max frequency to '{max_freq} MHz'"); + info!("Setting max frequency to '{max_freq} MHz'"); cpu::set_max_frequency(max_freq, None)?; } if let Some(profile) = &selected_profile_config.platform_profile { - println!("Engine: Setting platform profile to '{profile}'"); + info!("Setting platform profile to '{profile}'"); cpu::set_platform_profile(profile)?; } - println!("Engine: Profile settings applied successfully."); + debug!("Profile settings applied successfully."); Ok(()) } @@ -140,24 +138,24 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<() let enable_turbo = match (cpu_temp, avg_cpu_usage) { // If temperature is too high, disable turbo regardless of load (Some(temp), _) if temp >= turbo_settings.temp_threshold_high => { - println!( - "Engine: Auto Turbo: Disabled due to high temperature ({:.1}°C >= {:.1}°C)", + info!( + "Auto Turbo: Disabled due to high temperature ({:.1}°C >= {:.1}°C)", temp, turbo_settings.temp_threshold_high ); false } // If load is high enough, enable turbo (unless temp already caused it to disable) (_, Some(usage)) if usage >= turbo_settings.load_threshold_high => { - println!( - "Engine: Auto Turbo: Enabled due to high CPU load ({:.1}% >= {:.1}%)", + info!( + "Auto Turbo: Enabled due to high CPU load ({:.1}% >= {:.1}%)", usage, turbo_settings.load_threshold_high ); true } // If load is low, disable turbo (_, Some(usage)) if usage <= turbo_settings.load_threshold_low => { - println!( - "Engine: Auto Turbo: Disabled due to low CPU load ({:.1}% <= {:.1}%)", + info!( + "Auto Turbo: Disabled due to low CPU load ({:.1}% <= {:.1}%)", usage, turbo_settings.load_threshold_low ); false @@ -165,7 +163,7 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<() // In intermediate load scenarios or if we can't determine, leave turbo in current state // For now, we'll disable it as a safe default _ => { - println!("Engine: Auto Turbo: Disabled (default for indeterminate state)"); + info!("Auto Turbo: Disabled (default for indeterminate state)"); false } }; @@ -179,8 +177,8 @@ fn manage_auto_turbo(report: &SystemReport, config: &ProfileConfig) -> Result<() match cpu::set_turbo(turbo_setting) { Ok(()) => { - println!( - "Engine: Auto Turbo: Successfully set turbo to {}", + debug!( + "Auto Turbo: Successfully set turbo to {}", if enable_turbo { "enabled" } else { "disabled" } ); Ok(()) diff --git a/src/main.rs b/src/main.rs index c1e8126..9dfd67e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,6 +12,9 @@ use crate::config::AppConfig; use crate::core::{GovernorOverrideMode, TurboSetting}; use crate::util::error::ControlError; use clap::Parser; +use env_logger::Builder; +use log::{debug, error, info}; +use std::sync::Once; #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] @@ -77,6 +80,9 @@ enum Commands { } fn main() { + // Initialize logger once for the entire application + init_logger(); + let cli = Cli::parse(); // Load configuration first, as it might be needed by the monitor module @@ -84,7 +90,7 @@ fn main() { let config = match config::load_config() { Ok(cfg) => cfg, Err(e) => { - eprintln!("Error loading configuration: {e}. Using default values."); + error!("Error loading configuration: {e}. Using default values."); // Proceed with default config if loading fails, as per previous steps AppConfig::default() } @@ -100,12 +106,12 @@ fn main() { let total_width = title_len + 8; // 8 is for padding (4 on each side) let separator = "═".repeat(total_width); - println!("\n╔{}╗", separator); + println!("\n╔{separator}╗"); // Calculate centering - println!("║ {} ║", title); + println!("║ {title} ║"); - println!("╚{}╝", separator); + println!("╚{separator}╝"); }; format_section("System Information"); @@ -164,7 +170,7 @@ fn main() { "CPU Temperature: {}", report.cpu_global.average_temperature_celsius.map_or_else( || "N/A (No sensor detected)".to_string(), - |t| format!("{:.1}°C", t) + |t| format!("{t:.1}°C") ) ); @@ -205,9 +211,9 @@ fn main() { let max_freq = core_info.max_frequency_mhz.unwrap_or(0); if freq > max_freq && max_freq > 0 { // Special format for boosted frequencies - format!("{}*", freq) + format!("{freq}*") } else { - format!("{}", freq) + format!("{freq}") } } None => "N/A".to_string(), @@ -234,13 +240,13 @@ fn main() { "{}%", core_info .usage_percent - .map_or_else(|| "N/A".to_string(), |f| format!("{:.1}", f)) + .map_or_else(|| "N/A".to_string(), |f| format!("{f:.1}")) ), format!( "{}°C", core_info .temperature_celsius - .map_or_else(|| "N/A".to_string(), |f| format!("{:.1}", f)) + .map_or_else(|| "N/A".to_string(), |f| format!("{f:.1}")) ), width = max_core_id_len ); @@ -355,24 +361,24 @@ fn main() { Some(Commands::Daemon { verbose }) => daemon::run_daemon(config, verbose), Some(Commands::Debug) => cli::debug::run_debug(&config), None => { - println!("Welcome to superfreq! Use --help for commands."); - println!("Current effective configuration: {config:?}"); + info!("Welcome to superfreq! Use --help for commands."); + debug!("Current effective configuration: {config:?}"); Ok(()) } }; if let Err(e) = command_result { - eprintln!("Error executing command: {e}"); + error!("Error executing command: {e}"); if let Some(source) = e.source() { - eprintln!("Caused by: {source}"); + error!("Caused by: {source}"); } - // TODO: Consider specific error handling for PermissionDenied from cpu here - // For example, check if e.downcast_ref::() matches PermissionDenied + // TODO: Consider specific error handling for PermissionDenied from the cpu module here. + // For example, check if `e.downcast_ref::()` matches `PermissionDenied` // and print a more specific message like "Try running with sudo." // We'll revisit this in the future once CPU logic is more stable. if let Some(control_error) = e.downcast_ref::() { if matches!(control_error, ControlError::PermissionDenied(_)) { - eprintln!( + error!( "Hint: This operation may require administrator privileges (e.g., run with sudo)." ); } @@ -381,3 +387,20 @@ fn main() { std::process::exit(1); } } + +/// Initialize the logger for the entire application +static LOGGER_INIT: Once = Once::new(); +fn init_logger() { + LOGGER_INIT.call_once(|| { + // Set default log level based on environment or default to Info + let env_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()); + + Builder::new() + .parse_filters(&env_log) + .format_timestamp(None) + .format_module_path(false) + .init(); + + debug!("Logger initialized with RUST_LOG={env_log}"); + }); +}