From 4ba911d48c1f5c554bd447b35b42238ef3c1b419 Mon Sep 17 00:00:00 2001 From: jif-oai Date: Thu, 29 Jan 2026 11:25:22 +0100 Subject: [PATCH] chore: improve client (#10149) Screenshot 2026-01-29 at 11 13 12 --- codex-rs/Cargo.lock | 1 + codex-rs/state/Cargo.toml | 1 + codex-rs/state/migrations/0002_logs.sql | 1 - codex-rs/state/src/bin/logs_client.rs | 36 ++++++++++++++++++------- codex-rs/state/src/log_db.rs | 33 +++++++++-------------- codex-rs/state/src/model/log.rs | 1 - codex-rs/state/src/runtime.rs | 3 +-- 7 files changed, 43 insertions(+), 33 deletions(-) diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index f47e054ba6..4c321b3f62 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1855,6 +1855,7 @@ dependencies = [ "codex-otel", "codex-protocol", "dirs", + "owo-colors", "pretty_assertions", "serde", "serde_json", diff --git a/codex-rs/state/Cargo.toml b/codex-rs/state/Cargo.toml index a6e36a3e00..6019b5888b 100644 --- a/codex-rs/state/Cargo.toml +++ b/codex-rs/state/Cargo.toml @@ -11,6 +11,7 @@ clap = { workspace = true, features = ["derive", "env"] } codex-otel = { workspace = true } codex-protocol = { workspace = true } dirs = { workspace = true } +owo-colors = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } sqlx = { workspace = true } diff --git a/codex-rs/state/migrations/0002_logs.sql b/codex-rs/state/migrations/0002_logs.sql index 4a193500b9..b9a2c681d4 100644 --- a/codex-rs/state/migrations/0002_logs.sql +++ b/codex-rs/state/migrations/0002_logs.sql @@ -5,7 +5,6 @@ CREATE TABLE logs ( level TEXT NOT NULL, target TEXT NOT NULL, message TEXT, - fields_json TEXT NOT NULL, module_path TEXT, file TEXT, line INTEGER diff --git a/codex-rs/state/src/bin/logs_client.rs b/codex-rs/state/src/bin/logs_client.rs index 13bcb1571d..71acf67e85 100644 --- a/codex-rs/state/src/bin/logs_client.rs +++ b/codex-rs/state/src/bin/logs_client.rs @@ -9,6 +9,7 @@ use chrono::Utc; use clap::Parser; use codex_state::STATE_DB_FILENAME; use dirs::home_dir; +use owo_colors::OwoColorize; use sqlx::QueryBuilder; use sqlx::Row; use sqlx::Sqlite; @@ -64,8 +65,6 @@ struct LogRow { ts_nanos: i64, level: String, message: Option, - fields_json: String, - module_path: Option, file: Option, line: Option, } @@ -237,7 +236,7 @@ async fn fetch_max_id(pool: &SqlitePool, filter: &LogFilter) -> anyhow::Result() -> QueryBuilder<'a, Sqlite> { QueryBuilder::::new( - "SELECT id, ts, ts_nanos, level, message, fields_json, module_path, file, line FROM logs WHERE 1 = 1", + "SELECT id, ts, ts_nanos, level, message, file, line FROM logs WHERE 1 = 1", ) } @@ -269,19 +268,38 @@ fn push_filters<'a>(builder: &mut QueryBuilder<'a, Sqlite>, filter: &'a LogFilte fn format_row(row: &LogRow) -> String { let timestamp = format_timestamp(row.ts, row.ts_nanos); + let level = row.level.as_str(); let location = match (&row.file, row.line) { (Some(file), Some(line)) => format!("{file}:{line}"), (Some(file), None) => file.clone(), _ => "-".to_string(), }; - let module = row.module_path.as_deref().unwrap_or("-"); let message = row.message.as_deref().unwrap_or(""); - let fields = row.fields_json.as_str(); - let level = row.level.as_str(); - if fields == "{}" || fields.is_empty() { - return format!("{timestamp} {level:<5} [{module}] {location} - {message}"); + let level_colored = color_level(level); + let timestamp_colored = timestamp.dimmed().to_string(); + let location_colored = location.dimmed().to_string(); + let message_colored = message.bold().to_string(); + format!("{timestamp_colored} {level_colored} {location_colored} - {message_colored}") +} + +fn color_level(level: &str) -> String { + let padded = format!("{level:<5}"); + if level.eq_ignore_ascii_case("error") { + return padded.red().bold().to_string(); } - format!("{timestamp} {level:<5} [{module}] {location} - {message} {fields}") + if level.eq_ignore_ascii_case("warn") { + return padded.yellow().bold().to_string(); + } + if level.eq_ignore_ascii_case("info") { + return padded.green().bold().to_string(); + } + if level.eq_ignore_ascii_case("debug") { + return padded.blue().bold().to_string(); + } + if level.eq_ignore_ascii_case("trace") { + return padded.magenta().bold().to_string(); + } + padded.bold().to_string() } fn format_timestamp(ts: i64, ts_nanos: i64) -> String { diff --git a/codex-rs/state/src/log_db.rs b/codex-rs/state/src/log_db.rs index c03b7977d3..13f19d0baf 100644 --- a/codex-rs/state/src/log_db.rs +++ b/codex-rs/state/src/log_db.rs @@ -22,7 +22,6 @@ use std::time::Duration; use std::time::SystemTime; use std::time::UNIX_EPOCH; -use serde_json::Value; use tokio::sync::mpsc; use tracing::Event; use tracing::field::Field; @@ -54,7 +53,7 @@ where { fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) { let metadata = event.metadata(); - let mut visitor = JsonVisitor::default(); + let mut visitor = MessageVisitor::default(); event.record(&mut visitor); let now = SystemTime::now() @@ -66,7 +65,6 @@ where level: metadata.level().as_str().to_string(), target: metadata.target().to_string(), message: visitor.message, - fields_json: Value::Object(visitor.fields).to_string(), module_path: metadata.module_path().map(ToString::to_string), file: metadata.file().map(ToString::to_string), line: metadata.line().map(|line| line as i64), @@ -114,49 +112,44 @@ async fn flush(state_db: &std::sync::Arc, buffer: &mut Vec, +struct MessageVisitor { message: Option, } -impl JsonVisitor { - fn record_value(&mut self, field: &Field, value: Value) { +impl MessageVisitor { + fn record_message(&mut self, field: &Field, value: String) { if field.name() == "message" && self.message.is_none() { - self.message = Some(match &value { - Value::String(message) => message.clone(), - _ => value.to_string(), - }); + self.message = Some(value); } - self.fields.insert(field.name().to_string(), value); } } -impl Visit for JsonVisitor { +impl Visit for MessageVisitor { fn record_i64(&mut self, field: &Field, value: i64) { - self.record_value(field, Value::from(value)); + self.record_message(field, value.to_string()); } fn record_u64(&mut self, field: &Field, value: u64) { - self.record_value(field, Value::from(value)); + self.record_message(field, value.to_string()); } fn record_bool(&mut self, field: &Field, value: bool) { - self.record_value(field, Value::from(value)); + self.record_message(field, value.to_string()); } fn record_f64(&mut self, field: &Field, value: f64) { - self.record_value(field, Value::from(value)); + self.record_message(field, value.to_string()); } fn record_str(&mut self, field: &Field, value: &str) { - self.record_value(field, Value::from(value)); + self.record_message(field, value.to_string()); } fn record_error(&mut self, field: &Field, value: &(dyn std::error::Error + 'static)) { - self.record_value(field, Value::from(value.to_string())); + self.record_message(field, value.to_string()); } fn record_debug(&mut self, field: &Field, value: &dyn std::fmt::Debug) { - self.record_value(field, Value::from(format!("{value:?}"))); + self.record_message(field, format!("{value:?}")); } } diff --git a/codex-rs/state/src/model/log.rs b/codex-rs/state/src/model/log.rs index 5662a3725c..0fa4c0b38e 100644 --- a/codex-rs/state/src/model/log.rs +++ b/codex-rs/state/src/model/log.rs @@ -7,7 +7,6 @@ pub struct LogEntry { pub level: String, pub target: String, pub message: Option, - pub fields_json: String, pub module_path: Option, pub file: Option, pub line: Option, diff --git a/codex-rs/state/src/runtime.rs b/codex-rs/state/src/runtime.rs index 34add1347d..96b2d69122 100644 --- a/codex-rs/state/src/runtime.rs +++ b/codex-rs/state/src/runtime.rs @@ -214,7 +214,7 @@ FROM threads } let mut builder = QueryBuilder::::new( - "INSERT INTO logs (ts, ts_nanos, level, target, message, fields_json, module_path, file, line) ", + "INSERT INTO logs (ts, ts_nanos, level, target, message, module_path, file, line) ", ); builder.push_values(entries, |mut row, entry| { row.push_bind(entry.ts) @@ -222,7 +222,6 @@ FROM threads .push_bind(&entry.level) .push_bind(&entry.target) .push_bind(&entry.message) - .push_bind(&entry.fields_json) .push_bind(&entry.module_path) .push_bind(&entry.file) .push_bind(entry.line);