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

Add helper macro's for logging only once #10808

Merged
merged 10 commits into from
Dec 5, 2023
4 changes: 4 additions & 0 deletions crates/bevy_log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
//! For more fine-tuned control over logging behavior, set up the [`LogPlugin`] or
//! `DefaultPlugins` during app initialization.

mod once;

#[cfg(feature = "trace")]
use std::panic;

Expand All @@ -28,6 +30,8 @@ pub mod prelude {
pub use bevy_utils::tracing::{
debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span,
};

pub use crate::{debug_once, error_once, info_once, trace_once, warn_once};
}

pub use bevy_utils::tracing::{
Expand Down
99 changes: 99 additions & 0 deletions crates/bevy_log/src/once.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/// Call `trace!(...)` once per call site.
josfeenstra marked this conversation as resolved.
Show resolved Hide resolved
///
/// Useful for logging within systems which are called every frame.
///
/// Returns true the first time this is called
#[macro_export]
macro_rules! trace_once {
($($arg:tt)+) => ({
use ::std::sync::atomic::{AtomicBool, Ordering};

static FIRST_TIME: AtomicBool = AtomicBool::new(true);
if FIRST_TIME.swap(false, Ordering::Relaxed) {
trace!($($arg)+);
josfeenstra marked this conversation as resolved.
Show resolved Hide resolved
true
} else {
false
}
});
}

/// Call `debug!(...)` once per call site.
///
/// Useful for logging within systems which are called every frame.
///
/// Returns true the first time this is called
#[macro_export]
macro_rules! debug_once {
($($arg:tt)+) => ({
use ::std::sync::atomic::{AtomicBool, Ordering};

static FIRST_TIME: AtomicBool = AtomicBool::new(true);
if FIRST_TIME.swap(false, Ordering::Relaxed) {
debug!($($arg)+);
true
} else {
false
}
});
}

/// Call `info!(...)` once per call site.
///
/// Useful for logging within systems which are called every frame.
///
/// Returns true the first time this is called
#[macro_export]
macro_rules! info_once {
($($arg:tt)+) => ({
use ::std::sync::atomic::{AtomicBool, Ordering};

static FIRST_TIME: AtomicBool = AtomicBool::new(true);
if FIRST_TIME.swap(false, Ordering::Relaxed) {
info!($($arg)+);
true
} else {
false
}
});
}

/// Call `warn!(...)` once per call site.
///
/// Useful for logging within systems which are called every frame.
///
/// Returns true the first time this is called
#[macro_export]
macro_rules! warn_once {
($($arg:tt)+) => ({
use ::std::sync::atomic::{AtomicBool, Ordering};

static FIRST_TIME: AtomicBool = AtomicBool::new(true);
if FIRST_TIME.swap(false, Ordering::Relaxed) {
warn!($($arg)+);
true
} else {
false
}
});
}

/// Call `error!(...)` once per call site.
///
/// Useful for logging within systems which are called every frame.
///
/// Returns true the first time this is called
#[macro_export]
macro_rules! error_once {
($($arg:tt)+) => ({
use ::std::sync::atomic::{AtomicBool, Ordering};

static FIRST_TIME: AtomicBool = AtomicBool::new(true);
if FIRST_TIME.swap(false, Ordering::Relaxed) {
error!($($arg)+);
true
} else {
false
}
});
}
20 changes: 20 additions & 0 deletions examples/app/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ fn main() {
..default()
}))
.add_systems(Update, log_system)
.add_systems(Update, log_once_system)
.run();
}

Expand All @@ -30,3 +31,22 @@ fn log_system() {
// the format used here is super flexible. check out this documentation for more info:
// https://docs.rs/tracing-subscriber/*/tracing_subscriber/filter/struct.EnvFilter.html
}

fn log_once_system() {
// The 'once' variants of each log level are useful when a system is called every frame,
// but we still wish to inform the user only once. In other words, use these to prevent spam :)

trace_once!("one time noisy message");
debug_once!("one time debug message");
info_once!("some info which is printed only once");
warn_once!("some warning we wish to call out only once");
error_once!("some error we wish to report only once");

for i in 0..10 {
info_once!("logs once per call site, so this works just fine: {}", i);
}

if info_once!("some more info") {
info!("this is the first and only time 'some more info' was printed");
}
}