Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show log messages in egui toast notifications #1603

Merged
merged 10 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CODE_STYLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ The log is for several distinct users:

We are all sharing the same log stream, so we must cooperate carefully.

The Rerun viewer will show log messages at `INFO`, `WARNING` and `ERROR` to the user as a toast notifications.

#### `ERROR`
This is for _unrecoverable_ problems. The application or library couldn't complete an operation.

Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 13 additions & 1 deletion crates/re_analytics/src/pipeline_native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,19 @@ fn flush_events(
}

if let Err(err) = sink.send(analytics_id, session_id, &events) {
warn!(%err, "failed to send analytics down the sink, will try again later");
if matches!(err, SinkError::HttpTransport(_)) {
// No internet connection. No biggie, we'll try again later.
re_log::debug_once!(
"Failed to send analytics down the sink, will try again later.\n{err}
"
);
} else {
// A more unusual error. Show it to the user.
re_log::warn_once!(
"Failed to send analytics down the sink, will try again later.\n{err}
"
);
}
return Err(err);
}

Expand Down
1 change: 1 addition & 0 deletions crates/re_analytics/src/sink_native.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum SinkError {
#[error("JSON: {0}")]
Serde(#[from] serde_json::Error),

/// Usually because there is no internet.
#[error("HTTP transport: {0}")]
HttpTransport(Box<ureq::Transport>),

Expand Down
1 change: 1 addition & 0 deletions crates/re_log/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ all-features = true
[dependencies]
log = { version = "0.4", features = ["std"] }
log-once = "0.4"
parking_lot.workspace = true

# make sure dependencies that user tracing gets forwarded to `log`:
tracing = { version = "0.1", features = ["log"] }
Expand Down
47 changes: 47 additions & 0 deletions crates/re_log/src/channel_logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//! Capture log messages and send them to some receiver over a channel.

pub struct LogMsg {
pub level: log::Level,
pub msg: String,
}

/// Pipe log messages to a channel.
pub struct ChannelLogger {
filter: log::LevelFilter,
tx: parking_lot::Mutex<std::sync::mpsc::Sender<LogMsg>>,
}

impl ChannelLogger {
pub fn new(filter: log::LevelFilter) -> (Self, std::sync::mpsc::Receiver<LogMsg>) {
let (tx, rx) = std::sync::mpsc::channel();
(
Self {
filter,
tx: tx.into(),
},
rx,
)
}
}

impl log::Log for ChannelLogger {
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
crate::is_log_enabled(self.filter, metadata)
}

fn log(&self, record: &log::Record<'_>) {
if !self.enabled(record.metadata()) {
return;
}

self.tx
.lock()
.send(LogMsg {
level: record.level(),
msg: record.args().to_string(),
})
.ok();
}

fn flush(&self) {}
}
53 changes: 49 additions & 4 deletions crates/re_log/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
//! Text logging (nothing to do with rerun logging) for use in rerun libraries.
//!
//! Provides helpers for adding multiple loggers,
//! and for setting up logging on native and on web.
//!
//! * `trace`: spammy things
//! * `debug`: things that might be useful when debugging
//! * `info`: things that we want to show to users
Expand All @@ -9,6 +12,15 @@
//! The `warn_once` etc macros are for when you want to suppress repeated
//! logging of the exact same message.

mod channel_logger;
mod multi_logger;
mod setup;

#[cfg(target_arch = "wasm32")]
mod web_logger;

pub use log::{Level, LevelFilter};

// The tracing macros support more syntax features than the log, that's why we use them:
pub use tracing::{debug, error, info, trace, warn};

Expand All @@ -17,12 +29,45 @@ pub use tracing::{debug, error, info, trace, warn};
// similar to how the log console in a browser will automatically suppress duplicates.
pub use log_once::{debug_once, error_once, info_once, trace_once, warn_once};

mod setup;
pub use {
channel_logger::*,
multi_logger::{add_boxed_logger, add_logger},
setup::*,
};

pub use setup::*;
/// 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
// TODO(emilk): remove once https://github.com/gfx-rs/wgpu/issues/3206 is fixed
"naga",
"wgpu_core",
"wgpu_hal",
];

#[cfg(target_arch = "wasm32")]
mod log_web;
/// Never log anything less serious than a `INFO` from these crates.
const CRATES_FORCED_TO_INFO: [&str; 4] = [
// These are quite spammy on debug, drowning out what we care about:
"h2", "hyper", "rustls", "ureq",
];

/// Should we log this message given the filter?
fn is_log_enabled(filter: log::LevelFilter, metadata: &log::Metadata<'_>) -> bool {
if CRATES_AT_WARN_LEVEL
.iter()
.any(|crate_name| metadata.target().starts_with(crate_name))
{
return metadata.level() <= log::LevelFilter::Warn;
}

if CRATES_FORCED_TO_INFO
.iter()
.any(|crate_name| metadata.target().starts_with(crate_name))
{
return metadata.level() <= log::LevelFilter::Info;
}

metadata.level() <= filter
}

/// Shorten a path to a Rust source file.
///
Expand Down
52 changes: 52 additions & 0 deletions crates/re_log/src/multi_logger.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
//! Have multiple loggers implementing [`log::Log`] at once.

