diff --git a/crates/bevy_log/Cargo.toml b/crates/bevy_log/Cargo.toml index 5f59ce11454f8d..1a5a7f7c967d49 100644 --- a/crates/bevy_log/Cargo.toml +++ b/crates/bevy_log/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" keywords = ["bevy"] [features] -trace = [ "tracing-error" ] +trace = ["tracing-error"] [dependencies] bevy_app = { path = "../bevy_app", version = "0.9.0" } @@ -21,6 +21,7 @@ tracing-chrome = { version = "0.7.0", optional = true } tracing-tracy = { version = "0.10.0", optional = true } tracing-log = "0.1.2" tracing-error = { version = "0.2.0", optional = true } +tracing-appender = { version = "0.2.2" } [target.'cfg(target_os = "android")'.dependencies] android_log-sys = "0.2.0" diff --git a/crates/bevy_log/src/lib.rs b/crates/bevy_log/src/lib.rs index 7265f4fd621707..72cf3e387e9b75 100644 --- a/crates/bevy_log/src/lib.rs +++ b/crates/bevy_log/src/lib.rs @@ -14,6 +14,8 @@ #[cfg(feature = "trace")] use std::panic; +use std::path::PathBuf; + #[cfg(target_os = "android")] mod android_tracing; @@ -25,6 +27,7 @@ pub mod prelude { }; } +use bevy_ecs::system::Resource; pub use bevy_utils::tracing::{ debug, debug_span, error, error_span, info, info_span, trace, trace_span, warn, warn_span, Level, @@ -91,6 +94,9 @@ 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, + + /// Configure file Logging + pub file_appender_settings: Option, } impl Default for LogPlugin { @@ -98,6 +104,60 @@ impl Default for LogPlugin { Self { filter: "wgpu=error".to_string(), level: Level::INFO, + file_appender_settings: None, + } + } +} + +/// Enum to control how often a new log file will be created +#[derive(Debug, Clone, Copy)] +pub enum Rolling { + /// Creates a new file every minute and appends the date to the file name + /// Date format: YYYY-MM-DD-HH-mm + Minutely, + /// Creates a new file every hour and appends the date to the file name + /// Date format: YYYY-MM-DD-HH + Hourly, + /// Creates a new file every day and appends the date to the file name + /// Date format: YYYY-MM-DD + Daily, + /// Never creates a new file + Never, +} + +impl Into for Rolling { + fn into(self) -> tracing_appender::rolling::Rotation { + match self { + Rolling::Minutely => tracing_appender::rolling::Rotation::MINUTELY, + Rolling::Hourly => tracing_appender::rolling::Rotation::HOURLY, + Rolling::Daily => tracing_appender::rolling::Rotation::DAILY, + Rolling::Never => tracing_appender::rolling::Rotation::NEVER, + } + } +} + +#[derive(Resource)] +struct FileAppenderWorkerGuard(tracing_appender::non_blocking::WorkerGuard); + +/// Settings to control how to log to a file +#[derive(Debug, Clone)] +pub struct FileAppenderSettings { + /// Controls how often a new file will be created + pub rolling: Rolling, + /// The path of the directory where the log files will be added + /// + /// Defaults to the local directory + pub path: PathBuf, + /// The prefix added when creating a file + pub prefix: String, +} + +impl Default for FileAppenderSettings { + fn default() -> Self { + Self { + rolling: Rolling::Daily, + path: PathBuf::from("."), + prefix: String::from("log"), } } } @@ -165,6 +225,28 @@ impl Plugin for LogPlugin { let subscriber = subscriber.with(fmt_layer); + let file_appender_layer = if let Some(file_output) = &self.file_appender_settings { + let file_appender = tracing_appender::rolling::RollingFileAppender::new( + file_output.rolling.into(), + &file_output.path, + &file_output.prefix, + ); + + let (non_blocking, worker_guard) = tracing_appender::non_blocking(file_appender); + // WARN We need to keep this somewhere so it doesn't get dropped. + // If it gets dropped then it will silently stop writing to the file + app.insert_resource(FileAppenderWorkerGuard(worker_guard)); + + let file_fmt_layer = tracing_subscriber::fmt::Layer::default() + .with_ansi(false) + .with_writer(non_blocking); + + Some(file_fmt_layer) + } else { + None + }; + let subscriber = subscriber.with(file_appender_layer); + #[cfg(feature = "tracing-chrome")] let subscriber = subscriber.with(chrome_layer); #[cfg(feature = "tracing-tracy")] diff --git a/examples/app/logs.rs b/examples/app/logs.rs index d90b76bd3cefd6..0aec41270ec41c 100644 --- a/examples/app/logs.rs +++ b/examples/app/logs.rs @@ -1,6 +1,6 @@ //! This example illustrates how to use logs in bevy. -use bevy::prelude::*; +use bevy::{log::FileAppenderSettings, prelude::*}; fn main() { App::new() @@ -8,6 +8,8 @@ fn main() { // Uncomment this to override the default log settings: // level: bevy::log::Level::TRACE, // filter: "wgpu=warn,bevy_ecs=info".to_string(), + // This will let you configure file logging + // file_appender_settings: Some(FileAppenderSettings::default()), ..default() })) .add_system(log_system)