diff --git a/src/config/mod.rs b/src/config/mod.rs index d7310c5..dc6b315 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -122,7 +122,7 @@ pub struct LLMProviderConfig { fn get_default_config_path() -> PathBuf { let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); - home.join(".config").join("picobot").join("config.json") + home.join(".picobot").join("config.json") } impl Config { @@ -192,7 +192,7 @@ pub enum ConfigError { impl std::fmt::Display for ConfigError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ConfigError::ConfigNotFound(path) => write!(f, "Config file not found: {}. Use CONFIG_PATH env var or place config in ~/.config/picobot/config.json", path), + ConfigError::ConfigNotFound(path) => write!(f, "Config file not found: {}. Use CONFIG_PATH env var or place config in ~/.picobot/config.json", path), ConfigError::AgentNotFound(name) => write!(f, "Agent not found: {}", name), ConfigError::ProviderNotFound(name) => write!(f, "Provider not found: {}", name), ConfigError::ModelNotFound(name) => write!(f, "Model not found: {}", name), diff --git a/src/gateway/mod.rs b/src/gateway/mod.rs index c3ddded..e75b9ba 100644 --- a/src/gateway/mod.rs +++ b/src/gateway/mod.rs @@ -8,6 +8,7 @@ use tokio::net::TcpListener; use crate::channels::{ChannelManager, manager::GatewayMessageHandler}; use crate::config::Config; +use crate::logging; use session::SessionManager; pub struct GatewayState { @@ -39,6 +40,10 @@ impl GatewayState { } pub async fn run(host: Option, port: Option) -> Result<(), Box> { + // Initialize logging + logging::init_logging(); + tracing::info!("Starting PicoBot Gateway"); + let state = Arc::new(GatewayState::new()?); // Get provider config for channels diff --git a/src/lib.rs b/src/lib.rs index 305f795..5e8f880 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,3 +7,4 @@ pub mod gateway; pub mod client; pub mod protocol; pub mod channels; +pub mod logging; diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..0973fe6 --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,80 @@ +use std::path::PathBuf; +use tracing_appender::rolling::{RollingFileAppender, Rotation}; +use tracing_subscriber::{ + fmt, + layer::SubscriberExt, + util::SubscriberInitExt, + EnvFilter, +}; + +/// Get the default log directory path: ~/.picobot/logs +pub fn get_default_log_dir() -> PathBuf { + let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + home.join(".picobot").join("logs") +} + +/// Get the default config file path: ~/.picobot/config.json +pub fn get_default_config_path() -> PathBuf { + let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")); + home.join(".picobot").join("config.json") +} + +/// Initialize logging with file appender +/// Logs are written to ~/.picobot/logs/ with daily rotation +pub fn init_logging() { + let log_dir = get_default_log_dir(); + + // Create log directory if it doesn't exist + if !log_dir.exists() { + if let Err(e) = std::fs::create_dir_all(&log_dir) { + eprintln!("Warning: Failed to create log directory {}: {}", log_dir.display(), e); + } + } + + // Create file appender with daily rotation + let file_appender = RollingFileAppender::new( + Rotation::DAILY, + &log_dir, + "picobot.log", + ); + + // Build subscriber with both console and file output + let env_filter = EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("info")); + + let file_layer = fmt::layer() + .with_writer(file_appender) + .with_ansi(false) + .with_target(true) + .with_level(true) + .with_thread_ids(true); + + let console_layer = fmt::layer() + .with_target(true) + .with_level(true); + + tracing_subscriber::registry() + .with(env_filter) + .with(console_layer) + .with(file_layer) + .init(); + + tracing::info!("Logging initialized. Log directory: {}", log_dir.display()); +} + +/// Initialize logging without file output (console only) +pub fn init_logging_console_only() { + let env_filter = EnvFilter::try_from_default_env() + .unwrap_or_else(|_| EnvFilter::new("info")); + + let console_layer = fmt::layer() + .with_target(true) + .with_level(true); + + tracing_subscriber::registry() + .with(env_filter) + .with(console_layer) + .init(); + + tracing::info!("Logging initialized (console only)"); +}