Skip to content

Commit 1079b83

Browse files
Revert "bevy_log: refactor how log layers are wired together (#19248)" (#19705)
This reverts commit 8661e91, aka #19248. Fixes #19689.
1 parent a750cfe commit 1079b83

File tree

2 files changed

+100
-138
lines changed

2 files changed

+100
-138
lines changed

crates/bevy_log/src/lib.rs

Lines changed: 95 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ use bevy_app::{App, Plugin};
5656
use tracing_log::LogTracer;
5757
use tracing_subscriber::{
5858
filter::{FromEnvError, ParseError},
59+
layer::Layered,
5960
prelude::*,
6061
registry::Registry,
6162
EnvFilter, Layer,
@@ -83,8 +84,6 @@ pub(crate) struct FlushGuard(SyncCell<tracing_chrome::FlushGuard>);
8384
/// logging to `stdout`.
8485
/// * Using [`android_log-sys`](https://crates.io/crates/android_log-sys) on Android,
8586
/// logging to Android logs.
86-
/// * Using [`tracing_oslog`](https://crates.io/crates/tracing_oslog) on iOS,
87-
/// logging to iOS logs.
8887
/// * Using [`tracing-wasm`](https://crates.io/crates/tracing-wasm) in Wasm, logging
8988
/// to the browser console.
9089
///
@@ -255,15 +254,26 @@ pub struct LogPlugin {
255254
/// timestamp from the log output.
256255
///
257256
/// Please see the `examples/log_layers.rs` for a complete example.
258-
///
259-
/// Note that this field has no effect when `os_target` is `android`, `ios` or `wasm`, as on those
260-
/// platforms we don't use [`tracing_subscriber::fmt::Layer`] but rather the platform default.
261-
pub fmt_layer: fn(app: &mut App) -> Option<BoxedLayer>,
257+
pub fmt_layer: fn(app: &mut App) -> Option<BoxedFmtLayer>,
262258
}
263259

264260
/// A boxed [`Layer`] that can be used with [`LogPlugin::custom_layer`].
265261
pub type BoxedLayer = Box<dyn Layer<Registry> + Send + Sync + 'static>;
266262

263+
#[cfg(feature = "trace")]
264+
type BaseSubscriber =
265+
Layered<EnvFilter, Layered<Option<Box<dyn Layer<Registry> + Send + Sync>>, Registry>>;
266+
267+
#[cfg(feature = "trace")]
268+
type PreFmtSubscriber = Layered<tracing_error::ErrorLayer<BaseSubscriber>, BaseSubscriber>;
269+
270+
#[cfg(not(feature = "trace"))]
271+
type PreFmtSubscriber =
272+
Layered<EnvFilter, Layered<Option<Box<dyn Layer<Registry> + Send + Sync>>, Registry>>;
273+
274+
/// A boxed [`Layer`] that can be used with [`LogPlugin::fmt_layer`].
275+
pub type BoxedFmtLayer = Box<dyn Layer<PreFmtSubscriber> + Send + Sync + 'static>;
276+
267277
/// The default [`LogPlugin`] [`EnvFilter`].
268278
pub const DEFAULT_FILTER: &str = "wgpu=error,naga=warn";
269279

@@ -290,25 +300,30 @@ impl Plugin for LogPlugin {
290300
}));
291301
}
292302

293-
// We use a Vec of BoxedLayer instead of adding each layer individually using the
294-
// `layer.with(next_layer)`.
295-
// Otherwise, the types of each successive layer becomes unwieldy,
296-
// as the type of each new layer would depend on the types of the previous layers.
297-
let mut layers: Vec<BoxedLayer> = Vec::new();
298-
299-
// Add optional layer provided by user
300-
// As they are added first, any of the following layers won't be applied.
301-
// In particular, it won't be affected by the filtering we put in place next.
302-
if let Some(layer) = (self.custom_layer)(app) {
303-
layers.push(layer);
304-
}
303+
let finished_subscriber;
304+
let subscriber = Registry::default();
305305

