diagnostics: panic hook + frontend error capture + Settings → About diagnostic report
THE BRIEF: For the friends beta we need verbal feedback AND technical feedback — some bugs the user cannot describe but a stack trace can. Built it in two layers, with Layer 3 deferred until there is real volume. PRIVACY POSTURE (matches Kon's local-first positioning): - All capture is to disk only. Nothing is transmitted. - The manual report bundler shows the user exactly what would be shared and lets them choose to copy / save it. - No remote endpoint, no Sentry, no opt-out telemetry. LAYER 1 — Always-on local capture: - Rust panic hook (commands/diagnostics.rs::install_panic_hook) writes each panic to ~/.kon/crashes/<unix-ts>-<short-id>.crash. Captures thread name, OS/arch, RUST_BACKTRACE state, and the panic info. Installed before tauri::Builder so it catches setup-time panics too. - Frontend window.onerror + window.unhandledrejection in src/routes/+layout.svelte forward to the new log_frontend_error Tauri command, which inserts into the existing error_log SQLite table. Best-effort: errors in the error handler itself are swallowed so logging can never crash the app. - Existing error_log table (migration v1) is now actually used — closes the TODO from crates/storage/src/database.rs:409. LAYER 2 — Manual diagnostic report: - Settings → About → Diagnostics section: Generate report → Preview → Copy / Save. - Report is plain markdown so it pastes cleanly into email, Discord, GitHub issues. Sections: app version + OS, sanitised settings JSON, recent error_log rows, last 5 crash dumps with previews, log file tail (8KB). - Preview is shown in a <details> with the full text the user can inspect before deciding to share. - Save writes to ~/.kon/diagnostic-reports/kon-diagnostic-<ts>.md. NEW FILES: - src-tauri/src/commands/diagnostics.rs (panic hook + 5 Tauri commands) CHANGES: - crates/storage/src/file_storage.rs: crashes_dir() + logs_dir() helpers - crates/storage/src/database.rs: ErrorLogRow + list_recent_errors() - crates/storage/src/lib.rs: re-exports - src-tauri/src/commands/mod.rs: + diagnostics module - src-tauri/src/lib.rs: install_panic_hook() before Builder; 5 new Tauri commands registered (log_frontend_error, list_recent_errors_command, list_crash_files, generate_diagnostic_report, save_diagnostic_report) - src/routes/+layout.svelte: installGlobalErrorCapture() at mount - src/lib/pages/SettingsPage.svelte: diagnostic state + buttons + preview block at the end of the About section cargo check -p kon-storage clean. Settings.svelte if/each balanced. LAYER 3 (deferred): - Optional opt-in remote reporting (self-hosted Sentry on Tartarus or similar). Not for friends beta. Open up after volume justifies it.
This commit is contained in:
@@ -428,6 +428,41 @@ pub async fn log_error(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Read the most recent N rows from `error_log`, newest first. Used by the
|
||||
/// diagnostic-report bundler in Settings → About.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ErrorLogRow {
|
||||
pub id: i64,
|
||||
pub timestamp: String,
|
||||
pub context: String,
|
||||
pub error_code: Option<String>,
|
||||
pub message: String,
|
||||
pub metadata: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn list_recent_errors(pool: &SqlitePool, limit: i64) -> Result<Vec<ErrorLogRow>> {
|
||||
let rows = sqlx::query(
|
||||
"SELECT id, timestamp, context, error_code, message, metadata \
|
||||
FROM error_log ORDER BY id DESC LIMIT ?",
|
||||
)
|
||||
.bind(limit)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(|e| KonError::StorageError(format!("Read error_log failed: {e}")))?;
|
||||
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.map(|r| ErrorLogRow {
|
||||
id: r.get("id"),
|
||||
timestamp: r.get("timestamp"),
|
||||
context: r.get("context"),
|
||||
error_code: r.get("error_code"),
|
||||
message: r.get("message"),
|
||||
metadata: r.get("metadata"),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -26,3 +26,16 @@ pub fn database_path() -> PathBuf {
|
||||
pub fn recordings_dir() -> PathBuf {
|
||||
app_data_dir().join("recordings")
|
||||
}
|
||||
|
||||
/// Directory for crash dumps written by the Rust panic hook.
|
||||
/// Each crash is a single text file: `<unix-ts>-<short-id>.crash`.
|
||||
/// Used by the diagnostic-report bundler in Settings → About.
|
||||
pub fn crashes_dir() -> PathBuf {
|
||||
app_data_dir().join("crashes")
|
||||
}
|
||||
|
||||
/// Directory for the rolling Rust log file (kon.log + rotated kon.log.1, etc).
|
||||
/// Subscribers configured in src-tauri/src/lib.rs at startup.
|
||||
pub fn logs_dir() -> PathBuf {
|
||||
app_data_dir().join("logs")
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ pub mod migrations;
|
||||
pub use database::{
|
||||
add_dictionary_entry, complete_task, count_transcripts, delete_dictionary_entry,
|
||||
delete_task, delete_transcript, get_setting, get_transcript, init, insert_task,
|
||||
insert_transcript, list_dictionary, list_tasks, list_transcripts, list_transcripts_paged,
|
||||
log_error, search_transcripts, set_setting, update_transcript, DictionaryEntry,
|
||||
InsertTranscriptParams, TaskRow, TranscriptRow,
|
||||
insert_transcript, list_dictionary, list_recent_errors, list_tasks, list_transcripts,
|
||||
list_transcripts_paged, log_error, search_transcripts, set_setting, update_transcript,
|
||||
DictionaryEntry, ErrorLogRow, InsertTranscriptParams, TaskRow, TranscriptRow,
|
||||
};
|
||||
pub use file_storage::{app_data_dir, database_path, recordings_dir};
|
||||
pub use file_storage::{app_data_dir, crashes_dir, database_path, logs_dir, recordings_dir};
|
||||
|
||||
Reference in New Issue
Block a user