tenseleyflow/claudex / 89b9e84

Browse files

rust: daily file logging to ~/Library/Logs/claudex/

Authored by espadonne
SHA
89b9e849afbe8943f25969ac9ef8ad7e3b864c99
Parents
d8daf2f
Tree
74be941

3 changed files

StatusFile+-
M src-tauri/Cargo.lock 13 0
M src-tauri/Cargo.toml 1 0
M src-tauri/src/lib.rs 59 6
src-tauri/Cargo.lockmodified
@@ -375,6 +375,7 @@ dependencies = [
375375
  "thiserror 2.0.18",
376376
  "tokio",
377377
  "tracing",
378
+ "tracing-appender",
378379
  "tracing-subscriber",
379380
  "ts-rs",
380381
  "walkdir",
@@ -4288,6 +4289,18 @@ dependencies = [
42884289
  "tracing-core",
42894290
 ]
42904291
 
4292
+[[package]]
4293
+name = "tracing-appender"
4294
+version = "0.2.4"
4295
+source = "registry+https://github.com/rust-lang/crates.io-index"
4296
+checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
4297
+dependencies = [
4298
+ "crossbeam-channel",
4299
+ "thiserror 2.0.18",
4300
+ "time",
4301
+ "tracing-subscriber",
4302
+]
4303
+
42914304
 [[package]]
42924305
 name = "tracing-attributes"
42934306
 version = "0.1.31"
src-tauri/Cargo.tomlmodified
@@ -26,6 +26,7 @@ anyhow = "1"
2626
 chrono = { version = "0.4", features = ["serde"] }
2727
 tracing = "0.1"
2828
 tracing-subscriber = { version = "0.3", features = ["env-filter"] }
29
+tracing-appender = "0.2"
2930
 dirs = "5"
3031
 ts-rs = { version = "11", features = ["chrono-impl", "serde-compat", "serde-json-impl"] }
3132
 bincode = { version = "2", features = ["serde"] }
src-tauri/src/lib.rsmodified
@@ -8,16 +8,68 @@
88
 pub mod commands;
99
 pub mod core;
1010
 
11
+use std::path::PathBuf;
12
+use std::sync::OnceLock;
13
+
1114
 use tauri::Manager;
15
+use tracing_appender::non_blocking::WorkerGuard;
16
+use tracing_subscriber::layer::SubscriberExt;
17
+use tracing_subscriber::util::SubscriberInitExt;
18
+use tracing_subscriber::Layer;
19
+
20
+/// Holds the non-blocking appender's worker thread. Dropping the
21
+/// guard flushes pending log writes, so we stash it in a static
22
+/// `OnceLock` keyed to process lifetime.
23
+static LOG_GUARD: OnceLock<WorkerGuard> = OnceLock::new();
24
+
25
+/// Resolve the claudex log directory. macOS convention is
26
+/// `~/Library/Logs/claudex/`. We create it eagerly so the file
27
+/// appender has somewhere to write on first boot.
28
+fn log_dir() -> PathBuf {
29
+    let home = dirs::home_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
30
+    let dir = home.join("Library").join("Logs").join("claudex");
31
+    let _ = std::fs::create_dir_all(&dir);
32
+    dir
33
+}
34
+
35
+/// Two-layer tracing setup: one `fmt::Layer` writing ANSI to stdout
36
+/// for `pnpm tauri dev` users, and one writing plain text to a
37
+/// daily-rotated file under `~/Library/Logs/claudex/claudex.log`.
38
+/// Both layers share the same env filter (default
39
+/// `claudex=debug,warn`) so silencing works from either direction.
40
+fn init_logging() {
41
+    let filter = tracing_subscriber::EnvFilter::try_from_default_env()
42
+        .unwrap_or_else(|_| "claudex=debug,warn".into());
43
+
44
+    let file_appender = tracing_appender::rolling::daily(log_dir(), "claudex.log");
45
+    let (non_blocking, guard) = tracing_appender::non_blocking(file_appender);
46
+    // Keep the worker alive for the process lifetime.
47
+    let _ = LOG_GUARD.set(guard);
48
+
49
+    let stdout_layer = tracing_subscriber::fmt::layer()
50
+        .with_writer(std::io::stdout)
51
+        .with_target(true)
52
+        .with_filter(filter);
53
+
54
+    let file_filter = tracing_subscriber::EnvFilter::try_from_default_env()
55
+        .unwrap_or_else(|_| "claudex=debug,warn".into());
56
+    let file_layer = tracing_subscriber::fmt::layer()
57
+        .with_writer(non_blocking)
58
+        .with_ansi(false)
59
+        .with_target(true)
60
+        .with_filter(file_filter);
61
+
62
+    tracing_subscriber::registry()
63
+        .with(stdout_layer)
64
+        .with(file_layer)
65
+        .init();
66
+
67
+    tracing::info!(log_dir = %log_dir().display(), "claudex log file ready");
68
+}
1269
 
1370
 #[cfg_attr(mobile, tauri::mobile_entry_point)]
1471
 pub fn run() {
15
-    tracing_subscriber::fmt()
16
-        .with_env_filter(
17
-            tracing_subscriber::EnvFilter::try_from_default_env()
18
-                .unwrap_or_else(|_| "claudex=info,warn".into()),
19
-        )
20
-        .init();
72
+    init_logging();
2173
 
2274
     tauri::Builder::default()
2375
         .setup(|app| {
@@ -52,6 +104,7 @@ pub fn run() {
52104
             commands::close_pty,
53105
             commands::get_pty_buffer,
54106
             commands::list_ptys,
107
+            commands::log_frontend,
55108
         ])
56109
         .run(tauri::generate_context!())
57110
         .expect("error while running claudex");