use std::{
io::{BufRead, Write},
sync::{Arc, Mutex},
};
use bevy::{
app::{App, Update},
log::tracing_subscriber::{self, EnvFilter, Layer, Registry},
prelude::{IntoScheduleConfigs, MessageWriter, ResMut, Resource},
};
use crate::{ConsoleSet, PrintConsoleLine};
#[derive(Resource)]
pub struct BevyLogBuffer(Arc<Mutex<std::io::Cursor<Vec<u8>>>>);
pub struct BevyLogBufferWriter(Arc<Mutex<std::io::Cursor<Vec<u8>>>>);
impl Write for BevyLogBufferWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let mut lock = self
.0
.lock()
.map_err(|e| std::io::Error::other(format!("Failed to lock buffer: {}", e)))?;
lock.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
let mut lock = self
.0
.lock()
.map_err(|e| std::io::Error::other(format!("Failed to lock buffer: {}", e)))?;
lock.flush()
}
}
pub fn send_log_buffer_to_console(
buffer: ResMut<BevyLogBuffer>,
mut console_lines: MessageWriter<PrintConsoleLine>,
) {
let mut buffer = buffer.0.lock().unwrap();
let buffer = buffer.get_mut();
for line in buffer.lines().map_while(Result::ok) {
console_lines.write(PrintConsoleLine { line });
}
buffer.clear();
}
pub fn make_layer(
app: &mut App,
) -> Option<Box<dyn tracing_subscriber::Layer<Registry> + Send + Sync>> {
setup_layer(app, None)
}
pub fn make_filtered_layer(
app: &mut App,
filter: String,
) -> Option<Box<dyn tracing_subscriber::Layer<Registry> + Send + Sync>> {
let env_filter = EnvFilter::builder().parse_lossy(filter);
setup_layer(app, Some(env_filter))
}
fn setup_layer(
app: &mut App,
filter: Option<EnvFilter>,
) -> Option<Box<dyn tracing_subscriber::Layer<Registry> + Send + Sync>> {
let buffer = Arc::new(Mutex::new(std::io::Cursor::new(Vec::new())));
app.insert_resource(BevyLogBuffer(buffer.clone()));
app.add_systems(
Update,
send_log_buffer_to_console.in_set(ConsoleSet::PostCommands),
);
let layer: Box<dyn tracing_subscriber::Layer<Registry> + Send + Sync> = match filter {
Some(filter) => Box::new(
tracing_subscriber::fmt::Layer::new()
.with_target(false)
.with_ansi(true)
.with_writer(move || BevyLogBufferWriter(buffer.clone()))
.with_filter(filter),
),
None => Box::new(
tracing_subscriber::fmt::Layer::new()
.with_target(false)
.with_ansi(true)
.with_writer(move || BevyLogBufferWriter(buffer.clone())),
),
};
Some(layer)
}