mirror of
https://github.com/openai/codex.git
synced 2026-11-11 06:31:37 +00:00
chore: improve client (#10149)
<img width="883" height="84" alt="Screenshot 2026-01-29 at 11 13 12" src="https://github.com/user-attachments/assets/090a2fec-94ed-4c0f-aee5-1653ed8b1439" />
This commit is contained in:
1 parent
6a06726af2
commit
4ba911d48c
7 files changed
+43
-33
No files matched your search
Generated
+1
@@ -1855,6 +1855,7 @@ dependencies = [
|
|||||||
"codex-otel",
|
"codex-otel",
|
||||||
"codex-protocol",
|
"codex-protocol",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"owo-colors",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ clap = { workspace = true, features = ["derive", "env"] }
|
|||||||
codex-otel = { workspace = true }
|
codex-otel = { workspace = true }
|
||||||
codex-protocol = { workspace = true }
|
codex-protocol = { workspace = true }
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
|
owo-colors = { workspace = true }
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
sqlx = { workspace = true }
|
sqlx = { workspace = true }
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ CREATE TABLE logs (
|
|||||||
level TEXT NOT NULL,
|
level TEXT NOT NULL,
|
||||||
target TEXT NOT NULL,
|
target TEXT NOT NULL,
|
||||||
message TEXT,
|
message TEXT,
|
||||||
fields_json TEXT NOT NULL,
|
|
||||||
module_path TEXT,
|
module_path TEXT,
|
||||||
file TEXT,
|
file TEXT,
|
||||||
line INTEGER
|
line INTEGER
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use chrono::Utc;
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use codex_state::STATE_DB_FILENAME;
|
use codex_state::STATE_DB_FILENAME;
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
|
use owo_colors::OwoColorize;
|
||||||
use sqlx::QueryBuilder;
|
use sqlx::QueryBuilder;
|
||||||
use sqlx::Row;
|
use sqlx::Row;
|
||||||
use sqlx::Sqlite;
|
use sqlx::Sqlite;
|
||||||
@@ -64,8 +65,6 @@ struct LogRow {
|
|||||||
ts_nanos: i64,
|
ts_nanos: i64,
|
||||||
level: String,
|
level: String,
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
fields_json: String,
|
|
||||||
module_path: Option<String>,
|
|
||||||
file: Option<String>,
|
file: Option<String>,
|
||||||
line: Option<i64>,
|
line: Option<i64>,
|
||||||
}
|
}
|
||||||
@@ -237,7 +236,7 @@ async fn fetch_max_id(pool: &SqlitePool, filter: &LogFilter) -> anyhow::Result<i
|
|||||||
|
|
||||||
fn base_select_builder<'a>() -> QueryBuilder<'a, Sqlite> {
|
fn base_select_builder<'a>() -> QueryBuilder<'a, Sqlite> {
|
||||||
QueryBuilder::<Sqlite>::new(
|
QueryBuilder::<Sqlite>::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 {
|
fn format_row(row: &LogRow) -> String {
|
||||||
let timestamp = format_timestamp(row.ts, row.ts_nanos);
|
let timestamp = format_timestamp(row.ts, row.ts_nanos);
|
||||||
|
let level = row.level.as_str();
|
||||||
let location = match (&row.file, row.line) {
|
let location = match (&row.file, row.line) {
|
||||||
(Some(file), Some(line)) => format!("{file}:{line}"),
|
(Some(file), Some(line)) => format!("{file}:{line}"),
|
||||||
(Some(file), None) => file.clone(),
|
(Some(file), None) => file.clone(),
|
||||||
_ => "-".to_string(),
|
_ => "-".to_string(),
|
||||||
};
|
};
|
||||||
let module = row.module_path.as_deref().unwrap_or("-");
|
|
||||||
let message = row.message.as_deref().unwrap_or("");
|
let message = row.message.as_deref().unwrap_or("");
|
||||||
let fields = row.fields_json.as_str();
|
let level_colored = color_level(level);
|
||||||
let level = row.level.as_str();
|
let timestamp_colored = timestamp.dimmed().to_string();
|
||||||
if fields == "{}" || fields.is_empty() {
|
let location_colored = location.dimmed().to_string();
|
||||||
return format!("{timestamp} {level:<5} [{module}] {location} - {message}");
|
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 {
|
fn format_timestamp(ts: i64, ts_nanos: i64) -> String {
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ use std::time::Duration;
|
|||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use std::time::UNIX_EPOCH;
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
use serde_json::Value;
|
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tracing::Event;
|
use tracing::Event;
|
||||||
use tracing::field::Field;
|
use tracing::field::Field;
|
||||||
@@ -54,7 +53,7 @@ where
|
|||||||
{
|
{
|
||||||
fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
|
fn on_event(&self, event: &Event<'_>, _ctx: tracing_subscriber::layer::Context<'_, S>) {
|
||||||
let metadata = event.metadata();
|
let metadata = event.metadata();
|
||||||
let mut visitor = JsonVisitor::default();
|
let mut visitor = MessageVisitor::default();
|
||||||
event.record(&mut visitor);
|
event.record(&mut visitor);
|
||||||
|
|
||||||
let now = SystemTime::now()
|
let now = SystemTime::now()
|
||||||
@@ -66,7 +65,6 @@ where
|
|||||||
level: metadata.level().as_str().to_string(),
|
level: metadata.level().as_str().to_string(),
|
||||||
target: metadata.target().to_string(),
|
target: metadata.target().to_string(),
|
||||||
message: visitor.message,
|
message: visitor.message,
|
||||||
fields_json: Value::Object(visitor.fields).to_string(),
|
|
||||||
module_path: metadata.module_path().map(ToString::to_string),
|
module_path: metadata.module_path().map(ToString::to_string),
|
||||||
file: metadata.file().map(ToString::to_string),
|
file: metadata.file().map(ToString::to_string),
|
||||||
line: metadata.line().map(|line| line as i64),
|
line: metadata.line().map(|line| line as i64),
|
||||||
@@ -114,49 +112,44 @@ async fn flush(state_db: &std::sync::Arc<StateRuntime>, buffer: &mut Vec<LogEntr
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct JsonVisitor {
|
struct MessageVisitor {
|
||||||
fields: serde_json::Map<String, Value>,
|
|
||||||
message: Option<String>,
|
message: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl JsonVisitor {
|
impl MessageVisitor {
|
||||||
fn record_value(&mut self, field: &Field, value: Value) {
|
fn record_message(&mut self, field: &Field, value: String) {
|
||||||
if field.name() == "message" && self.message.is_none() {
|
if field.name() == "message" && self.message.is_none() {
|
||||||
self.message = Some(match &value {
|
self.message = Some(value);
|
||||||
Value::String(message) => message.clone(),
|
|
||||||
_ => value.to_string(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
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) {
|
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) {
|
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) {
|
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) {
|
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) {
|
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)) {
|
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) {
|
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:?}"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,6 @@ pub struct LogEntry {
|
|||||||
pub level: String,
|
pub level: String,
|
||||||
pub target: String,
|
pub target: String,
|
||||||
pub message: Option<String>,
|
pub message: Option<String>,
|
||||||
pub fields_json: String,
|
|
||||||
pub module_path: Option<String>,
|
pub module_path: Option<String>,
|
||||||
pub file: Option<String>,
|
pub file: Option<String>,
|
||||||
pub line: Option<i64>,
|
pub line: Option<i64>,
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ FROM threads
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut builder = QueryBuilder::<Sqlite>::new(
|
let mut builder = QueryBuilder::<Sqlite>::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| {
|
builder.push_values(entries, |mut row, entry| {
|
||||||
row.push_bind(entry.ts)
|
row.push_bind(entry.ts)
|
||||||
@@ -222,7 +222,6 @@ FROM threads
|
|||||||
.push_bind(&entry.level)
|
.push_bind(&entry.level)
|
||||||
.push_bind(&entry.target)
|
.push_bind(&entry.target)
|
||||||
.push_bind(&entry.message)
|
.push_bind(&entry.message)
|
||||||
.push_bind(&entry.fields_json)
|
|
||||||
.push_bind(&entry.module_path)
|
.push_bind(&entry.module_path)
|
||||||
.push_bind(&entry.file)
|
.push_bind(&entry.file)
|
||||||
.push_bind(entry.line);
|
.push_bind(entry.line);
|
||||||
|
|||||||
Reference in new issue
Block a user