use clap::Args;
use std::path::{Path, PathBuf};
#[derive(Args, Debug)]
pub struct LogsArgs {
#[arg(long)]
pub all: bool,
#[arg(long, short)]
pub follow: bool,
#[arg(long, short, default_value = "100")]
pub lines: usize,
#[arg(long)]
pub errors_only: bool,
}
pub async fn execute(args: &LogsArgs) -> Result<(), anyhow::Error> {
let current_dir = std::env::current_dir()?;
if args.all {
show_all_project_logs(¤t_dir).await
} else {
show_current_project_logs(¤t_dir, args).await
}
}
async fn show_current_project_logs(base_dir: &Path, args: &LogsArgs) -> Result<(), anyhow::Error> {
use octocode::mcp::logging::get_all_log_directories;
let log_dirs = get_all_log_directories(base_dir)?;
if log_dirs.is_empty() {
println!("No MCP server logs found for this project.");
println!("Logs are created when the MCP server is started.");
return Ok(());
}
let latest_log_dir = &log_dirs[0];
println!("Showing logs from: {}", latest_log_dir.display());
show_logs_from_directory(latest_log_dir, args).await
}
async fn show_all_project_logs(base_dir: &Path) -> Result<(), anyhow::Error> {
use octocode::mcp::logging::print_log_directories;
println!("All MCP Server Log Directories:");
print_log_directories(base_dir)?;
Ok(())
}
async fn show_logs_from_directory(log_dir: &PathBuf, args: &LogsArgs) -> Result<(), anyhow::Error> {
use std::process::Command;
let mut log_files: Vec<_> = std::fs::read_dir(log_dir)?
.filter_map(|entry| {
let path = entry.ok()?.path();
if path.extension().and_then(|s| s.to_str()) == Some("log")
|| path
.file_name()
.and_then(|s| s.to_str())
.map(|s| s.contains("mcp_server"))
.unwrap_or(false)
{
Some(path)
} else {
None
}
})
.collect();
if log_files.is_empty() {
println!("No log files found in {}", log_dir.display());
return Ok(());
}
log_files.sort_by(|a, b| {
let a_time = a
.metadata()
.and_then(|m| m.modified())
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
let b_time = b
.metadata()
.and_then(|m| m.modified())
.unwrap_or(std::time::SystemTime::UNIX_EPOCH);
b_time.cmp(&a_time)
});
let log_file = &log_files[0];
println!("Reading from: {}", log_file.display());
if args.follow {
let mut cmd = Command::new("tail");
cmd.arg("-f")
.arg("-n")
.arg(args.lines.to_string())
.arg(log_file);
if args.errors_only {
cmd.arg("|").arg("grep").arg("-i").arg("error");
}
let status = cmd.status()?;
if !status.success() {
eprintln!("Failed to tail log file");
}
} else {
let content = std::fs::read_to_string(log_file)?;
let lines: Vec<&str> = content.lines().collect();
let start_idx = if lines.len() > args.lines {
lines.len() - args.lines
} else {
0
};
for line in &lines[start_idx..] {
if args.errors_only {
if line.to_lowercase().contains("error") || line.to_lowercase().contains("critical")
{
println!("{}", line);
}
} else {
println!("{}", line);
}
}
}
Ok(())
}