aboutsummaryrefslogtreecommitdiffstats
path: root/crates/atuin-ai/src/commands.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/atuin-ai/src/commands.rs')
-rw-r--r--crates/atuin-ai/src/commands.rs103
1 files changed, 82 insertions, 21 deletions
diff --git a/crates/atuin-ai/src/commands.rs b/crates/atuin-ai/src/commands.rs
index 7d5ca16b..b35cec9e 100644
--- a/crates/atuin-ai/src/commands.rs
+++ b/crates/atuin-ai/src/commands.rs
@@ -1,8 +1,13 @@
+use std::{
+ fs,
+ path::{Path, PathBuf},
+};
+
use atuin_common::shell::Shell;
use clap::{Parser, Subcommand};
-use tracing::Level;
+use eyre::Result;
+use tracing_appender::rolling::{RollingFileAppender, Rotation};
use tracing_subscriber::{EnvFilter, Layer, fmt, layer::SubscriberExt, util::SubscriberInitExt};
-
#[cfg(debug_assertions)]
pub mod debug_render;
@@ -72,7 +77,11 @@ enum Commands {
pub async fn run() -> eyre::Result<()> {
let cli = Cli::parse();
- init_tracing(cli.verbose);
+ let settings = atuin_client::settings::Settings::new()?;
+
+ if settings.logs.ai_enabled() {
+ init_logging(&settings, cli.verbose)?;
+ }
match cli.command {
Commands::Init { shell } => init::run(shell).await,
@@ -89,6 +98,7 @@ pub async fn run() -> eyre::Result<()> {
cli.api_token,
keep,
debug_state,
+ &settings,
)
.await
}
@@ -104,39 +114,90 @@ pub async fn run() -> eyre::Result<()> {
}
}
-fn init_tracing(verbose: bool) {
- let level = if verbose { Level::DEBUG } else { Level::INFO };
+pub fn detect_shell() -> Option<String> {
+ Some(Shell::current().to_string())
+}
- // Create env filter
- let env_filter = EnvFilter::from_default_env().add_directive(
- format!("atuin_ai={}", level.as_str().to_lowercase())
- .parse()
- .unwrap(),
- );
+/// Initializes logging for the AI commands.
+fn init_logging(settings: &atuin_client::settings::Settings, verbose: bool) -> Result<()> {
+ // ATUIN_LOG env var overrides config file level settings
+ let env_log_set = std::env::var("ATUIN_LOG").is_ok();
+
+ // Base filter from env var (or empty if not set)
+ let base_filter =
+ EnvFilter::from_env("ATUIN_LOG").add_directive("sqlx_sqlite::regexp=off".parse()?);
+
+ // Use config level unless ATUIN_LOG is set
+ let filter = if env_log_set {
+ base_filter
+ } else {
+ EnvFilter::default()
+ .add_directive(settings.logs.ai_level().as_directive().parse()?)
+ .add_directive("sqlx_sqlite::regexp=off".parse()?)
+ };
+
+ let log_dir = PathBuf::from(&settings.logs.dir);
+ fs::create_dir_all(&log_dir)?;
+
+ let filename = settings.logs.ai.file.clone();
+
+ // Clean up old log files
+ cleanup_old_logs(&log_dir, &filename, settings.logs.ai_retention());
- // Create console layer (only for verbose mode)
let console_layer = if verbose {
Some(
fmt::layer()
.with_writer(std::io::stderr)
.with_ansi(true)
.with_target(false)
- .with_filter(env_filter),
+ .with_filter(filter.clone()),
)
} else {
None
};
- // Initialize subscriber
- let subscriber = tracing_subscriber::registry();
+ let file_appender = RollingFileAppender::new(Rotation::DAILY, &log_dir, &filename);
- if let Some(console) = console_layer {
- subscriber.with(console).init();
+ let base = tracing_subscriber::registry().with(
+ fmt::layer()
+ .with_writer(file_appender)
+ .with_ansi(false)
+ .with_filter(filter),
+ );
+
+ if let Some(console_layer) = console_layer {
+ base.with(console_layer).init();
} else {
- subscriber.init();
- }
+ base.init();
+ };
+
+ Ok(())
}
-pub fn detect_shell() -> Option<String> {
- Some(Shell::current().to_string())
+fn cleanup_old_logs(log_dir: &Path, prefix: &str, retention_days: u64) {
+ let cutoff = std::time::SystemTime::now()
+ - std::time::Duration::from_secs(retention_days * 24 * 60 * 60);
+
+ let Ok(entries) = fs::read_dir(log_dir) else {
+ return;
+ };
+
+ for entry in entries.flatten() {
+ let path = entry.path();
+ let Some(name) = path.file_name().and_then(|n| n.to_str()) else {
+ continue;
+ };
+
+ // Match files like "search.log.2024-02-23" or "daemon.log.2024-02-23"
+ if !name.starts_with(prefix) || name == prefix {
+ continue;
+ }
+
+ if let Ok(metadata) = entry.metadata()
+ && let Ok(modified) = metadata.modified()
+ && modified < cutoff
+ {
+ let _ = fs::remove_file(&path);
+ }
+ }
}