mirror of
https://github.com/Xevion/Pac-Man.git
synced 2025-12-06 07:15:41 -06:00
feat: buffer tracing logs before console init
This commit is contained in:
@@ -25,8 +25,8 @@ pub struct App {
|
|||||||
impl App {
|
impl App {
|
||||||
/// Initializes SDL subsystems, creates the game window, and sets up the game state.
|
/// Initializes SDL subsystems, creates the game window, and sets up the game state.
|
||||||
///
|
///
|
||||||
/// Performs comprehensive initialization including video/audio subsystems, platform-specific
|
/// Performs comprehensive initialization including video/audio subsystems,
|
||||||
/// console setup, window creation with proper scaling, and canvas configuration. All SDL
|
/// window creation with proper scaling, and canvas configuration. All SDL
|
||||||
/// resources are leaked to maintain 'static lifetimes required by the game architecture.
|
/// resources are leaked to maintain 'static lifetimes required by the game architecture.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@@ -44,9 +44,6 @@ impl App {
|
|||||||
let event_pump: &'static mut EventPump =
|
let event_pump: &'static mut EventPump =
|
||||||
Box::leak(Box::new(sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?));
|
Box::leak(Box::new(sdl_context.event_pump().map_err(|e| GameError::Sdl(e.to_string()))?));
|
||||||
|
|
||||||
// Initialize platform-specific console
|
|
||||||
get_platform().init_console()?;
|
|
||||||
|
|
||||||
let window = video_subsystem
|
let window = video_subsystem
|
||||||
.window(
|
.window(
|
||||||
"Pac-Man",
|
"Pac-Man",
|
||||||
|
|||||||
24
src/main.rs
24
src/main.rs
@@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
use crate::{app::App, constants::LOOP_TIME};
|
use crate::{app::App, constants::LOOP_TIME};
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
use tracing_error::ErrorLayer;
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod asset;
|
mod asset;
|
||||||
@@ -23,14 +21,22 @@ mod texture;
|
|||||||
/// This function initializes SDL, the window, the game state, and then enters
|
/// This function initializes SDL, the window, the game state, and then enters
|
||||||
/// the main game loop.
|
/// the main game loop.
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
// Setup tracing
|
// Setup buffered tracing subscriber that will buffer logs until console is ready
|
||||||
let subscriber = tracing_subscriber::fmt()
|
let switchable_writer = platform::tracing_buffer::setup_switchable_subscriber();
|
||||||
.with_ansi(cfg!(not(target_os = "emscripten")))
|
|
||||||
.with_max_level(tracing::Level::DEBUG)
|
|
||||||
.finish()
|
|
||||||
.with(ErrorLayer::default());
|
|
||||||
|
|
||||||
tracing::subscriber::set_global_default(subscriber).expect("Could not set global default");
|
// Log early to show buffering is working
|
||||||
|
tracing::debug!("Tracing subscriber initialized with buffering - logs will be buffered until console is ready");
|
||||||
|
|
||||||
|
// Initialize platform-specific console
|
||||||
|
tracing::debug!("Starting console initialization...");
|
||||||
|
platform::get_platform().init_console().expect("Could not initialize console");
|
||||||
|
tracing::debug!("Console initialization completed");
|
||||||
|
|
||||||
|
// Now that console is initialized, flush buffered logs and switch to direct output
|
||||||
|
tracing::debug!("Switching to direct logging mode and flushing buffer...");
|
||||||
|
if let Err(e) = switchable_writer.switch_to_direct_mode() {
|
||||||
|
tracing::warn!("Failed to flush buffered logs to console: {}", e);
|
||||||
|
}
|
||||||
|
|
||||||
let mut app = App::new().expect("Could not create app");
|
let mut app = App::new().expect("Could not create app");
|
||||||
|
|
||||||
|
|||||||
55
src/platform/buffered_writer.rs
Normal file
55
src/platform/buffered_writer.rs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
//! Buffered writer for tracing logs that can store logs before console attachment.
|
||||||
|
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
/// A thread-safe buffered writer that stores logs in memory until flushed.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct BufferedWriter {
|
||||||
|
buffer: Arc<Mutex<Vec<u8>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BufferedWriter {
|
||||||
|
/// Creates a new buffered writer.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
buffer: Arc::new(Mutex::new(Vec::new())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Flushes all buffered content to the provided writer and clears the buffer.
|
||||||
|
pub fn flush_to<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
let mut buffer = self.buffer.lock();
|
||||||
|
if !buffer.is_empty() {
|
||||||
|
writer.write_all(&buffer)?;
|
||||||
|
writer.flush()?;
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the current buffer size in bytes.
|
||||||
|
pub fn buffer_size(&self) -> usize {
|
||||||
|
self.buffer.lock().len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for BufferedWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
let mut buffer = self.buffer.lock();
|
||||||
|
buffer.extend_from_slice(buf);
|
||||||
|
Ok(buf.len())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
// For buffered writer, flush is a no-op since we're storing in memory
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for BufferedWriter {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,8 @@ mod desktop;
|
|||||||
#[cfg(target_os = "emscripten")]
|
#[cfg(target_os = "emscripten")]
|
||||||
mod emscripten;
|
mod emscripten;
|
||||||
|
|
||||||
|
pub mod buffered_writer;
|
||||||
|
pub mod tracing_buffer;
|
||||||
/// Cross-platform abstraction layer providing unified APIs for platform-specific operations.
|
/// Cross-platform abstraction layer providing unified APIs for platform-specific operations.
|
||||||
pub trait CommonPlatform {
|
pub trait CommonPlatform {
|
||||||
/// Platform-specific sleep function (required due to Emscripten's non-standard sleep requirements).
|
/// Platform-specific sleep function (required due to Emscripten's non-standard sleep requirements).
|
||||||
|
|||||||
91
src/platform/tracing_buffer.rs
Normal file
91
src/platform/tracing_buffer.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
//! Buffered tracing setup for handling logs before console attachment.
|
||||||
|
|
||||||
|
use crate::platform::buffered_writer::BufferedWriter;
|
||||||
|
use std::io;
|
||||||
|
use tracing::Level;
|
||||||
|
use tracing_error::ErrorLayer;
|
||||||
|
use tracing_subscriber::fmt::MakeWriter;
|
||||||
|
use tracing_subscriber::layer::SubscriberExt;
|
||||||
|
|
||||||
|
/// A writer that can switch between buffering and direct output.
|
||||||
|
#[derive(Clone, Default)]
|
||||||
|
pub struct SwitchableWriter {
|
||||||
|
buffered_writer: BufferedWriter,
|
||||||
|
direct_mode: std::sync::Arc<parking_lot::Mutex<bool>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwitchableWriter {
|
||||||
|
pub fn switch_to_direct_mode(&self) -> io::Result<()> {
|
||||||
|
// Get buffer size before flushing for debug logging
|
||||||
|
let buffer_size = self.buffered_writer.buffer_size();
|
||||||
|
|
||||||
|
// First flush any buffered content
|
||||||
|
self.buffered_writer.flush_to(io::stdout())?;
|
||||||
|
|
||||||
|
// Switch to direct mode
|
||||||
|
*self.direct_mode.lock() = true;
|
||||||
|
|
||||||
|
// Log how much was buffered (this will now go directly to stdout)
|
||||||
|
tracing::debug!("Flushed {} bytes of buffered logs to console", buffer_size);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl io::Write for SwitchableWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
if *self.direct_mode.lock() {
|
||||||
|
io::stdout().write(buf)
|
||||||
|
} else {
|
||||||
|
self.buffered_writer.clone().write(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
if *self.direct_mode.lock() {
|
||||||
|
io::stdout().flush()
|
||||||
|
} else {
|
||||||
|
// For buffered mode, flush is a no-op
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A make writer that uses the switchable writer.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SwitchableMakeWriter {
|
||||||
|
writer: SwitchableWriter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SwitchableMakeWriter {
|
||||||
|
pub fn new(writer: SwitchableWriter) -> Self {
|
||||||
|
Self { writer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MakeWriter<'a> for SwitchableMakeWriter {
|
||||||
|
type Writer = SwitchableWriter;
|
||||||
|
|
||||||
|
fn make_writer(&'a self) -> Self::Writer {
|
||||||
|
self.writer.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up a switchable tracing subscriber that can transition from buffered to direct output.
|
||||||
|
///
|
||||||
|
/// Returns the switchable writer that can be used to control the behavior.
|
||||||
|
pub fn setup_switchable_subscriber() -> SwitchableWriter {
|
||||||
|
let switchable_writer = SwitchableWriter::default();
|
||||||
|
let make_writer = SwitchableMakeWriter::new(switchable_writer.clone());
|
||||||
|
|
||||||
|
let _subscriber = tracing_subscriber::fmt()
|
||||||
|
.with_ansi(cfg!(not(target_os = "emscripten")))
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.with_writer(make_writer)
|
||||||
|
.finish()
|
||||||
|
.with(ErrorLayer::default());
|
||||||
|
|
||||||
|
tracing::subscriber::set_global_default(_subscriber).expect("Could not set global default switchable subscriber");
|
||||||
|
|
||||||
|
switchable_writer
|
||||||
|
}
|
||||||
19
tests/tracing_buffer.rs
Normal file
19
tests/tracing_buffer.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use pacman::platform::tracing_buffer::SwitchableWriter;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_switchable_writer_buffering() {
|
||||||
|
let mut writer = SwitchableWriter::default();
|
||||||
|
|
||||||
|
// Write some data while in buffered mode
|
||||||
|
writer.write_all(b"Hello, ").unwrap();
|
||||||
|
writer.write_all(b"world!").unwrap();
|
||||||
|
writer.write_all(b"This is buffered content.\n").unwrap();
|
||||||
|
|
||||||
|
// Switch to direct mode (this should flush to stdout and show buffer size)
|
||||||
|
// In a real test we can't easily capture stdout, so we'll just verify it doesn't panic
|
||||||
|
writer.switch_to_direct_mode().unwrap();
|
||||||
|
|
||||||
|
// Write more data in direct mode
|
||||||
|
writer.write_all(b"Direct output after flush\n").unwrap();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user