static MULTI_LOGGER: MultiLogger = MultiLogger::new();

/// Install the multi-logger as the default logger.
pub fn init() -> Result<(), log::SetLoggerError> {
log::set_logger(&MULTI_LOGGER)
}

/// Install an additional global logger.
pub fn add_boxed_logger(logger: Box<dyn log::Log>) {
add_logger(Box::leak(logger));
}

/// Install an additional global logger.
pub fn add_logger(logger: &'static dyn log::Log) {
MULTI_LOGGER.loggers.write().push(logger);
}

/// Forward log messages to multiple [`log::log`] receivers.
struct MultiLogger {
loggers: parking_lot::RwLock<Vec<&'static dyn log::Log>>,
}

impl MultiLogger {
pub const fn new() -> Self {
Self {
loggers: parking_lot::RwLock::new(vec![]),
}
}
}

impl log::Log for MultiLogger {
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
self.loggers
.read()
.iter()
.any(|logger| logger.enabled(metadata))
}

fn log(&self, record: &log::Record<'_>) {
for logger in self.loggers.read().iter() {
logger.log(record);
}
}

fn flush(&self) {
for logger in self.loggers.read().iter() {
logger.flush();
}
}
}
62 changes: 36 additions & 26 deletions crates/re_log/src/setup.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@
//! Function to setup logging in binaries and web apps.

/// Set `RUST_LOG` environment variable to `info`, unless set,
/// and also set some other log levels on crates that are too loud.
/// Get `RUST_LOG` environment variable or `info`, if not set.
///
/// Also set some other log levels on crates that are too loud.
#[cfg(not(target_arch = "wasm32"))]
fn set_default_rust_log_env() {
fn log_filter() -> String {
let mut rust_log = std::env::var("RUST_LOG").unwrap_or_else(|_| "info".to_owned());

const LOUD_CRATES: [&str; 7] = [
// wgpu crates spam a lot on info level, which is really annoying
// TODO(emilk): remove once https://github.com/gfx-rs/wgpu/issues/3206 is fixed
"naga",
"wgpu_core",
"wgpu_hal",
// These are quite spammy on debug, drowning out what we care about:
"h2",
"hyper",
"rustls",
"ureq",
];
for loud_crate in LOUD_CRATES {
if !rust_log.contains(&format!("{loud_crate}=")) {
rust_log += &format!(",{loud_crate}=warn");
for crate_name in crate::CRATES_AT_WARN_LEVEL {
if !rust_log.contains(&format!("{crate_name}=")) {
rust_log += &format!(",{crate_name}=warn");
}
}
for crate_name in crate::CRATES_FORCED_TO_INFO {
if !rust_log.contains(&format!("{crate_name}=")) {
rust_log += &format!(",{crate_name}=info");
}
}

std::env::set_var("RUST_LOG", rust_log);
rust_log
}

#[cfg(not(target_arch = "wasm32"))]
pub fn setup_native_logging() {
if std::env::var("RUST_BACKTRACE").is_err() {
// Make sure we always produce backtraces for the (hopefully rare) cases when we crash!
std::env::set_var("RUST_BACKTRACE", "1");
}
}

#[cfg(not(target_arch = "wasm32"))]
pub fn setup_native_logging() {
set_default_rust_log_env();
env_logger::init();
crate::multi_logger::init().expect("Failed to set logger");

let log_filter = log_filter();

if log_filter.contains("trace") {
log::set_max_level(log::LevelFilter::Trace);
} else if log_filter.contains("debug") {
log::set_max_level(log::LevelFilter::Debug);
} else {
log::set_max_level(log::LevelFilter::Info);
}

let mut stderr_logger = env_logger::Builder::new();
stderr_logger.parse_filters(&log_filter);
crate::add_boxed_logger(Box::new(stderr_logger.build()));
}

#[cfg(target_arch = "wasm32")]
pub fn setup_web_logging() {
crate::log_web::WebLogger::init(log::LevelFilter::Debug)
.expect("Failed to set log subscriber.");
crate::multi_logger::init().expect("Failed to set logger");
log::set_max_level(log::LevelFilter::Debug);
crate::add_boxed_logger(Box::new(crate::web_logger::WebLogger::new(
log::LevelFilter::Debug,
)))
}
13 changes: 1 addition & 12 deletions crates/re_log/src/log_web.rs → crates/re_log/src/web_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,11 @@ impl WebLogger {
pub fn new(filter: log::LevelFilter) -> Self {
Self { filter }
}

/// Install this logger as the global logger.
pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {
log::set_max_level(filter);
log::set_boxed_logger(Box::new(Self::new(filter)))
}
}

impl log::Log for WebLogger {
fn enabled(&self, metadata: &log::Metadata<'_>) -> bool {
if metadata.target().starts_with("wgpu") || metadata.target().starts_with("naga") {
// TODO(emilk): remove once https://github.com/gfx-rs/wgpu/issues/3206 is fixed
return metadata.level() <= log::LevelFilter::Warn;
}

metadata.level() <= self.filter
crate::is_log_enabled(self.filter, metadata)
}

fn log(&self, record: &log::Record<'_>) {
Expand Down
Loading