From 511d4117176949dafd3b218efae55d6b3c786bc8 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 11 Apr 2023 14:04:12 +0200 Subject: [PATCH 1/2] Add `rerun --strict`: crash if any warning or error is logged Part of https://github.com/rerun-io/rerun/issues/1483 --- crates/re_log/src/lib.rs | 5 ++++ crates/rerun/src/crash_handler.rs | 29 +++++++++++++++++----- crates/rerun/src/run.rs | 41 +++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+), 6 deletions(-) diff --git a/crates/re_log/src/lib.rs b/crates/re_log/src/lib.rs index 68f77b420b09..653451a37405 100644 --- a/crates/re_log/src/lib.rs +++ b/crates/re_log/src/lib.rs @@ -35,6 +35,11 @@ pub use { setup::*, }; +/// Re-exports of other crates. +pub mod external { + pub use log; +} + /// Never log anything less serious than a `WARN` from these crates. const CRATES_AT_WARN_LEVEL: [&str; 3] = [ // wgpu crates spam a lot on info level, which is really annoying diff --git a/crates/rerun/src/crash_handler.rs b/crates/rerun/src/crash_handler.rs index e73ffd62b006..7344d583901d 100644 --- a/crates/rerun/src/crash_handler.rs +++ b/crates/rerun/src/crash_handler.rs @@ -23,7 +23,7 @@ fn install_panic_hook(_build_info: BuildInfo) { let previous_panic_hook = std::panic::take_hook(); std::panic::set_hook(Box::new(move |panic_info: &std::panic::PanicInfo<'_>| { - let callstack = callstack_from("panicking::panic_fmt\n"); + let callstack = callstack_from(&["panicking::panic_fmt\n"]); let file_line = panic_info.location().map(|location| { let file = anonymize_source_file_path(&std::path::PathBuf::from(location.file())); @@ -210,21 +210,38 @@ fn install_signal_handler(build_info: BuildInfo) { } fn callstack() -> String { - callstack_from("install_signal_handler::signal_handler\n") + callstack_from(&["install_signal_handler::signal_handler\n"]) } } -fn callstack_from(start_pattern: &str) -> String { +/// Get a nicely formatted callstack. +/// +/// You can give this function a list of substrings to look for, e.g. names of functions. +/// If any of these substrings matches, anything before that is removed from the callstack. +/// For example: +/// +/// ``` +/// fn print_callstack() { +/// eprintln!("{}", callstack_from(&["print_callstack"])); +/// } +/// ``` +pub fn callstack_from(start_patterns: &[&str]) -> String { let backtrace = backtrace::Backtrace::new(); let stack = backtrace_to_string(&backtrace); // Trim it a bit: let mut stack = stack.as_str(); + let start_patterns = start_patterns + .iter() + .chain(std::iter::once(&"callstack_from")); + // Trim the top (closest to the panic handler) to cut out some noise: - if let Some(offset) = stack.find(start_pattern) { - let prev_newline = stack[..offset].rfind('\n').map_or(0, |newline| newline + 1); - stack = &stack[prev_newline..]; + for start_pattern in start_patterns { + if let Some(offset) = stack.find(start_pattern) { + let prev_newline = stack[..offset].rfind('\n').map_or(0, |newline| newline + 1); + stack = &stack[prev_newline..]; + } } // Trim the bottom to cut out code that sets up the callstack: diff --git a/crates/rerun/src/run.rs b/crates/rerun/src/run.rs index 495fd680eb7f..691b3c359250 100644 --- a/crates/rerun/src/run.rs +++ b/crates/rerun/src/run.rs @@ -64,6 +64,10 @@ struct Args { #[clap(long)] profile: bool, + /// Exit with a non-zero exit code if any warning or error is logged. Useful for tests. + #[clap(long)] + strict: bool, + /// An upper limit on how much memory the Rerun Viewer should use. /// /// When this limit is used, Rerun will purge the oldest data. @@ -187,6 +191,11 @@ where return Ok(0); } + if args.strict { + re_log::add_boxed_logger(Box::new(StrictLogger {})).expect("Failed to enter --strict mode"); + re_log::info!("--strict mode: any warning or error will cause Rerun to panic."); + } + let res = if let Some(commands) = &args.commands { match commands { #[cfg(all(feature = "analytics"))] @@ -539,3 +548,35 @@ pub fn setup_ctrl_c_handler() -> (tokio::sync::broadcast::Receiver<()>, Arc) -> bool { + match metadata.level() { + log::Level::Error | log::Level::Warn => true, + log::Level::Info | log::Level::Debug | log::Level::Trace => false, + } + } + + fn log(&self, record: &log::Record<'_>) { + let level = match record.level() { + log::Level::Error => "error", + log::Level::Warn => "warning", + log::Level::Info | log::Level::Debug | log::Level::Trace => return, + }; + + eprintln!("{level} logged in --strict mode: {}", record.args()); + eprintln!( + "{}", + crate::crash_handler::callstack_from(&["log::__private_api_log"]) + ); + std::process::exit(1); + } + + fn flush(&self) {} +} From 67cd59f52c042878f4350580db272e885ea08b9d Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 11 Apr 2023 15:16:15 +0200 Subject: [PATCH 2/2] Can't doc-test private functions --- crates/rerun/src/crash_handler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/rerun/src/crash_handler.rs b/crates/rerun/src/crash_handler.rs index 7344d583901d..98b3527dea4a 100644 --- a/crates/rerun/src/crash_handler.rs +++ b/crates/rerun/src/crash_handler.rs @@ -220,7 +220,7 @@ fn install_signal_handler(build_info: BuildInfo) { /// If any of these substrings matches, anything before that is removed from the callstack. /// For example: /// -/// ``` +/// ```ignore /// fn print_callstack() { /// eprintln!("{}", callstack_from(&["print_callstack"])); /// }