Compare commits

...

4 Commits

Author SHA1 Message Date
Ahmed Ibrahim
20cb5295d1 jsonl-logs-feedback 2025-12-04 17:00:34 -08:00
Ahmed Ibrahim
3b549acb3e jsonl-logs-feedback 2025-12-04 16:59:26 -08:00
Ahmed Ibrahim
b52d31f2c2 jsonl-logs-feedback 2025-12-04 16:56:35 -08:00
Ahmed Ibrahim
66f6fae194 jsonl-logs-feedback 2025-12-04 16:53:49 -08:00
7 changed files with 57 additions and 9 deletions

14
codex-rs/Cargo.lock generated
View File

@@ -1315,6 +1315,7 @@ dependencies = [
"codex-protocol",
"pretty_assertions",
"sentry",
"serde_json",
"tracing-subscriber",
]
@@ -6804,6 +6805,16 @@ dependencies = [
"tracing-core",
]
[[package]]
name = "tracing-serde"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1"
dependencies = [
"serde",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.20"
@@ -6814,12 +6825,15 @@ dependencies = [
"nu-ansi-term",
"once_cell",
"regex-automata",
"serde",
"serde_json",
"sharded-slab",
"smallvec",
"thread_local",
"tracing",
"tracing-core",
"tracing-log",
"tracing-serde",
]
[[package]]

View File

@@ -42,7 +42,7 @@ tokio = { workspace = true, features = [
"signal",
] }
tracing = { workspace = true, features = ["log"] }
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt"] }
tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "json"] }
opentelemetry-appender-tracing = { workspace = true }
uuid = { workspace = true, features = ["serde", "v7"] }

View File

@@ -106,6 +106,9 @@ pub async fn run_main(
.with_filter(EnvFilter::from_default_env());
let feedback_layer = tracing_subscriber::fmt::layer()
.json()
.flatten_event(true)
.with_current_span(false)
.with_writer(feedback.make_writer())
.with_ansi(false)
.with_target(false)

View File

@@ -12,3 +12,4 @@ tracing-subscriber = { workspace = true }
[dev-dependencies]
pretty_assertions = { workspace = true }
serde_json = { workspace = true }

View File

@@ -160,9 +160,18 @@ impl CodexLogSnapshot {
&self.bytes
}
fn logs_attachment(&self) -> sentry::protocol::Attachment {
sentry::protocol::Attachment {
buffer: self.bytes.clone(),
filename: String::from("codex-logs.jsonl"),
content_type: Some("text/plain".to_string()),
ty: None,
}
}
pub fn save_to_temp_file(&self) -> io::Result<PathBuf> {
let dir = std::env::temp_dir();
let filename = format!("codex-feedback-{}.log", self.thread_id);
let filename = format!("codex-feedback-{}.jsonl", self.thread_id);
let path = dir.join(filename);
fs::write(&path, self.as_bytes())?;
Ok(path)
@@ -243,12 +252,7 @@ impl CodexLogSnapshot {
envelope.add_item(EnvelopeItem::Event(event));
if include_logs {
envelope.add_item(EnvelopeItem::Attachment(Attachment {
buffer: self.bytes.clone(),
filename: String::from("codex-logs.log"),
content_type: Some("text/plain".to_string()),
ty: None,
}));
envelope.add_item(EnvelopeItem::Attachment(self.logs_attachment()));
}
if let Some((path, data)) = rollout_path.and_then(|p| fs::read(p).ok().map(|d| (p, d))) {
@@ -283,6 +287,7 @@ fn display_classification(classification: &str) -> String {
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn ring_buffer_drops_front_when_full() {
@@ -296,4 +301,26 @@ mod tests {
// Capacity 8: after writing 10 bytes, we should keep the last 8.
pretty_assertions::assert_eq!(std::str::from_utf8(snap.as_bytes()).unwrap(), "cdefghij");
}
#[test]
fn logs_attachment_preserves_filename_and_mime() {
let sample = b"{\"event\":\"test\"}\n{\"event\":\"test2\"}";
let snapshot = CodexLogSnapshot {
bytes: sample.to_vec(),
thread_id: "thread-123".to_string(),
};
let attachment = snapshot.logs_attachment();
pretty_assertions::assert_eq!(attachment.filename, "codex-logs.jsonl");
pretty_assertions::assert_eq!(attachment.content_type.as_deref(), Some("text/plain"));
pretty_assertions::assert_eq!(attachment.buffer, snapshot.bytes);
let parsed: Vec<_> = attachment
.buffer
.split(|b| *b == b'\n')
.map(|line| serde_json::from_slice::<serde_json::Value>(line).unwrap())
.collect();
pretty_assertions::assert_eq!(
parsed,
vec![json!({"event":"test"}), json!({"event":"test2"})]
);
}
}

View File

@@ -85,7 +85,7 @@ tokio-stream = { workspace = true }
toml = { workspace = true }
tracing = { workspace = true, features = ["log"] }
tracing-appender = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
tracing-subscriber = { workspace = true, features = ["env-filter", "json"] }
tree-sitter-bash = { workspace = true }
tree-sitter-highlight = { workspace = true }
unicode-segmentation = { workspace = true }

View File

@@ -277,6 +277,9 @@ pub async fn run_main(
let targets = Targets::new().with_default(tracing::Level::TRACE);
let feedback_layer = tracing_subscriber::fmt::layer()
.json()
.flatten_event(true)
.with_current_span(false)
.with_writer(feedback.make_writer())
.with_ansi(false)
.with_target(false)