rush_sync_server/commands/log_level/
manager.rs

1use log::LevelFilter;
2use std::sync::Mutex;
3
4pub struct LogLevelManager;
5
6static CURRENT_LOG_LEVEL: Mutex<LevelFilter> = Mutex::new(LevelFilter::Info);
7
8impl LogLevelManager {
9    pub fn show_status() -> String {
10        let current = Self::get_current_level();
11        let current_name = Self::level_to_name(current);
12        let current_number = Self::level_to_number(current);
13
14        format!(
15            "Current log level: {} ({})\n{}",
16            current_name,
17            current_number,
18            Self::show_help()
19        )
20    }
21
22    pub fn set_level_persistent(level_input: &str) -> Result<String, String> {
23        let level_filter = match level_input {
24            "1" => LevelFilter::Error,
25            "2" => LevelFilter::Warn,
26            "3" => LevelFilter::Info,
27            "4" => LevelFilter::Debug,
28            "5" => LevelFilter::Trace,
29            "error" | "ERROR" => LevelFilter::Error,
30            "warn" | "WARN" | "warning" => LevelFilter::Warn,
31            "info" | "INFO" => LevelFilter::Info,
32            "debug" | "DEBUG" => LevelFilter::Debug,
33            "trace" | "TRACE" => LevelFilter::Trace,
34            _ => {
35                return Err(format!("Invalid log level: {}", level_input));
36            }
37        };
38
39        Self::set_level_runtime(level_filter);
40
41        tokio::spawn(async move {
42            if let Err(e) = Self::save_to_config(level_filter).await {
43                log::warn!("Failed to save log level to config: {}", e);
44            } else {
45                // ✅ Config save completed (success or failure logged above)
46            }
47        });
48
49        let level_name = Self::level_to_name(level_filter);
50        let level_number = Self::level_to_number(level_filter);
51
52        Ok(format!(
53            "✅ Log level changed to: {} ({}) - Persistent saved",
54            level_name, level_number
55        ))
56    }
57
58    pub fn set_level_runtime(level_filter: LevelFilter) {
59        if let Ok(mut current) = CURRENT_LOG_LEVEL.lock() {
60            *current = level_filter;
61        }
62        log::set_max_level(level_filter);
63    }
64
65    pub async fn load_from_config() -> LevelFilter {
66        match crate::core::config::Config::load_with_messages(false).await {
67            Ok(config) => match Self::string_to_level_filter(&config.log_level) {
68                Ok(level) => level,
69                Err(_) => {
70                    log::warn!(
71                        "Invalid log level in config: '{}', using INFO",
72                        config.log_level
73                    );
74                    LevelFilter::Info
75                }
76            },
77            Err(_) => LevelFilter::Info,
78        }
79    }
80
81    async fn save_to_config(level_filter: LevelFilter) -> Result<(), String> {
82        match crate::core::config::Config::load_with_messages(false).await {
83            Ok(mut config) => {
84                config.log_level = Self::level_filter_to_string(level_filter);
85                config
86                    .save()
87                    .await
88                    .map_err(|e| format!("Config save error: {}", e))
89            }
90            Err(e) => Err(format!("Config load error: {}", e)),
91        }
92    }
93
94    pub fn get_current_level() -> LevelFilter {
95        if let Ok(current) = CURRENT_LOG_LEVEL.lock() {
96            *current
97        } else {
98            log::max_level()
99        }
100    }
101
102    pub fn init_with_level(level: LevelFilter) {
103        if let Ok(mut current) = CURRENT_LOG_LEVEL.lock() {
104            *current = level;
105        }
106        log::set_max_level(level);
107    }
108
109    pub fn show_help() -> String {
110        "Available log levels:\n  1 = ERROR   (Only critical errors)\n  2 = WARN    (Warnings and errors)\n  3 = INFO    (General information) [DEFAULT]\n  4 = DEBUG   (Debug information)\n  5 = TRACE   (Very detailed tracing)\n\nUsage:\n  log-level           Show current level\n  log-level 3         Set to INFO level\n  log-level DEBUG     Set to DEBUG level\n  log-level -h        Show this help".to_string()
111    }
112
113    fn string_to_level_filter(s: &str) -> Result<LevelFilter, ()> {
114        match s.to_lowercase().as_str() {
115            "error" | "1" => Ok(LevelFilter::Error),
116            "warn" | "warning" | "2" => Ok(LevelFilter::Warn),
117            "info" | "3" => Ok(LevelFilter::Info),
118            "debug" | "4" => Ok(LevelFilter::Debug),
119            "trace" | "5" => Ok(LevelFilter::Trace),
120            "off" | "0" => Ok(LevelFilter::Off),
121            _ => Err(()),
122        }
123    }
124
125    fn level_filter_to_string(level: LevelFilter) -> String {
126        match level {
127            LevelFilter::Error => "error".to_string(),
128            LevelFilter::Warn => "warn".to_string(),
129            LevelFilter::Info => "info".to_string(),
130            LevelFilter::Debug => "debug".to_string(),
131            LevelFilter::Trace => "trace".to_string(),
132            LevelFilter::Off => "off".to_string(),
133        }
134    }
135
136    fn level_to_name(level: LevelFilter) -> String {
137        match level {
138            LevelFilter::Error => "ERROR".to_string(),
139            LevelFilter::Warn => "WARN".to_string(),
140            LevelFilter::Info => "INFO".to_string(),
141            LevelFilter::Debug => "DEBUG".to_string(),
142            LevelFilter::Trace => "TRACE".to_string(),
143            LevelFilter::Off => "OFF".to_string(),
144        }
145    }
146
147    fn level_to_number(level: LevelFilter) -> String {
148        match level {
149            LevelFilter::Error => "1".to_string(),
150            LevelFilter::Warn => "2".to_string(),
151            LevelFilter::Info => "3".to_string(),
152            LevelFilter::Debug => "4".to_string(),
153            LevelFilter::Trace => "5".to_string(),
154            LevelFilter::Off => "0".to_string(),
155        }
156    }
157}