306-
layers.push(Self::build_filter_layer(self.level, &self.filter));
306+
// add optional layer provided by user
307+
let subscriber = subscriber.with((self.custom_layer)(app));
307308

308-
#[cfg(feature = "trace")]
309-
layers.push(tracing_error::ErrorLayer::default().boxed());
309+
let default_filter = { format!("{},{}", self.level, self.filter) };
310+
let filter_layer = EnvFilter::try_from_default_env()
311+
.or_else(|from_env_error| {
312+
_ = from_env_error
313+
.source()
314+
.and_then(|source| source.downcast_ref::<ParseError>())
315+
.map(|parse_err| {
316+
// we cannot use the `error!` macro here because the logger is not ready yet.
317+
eprintln!("LogPlugin failed to parse filter from env: {}", parse_err);
318+
});
319+
320+
Ok::<EnvFilter, FromEnvError>(EnvFilter::builder().parse_lossy(&default_filter))
321+
})
322+
.unwrap();
323+
let subscriber = subscriber.with(filter_layer);
310324

311-
layers.push(Self::build_system_output_layer((self.fmt_layer)(app)));
325+
#[cfg(feature = "trace")]
326+
let subscriber = subscriber.with(tracing_error::ErrorLayer::default());
312327

313328
#[cfg(all(
314329
not(target_arch = "wasm32"),
@@ -317,136 +332,84 @@ impl Plugin for LogPlugin {
317332
))]
318333
{
319334
#[cfg(feature = "tracing-chrome")]
320-
{
321-
let (chrome_layer, guard) = Self::build_chrome_layer();
335+
let chrome_layer = {
336+
let mut layer = tracing_chrome::ChromeLayerBuilder::new();
337+
if let Ok(path) = std::env::var("TRACE_CHROME") {
338+
layer = layer.file(path);
339+
}
340+
let (chrome_layer, guard) = layer
341+
.name_fn(Box::new(|event_or_span| match event_or_span {
342+
tracing_chrome::EventOrSpan::Event(event) => event.metadata().name().into(),
343+
tracing_chrome::EventOrSpan::Span(span) => {
344+
if let Some(fields) =
345+
span.extensions().get::<FormattedFields<DefaultFields>>()
346+
{
347+
format!("{}: {}", span.metadata().name(), fields.fields.as_str())
348+
} else {
349+
span.metadata().name().into()
350+
}
351+
}
352+
}))
353+
.build();
322354
app.insert_resource(FlushGuard(SyncCell::new(guard)));
323-
layers.push(chrome_layer);
324-
}
325-
#[cfg(feature = "tracing-tracy")]
326-
layers.push(tracing_tracy::TracyLayer::default().boxed());
327-
}
355+
chrome_layer
356+
};
328357

329-
let subscriber = Registry::default().with(layers);
330-
331-
let logger_already_set = LogTracer::init().is_err();
332-
let subscriber_already_set = tracing::subscriber::set_global_default(subscriber).is_err();
333-
334-
match (logger_already_set, subscriber_already_set) {
335-
(true, true) => error!(
336-
"Could not set global logger and tracing subscriber as they are already set. Consider disabling LogPlugin."
337-
),
338-
(true, false) => error!("Could not set global logger as it is already set. Consider disabling LogPlugin."),
339-
(false, true) => error!("Could not set global tracing subscriber as it is already set. Consider disabling LogPlugin."),
340-
(false, false) => (),
341-
}
342-
}
343-
}
358+
#[cfg(feature = "tracing-tracy")]
359+
let tracy_layer = tracing_tracy::TracyLayer::default();
344360

