Add logging to ask app: save questions, SQLs, success/error status, and timestamps to logs/log.json
This commit is contained in:
3
ask/Cargo.lock
generated
3
ask/Cargo.lock
generated
@@ -246,6 +246,7 @@ name = "ask"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"crossterm",
|
||||
"dotenvy",
|
||||
"duckdb",
|
||||
@@ -420,7 +421,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
|
||||
dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ serde_json = "1"
|
||||
duckdb = { version = "1", features = ["bundled"] }
|
||||
dotenvy = "0.15"
|
||||
anyhow = "1"
|
||||
chrono = "0.4"
|
||||
syntect = "5"
|
||||
ratatui = "0.29"
|
||||
crossterm = { version = "0.28", features = ["event-stream"] }
|
||||
|
||||
@@ -16,6 +16,7 @@ use ratatui::{
|
||||
widgets::{Block, Borders, Gauge, Paragraph, Row, Table, TableState, Wrap},
|
||||
Frame, Terminal,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use serde_json::{json, Value};
|
||||
use std::{
|
||||
env, fs,
|
||||
@@ -125,6 +126,35 @@ fn fmt_timer(d: Duration) -> String {
|
||||
format!("{:02}:{:02}s", d.as_secs() / 60, d.as_secs() % 60)
|
||||
}
|
||||
|
||||
fn log_question(question: &str, sql: &str, success: bool, error: Option<&str>) {
|
||||
let log_dir = std::path::Path::new("logs");
|
||||
if !log_dir.exists() {
|
||||
std::fs::create_dir_all(log_dir).ok();
|
||||
}
|
||||
let log_file = log_dir.join("log.json");
|
||||
|
||||
let timestamp = Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string();
|
||||
|
||||
let entry = serde_json::json!({
|
||||
"timestamp": timestamp,
|
||||
"question": question,
|
||||
"sql": sql,
|
||||
"success": success,
|
||||
"error": error,
|
||||
});
|
||||
|
||||
let mut entries: Vec<Value> = if log_file.exists() {
|
||||
let content = std::fs::read_to_string(&log_file).unwrap_or_else(|_| "[]".to_string());
|
||||
serde_json::from_str(&content).unwrap_or_else(|_| vec![])
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
entries.push(entry);
|
||||
let json = serde_json::to_string_pretty(&entries).unwrap_or_else(|_| "[]".to_string());
|
||||
std::fs::write(&log_file, json).ok();
|
||||
}
|
||||
|
||||
fn wrap_text(text: &str, max_width: usize) -> Vec<String> {
|
||||
if max_width == 0 {
|
||||
return vec![text.to_string()];
|
||||
@@ -209,16 +239,21 @@ fn spawn_worker(
|
||||
std::thread::spawn(
|
||||
move || match ask_model(&question, &schema, &model, &prompt_file) {
|
||||
Err(e) => {
|
||||
tx.send(WorkerMsg::SqlError(format!("{:#}", e))).ok();
|
||||
let err = format!("{:#}", e);
|
||||
log_question(&question, "", false, Some(&err));
|
||||
tx.send(WorkerMsg::SqlError(err)).ok();
|
||||
}
|
||||
Ok(sql) => {
|
||||
tx.send(WorkerMsg::SqlReady(sql.clone())).ok();
|
||||
match run_query(&db_file, &sql) {
|
||||
Ok((cols, rows)) => {
|
||||
log_question(&question, &sql, true, None);
|
||||
tx.send(WorkerMsg::QueryOk(cols, rows)).ok();
|
||||
}
|
||||
Err(e) => {
|
||||
tx.send(WorkerMsg::QueryError(format!("{:#}", e))).ok();
|
||||
let err = format!("{:#}", e);
|
||||
log_question(&question, &sql, false, Some(&err));
|
||||
tx.send(WorkerMsg::QueryError(err)).ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user