Revert "Allow partial support for bevy_log in no_std (#18782)" (#18816)

This reverts commit ac52cca033.

Fixes #18815

the interest of providing no_std support, specifically no_atomic
support). That tradeoff isn't worth it, especially given that tracing is
likely to get no_atomic support.

Revert #18782
This commit is contained in:
Carter Anderson 2025-04-14 14:15:01 -07:00 committed by François Mockers
parent f006f02f78
commit dc48fd41e8
15 changed files with 378 additions and 476 deletions

View File

@ -137,6 +137,7 @@ default = [
"bevy_gizmos",
"bevy_gltf",
"bevy_input_focus",
"bevy_log",
"bevy_mesh_picking_backend",
"bevy_pbr",
"bevy_picking",
@ -158,7 +159,6 @@ default = [
"smaa_luts",
"sysinfo_plugin",
"tonemapping_luts",
"tracing",
"vorbis",
"webgl2",
"x11",
@ -274,6 +274,9 @@ bevy_dev_tools = ["bevy_internal/bevy_dev_tools"]
# Enable the Bevy Remote Protocol
bevy_remote = ["bevy_internal/bevy_remote"]
# Enable integration with `tracing` and `log`
bevy_log = ["bevy_internal/bevy_log"]
# Enable input focus subsystem
bevy_input_focus = ["bevy_internal/bevy_input_focus"]
@ -300,10 +303,7 @@ trace_tracy_memory = [
]
# Tracing support
tracing = ["bevy_internal/tracing", "dep:tracing"]
# Enables traces within Bevy using tracing
trace = ["bevy_internal/trace", "tracing"]
trace = ["bevy_internal/trace", "dep:tracing"]
# Basis Universal compressed texture support
basis-universal = ["bevy_internal/basis-universal"]
@ -1581,7 +1581,7 @@ wasm = true
name = "headless"
path = "examples/app/headless.rs"
doc-scrape-examples = true
required-features = []
required-features = ["bevy_log"]
[package.metadata.example.headless]
name = "Headless"

View File

@ -47,18 +47,6 @@ tracing = { version = "0.1", default-features = false, features = ["std"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
uuid = { version = "1.13.1", default-features = false, features = ["js"] }
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
[lints]
workspace = true

View File

@ -66,24 +66,6 @@ serde_json = "1"
smallvec = "1.11"
tracing = { version = "0.1", default-features = false, features = ["std"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
[dev-dependencies]
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4" }

View File

@ -10,9 +10,7 @@ keywords = ["game", "engine", "gamedev", "graphics", "bevy"]
categories = ["game-engines", "graphics", "gui", "rendering"]
[features]
tracing = ["bevy_log/tracing"]
trace = [
"tracing",
"bevy_app/trace",
"bevy_asset?/trace",
"bevy_core_pipeline?/trace",
@ -22,18 +20,10 @@ trace = [
"bevy_render?/trace",
"bevy_winit?/trace",
]
trace_chrome = ["tracing", "bevy_log/tracing-chrome"]
trace_tracy = [
"tracing",
"bevy_render?/tracing-tracy",
"bevy_log/tracing-tracy",
]
trace_tracy_memory = ["tracing", "bevy_log/trace_tracy_memory"]
detailed_trace = [
"tracing",
"bevy_ecs/detailed_trace",
"bevy_render?/detailed_trace",
]
trace_chrome = ["bevy_log/tracing-chrome"]
trace_tracy = ["bevy_render?/tracing-tracy", "bevy_log/tracing-tracy"]
trace_tracy_memory = ["bevy_log/trace_tracy_memory"]
detailed_trace = ["bevy_ecs/detailed_trace", "bevy_render?/detailed_trace"]
sysinfo_plugin = ["bevy_diagnostic/sysinfo_plugin"]
@ -304,7 +294,6 @@ std = [
"bevy_ecs/std",
"bevy_input/std",
"bevy_input_focus?/std",
"bevy_log/std",
"bevy_math/std",
"bevy_platform/std",
"bevy_reflect/std",
@ -356,7 +345,6 @@ async_executor = [
# Note this is currently only applicable on `wasm32` architectures.
web = [
"bevy_app/web",
"bevy_log/web",
"bevy_platform/web",
"bevy_reflect/web",
"bevy_tasks/web",
@ -398,7 +386,8 @@ bevy_utils = { path = "../bevy_utils", version = "0.16.0-rc.4", default-features
] }
bevy_tasks = { path = "../bevy_tasks", version = "0.16.0-rc.4", default-features = false }
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4", default-features = false }
# bevy (std required)
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4", optional = true }
# bevy (optional)
bevy_a11y = { path = "../bevy_a11y", optional = true, version = "0.16.0-rc.4", features = [

View File

@ -4,7 +4,7 @@ plugin_group! {
/// This plugin group will add all the default plugins for a *Bevy* application:
pub struct DefaultPlugins {
bevy_app:::PanicHandlerPlugin,
#[cfg(feature = "tracing")]
#[cfg(feature = "bevy_log")]
bevy_log:::LogPlugin,
bevy_app:::TaskPoolPlugin,
bevy_diagnostic:::FrameCountPlugin,

View File

@ -42,6 +42,7 @@ pub use bevy_image as image;
pub use bevy_input as input;
#[cfg(feature = "bevy_input_focus")]
pub use bevy_input_focus as input_focus;
#[cfg(feature = "bevy_log")]
pub use bevy_log as log;
pub use bevy_math as math;
#[cfg(feature = "bevy_pbr")]

View File

@ -1,10 +1,14 @@
#[doc(hidden)]
pub use crate::{
app::prelude::*, ecs::prelude::*, input::prelude::*, log::prelude::*, math::prelude::*,
platform::prelude::*, reflect::prelude::*, time::prelude::*, transform::prelude::*,
utils::prelude::*, DefaultPlugins, MinimalPlugins,
app::prelude::*, ecs::prelude::*, input::prelude::*, math::prelude::*, platform::prelude::*,
reflect::prelude::*, time::prelude::*, transform::prelude::*, utils::prelude::*,
DefaultPlugins, MinimalPlugins,
};
#[doc(hidden)]
#[cfg(feature = "bevy_log")]
pub use crate::log::prelude::*;
#[doc(hidden)]
#[cfg(feature = "bevy_window")]
pub use crate::window::prelude::*;

View File

@ -9,44 +9,24 @@ license = "MIT OR Apache-2.0"
keywords = ["bevy"]
[features]
default = ["std", "tracing"]
tracing = ["dep:tracing", "dep:tracing-subscriber", "dep:tracing-log"]
trace = ["tracing", "tracing-error"]
trace_tracy_memory = ["tracing", "dep:tracy-client"]
tracing-chrome = ["tracing", "dep:tracing-chrome"]
tracing-error = ["tracing", "dep:tracing-error"]
tracing-tracy = ["tracing", "dep:tracing-tracy"]
# Allows access to the `std` crate. Enabling this feature will prevent compilation
# on `no_std` targets, but provides access to certain additional features on
# supported platforms.
std = ["bevy_app/std", "bevy_utils/std", "bevy_ecs/std"]
# Enables use of browser APIs.
# Note this is currently only applicable on `wasm32` architectures.
web = ["bevy_app/web", "dep:tracing-wasm"]
trace = ["tracing-error"]
trace_tracy_memory = ["dep:tracy-client"]
[dependencies]
# bevy
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.4", default-features = false }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-rc.4", default-features = false, features = [
"alloc",
] }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-rc.4", default-features = false }
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.4" }
bevy_utils = { path = "../bevy_utils", version = "0.16.0-rc.4" }
bevy_ecs = { path = "../bevy_ecs", version = "0.16.0-rc.4" }
# other
log = { version = "0.4", default-features = false }
tracing-subscriber = { version = "0.3.1", optional = true, features = [
tracing-subscriber = { version = "0.3.1", features = [
"registry",
"env-filter",
] }
tracing-chrome = { version = "0.7.0", optional = true }
tracing-log = { version = "0.2.0", optional = true }
tracing-log = "0.2.0"
tracing-error = { version = "0.2.0", optional = true }
tracing = { version = "0.1", default-features = false, optional = true, features = [
"std",
] }
tracing = { version = "0.1", default-features = false, features = ["std"] }
# Tracy dependency compatibility table:
# https://github.com/nagisa/rust_tracy_client
@ -57,7 +37,11 @@ tracy-client = { version = "0.18.0", optional = true }
android_log-sys = "0.3.0"
[target.'cfg(target_arch = "wasm32")'.dependencies]
tracing-wasm = { version = "0.2.1", optional = true }
tracing-wasm = "0.2.1"
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
bevy_app = { path = "../bevy_app", version = "0.16.0-dev", default-features = false, features = [
"web",
] }
[target.'cfg(target_os = "ios")'.dependencies]
tracing-oslog = "0.2"

View File

@ -1,4 +1,4 @@
use alloc::{ffi::CString, format, string::String, vec::Vec};
use alloc::ffi::CString;
use core::fmt::{Debug, Write};
use tracing::{
field::Field,

View File

@ -15,22 +15,14 @@
//!
//! For more fine-tuned control over logging behavior, set up the [`LogPlugin`] or
//! `DefaultPlugins` during app initialization.
#![cfg_attr(
not(feature = "tracing"),
doc = "\n\n[`LogPlugin`]: https://docs.rs/bevy_log"
)]
#![no_std]
#[cfg(feature = "std")]
extern crate std;
extern crate alloc;
#[cfg(all(target_os = "android", feature = "std"))]
use core::error::Error;
#[cfg(target_os = "android")]
mod android_tracing;
mod once;
#[cfg(feature = "tracing")]
mod plugin;
#[cfg(feature = "trace_tracy_memory")]
#[global_allocator]
@ -42,24 +34,350 @@ static GLOBAL: tracy_client::ProfiledAllocator<std::alloc::System> =
/// This includes the most common types in this crate, re-exported for your convenience.
pub mod prelude {
#[doc(hidden)]
pub use crate::{
debug, debug_once, error, error_once, info, info_once, trace, trace_once, warn, warn_once,
pub use tracing::{
debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span,
};
#[doc(hidden)]
pub use bevy_utils::once;
pub use crate::{debug_once, error_once, info_once, trace_once, warn_once};
#[cfg(feature = "tracing")]
#[doc(hidden)]
pub use crate::{debug_span, error_span, info_span, trace_span, warn_span};
pub use bevy_utils::once;
}
pub use bevy_utils::once;
pub use log::{debug, error, info, trace, warn};
#[cfg(feature = "tracing")]
pub use {
crate::plugin::{BoxedLayer, LogPlugin, DEFAULT_FILTER},
tracing::{self, debug_span, error_span, info_span, trace_span, warn_span, Level},
tracing_subscriber,
pub use tracing::{
self, debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn,
warn_span, Level,
};
pub use tracing_subscriber;
use bevy_app::{App, Plugin};
use tracing_log::LogTracer;
use tracing_subscriber::{
filter::{FromEnvError, ParseError},
prelude::*,
registry::Registry,
EnvFilter, Layer,
};
#[cfg(feature = "tracing-chrome")]
use {
bevy_ecs::resource::Resource,
bevy_utils::synccell::SyncCell,
tracing_subscriber::fmt::{format::DefaultFields, FormattedFields},
};
/// Wrapper resource for `tracing-chrome`'s flush guard.
/// When the guard is dropped the chrome log is written to file.
#[cfg(feature = "tracing-chrome")]
#[expect(
dead_code,
reason = "`FlushGuard` never needs to be read, it just needs to be kept alive for the `App`'s lifetime."
)]
#[derive(Resource)]
pub(crate) struct FlushGuard(SyncCell<tracing_chrome::FlushGuard>);
/// Adds logging to Apps. This plugin is part of the `DefaultPlugins`. Adding
/// this plugin will setup a collector appropriate to your target platform:
/// * Using [`tracing-subscriber`](https://crates.io/crates/tracing-subscriber) by default,
/// logging to `stdout`.
/// * Using [`android_log-sys`](https://crates.io/crates/android_log-sys) on Android,
/// logging to Android logs.
/// * Using [`tracing-wasm`](https://crates.io/crates/tracing-wasm) in Wasm, logging
/// to the browser console.
///
/// You can configure this plugin.
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::LogPlugin;
/// # use tracing::Level;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins.set(LogPlugin {
/// level: Level::DEBUG,
/// filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".to_string(),
/// custom_layer: |_| None,
/// }))
/// .run();
/// }
/// ```
///
/// Log level can also be changed using the `RUST_LOG` environment variable.
/// For example, using `RUST_LOG=wgpu=error,bevy_render=info,bevy_ecs=trace cargo run ..`
///
/// It has the same syntax as the field [`LogPlugin::filter`], see [`EnvFilter`].
/// If you define the `RUST_LOG` environment variable, the [`LogPlugin`] settings
/// will be ignored.
///
/// Also, to disable color terminal output (ANSI escape codes), you can
/// set the environment variable `NO_COLOR` to any value. This common
/// convention is documented at [no-color.org](https://no-color.org/).
/// For example:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::LogPlugin;
/// fn main() {
/// # // SAFETY: Single-threaded
/// # unsafe {
/// std::env::set_var("NO_COLOR", "1");
/// # }
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .run();
/// }
/// ```
///
/// If you want to setup your own tracing collector, you should disable this
/// plugin from `DefaultPlugins`:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::LogPlugin;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins.build().disable::<LogPlugin>())
/// .run();
/// }
/// ```
/// # Example Setup
///
/// For a quick setup that enables all first-party logging while not showing any of your dependencies'
/// log data, you can configure the plugin as shown below.
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::*;
/// App::new()
/// .add_plugins(DefaultPlugins.set(LogPlugin {
/// filter: "warn,my_crate=trace".to_string(), //specific filters
/// level: Level::TRACE,//Change this to be globally change levels
/// ..Default::default()
/// }))
/// .run();
/// ```
/// The filter (in this case an `EnvFilter`) chooses whether to print the log. The most specific filters apply with higher priority.
/// Let's start with an example: `filter: "warn".to_string()` will only print logs with level `warn` level or greater.
/// From here, we can change to `filter: "warn,my_crate=trace".to_string()`. Logs will print at level `warn` unless it's in `mycrate`,
/// which will instead print at `trace` level because `my_crate=trace` is more specific.
///
///
/// ## Log levels
/// Events can be logged at various levels of importance.
/// Only events at your configured log level and higher will be shown.
/// ```no_run
/// # use bevy_log::*;
/// // here is how you write new logs at each "log level" (in "most important" to
/// // "least important" order)
/// error!("something failed");
/// warn!("something bad happened that isn't a failure, but that's worth calling out");
/// info!("helpful information that is worth printing by default");
/// debug!("helpful for debugging");
/// trace!("very noisy");
/// ```
/// In addition to `format!` style arguments, you can print a variable's debug
/// value by using syntax like: `trace(?my_value)`.
///
/// ## Per module logging levels
/// Modules can have different logging levels using syntax like `crate_name::module_name=debug`.
///
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::*;
/// App::new()
/// .add_plugins(DefaultPlugins.set(LogPlugin {
/// filter: "warn,my_crate=trace,my_crate::my_module=debug".to_string(), // Specific filters
/// level: Level::TRACE, // Change this to be globally change levels
/// ..Default::default()
/// }))
/// .run();
/// ```
/// The idea is that instead of deleting logs when they are no longer immediately applicable,
/// you just disable them. If you do need to log in the future, then you can enable the logs instead of having to rewrite them.
///
/// ## Further reading
///
/// The `tracing` crate has much more functionality than these examples can show.
/// Much of this configuration can be done with "layers" in the `log` crate.
/// Check out:
/// - Using spans to add more fine grained filters to logs
/// - Adding instruments to capture more function information
/// - Creating layers to add additional context such as line numbers
/// # Panics
///
/// This plugin should not be added multiple times in the same process. This plugin
/// sets up global logging configuration for **all** Apps in a given process, and
/// rerunning the same initialization multiple times will lead to a panic.
///
/// # Performance
///
/// Filters applied through this plugin are computed at _runtime_, which will
/// have a non-zero impact on performance.
/// To achieve maximum performance, consider using
/// [_compile time_ filters](https://docs.rs/log/#compile-time-filters)
/// provided by the [`log`](https://crates.io/crates/log) crate.
///
/// ```toml
/// # cargo.toml
/// [dependencies]
/// log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] }
/// ```
pub struct LogPlugin {
/// Filters logs using the [`EnvFilter`] format
pub filter: String,
/// Filters out logs that are "less than" the given level.
/// This can be further filtered using the `filter` setting.
pub level: Level,
/// Optionally add an extra [`Layer`] to the tracing subscriber
///
/// This function is only called once, when the plugin is built.
///
/// Because [`BoxedLayer`] takes a `dyn Layer`, `Vec<Layer>` is also an acceptable return value.
///
/// Access to [`App`] is also provided to allow for communication between the
/// [`Subscriber`](tracing::Subscriber) and the [`App`].
///
/// Please see the `examples/log_layers.rs` for a complete example.
pub custom_layer: fn(app: &mut App) -> Option<BoxedLayer>,
}
/// A boxed [`Layer`] that can be used with [`LogPlugin`].
pub type BoxedLayer = Box<dyn Layer<Registry> + Send + Sync + 'static>;
/// The default [`LogPlugin`] [`EnvFilter`].
pub const DEFAULT_FILTER: &str = "wgpu=error,naga=warn";
impl Default for LogPlugin {
fn default() -> Self {
Self {
filter: DEFAULT_FILTER.to_string(),
level: Level::INFO,
custom_layer: |_| None,
}
}
}
impl Plugin for LogPlugin {
#[expect(clippy::print_stderr, reason = "Allowed during logger setup")]
fn build(&self, app: &mut App) {
#[cfg(feature = "trace")]
{
let old_handler = std::panic::take_hook();
std::panic::set_hook(Box::new(move |infos| {
eprintln!("{}", tracing_error::SpanTrace::capture());
old_handler(infos);
}));
}
let finished_subscriber;
let subscriber = Registry::default();
// add optional layer provided by user
let subscriber = subscriber.with((self.custom_layer)(app));
let default_filter = { format!("{},{}", self.level, self.filter) };
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|from_env_error| {
_ = from_env_error
.source()
.and_then(|source| source.downcast_ref::<ParseError>())
.map(|parse_err| {
// we cannot use the `error!` macro here because the logger is not ready yet.
eprintln!("LogPlugin failed to parse filter from env: {}", parse_err);
});
Ok::<EnvFilter, FromEnvError>(EnvFilter::builder().parse_lossy(&default_filter))
})
.unwrap();
let subscriber = subscriber.with(filter_layer);
#[cfg(feature = "trace")]
let subscriber = subscriber.with(tracing_error::ErrorLayer::default());
#[cfg(all(
not(target_arch = "wasm32"),
not(target_os = "android"),
not(target_os = "ios")
))]
{
#[cfg(feature = "tracing-chrome")]
let chrome_layer = {
let mut layer = tracing_chrome::ChromeLayerBuilder::new();
if let Ok(path) = std::env::var("TRACE_CHROME") {
layer = layer.file(path);
}
let (chrome_layer, guard) = layer
.name_fn(Box::new(|event_or_span| match event_or_span {
tracing_chrome::EventOrSpan::Event(event) => event.metadata().name().into(),
tracing_chrome::EventOrSpan::Span(span) => {
if let Some(fields) =
span.extensions().get::<FormattedFields<DefaultFields>>()
{
format!("{}: {}", span.metadata().name(), fields.fields.as_str())
} else {
span.metadata().name().into()
}
}
}))
.build();
app.insert_resource(FlushGuard(SyncCell::new(guard)));
chrome_layer
};
#[cfg(feature = "tracing-tracy")]
let tracy_layer = tracing_tracy::TracyLayer::default();
// note: the implementation of `Default` reads from the env var NO_COLOR
// to decide whether to use ANSI color codes, which is common convention
// https://no-color.org/
let fmt_layer = tracing_subscriber::fmt::Layer::default().with_writer(std::io::stderr);
// bevy_render::renderer logs a `tracy.frame_mark` event every frame
// at Level::INFO. Formatted logs should omit it.
#[cfg(feature = "tracing-tracy")]
let fmt_layer =
fmt_layer.with_filter(tracing_subscriber::filter::FilterFn::new(|meta| {
meta.fields().field("tracy.frame_mark").is_none()
}));
let subscriber = subscriber.with(fmt_layer);
#[cfg(feature = "tracing-chrome")]
let subscriber = subscriber.with(chrome_layer);
#[cfg(feature = "tracing-tracy")]
let subscriber = subscriber.with(tracy_layer);
finished_subscriber = subscriber;
}
#[cfg(target_arch = "wasm32")]
{
finished_subscriber = subscriber.with(tracing_wasm::WASMLayer::new(
tracing_wasm::WASMLayerConfig::default(),
));
}
#[cfg(target_os = "android")]
{
finished_subscriber = subscriber.with(android_tracing::AndroidLayer::default());
}
#[cfg(target_os = "ios")]
{
finished_subscriber = subscriber.with(tracing_oslog::OsLogger::default());
}
let logger_already_set = LogTracer::init().is_err();
let subscriber_already_set =
tracing::subscriber::set_global_default(finished_subscriber).is_err();
match (logger_already_set, subscriber_already_set) {
(true, true) => error!(
"Could not set global logger and tracing subscriber as they are already set. Consider disabling LogPlugin."
),
(true, false) => error!("Could not set global logger as it is already set. Consider disabling LogPlugin."),
(false, true) => error!("Could not set global tracing subscriber as it is already set. Consider disabling LogPlugin."),
(false, false) => (),
}
}
}

View File

@ -1,344 +0,0 @@
use alloc::{
boxed::Box,
format,
string::{String, ToString},
};
use core::error::Error;
use std::eprintln;
use bevy_app::{App, Plugin};
use tracing::Level;
use tracing_log::LogTracer;
use tracing_subscriber::{
filter::{FromEnvError, ParseError},
prelude::*,
registry::Registry,
EnvFilter, Layer,
};
use crate::error;
#[cfg(all(target_os = "android", feature = "std"))]
use crate::android_tracing;
#[cfg(feature = "tracing-chrome")]
use {
bevy_ecs::resource::Resource,
bevy_utils::synccell::SyncCell,
tracing_subscriber::fmt::{format::DefaultFields, FormattedFields},
};
/// Wrapper resource for `tracing-chrome`'s flush guard.
/// When the guard is dropped the chrome log is written to file.
#[cfg(feature = "tracing-chrome")]
#[expect(
dead_code,
reason = "`FlushGuard` never needs to be read, it just needs to be kept alive for the `App`'s lifetime."
)]
#[derive(Resource)]
pub(crate) struct FlushGuard(SyncCell<tracing_chrome::FlushGuard>);
/// Adds logging to Apps. This plugin is part of the `DefaultPlugins`. Adding
/// this plugin will setup a collector appropriate to your target platform:
/// * Using [`tracing-subscriber`](https://crates.io/crates/tracing-subscriber) by default,
/// logging to `stdout`.
/// * Using [`android_log-sys`](https://crates.io/crates/android_log-sys) on Android,
/// logging to Android logs.
/// * Using [`tracing-wasm`](https://crates.io/crates/tracing-wasm) in Wasm, logging
/// to the browser console.
///
/// You can configure this plugin.
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::LogPlugin;
/// # use tracing::Level;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins.set(LogPlugin {
/// level: Level::DEBUG,
/// filter: "wgpu=error,bevy_render=info,bevy_ecs=trace".to_string(),
/// custom_layer: |_| None,
/// }))
/// .run();
/// }
/// ```
///
/// Log level can also be changed using the `RUST_LOG` environment variable.
/// For example, using `RUST_LOG=wgpu=error,bevy_render=info,bevy_ecs=trace cargo run ..`
///
/// It has the same syntax as the field [`LogPlugin::filter`], see [`EnvFilter`].
/// If you define the `RUST_LOG` environment variable, the [`LogPlugin`] settings
/// will be ignored.
///
/// Also, to disable color terminal output (ANSI escape codes), you can
/// set the environment variable `NO_COLOR` to any value. This common
/// convention is documented at [no-color.org](https://no-color.org/).
/// For example:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::LogPlugin;
/// fn main() {
/// # // SAFETY: Single-threaded
/// # unsafe {
/// std::env::set_var("NO_COLOR", "1");
/// # }
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .run();
/// }
/// ```
///
/// If you want to setup your own tracing collector, you should disable this
/// plugin from `DefaultPlugins`:
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::LogPlugin;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins.build().disable::<LogPlugin>())
/// .run();
/// }
/// ```
/// # Example Setup
///
/// For a quick setup that enables all first-party logging while not showing any of your dependencies'
/// log data, you can configure the plugin as shown below.
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::*;
/// App::new()
/// .add_plugins(DefaultPlugins.set(LogPlugin {
/// filter: "warn,my_crate=trace".to_string(), //specific filters
/// level: Level::TRACE,//Change this to be globally change levels
/// ..Default::default()
/// }))
/// .run();
/// ```
/// The filter (in this case an `EnvFilter`) chooses whether to print the log. The most specific filters apply with higher priority.
/// Let's start with an example: `filter: "warn".to_string()` will only print logs with level `warn` level or greater.
/// From here, we can change to `filter: "warn,my_crate=trace".to_string()`. Logs will print at level `warn` unless it's in `mycrate`,
/// which will instead print at `trace` level because `my_crate=trace` is more specific.
///
///
/// ## Log levels
/// Events can be logged at various levels of importance.
/// Only events at your configured log level and higher will be shown.
/// ```no_run
/// # use bevy_log::*;
/// // here is how you write new logs at each "log level" (in "most important" to
/// // "least important" order)
/// error!("something failed");
/// warn!("something bad happened that isn't a failure, but that's worth calling out");
/// info!("helpful information that is worth printing by default");
/// debug!("helpful for debugging");
/// trace!("very noisy");
/// ```
/// In addition to `format!` style arguments, you can print a variable's debug
/// value by using syntax like: `trace(?my_value)`.
///
/// ## Per module logging levels
/// Modules can have different logging levels using syntax like `crate_name::module_name=debug`.
///
///
/// ```no_run
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_log::*;
/// App::new()
/// .add_plugins(DefaultPlugins.set(LogPlugin {
/// filter: "warn,my_crate=trace,my_crate::my_module=debug".to_string(), // Specific filters
/// level: Level::TRACE, // Change this to be globally change levels
/// ..Default::default()
/// }))
/// .run();
/// ```
/// The idea is that instead of deleting logs when they are no longer immediately applicable,
/// you just disable them. If you do need to log in the future, then you can enable the logs instead of having to rewrite them.
///
/// ## Further reading
///
/// The `tracing` crate has much more functionality than these examples can show.
/// Much of this configuration can be done with "layers" in the `log` crate.
/// Check out:
/// - Using spans to add more fine grained filters to logs
/// - Adding instruments to capture more function information
/// - Creating layers to add additional context such as line numbers
/// # Panics
///
/// This plugin should not be added multiple times in the same process. This plugin
/// sets up global logging configuration for **all** Apps in a given process, and
/// rerunning the same initialization multiple times will lead to a panic.
///
/// # Performance
///
/// Filters applied through this plugin are computed at _runtime_, which will
/// have a non-zero impact on performance.
/// To achieve maximum performance, consider using
/// [_compile time_ filters](https://docs.rs/log/#compile-time-filters)
/// provided by the [`log`](https://crates.io/crates/log) crate.
///
/// ```toml
/// # cargo.toml
/// [dependencies]
/// log = { version = "0.4", features = ["max_level_debug", "release_max_level_warn"] }
/// ```
pub struct LogPlugin {
/// Filters logs using the [`EnvFilter`] format
pub filter: String,
/// Filters out logs that are "less than" the given level.
/// This can be further filtered using the `filter` setting.
pub level: Level,
/// Optionally add an extra [`Layer`] to the tracing subscriber
///
/// This function is only called once, when the plugin is built.
///
/// Because [`BoxedLayer`] takes a `dyn Layer`, `Vec<Layer>` is also an acceptable return value.
///
/// Access to [`App`] is also provided to allow for communication between the
/// [`Subscriber`](tracing::Subscriber) and the [`App`].
///
/// Please see the `examples/log_layers.rs` for a complete example.
pub custom_layer: fn(app: &mut App) -> Option<BoxedLayer>,
}
/// A boxed [`Layer`] that can be used with [`LogPlugin`].
pub type BoxedLayer = Box<dyn Layer<Registry> + Send + Sync + 'static>;
/// The default [`LogPlugin`] [`EnvFilter`].
pub const DEFAULT_FILTER: &str = "wgpu=error,naga=warn";
impl Default for LogPlugin {
fn default() -> Self {
Self {
filter: DEFAULT_FILTER.to_string(),
level: Level::INFO,
custom_layer: |_| None,
}
}
}
impl Plugin for LogPlugin {
#[expect(clippy::print_stderr, reason = "Allowed during logger setup")]
fn build(&self, app: &mut App) {
#[cfg(feature = "trace")]
{
let old_handler = std::panic::take_hook();
std::panic::set_hook(Box::new(move |infos| {
eprintln!("{}", tracing_error::SpanTrace::capture());
old_handler(infos);
}));
}
let finished_subscriber;
let subscriber = Registry::default();
// add optional layer provided by user
let subscriber = subscriber.with((self.custom_layer)(app));
let default_filter = { format!("{},{}", self.level, self.filter) };
let filter_layer = EnvFilter::try_from_default_env()
.or_else(|from_env_error| {
_ = from_env_error
.source()
.and_then(|source| source.downcast_ref::<ParseError>())
.map(|parse_err| {
// we cannot use the `error!` macro here because the logger is not ready yet.
eprintln!("LogPlugin failed to parse filter from env: {}", parse_err);
});
Ok::<EnvFilter, FromEnvError>(EnvFilter::builder().parse_lossy(&default_filter))
})
.unwrap();
let subscriber = subscriber.with(filter_layer);
#[cfg(feature = "trace")]
let subscriber = subscriber.with(tracing_error::ErrorLayer::default());
#[cfg(all(
not(target_arch = "wasm32"),
not(target_os = "android"),
not(target_os = "ios")
))]
{
#[cfg(feature = "tracing-chrome")]
let chrome_layer = {
let mut layer = tracing_chrome::ChromeLayerBuilder::new();
if let Ok(path) = std::env::var("TRACE_CHROME") {
layer = layer.file(path);
}
let (chrome_layer, guard) = layer
.name_fn(Box::new(|event_or_span| match event_or_span {
tracing_chrome::EventOrSpan::Event(event) => event.metadata().name().into(),
tracing_chrome::EventOrSpan::Span(span) => {
if let Some(fields) =
span.extensions().get::<FormattedFields<DefaultFields>>()
{
format!("{}: {}", span.metadata().name(), fields.fields.as_str())
} else {
span.metadata().name().into()
}
}
}))
.build();
app.insert_resource(FlushGuard(SyncCell::new(guard)));
chrome_layer
};
#[cfg(feature = "tracing-tracy")]
let tracy_layer = tracing_tracy::TracyLayer::default();
// note: the implementation of `Default` reads from the env var NO_COLOR
// to decide whether to use ANSI color codes, which is common convention
// https://no-color.org/
let fmt_layer = tracing_subscriber::fmt::Layer::default().with_writer(std::io::stderr);
// bevy_render::renderer logs a `tracy.frame_mark` event every frame
// at Level::INFO. Formatted logs should omit it.
#[cfg(feature = "tracing-tracy")]
let fmt_layer =
fmt_layer.with_filter(tracing_subscriber::filter::FilterFn::new(|meta| {
meta.fields().field("tracy.frame_mark").is_none()
}));
let subscriber = subscriber.with(fmt_layer);
#[cfg(feature = "tracing-chrome")]
let subscriber = subscriber.with(chrome_layer);
#[cfg(feature = "tracing-tracy")]
let subscriber = subscriber.with(tracy_layer);
finished_subscriber = subscriber;
}
#[cfg(target_arch = "wasm32")]
{
finished_subscriber = subscriber.with(tracing_wasm::WASMLayer::new(
tracing_wasm::WASMLayerConfig::default(),
));
}
#[cfg(all(target_os = "android", feature = "std"))]
{
finished_subscriber = subscriber.with(android_tracing::AndroidLayer::default());
}
#[cfg(target_os = "ios")]
{
finished_subscriber = subscriber.with(tracing_oslog::OsLogger::default());
}
let logger_already_set = LogTracer::init().is_err();
let subscriber_already_set =
tracing::subscriber::set_global_default(finished_subscriber).is_err();
match (logger_already_set, subscriber_already_set) {
(true, true) => error!(
"Could not set global logger and tracing subscriber as they are already set. Consider disabling LogPlugin."
),
(true, false) => error!("Could not set global logger as it is already set. Consider disabling LogPlugin."),
(false, true) => error!("Could not set global tracing subscriber as it is already set. Consider disabling LogPlugin."),
(false, false) => (),
}
}
}

View File

@ -41,21 +41,6 @@ unicode-bidi = "0.3.13"
sys-locale = "0.3.0"
tracing = { version = "0.1", default-features = false, features = ["std"] }
[target.'cfg(target_arch = "wasm32")'.dependencies]
# TODO: Assuming all wasm builds are for the browser. Require `no_std` support to break assumption.
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_app = { path = "../bevy_app", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_platform = { path = "../bevy_platform", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
[dev-dependencies]
approx = "0.5.1"

View File

@ -70,8 +70,6 @@ std = [
"alloc",
"bevy_app?/std",
"bevy_log",
"bevy_log?/std",
"bevy_log?/tracing",
"bevy_ecs?/std",
"bevy_math/std",
"bevy_reflect?/std",

View File

@ -80,9 +80,6 @@ bevy_platform = { path = "../bevy_platform", version = "0.16.0-rc.4", default-fe
bevy_reflect = { path = "../bevy_reflect", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
bevy_log = { path = "../bevy_log", version = "0.16.0-rc.4", default-features = false, features = [
"web",
] }
[lints]
workspace = true

View File

@ -24,6 +24,7 @@ The default feature set enables most of the expected features of a game engine,
|bevy_gizmos|Adds support for rendering gizmos|
|bevy_gltf|[glTF](https://www.khronos.org/gltf/) support|
|bevy_input_focus|Enable input focus subsystem|
|bevy_log|Enable integration with `tracing` and `log`|
|bevy_mesh_picking_backend|Provides an implementation for picking meshes|
|bevy_pbr|Adds PBR rendering|
|bevy_picking|Provides picking functionality|
@ -47,7 +48,6 @@ The default feature set enables most of the expected features of a game engine,
|std|Allows access to the `std` crate.|
|sysinfo_plugin|Enables system information diagnostic plugin|
|tonemapping_luts|Include tonemapping Look Up Tables KTX2 files. If everything is pink, you need to enable this feature or change the `Tonemapping` method for your `Camera2d` or `Camera3d`.|
|tracing|Tracing support|
|vorbis|OGG/VORBIS audio format support|
|webgl2|Enable some limitations to be able to use WebGL2. Please refer to the [WebGL2 and WebGPU](https://github.com/bevyengine/bevy/tree/latest/examples#webgl2-and-webgpu) section of the examples README for more information on how to run Wasm builds with WebGPU.|
|x11|X11 display server support|
@ -114,7 +114,7 @@ The default feature set enables most of the expected features of a game engine,
|symphonia-wav|WAV audio format support (through symphonia)|
|tga|TGA image format support|
|tiff|TIFF image format support|
|trace|Enables traces within Bevy using tracing|
|trace|Tracing support|
|trace_chrome|Tracing support, saving a file in Chrome Tracing format|
|trace_tracy|Tracing support, exposing a port for Tracy|
|trace_tracy_memory|Tracing support, with memory profiling, exposing a port for Tracy|