feagi_observability/
cli.rs1use std::collections::HashMap;
10use std::env;
11
12use crate::KNOWN_CRATES;
13
14#[derive(Debug, Clone, Default)]
24pub struct CrateDebugFlags {
25 pub enabled_crates: HashMap<String, bool>,
26}
27
28impl CrateDebugFlags {
29 pub fn from_args<I>(args: I) -> Self
34 where
35 I: IntoIterator<Item = String>,
36 {
37 let mut enabled_crates = HashMap::new();
38 let mut debug_all = false;
39
40 for arg in args {
41 if arg == "--debug-all" {
42 debug_all = true;
43 continue;
44 }
45
46 if let Some(crate_name) = arg.strip_prefix("--debug-") {
47 enabled_crates.insert(crate_name.to_string(), true);
48 }
49 }
50
51 if debug_all {
52 for crate_name in KNOWN_CRATES {
54 enabled_crates.insert(crate_name.to_string(), true);
55 }
56 }
57
58 CrateDebugFlags { enabled_crates }
59 }
60
61 pub fn is_enabled(&self, crate_name: &str) -> bool {
63 self.enabled_crates.contains_key(crate_name)
64 }
65
66 pub fn enabled_crates(&self) -> Vec<&String> {
68 self.enabled_crates.keys().collect()
69 }
70
71 pub fn any_enabled(&self) -> bool {
73 !self.enabled_crates.is_empty()
74 }
75
76 pub fn log_level(&self, crate_name: &str) -> tracing::Level {
80 if self.is_enabled(crate_name) {
81 tracing::Level::DEBUG
82 } else {
83 tracing::Level::INFO
84 }
85 }
86
87 pub fn to_filter_string(&self) -> String {
92 if self.enabled_crates.is_empty() {
93 return "info".to_string();
94 }
95
96 let mut filters = Vec::new();
97 for crate_name in self.enabled_crates.keys() {
98 filters.push(format!("{}=debug", crate_name));
99 }
100 filters.push("info".to_string());
102 filters.join(",")
103 }
104}
105
106pub fn parse_debug_flags() -> CrateDebugFlags {
111 let mut flags = CrateDebugFlags::from_args(env::args());
112
113 if let Ok(env_var) = env::var("FEAGI_DEBUG") {
115 if env_var == "all" {
116 for crate_name in KNOWN_CRATES {
118 flags.enabled_crates.insert(crate_name.to_string(), true);
119 }
120 } else {
121 for crate_name in env_var.split(',') {
123 let crate_name = crate_name.trim();
124 if !crate_name.is_empty() {
125 flags.enabled_crates.insert(crate_name.to_string(), true);
126 }
127 }
128 }
129 }
130
131 flags
132}
133
134pub fn debug_flags_help() -> String {
136 format!(
137 r#"Debug Flags:
138 --debug-all Enable debug logging for all crates
139 --debug-{{crate-name}} Enable debug logging for specific crate
140
141Available crates:
142 {}
143
144Environment Variable:
145 FEAGI_DEBUG={{crate-name}}[,{{crate-name}}] Enable debug for crates (comma-separated)
146 FEAGI_DEBUG=all Enable debug for all crates
147
148Examples:
149 --debug-feagi-api
150 --debug-feagi-api --debug-feagi-burst-engine
151 FEAGI_DEBUG=feagi-api,feagi-burst-engine
152"#,
153 KNOWN_CRATES.join(", ")
154 )
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_single_crate_flag() {
163 let flags = CrateDebugFlags::from_args(vec!["--debug-feagi-api".to_string()]);
164 assert!(flags.is_enabled("feagi-api"));
165 assert!(!flags.is_enabled("feagi-burst-engine"));
166 }
167
168 #[test]
169 fn test_multiple_crate_flags() {
170 let flags = CrateDebugFlags::from_args(vec![
171 "--debug-feagi-api".to_string(),
172 "--debug-feagi-burst-engine".to_string(),
173 ]);
174 assert!(flags.is_enabled("feagi-api"));
175 assert!(flags.is_enabled("feagi-burst-engine"));
176 assert!(!flags.is_enabled("feagi-bdu"));
177 }
178
179 #[test]
180 fn test_debug_all() {
181 let flags = CrateDebugFlags::from_args(vec!["--debug-all".to_string()]);
182 for crate_name in KNOWN_CRATES {
183 assert!(
184 flags.is_enabled(crate_name),
185 "{} should be enabled",
186 crate_name
187 );
188 }
189 }
190
191 #[test]
192 fn test_filter_string() {
193 let flags = CrateDebugFlags::from_args(vec!["--debug-feagi-api".to_string()]);
194 let filter = flags.to_filter_string();
195 assert!(filter.contains("feagi-api=debug"));
196 }
197
198 #[test]
199 fn test_log_level() {
200 let flags = CrateDebugFlags::from_args(vec!["--debug-feagi-api".to_string()]);
201 assert_eq!(flags.log_level("feagi-api"), tracing::Level::DEBUG);
202 assert_eq!(flags.log_level("feagi-burst-engine"), tracing::Level::INFO);
203 }
204}