1use std::env;
2use std::fs::OpenOptions;
3use std::io::Write;
4use std::path::PathBuf;
5use tracing_subscriber::{fmt, EnvFilter};
6
7#[allow(dead_code)]
8pub fn init_logging() {
9 let home = env::var("HOME").unwrap_or_else(|_| ".".to_string());
11 let log_dir = PathBuf::from(home).join(".limit").join("logs");
12
13 let _ = std::fs::create_dir_all(&log_dir);
14
15 let log_path = log_dir.join("tui.log");
16
17 install_panic_hook(&log_path);
19
20 let file = OpenOptions::new().create(true).append(true).open(&log_path);
22
23 match file {
24 Ok(f) => {
25 let default_level = if cfg!(debug_assertions) {
26 "debug,limit_llm=debug,limit_agent=debug,limit_cli=debug,reqwest=warn,hyper=warn,h2=warn,ignore=warn,globset=warn,rustls=warn"
27 } else {
28 "warn,limit_llm=warn,limit_agent=warn,limit_cli=warn,reqwest=warn,hyper=warn,h2=warn,ignore=warn,globset=warn,rustls=warn"
29 };
30 let filter =
31 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(default_level));
32
33 fmt()
34 .with_writer(f)
35 .with_target(true)
36 .with_ansi(false)
37 .with_env_filter(filter)
38 .init();
39
40 tracing::info!("=== Logging initialized to {:?} ===", log_path);
41 }
42 Err(e) => {
43 eprintln!("Warning: Could not open log file {:?}: {}", log_path, e);
44 let default_level = if cfg!(debug_assertions) {
46 "debug"
47 } else {
48 "warn"
49 };
50 let filter =
51 EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(default_level));
52
53 fmt().with_env_filter(filter).init();
54 }
55 }
56}
57
58fn install_panic_hook(log_path: &std::path::Path) {
59 let log_path = log_path.to_path_buf();
60 std::panic::set_hook(Box::new(move |info| {
61 let timestamp = chrono::Local::now().format("%Y-%m-%dT%H:%M:%S%.6fZ");
62 let thread = std::thread::current()
63 .name()
64 .unwrap_or("unnamed")
65 .to_string();
66 let payload = info
67 .payload()
68 .downcast_ref::<&str>()
69 .map(|s| s.to_string())
70 .or_else(|| info.payload().downcast_ref::<String>().cloned())
71 .unwrap_or_else(|| "unknown panic".to_string());
72 let location = info
73 .location()
74 .map(|loc| format!("{}:{}", loc.file(), loc.line()))
75 .unwrap_or_else(|| "unknown location".to_string());
76
77 let msg = format!(
78 "{timestamp} PANIC [{thread}] {payload}\n at {location}\n",
79 timestamp = timestamp,
80 thread = thread,
81 payload = payload,
82 location = location,
83 );
84
85 if let Ok(mut f) = OpenOptions::new().create(true).append(true).open(&log_path) {
87 let _ = f.write_all(msg.as_bytes());
88 }
89 eprintln!("{}", msg);
91 }));
92}