Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cortex-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ tokio = { workspace = true, features = ["full"] }
anyhow = { workspace = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tracing-appender = { workspace = true }
dirs = { workspace = true }
serde_json = { workspace = true }
chrono = { workspace = true }
Expand Down
82 changes: 73 additions & 9 deletions cortex-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,10 @@ struct InteractiveArgs {
)]
log_level: LogLevel,

/// Enable debug logging (deprecated: use --log-level debug instead)
#[arg(long, hide = true)]
/// Enable debug mode: writes ALL trace-level logs to ./debug.txt
/// This captures all Rust logs, tracing events, and internal diagnostics
/// to help troubleshoot issues when the agent doesn't return expected output.
#[arg(long = "debug", help_heading = "Debugging")]
debug: bool,

/// Initial prompt (if no subcommand)
Expand Down Expand Up @@ -672,21 +674,83 @@ fn pre_main_hardening() {
cortex_process_hardening::pre_main_hardening();
}

/// Guard that ensures debug log file is properly flushed when dropped.
struct DebugLogGuard {
_guard: tracing_appender::non_blocking::WorkerGuard,
}

/// Set up debug file logging that writes ALL trace-level logs to ./debug.txt.
///
/// This function configures tracing to write comprehensive debug information
/// to a file in the current working directory. It captures:
/// - All Rust log levels (trace, debug, info, warn, error)
/// - Tracing spans and events from all crates
/// - Internal diagnostics for troubleshooting
///
/// Returns a guard that must be kept alive for the duration of logging.
fn setup_debug_file_logging() -> Result<DebugLogGuard> {
use std::fs::File;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;

let debug_file_path = std::env::current_dir()?.join("debug.txt");

// Create/truncate the debug file
let file = File::create(&debug_file_path).map_err(|e| {
anyhow::anyhow!(
"Failed to create debug.txt: {}. Check write permissions.",
e
)
})?;

// Use non-blocking writer for performance
let (non_blocking, guard) = tracing_appender::non_blocking(file);

// Create a formatting layer that writes to the file
let file_layer = tracing_subscriber::fmt::layer()
.with_writer(non_blocking)
.with_ansi(false) // No ANSI colors in file
.with_target(true) // Include module path
.with_thread_ids(true) // Include thread IDs
.with_thread_names(true) // Include thread names
.with_file(true) // Include source file
.with_line_number(true); // Include line numbers

// Initialize the subscriber with trace level (captures everything)
tracing_subscriber::registry()
.with(tracing_subscriber::EnvFilter::new("trace"))
.with(file_layer)
.init();

eprintln!(
"Debug mode enabled: logging to {}",
debug_file_path.display()
);

Ok(DebugLogGuard { _guard: guard })
}

#[tokio::main]
async fn main() -> Result<()> {
// Install Ctrl+C handler to restore terminal state before exiting
cortex_cli::install_cleanup_handler();

let cli = Cli::parse();

// Initialize logging only for non-TUI commands
// Initialize debug file logging if --debug flag is passed
// This writes ALL trace-level logs to ./debug.txt for troubleshooting
let _debug_guard = if cli.interactive.debug {
Some(setup_debug_file_logging()?)
} else {
None
};

// Initialize logging only for non-TUI commands (when not in debug mode)
// TUI mode has its own file-based logging to avoid stdout pollution
if cli.command.is_some() {
// Determine log level with precedence: --debug flag > --log-level > CORTEX_LOG_LEVEL env > default (info)
let log_level = if cli.interactive.debug {
// Deprecated --debug flag takes precedence for backward compatibility
LogLevel::Debug
} else if let Ok(env_level) = std::env::var("CORTEX_LOG_LEVEL") {
// Debug mode uses file logging configured above
if cli.command.is_some() && !cli.interactive.debug {
// Determine log level with precedence: --log-level > CORTEX_LOG_LEVEL env > default (info)
let log_level = if let Ok(env_level) = std::env::var("CORTEX_LOG_LEVEL") {
// Check environment variable
LogLevel::from_str_loose(&env_level).unwrap_or(cli.interactive.log_level)
} else {
Expand Down
Loading