Make the Rust client the sole app
This commit is contained in:
1 parent
a8602c1626
commit
d8bb1699a8
230 files changed
+762
-27300
No files matched your search
@@ -0,0 +1,101 @@
|
||||
//! The platform half of this app's logging: what
|
||||
//! `crate::client::log_ring` needs that only Android can supply, which is
|
||||
//! `android_logger` as the logger to forward to and nothing else.
|
||||
|
||||
use crate::client::log_ring::{self, LogRing};
|
||||
use std::{
|
||||
fs, panic,
|
||||
path::{Path, PathBuf},
|
||||
sync::OnceLock,
|
||||
};
|
||||
|
||||
/// Installs the in-process ring in front of Android's logger.
|
||||
pub fn install(max_level: log::LevelFilter) {
|
||||
let inner = android_logger::AndroidLogger::new(
|
||||
android_logger::Config::default()
|
||||
.with_max_level(max_level)
|
||||
.with_tag("iris-android-app"),
|
||||
);
|
||||
if log_ring::install_process_logger(
|
||||
Box::new(inner),
|
||||
max_level,
|
||||
iris::diagnostics::trace_enabled,
|
||||
)
|
||||
.is_err()
|
||||
{
|
||||
log::warn!("iris app log: a logger was already installed, so there is no ring");
|
||||
}
|
||||
install_panic_hook();
|
||||
}
|
||||
|
||||
pub fn ring() -> &'static LogRing {
|
||||
log_ring::process_ring()
|
||||
}
|
||||
|
||||
#[cfg(feature = "bench")]
|
||||
pub fn diagnostics_line() -> String {
|
||||
let where_to_read = match crate::android::devlog::authority() {
|
||||
Some(authority) => format!("devlog provider: content://{authority}"),
|
||||
None => "devlog provider: declared, not created yet".to_string(),
|
||||
};
|
||||
format!("{}\n{where_to_read}", ring().summary())
|
||||
}
|
||||
|
||||
/// Where the panic hook leaves its report, under the app's private
|
||||
/// directory. Read back and dropped by [`set_crash_dir`] on the next
|
||||
/// start.
|
||||
const CRASH_FILE: &str = "last-panic.txt";
|
||||
|
||||
/// Enough preceding log lines to explain a crash without evicting the next run.
|
||||
const CRASH_CONTEXT_LINES: usize = 80;
|
||||
|
||||
const PREVIOUS_RUN_TARGET: &str = "previous_run";
|
||||
|
||||
static CRASH_PATH: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
/// Copies aborting panics into the device-readable log ring.
|
||||
fn install_panic_hook() {
|
||||
let previous = panic::take_hook();
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
let where_at = match info.location() {
|
||||
Some(at) => format!("{}:{}:{}", at.file(), at.line(), at.column()),
|
||||
None => "an unknown location".to_string(),
|
||||
};
|
||||
let message = info.payload_as_str().unwrap_or("Box<dyn Any>");
|
||||
let line = format!("iris panic at {where_at}: {message}");
|
||||
log::error!("{line}");
|
||||
if let Some(path) = CRASH_PATH.get() {
|
||||
let context = ring()
|
||||
.try_tail_text(CRASH_CONTEXT_LINES)
|
||||
.unwrap_or_else(|| {
|
||||
"(the log ring was locked as this run died; no context)".to_string()
|
||||
});
|
||||
let _ = fs::write(path, format!("{line}\n{context}"));
|
||||
}
|
||||
previous(info);
|
||||
}));
|
||||
}
|
||||
|
||||
/// Configures crash persistence and replays a report left by the previous run.
|
||||
pub fn set_crash_dir(dir: &Path) {
|
||||
let path = dir.join(CRASH_FILE);
|
||||
if let Ok(previous) = fs::read_to_string(&path) {
|
||||
// Delete first so a panic during replay cannot create a replay loop.
|
||||
let _ = fs::remove_file(&path);
|
||||
replay_crash(&previous);
|
||||
}
|
||||
let _ = CRASH_PATH.set(path);
|
||||
}
|
||||
|
||||
/// Puts a previous run's report back in the ring: its context lines in
|
||||
/// the order they happened, then the panic itself.
|
||||
fn replay_crash(report: &str) {
|
||||
let (panic_line, context) = report.split_once('\n').unwrap_or((report, ""));
|
||||
for line in context.lines().filter(|line| !line.is_empty()) {
|
||||
ring().push(log::Level::Info, PREVIOUS_RUN_TARGET, line.to_string());
|
||||
}
|
||||
log::error!(
|
||||
"iris app log: the previous run died -- {}",
|
||||
panic_line.trim()
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user