From dc48fd41e8761fc15aeb94d282d45875cd485a64 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 14 Apr 2025 14:15:01 -0700 Subject: [PATCH] Revert "Allow partial support for `bevy_log` in `no_std` (#18782)" (#18816) This reverts commit ac52cca033b351cc966cd3d40eb99ffbefbdb104. 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 --- Cargo.toml | 12 +- crates/bevy_animation/Cargo.toml | 12 - crates/bevy_gltf/Cargo.toml | 18 - crates/bevy_internal/Cargo.toml | 23 +- crates/bevy_internal/src/default_plugins.rs | 2 +- crates/bevy_internal/src/lib.rs | 1 + crates/bevy_internal/src/prelude.rs | 10 +- crates/bevy_log/Cargo.toml | 42 +-- crates/bevy_log/src/android_tracing.rs | 2 +- crates/bevy_log/src/lib.rs | 364 ++++++++++++++++++-- crates/bevy_log/src/plugin.rs | 344 ------------------ crates/bevy_text/Cargo.toml | 15 - crates/bevy_transform/Cargo.toml | 2 - crates/bevy_winit/Cargo.toml | 3 - docs/cargo_features.md | 4 +- 15 files changed, 378 insertions(+), 476 deletions(-) delete mode 100644 crates/bevy_log/src/plugin.rs diff --git a/Cargo.toml b/Cargo.toml index 6c6ab86c79..ffe6a9ac41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/crates/bevy_animation/Cargo.toml b/crates/bevy_animation/Cargo.toml index 97543d9c6c..43b5193a8e 100644 --- a/crates/bevy_animation/Cargo.toml +++ b/crates/bevy_animation/Cargo.toml @@ -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 diff --git a/crates/bevy_gltf/Cargo.toml b/crates/bevy_gltf/Cargo.toml index c1c03189b7..943b37f5f3 100644 --- a/crates/bevy_gltf/Cargo.toml +++ b/crates/bevy_gltf/Cargo.toml @@ -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" } diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index 4f220ff820..daed5a132a 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -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 = [ diff --git a/crates/bevy_internal/src/default_plugins.rs b/crates/bevy_internal/src/default_plugins.rs index c601506af1..16aad774d3 100644 --- a/crates/bevy_internal/src/default_plugins.rs +++ b/crates/bevy_internal/src/default_plugins.rs @@ -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, diff --git a/crates/bevy_internal/src/lib.rs b/crates/bevy_internal/src/lib.rs index e42d69b655..65411ad050 100644 --- a/crates/bevy_internal/src/lib.rs +++ b/crates/bevy_internal/src/lib.rs @@ -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")] diff --git a/crates/bevy_internal/src/prelude.rs b/crates/bevy_internal/src/prelude.rs index caa8e2a67f..26d5c7e2af 100644 --- a/crates/bevy_internal/src/prelude.rs +++ b/crates/bevy_internal/src/prelude.rs @@ -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::*; diff --git a/crates/bevy_log/Cargo.toml b/crates/bevy_log/Cargo.toml index 66a9ae2784..1a68c09f38 100644 --- a/crates/bevy_log/Cargo.toml +++ b/crates/bevy_log/Cargo.toml @@ -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" diff --git a/crates/bevy_log/src/android_tracing.rs b/crates/bevy_log/src/android_tracing.rs index fc9931607d..ba0b3b7a27 100644 --- a/crates/bevy_log/src/android_tracing.rs +++ b/crates/bevy_log/src/android_tracing.rs @@ -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, diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 1bcf20709f..055395bad7 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -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 = /// 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); + +/// 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::()) +/// .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` 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, +} + +/// A boxed [`Layer`] that can be used with [`LogPlugin`]. +pub type BoxedLayer = Box + 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::()) + .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::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::>() + { + 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) => (), + } + } +} diff --git a/crates/bevy_log/src/plugin.rs b/crates/bevy_log/src/plugin.rs deleted file mode 100644 index 10745dc03c..0000000000 --- a/crates/bevy_log/src/plugin.rs +++ /dev/null @@ -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); - -/// 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::()) -/// .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` 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, -} - -/// A boxed [`Layer`] that can be used with [`LogPlugin`]. -pub type BoxedLayer = Box + 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::()) - .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::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::>() - { - 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) => (), - } - } -} diff --git a/crates/bevy_text/Cargo.toml b/crates/bevy_text/Cargo.toml index 6c90b3c39a..bb060f36e3 100644 --- a/crates/bevy_text/Cargo.toml +++ b/crates/bevy_text/Cargo.toml @@ -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" diff --git a/crates/bevy_transform/Cargo.toml b/crates/bevy_transform/Cargo.toml index 2901db306e..031d1adc32 100644 --- a/crates/bevy_transform/Cargo.toml +++ b/crates/bevy_transform/Cargo.toml @@ -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", diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index aab736951f..647f2a8738 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -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 diff --git a/docs/cargo_features.md b/docs/cargo_features.md index e62747b29f..7f335dfd56 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -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|