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 support for adding custom log layers #7682

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -657,6 +657,16 @@ description = "Illustrate how to use generate log output"
category = "Application"
wasm = true

[[example]]
name = "log_layers"
path = "examples/app/log_layers.rs"

[package.metadata.example.log_layers]
name = "Log layers"
description = "Illustrate how to add custom log layers"
category = "Application"
wasm = false

[[example]]
name = "plugin"
path = "examples/app/plugin.rs"
Expand Down
32 changes: 30 additions & 2 deletions crates/bevy_log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ pub mod prelude {
};
}

use bevy_utils::tracing::Subscriber;
pub use bevy_utils::tracing::{
debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span,
Level,
};

use bevy_app::{App, Plugin};
use tracing_log::LogTracer;
pub use tracing_subscriber;
#[cfg(feature = "tracing-chrome")]
use tracing_subscriber::fmt::{format::DefaultFields, FormattedFields};
use tracing_subscriber::{prelude::*, registry::Registry, EnvFilter};
Expand Down Expand Up @@ -91,13 +93,29 @@ pub struct LogPlugin {
/// Filters out logs that are "less than" the given level.
/// This can be further filtered using the `filter` setting.
pub level: Level,

// todo: better way of passing layers to plugin
pub extra_layers: std::sync::Arc<
std::sync::Mutex<
Option<
Vec<
Box<
dyn tracing_subscriber::layer::Layer<Box<dyn Subscriber + Send + Sync>>
+ Send
+ Sync,
>,
>,
>,
>,
>,
}

impl Default for LogPlugin {
fn default() -> Self {
Self {
filter: "wgpu=error".to_string(),
level: Level::INFO,
extra_layers: Default::default(),
}
}
}
Expand Down Expand Up @@ -187,8 +205,18 @@ impl Plugin for LogPlugin {
}

let logger_already_set = LogTracer::init().is_err();
let subscriber_already_set =
bevy_utils::tracing::subscriber::set_global_default(finished_subscriber).is_err();

let extra_layers = self.extra_layers.lock().unwrap().take();

let subscriber_already_set = if let Some(layers) = extra_layers {
let subscriber = Box::new(finished_subscriber);
let subscriber = layers.with_subscriber(subscriber);
// let subscriber = subscriber.with(self.extra_layers);
bevy_utils::tracing::subscriber::set_global_default(subscriber)
} else {
bevy_utils::tracing::subscriber::set_global_default(finished_subscriber)
}
.is_err();

match (logger_already_set, subscriber_already_set) {
(true, true) => warn!(
Expand Down
116 changes: 116 additions & 0 deletions examples/app/log_layers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//! This example illustrates how to add custom log layers in bevy.

use bevy::{
log::tracing_subscriber::Layer,
prelude::*,
utils::tracing::{field::Visit, Subscriber},
};
use std::sync::{Arc, Mutex};

struct DebugLogToStdErrLayer;

impl<S: Subscriber> Layer<S> for DebugLogToStdErrLayer {
fn on_event(
&self,
event: &bevy::utils::tracing::Event<'_>,
_ctx: bevy::log::tracing_subscriber::layer::Context<'_, S>,
) {
// pretty print received events to std err, including metadata
eprintln!("logged my way: {event:#?}");
}
}

struct ChannelLayer {
sender: crossbeam_channel::Sender<String>,
max_level: bevy::log::Level,
}

#[derive(Resource)]
struct ErrorMessageReceiver(crossbeam_channel::Receiver<String>);

#[derive(Component)]
struct LastErrorText;

impl<S: Subscriber> Layer<S> for ChannelLayer {
fn on_event(
&self,
event: &bevy::utils::tracing::Event<'_>,
_ctx: bevy::log::tracing_subscriber::layer::Context<'_, S>,
) {
if event.metadata().level() <= &self.max_level {
let mut visitor = ChannelSendVisitor(self.sender.clone());
event.record(&mut visitor);
}
}
}

struct ChannelSendVisitor(crossbeam_channel::Sender<String>);

impl Visit for ChannelSendVisitor {
fn record_debug(
&mut self,
_field: &bevy::utils::tracing::field::Field,
value: &dyn std::fmt::Debug,
) {
// will fail if the receiver is dropped. In that case, we do nothing.
_ = self.0.try_send(format!("{value:?}"));
}
}

fn main() {
let (sender, receiver) = crossbeam_channel::unbounded();

let log_to_screen_layer = ChannelLayer {
sender,
max_level: bevy::log::Level::ERROR,
};

App::new()
.insert_resource(ErrorMessageReceiver(receiver))
.add_plugins(DefaultPlugins.set(bevy::log::LogPlugin {
// todo: fix horrible hack
extra_layers: Arc::new(Mutex::new(Some(vec![
log_to_screen_layer.boxed(),
DebugLogToStdErrLayer.boxed(),
]))),
..default()
}))
.add_startup_system(log_system)
.add_startup_system(text_setup)
.add_system(update_screen_text)
.run();
}

fn text_setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2dBundle::default());

commands.spawn((
TextBundle::from_sections([TextSection::from_style(TextStyle {
font: asset_server.load("fonts/FiraMono-Medium.ttf"),
font_size: 60.0,
color: Color::MAROON,
})]),
LastErrorText,
));
}

fn log_system() {
// here is how you write new logs at each "log level" (in "most import" to
// "least important" order)
error!("something failed");
warn!("something bad happened that isn't a failure, but thats worth calling out");
info!("helpful information that is worth printing by default");
debug!("helpful for debugging");
trace!("very noisy");
}

fn update_screen_text(
errors: Res<ErrorMessageReceiver>,
mut query: Query<&mut Text, With<LastErrorText>>,
) {
for error in errors.0.try_iter() {
if let Ok(mut text) = query.get_single_mut() {
text.sections[0].value = error;
}
}
}