Fix Windows crash from missing ICO decoder and add file logging
The app crashed on startup because the image-ico Tauri feature was missing, causing Image::from_bytes to panic when decoding icon.ico. Added the feature flag and replaced env_logger with fern to log to both stderr and <data_dir>/triple-c/logs/triple-c.log. A panic hook captures crash details with backtraces. Store init and icon loading errors are now logged before failing so future issues are diagnosable from the log file. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,7 +12,7 @@ name = "triple-c"
|
|||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2", features = ["image-png"] }
|
tauri = { version = "2", features = ["image-png", "image-ico"] }
|
||||||
tauri-plugin-store = "2"
|
tauri-plugin-store = "2"
|
||||||
tauri-plugin-dialog = "2"
|
tauri-plugin-dialog = "2"
|
||||||
tauri-plugin-opener = "2"
|
tauri-plugin-opener = "2"
|
||||||
@@ -26,7 +26,7 @@ uuid = { version = "1", features = ["v4"] }
|
|||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
dirs = "6"
|
dirs = "6"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
env_logger = "0.11"
|
fern = { version = "0.7", features = ["date-based"] }
|
||||||
tar = "0.4"
|
tar = "0.4"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
mod commands;
|
mod commands;
|
||||||
mod docker;
|
mod docker;
|
||||||
|
mod logging;
|
||||||
mod models;
|
mod models;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
|
||||||
@@ -15,22 +16,42 @@ pub struct AppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
env_logger::init();
|
logging::init();
|
||||||
|
|
||||||
|
let projects_store = match ProjectsStore::new() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to initialize projects store: {}", e);
|
||||||
|
panic!("Failed to initialize projects store: {}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let settings_store = match SettingsStore::new() {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to initialize settings store: {}", e);
|
||||||
|
panic!("Failed to initialize settings store: {}", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_store::Builder::default().build())
|
.plugin(tauri_plugin_store::Builder::default().build())
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.manage(AppState {
|
.manage(AppState {
|
||||||
projects_store: ProjectsStore::new().expect("Failed to initialize projects store"),
|
projects_store,
|
||||||
settings_store: SettingsStore::new().expect("Failed to initialize settings store"),
|
settings_store,
|
||||||
exec_manager: ExecSessionManager::new(),
|
exec_manager: ExecSessionManager::new(),
|
||||||
})
|
})
|
||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let icon = tauri::image::Image::from_bytes(include_bytes!("../icons/icon.ico"))
|
match tauri::image::Image::from_bytes(include_bytes!("../icons/icon.ico")) {
|
||||||
.expect("Failed to load window icon");
|
Ok(icon) => {
|
||||||
if let Some(window) = app.get_webview_window("main") {
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
let _ = window.set_icon(icon);
|
let _ = window.set_icon(icon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to load window icon: {}", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
73
app/src-tauri/src/logging.rs
Normal file
73
app/src-tauri/src/logging.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
/// Returns the log directory path: `<data_dir>/triple-c/logs/`
|
||||||
|
fn log_dir() -> Option<PathBuf> {
|
||||||
|
dirs::data_dir().map(|d| d.join("triple-c").join("logs"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialise logging to both stderr and a log file in the app data directory.
|
||||||
|
///
|
||||||
|
/// Logs are written to `<data_dir>/triple-c/logs/triple-c.log`.
|
||||||
|
/// A panic hook is also installed so that unexpected crashes are captured in the
|
||||||
|
/// same log file before the process exits.
|
||||||
|
pub fn init() {
|
||||||
|
let log_file_path = log_dir().and_then(|dir| {
|
||||||
|
fs::create_dir_all(&dir).ok()?;
|
||||||
|
let path = dir.join("triple-c.log");
|
||||||
|
fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&path)
|
||||||
|
.ok()
|
||||||
|
.map(|file| (path, file))
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut dispatch = fern::Dispatch::new()
|
||||||
|
.format(|out, message, record| {
|
||||||
|
out.finish(format_args!(
|
||||||
|
"[{} {} {}] {}",
|
||||||
|
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
|
||||||
|
record.level(),
|
||||||
|
record.target(),
|
||||||
|
message
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.level(log::LevelFilter::Info)
|
||||||
|
.chain(std::io::stderr());
|
||||||
|
|
||||||
|
if let Some((_path, file)) = &log_file_path {
|
||||||
|
dispatch = dispatch.chain(fern::Dispatch::new().chain(file.try_clone().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = dispatch.apply() {
|
||||||
|
eprintln!("Failed to initialise logger: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install a panic hook that writes to the log file so crashes are captured.
|
||||||
|
let crash_log_dir = log_dir();
|
||||||
|
std::panic::set_hook(Box::new(move |info| {
|
||||||
|
let msg = format!(
|
||||||
|
"[{} PANIC] {}\nBacktrace:\n{:?}",
|
||||||
|
chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
|
||||||
|
info,
|
||||||
|
std::backtrace::Backtrace::force_capture(),
|
||||||
|
);
|
||||||
|
eprintln!("{}", msg);
|
||||||
|
if let Some(ref dir) = crash_log_dir {
|
||||||
|
let crash_path = dir.join("triple-c.log");
|
||||||
|
let _ = fs::OpenOptions::new()
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(&crash_path)
|
||||||
|
.and_then(|mut f| {
|
||||||
|
use std::io::Write;
|
||||||
|
writeln!(f, "{}", msg)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
if let Some((ref path, _)) = log_file_path {
|
||||||
|
log::info!("Logging to {}", path.display());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user