Просмотр исходного кода

app/logger: make logfile also catch panics and java exceptions

darkfi 1 неделя назад
Родитель
Сommit
11602700ec
3 измененных файлов с 63 добавлено и 6 удалено
  1. 21 0
      bin/app/java/MainActivity.java
  2. 26 5
      bin/app/src/logger.rs
  3. 16 1
      bin/app/src/main.rs

+ 21 - 0
bin/app/java/MainActivity.java

@@ -7,6 +7,9 @@ import android.text.InputType;
 import android.content.Intent;
 import android.net.Uri;
 import android.util.Log;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.PrintWriter;
 import java.util.HashMap;
 
 import videodecode.VideoDecoder;
@@ -172,5 +175,23 @@ public void openUrl(String url) {
 Intent serviceIntent = new Intent(this, ForegroundService.class);
 startForegroundService(serviceIntent);
 
+final Thread.UncaughtExceptionHandler defaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
+    @Override
+    public void uncaughtException(Thread thread, Throwable throwable) {
+        try {
+            File logFile = new File(getExternalFilesDir(null), "darkfi-app.log");
+            PrintWriter writer = new PrintWriter(new FileWriter(logFile, true));
+            writer.println("Uncaught exception on thread " + thread.getName() + ":");
+            writer.println(Log.getStackTraceString(throwable));
+            writer.close();
+        } catch (Exception ignored) {
+        }
+        if (defaultHandler != null) {
+            defaultHandler.uncaughtException(thread, throwable);
+        }
+    }
+});
+
 //% END
 

+ 26 - 5
bin/app/src/logger.rs

@@ -17,20 +17,19 @@
  */
 use tracing_appender::non_blocking::WorkerGuard;
 use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer, Registry};
+
 #[cfg(feature = "enable-filelog")]
 use {
     file_rotate::{compression::Compression, suffix::AppendCount, ContentLimit, FileRotate},
     std::path::PathBuf,
+    std::sync::OnceLock,
 };
 
 #[cfg(target_os = "android")]
 use tracing_subscriber::filter::{LevelFilter, Targets};
 
 #[cfg(any(not(target_os = "android"), feature = "enable-filelog"))]
-use {
-    darkfi::util::logger::{EventFormatter, Level, TargetFilter},
-    //tracing_subscriber::fmt::format::FmtSpan,
-};
+use darkfi::util::logger::{EventFormatter, Level, TargetFilter};
 
 // Measured in bytes
 #[cfg(feature = "enable-filelog")]
@@ -69,13 +68,35 @@ fn logfile_path() -> PathBuf {
     dirs::cache_dir().unwrap().join("darkfi/darkfi-app.log")
 }
 
+// On Android, resolving the log path is a JNI call into the JVM, which can
+// deadlock if invoked from the panic hook. We therefore resolve it once at
+// startup (before the panic hook is installed) and cache it here, so the
+// hook only performs an atomic load plus a synchronous file write.
+#[cfg(feature = "enable-filelog")]
+static LOGFILE_PATH: OnceLock<PathBuf> = OnceLock::new();
+
+#[cfg(feature = "enable-filelog")]
+pub fn init_logfile_path() {
+    let _ = LOGFILE_PATH.set(logfile_path());
+}
+
+#[cfg(feature = "enable-filelog")]
+pub fn cached_logfile_path() -> Option<&'static std::path::Path> {
+    LOGFILE_PATH.get().map(|path| path.as_path())
+}
+
+#[cfg(not(feature = "enable-filelog"))]
+pub fn cached_logfile_path() -> Option<&'static std::path::Path> {
+    None
+}
+
 pub fn setup_logging() -> Option<WorkerGuard> {
     let mut layers: Vec<(Box<dyn Layer<Registry> + Send + Sync>, Option<WorkerGuard>)> = vec![];
 
     #[cfg(feature = "enable-filelog")]
     {
         let (non_blocking_file_rotate, guard) = tracing_appender::non_blocking(FileRotate::new(
-            logfile_path(),
+            LOGFILE_PATH.get_or_init(logfile_path).clone(),
             AppendCount::new(0),
             ContentLimit::BytesSurpassed(LOGFILE_MAXSIZE),
             Compression::None,

+ 16 - 1
bin/app/src/main.rs

@@ -84,7 +84,19 @@ macro_rules! i { ($($arg:tt)*) => { trace!(target: "main", $($arg)*); } }
 
 fn panic_hook(panic_info: &std::panic::PanicHookInfo) {
     error!("panic occurred: {panic_info}");
-    error!("{}", std::backtrace::Backtrace::force_capture().to_string());
+    let backtrace = std::backtrace::Backtrace::force_capture().to_string();
+    error!("{backtrace}");
+
+    if let Some(logfile_path) = logger::cached_logfile_path() {
+        let timestamp = chrono::Utc::now().to_rfc3339();
+        let report = format!("[{timestamp}] PANIC: {panic_info}\n{backtrace}\n");
+        let _ = std::fs::OpenOptions::new()
+            .append(true)
+            .create(true)
+            .open(logfile_path)
+            .and_then(|mut file| std::io::Write::write_all(&mut file, report.as_bytes()));
+    }
+
     std::process::abort()
 }
 
@@ -117,6 +129,9 @@ struct God {
 
 impl God {
     fn new() -> Self {
+        #[cfg(feature = "enable-filelog")]
+        logger::init_logfile_path();
+
         // Abort the application on panic right away
         std::panic::set_hook(Box::new(panic_hook));