save request payload on any API error, not just timeouts
Serialize request JSON before send_and_check so it's available for both HTTP errors and stream errors. Extracted save logic into save_failed_request helper on SseReader. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
64dbcbf061
commit
e0a54a3b43
2 changed files with 40 additions and 22 deletions
|
|
@ -192,6 +192,7 @@ pub(crate) async fn send_and_check(
|
|||
extra_headers: &[(&str, &str)],
|
||||
ui_tx: &UiSender,
|
||||
debug_label: &str,
|
||||
request_json: Option<&str>,
|
||||
) -> Result<reqwest::Response> {
|
||||
let debug = std::env::var("POC_DEBUG").is_ok();
|
||||
let start = Instant::now();
|
||||
|
|
@ -262,6 +263,18 @@ pub(crate) async fn send_and_check(
|
|||
url,
|
||||
&body[..body.len().min(500)]
|
||||
)));
|
||||
if let Some(json) = request_json {
|
||||
let log_dir = dirs::home_dir()
|
||||
.unwrap_or_default()
|
||||
.join(".consciousness/logs");
|
||||
let ts = chrono::Local::now().format("%Y%m%dT%H%M%S");
|
||||
let path = log_dir.join(format!("failed-request-{}.json", ts));
|
||||
if std::fs::write(&path, json).is_ok() {
|
||||
let _ = ui_tx.send(UiMessage::Debug(format!(
|
||||
"saved failed request to {} (HTTP {})", path.display(), status
|
||||
)));
|
||||
}
|
||||
}
|
||||
anyhow::bail!("HTTP {} ({}): {}", status, url, &body[..body.len().min(1000)]);
|
||||
}
|
||||
|
||||
|
|
@ -291,8 +304,8 @@ pub(crate) struct SseReader {
|
|||
debug: bool,
|
||||
ui_tx: UiSender,
|
||||
done: bool,
|
||||
/// Serialized request payload — saved to disk on timeout for replay debugging.
|
||||
request_json: Option<String>,
|
||||
/// Serialized request payload — saved to disk on errors for replay debugging.
|
||||
pub(crate) request_json: Option<String>,
|
||||
}
|
||||
|
||||
impl SseReader {
|
||||
|
|
@ -312,8 +325,19 @@ impl SseReader {
|
|||
}
|
||||
|
||||
/// Attach the serialized request payload for error diagnostics.
|
||||
pub fn set_request(&mut self, request: &impl serde::Serialize) {
|
||||
self.request_json = serde_json::to_string_pretty(request).ok();
|
||||
/// Save the request payload to disk for replay debugging.
|
||||
fn save_failed_request(&self, reason: &str) {
|
||||
let Some(ref json) = self.request_json else { return };
|
||||
let log_dir = dirs::home_dir()
|
||||
.unwrap_or_default()
|
||||
.join(".consciousness/logs");
|
||||
let ts = chrono::Local::now().format("%Y%m%dT%H%M%S");
|
||||
let path = log_dir.join(format!("failed-request-{}.json", ts));
|
||||
if std::fs::write(&path, json).is_ok() {
|
||||
let _ = self.ui_tx.send(UiMessage::Debug(format!(
|
||||
"saved failed request to {} ({})", path.display(), reason
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the next SSE event from the response stream.
|
||||
|
|
@ -374,27 +398,19 @@ impl SseReader {
|
|||
self.line_buf.push_str(&String::from_utf8_lossy(&chunk));
|
||||
}
|
||||
Ok(Ok(None)) => return Ok(None),
|
||||
Ok(Err(e)) => return Err(e.into()),
|
||||
Ok(Err(e)) => {
|
||||
self.save_failed_request(&format!("stream error: {}", e));
|
||||
return Err(e.into());
|
||||
}
|
||||
Err(_) => {
|
||||
let _ = self.ui_tx.send(UiMessage::Debug(format!(
|
||||
"TIMEOUT: no data for {}s ({} chunks, {:.1}s elapsed)",
|
||||
let msg = format!(
|
||||
"stream timeout: no data for {}s ({} chunks, {:.1}s elapsed)",
|
||||
self.chunk_timeout.as_secs(),
|
||||
self.chunks_received,
|
||||
self.stream_start.elapsed().as_secs_f64()
|
||||
)));
|
||||
// Save the request for replay debugging
|
||||
if let Some(ref json) = self.request_json {
|
||||
let log_dir = dirs::home_dir()
|
||||
.unwrap_or_default()
|
||||
.join(".consciousness/logs");
|
||||
let ts = chrono::Local::now().format("%Y%m%dT%H%M%S");
|
||||
let path = log_dir.join(format!("failed-request-{}.json", ts));
|
||||
if std::fs::write(&path, json).is_ok() {
|
||||
let _ = self.ui_tx.send(UiMessage::Debug(format!(
|
||||
"saved failed request to {}", path.display()
|
||||
)));
|
||||
}
|
||||
}
|
||||
);
|
||||
let _ = self.ui_tx.send(UiMessage::Debug(msg.clone()));
|
||||
self.save_failed_request(&msg);
|
||||
anyhow::bail!(
|
||||
"stream timeout: no data for {}s ({} chunks received)",
|
||||
self.chunk_timeout.as_secs(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue