diff --git a/codex-rs/state/src/bin/logs_client.rs b/codex-rs/state/src/bin/logs_client.rs index 099b06164..6b0039ee9 100644 --- a/codex-rs/state/src/bin/logs_client.rs +++ b/codex-rs/state/src/bin/logs_client.rs @@ -61,6 +61,10 @@ struct Args { /// Poll interval in milliseconds. #[arg(long, default_value_t = 500)] poll_ms: u64, + + /// Show compact output with only time, level, and message. + #[arg(long)] + compact: bool, } #[derive(Debug, Clone)] @@ -86,7 +90,8 @@ async fn main() -> anyhow::Result<()> { .unwrap_or_else(|| PathBuf::from(".")); let runtime = StateRuntime::init(codex_home, "logs-client".to_string(), None).await?; - let mut last_id = print_backfill(runtime.as_ref(), &filter, args.backfill).await?; + let mut last_id = + print_backfill(runtime.as_ref(), &filter, args.backfill, args.compact).await?; if last_id == 0 { last_id = fetch_max_id(runtime.as_ref(), &filter).await?; } @@ -96,7 +101,7 @@ async fn main() -> anyhow::Result<()> { let rows = fetch_new_rows(runtime.as_ref(), &filter, last_id).await?; for row in rows { last_id = last_id.max(row.id); - println!("{}", format_row(&row)); + println!("{}", format_row(&row, args.compact)); } tokio::time::sleep(poll_interval).await; } @@ -178,6 +183,7 @@ async fn print_backfill( runtime: &StateRuntime, filter: &LogFilter, backfill: usize, + compact: bool, ) -> anyhow::Result { if backfill == 0 { return Ok(0); @@ -189,7 +195,7 @@ async fn print_backfill( let mut last_id = 0; for row in rows { last_id = last_id.max(row.id); - println!("{}", format_row(&row)); + println!("{}", format_row(&row, compact)); } Ok(last_id) } @@ -247,8 +253,8 @@ fn to_log_query( } } -fn format_row(row: &LogRow) -> String { - let timestamp = formatter::ts(row.ts, row.ts_nanos); +fn format_row(row: &LogRow, compact: bool) -> String { + let timestamp = formatter::ts(row.ts, row.ts_nanos, compact); let level = row.level.as_str(); let target = row.target.as_str(); let message = row.message.as_deref().unwrap_or(""); @@ -258,9 +264,13 @@ fn format_row(row: &LogRow) -> String { let thread_id_colored = thread_id.blue().dimmed().to_string(); let target_colored = target.dimmed().to_string(); let message_colored = heuristic_formatting(message); - format!( - "{timestamp_colored} {level_colored} [{thread_id_colored}] {target_colored} - {message_colored}" - ) + if compact { + format!("{timestamp_colored} {level_colored} {message_colored}") + } else { + format!( + "{timestamp_colored} {level_colored} [{thread_id_colored}] {target_colored} - {message_colored}" + ) + } } fn heuristic_formatting(message: &str) -> String { @@ -299,9 +309,10 @@ mod formatter { .join("\n") } - pub(super) fn ts(ts: i64, ts_nanos: i64) -> String { + pub(super) fn ts(ts: i64, ts_nanos: i64, compact: bool) -> String { let nanos = u32::try_from(ts_nanos).unwrap_or(0); match DateTime::::from_timestamp(ts, nanos) { + Some(dt) if compact => dt.format("%H:%M:%S").to_string(), Some(dt) => dt.to_rfc3339_opts(SecondsFormat::Millis, true), None => format!("{ts}.{ts_nanos:09}Z"), }