345-
impl LogPlugin {
346-
/// Build a [`BoxedLayer`] that will filter which logs are outputted.
347-
/// It will read the `RUST_LOG` env variable to override the settings
348-
/// on a given run, the default will fallback to the provided `level` and `filter`
349-
fn build_filter_layer(level: Level, filter: &str) -> BoxedLayer {
350-
let default_filter = { format!("{},{}", level, filter) };
361+
let fmt_layer = (self.fmt_layer)(app).unwrap_or_else(|| {
362+
// note: the implementation of `Default` reads from the env var NO_COLOR
363+
// to decide whether to use ANSI color codes, which is common convention
364+
// https://no-color.org/
365+
Box::new(tracing_subscriber::fmt::Layer::default().with_writer(std::io::stderr))
366+
});
351367

352-
EnvFilter::try_from_default_env()
353-
.or_else(|from_env_error| {
354-
_ = from_env_error
355-
.source()
356-
.and_then(|source| source.downcast_ref::<ParseError>())
357-
.map(|parse_err| {
358-
#[expect(
359-
clippy::print_stderr,
360-
reason = "We cannot use the `error!` macro here because the logger is not ready yet."
361-
)]
362-
{
363-
eprintln!("LogPlugin failed to parse filter from env: {}", parse_err);
364-
}
365-
});
368+
// bevy_render::renderer logs a `tracy.frame_mark` event every frame
369+
// at Level::INFO. Formatted logs should omit it.
370+
#[cfg(feature = "tracing-tracy")]
371+
let fmt_layer =
372+
fmt_layer.with_filter(tracing_subscriber::filter::FilterFn::new(|meta| {
373+
meta.fields().field("tracy.frame_mark").is_none()
374+
}));
366375

367-
Ok::<EnvFilter, FromEnvError>(EnvFilter::builder().parse_lossy(&default_filter))
368-
})
369-
.unwrap().boxed()
370-
}
376+
let subscriber = subscriber.with(fmt_layer);
371377

372-
#[cfg(feature = "tracing-chrome")]
373-
/// [`BoxedLayer`] to build the necessary output when the `tracing-chrome` feature is enabled.
374-
/// The [`tracing_chrome::FlushGuard`] must be kept around till we don't need to output logs
375-
/// any more
376-
fn build_chrome_layer() -> (BoxedLayer, tracing_chrome::FlushGuard) {
377-
let mut layer = tracing_chrome::ChromeLayerBuilder::new();
378-
if let Ok(path) = std::env::var("TRACE_CHROME") {
379-
layer = layer.file(path);
378+
#[cfg(feature = "tracing-chrome")]
379+
let subscriber = subscriber.with(chrome_layer);
380+
#[cfg(feature = "tracing-tracy")]
381+
let subscriber = subscriber.with(tracy_layer);
382+
finished_subscriber = subscriber;
380383
}
381-
let (chrome_layer, guard) = layer
382-
.name_fn(Box::new(|event_or_span| match event_or_span {
383-
tracing_chrome::EventOrSpan::Event(event) => event.metadata().name().into(),
384-
tracing_chrome::EventOrSpan::Span(span) => {
385-
if let Some(fields) = span.extensions().get::<FormattedFields<DefaultFields>>()
386-
{
387-
format!("{}: {}", span.metadata().name(), fields.fields.as_str())
388-
} else {
389-
span.metadata().name().into()
390-
}
391-
}
392-
}))
393-
.build();
394-
(chrome_layer.boxed(), guard)
395-
}
396384

