@@ -11,7 +11,8 @@ use color_eyre::Result;
11use dialoguer::Confirm;
11use dialoguer::Confirm;
12use time::OffsetDateTime;
12use time::OffsetDateTime;
13use time::format_description::well_known::Rfc3339;
13use time::format_description::well_known::Rfc3339;
14−use tracing_subscriber::EnvFilter;
14+use tracing_subscriber::filter::LevelFilter;
15+use tracing_subscriber::{EnvFilter, fmt, prelude::*};
16use vibebox::tui::{AppState, VmInfo};
17use vibebox::tui::{AppState, VmInfo};
17use vibebox::{
18use vibebox::{
@@ -39,11 +40,11 @@ enum Command {
41fn main() -> Result<()> {
42fn main() -> Result<()> {
43 color_eyre::install()?;
43 color_eyre::install()?;
44+ let cwd = env::current_dir().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
45 let cli = Cli::parse();
47 let cli = Cli::parse();
46− let cwd = env::current_dir().map_err(|err| color_eyre::eyre::eyre!(err.to_string()))?;
47 tracing::info!(cwd = %cwd.display(), "starting vibebox cli");
48 tracing::info!(cwd = %cwd.display(), "starting vibebox cli");
48 if let Some(command) = cli.command {
49 if let Some(command) = cli.command {
49 return handle_command(command, &cwd, cli.config.as_deref());
50 return handle_command(command, &cwd, cli.config.as_deref());
@@ -249,13 +250,58 @@ fn format_last_active(value: Option<&str>) -> String {
249 format!("{} day{} ago", days, if days == 1 { "" } else { "s" })
250 format!("{} day{} ago", days, if days == 1 { "" } else { "s" })
252−fn init_tracing() {
253+fn init_tracing(cwd: &Path) {
253 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
254 let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info"));
255+ let stderr_is_tty = std::io::stderr().is_terminal();
254− let ansi = std::io::stderr().is_terminal() && env::var("VIBEBOX_LOG_NO_COLOR").is_err();
256+ let ansi = stderr_is_tty && env::var("VIBEBOX_LOG_NO_COLOR").is_err();
255− let _ = tracing_subscriber::fmt()
257+ let file = instance::ensure_instance_dir(cwd)
256− .with_env_filter(filter)
258+ .ok()
259+ .and_then(|instance_dir| {
260+ let log_path = instance_dir.join("cli.log");
261+ std::fs::OpenOptions::new()
270+ let stderr_layer = fmt::layer()
274+ .with_writer(std::io::stderr)
275+ .with_filter(LevelFilter::INFO);
276+ let subscriber = tracing_subscriber::registry()
278+ .with(stderr_layer);
279+ if let Some(file) = file {
280+ let file_layer = fmt::layer()
257− .with_target(false)
281+ .with_target(false)
284+ let _ = subscriber.with(file_layer).try_init();
286+ let _ = subscriber.try_init();
289+ let stderr_layer = fmt::layer()
258− .with_ansi(ansi)
291+ .with_ansi(ansi)
259− .with_writer(std::io::stderr)
292+ .with_writer(std::io::stderr)
293+ .with_filter(LevelFilter::INFO);
294+ let subscriber = tracing_subscriber::registry()
296+ .with(stderr_layer);
297+ if let Some(file) = file {
298+ let file_layer = fmt::layer()
302+ let _ = subscriber.with(file_layer).try_init();
260− .try_init();
304+ let _ = subscriber.try_init();