397-
#[expect(
398-
clippy::allow_attributes,
399-
reason = "We can't switch to `expect` for allow(unused_variables) as we use it if not on those platforms"
400-
)]
401-
#[allow(unused_variables, reason = "Not used on `wasm32`, `android` or `ios")]
402-
/// Build a [`BoxedLayer`] that outputs logs to the system default.
403-
/// On most platforms, it will be `stderr` with [`tracing_subscriber::fmt::Layer`], expect on `android`, `ios` and` wasm32` where it
404-
/// uses those system default log infrastructure.
405-
/// It is possible to override how you output those logs by providing a `custom_format_layer`.
406-
/// Note that won't have an effect on platform that don't use [`tracing_subscriber::fmt::Layer`]
407-
fn build_system_output_layer(custom_format_layer: Option<BoxedLayer>) -> BoxedLayer {
408-
let layer: BoxedLayer;
409385
#[cfg(target_arch = "wasm32")]
410386
{
411-
layer = tracing_wasm::WASMLayer::new(tracing_wasm::WASMLayerConfig::default()).boxed();
387+
finished_subscriber = subscriber.with(tracing_wasm::WASMLayer::new(
388+
tracing_wasm::WASMLayerConfig::default(),
389+
));
412390
}
413391

414392
#[cfg(target_os = "android")]
415393
{
416-
layer = android_tracing::AndroidLayer::default().boxed();
394+
finished_subscriber = subscriber.with(android_tracing::AndroidLayer::default());
417395
}
418396

419397
#[cfg(target_os = "ios")]
420398
{
421-
layer = tracing_oslog::OsLogger::default().boxed();
399+
finished_subscriber = subscriber.with(tracing_oslog::OsLogger::default());
422400
}
423401

424-
#[cfg(all(
425-
not(target_arch = "wasm32"),
426-
not(target_os = "android"),
427-
not(target_os = "ios")
428-
))]
429-
{
430-
layer = {
431-
let fmt_layer = custom_format_layer.unwrap_or_else(|| {
432-
tracing_subscriber::fmt::Layer::default()
433-
// note: the implementation of `Default` reads from the env var NO_COLOR
434-
// to decide whether to use ANSI color codes, which is common convention
435-
// https://no-color.org/
436-
.with_writer(std::io::stderr)
437-
.boxed()
438-
});
439-
440-
// bevy_render::renderer logs a `tracy.frame_mark` event every frame
441-
// at Level::INFO. Formatted logs should omit it.
442-
#[cfg(feature = "tracing-tracy")]
443-
let fmt_layer =
444-
fmt_layer.with_filter(tracing_subscriber::filter::FilterFn::new(|meta| {
445-
meta.fields().field("tracy.frame_mark").is_none()
446-
}));
447-
fmt_layer.boxed()
448-
}
402+
let logger_already_set = LogTracer::init().is_err();
403+
let subscriber_already_set =
404+
tracing::subscriber::set_global_default(finished_subscriber).is_err();
405+
406+
match (logger_already_set, subscriber_already_set) {
407+
(true, true) => error!(
408+
"Could not set global logger and tracing subscriber as they are already set. Consider disabling LogPlugin."
409+
),
410+
(true, false) => error!("Could not set global logger as it is already set. Consider disabling LogPlugin."),
411+
(false, true) => error!("Could not set global tracing subscriber as it is already set. Consider disabling LogPlugin."),
412+
(false, false) => (),
449413
}
450-
layer
451414
}
452415
}

examples/app/log_layers.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bevy::{
44
log::{
55
tracing::{self, Subscriber},
66
tracing_subscriber::Layer,
7-
BoxedLayer,
7+
BoxedFmtLayer, BoxedLayer,
88
},
99
prelude::*,
1010
};
@@ -41,13 +41,12 @@ fn custom_layer(_app: &mut App) -> Option<BoxedLayer> {
4141
// `fmt_layer` option.
4242
//
4343
// In this example, we're disabling the timestamp in the log output.
44-
fn fmt_layer(_app: &mut App) -> Option<BoxedLayer> {
45-
Some(
44+
fn fmt_layer(_app: &mut App) -> Option<BoxedFmtLayer> {
45+
Some(Box::new(
4646
bevy::log::tracing_subscriber::fmt::Layer::default()
4747
.without_time()
48-
.with_writer(std::io::stderr)
49-
.boxed(),
50-
)
48+
.with_writer(std::io::stderr),
49+
))
5150
}
5251

5352
fn main() {

0 commit comments

Comments
 (0)